/* LocalSlice(Test) - verify access to thread-local services Copyright (C) 2026, Hermann Vosseler   **Lumiera** is free software; you can redistribute it and/or modify it   under the terms of the GNU General Public License as published by the   Free Software Foundation; either version 2 of the License, or (at your   option) any later version. See the file COPYING for further details. * *****************************************************************/ /** @file local-slice-test.cpp ** unit test \ref LocalSlice_test */ #include "test/run.hpp" #include "test/test-helper.hpp" #include "lib/scoped-collection.hpp" #include "test/microbenchmark.hpp" #include "lib/iter-explorer.hpp" #include "lib/format-cout.hpp" #include "lib/thread.hpp" #include "lib/local-slice.hpp" using lib::Thread; using lib::explore; using test::threadBenchmark; using std::this_thread::yield; using std::this_thread::sleep_for; using std::chrono_literals::operator ""us; namespace lib { namespace test { namespace { // test fixture... const uint NUM_THREADS = 200; constexpr size_t CONCURR_BENCH = 4; const size_t REPETITIONS_BENCH = 1'000'000; /** * the "service" to be instantiated thread-local */ class DummyBackend { uint step{0}; public: uint getStep() { return ++step; } }; using DummySlice = LocalSlice; /** * A common shared service instance * that dispatches through LocalSlice * into the thread-local service back-ends */ class DummyFrontend { DummySlice dummySrv_; public: uint invoke() { return dummySrv_->getStep() + dummySrv_->getStep() + dummySrv_->getStep() + dummySrv_->getStep() ; } }; // } //(End) test fixture /***********************************************************************//** * @test demonstrate access to thread-local service instances. * - setup a common front-end service instance * - its functionality is implemented by delegating * through the LocalSlice into the thread-local back-ends * - launch threads and invoke the front-end concurrently * @see local-slice.hpp */ class LocalSlice_test : public Test { virtual void run (Arg) { simpleUsage(); investigatePerformance(); } /** @test access thread-local service back-ends * through a shared front-end instance; * verify that each thread invokes a * separate instance without interference * @see ThreadWrapper_test */ void simpleUsage () { // Σi ≡ N/2·(N+1) auto expectSum = [](uint n){ return n/2 * (n+1); }; // the front-end invokes the back-end four times in row... uint i1 = expectSum(4); uint i2 = expectSum(8) - expectSum(4); uint i3 = expectSum(12) - expectSum(8); // Note: all threads use this common service front-end instance DummyFrontend myDummy; // Now invoke through this front-end CHECK (i1 == myDummy.invoke()); CHECK (i2 == myDummy.invoke()); // launch a significant number of threads, // each verifying the same invocation sequence lib::ScopedCollection threads{NUM_THREADS}; for (uint i=1; i<=NUM_THREADS; ++i) threads.emplace ([&]{ CHECK (i1 == myDummy.invoke()); sleep_for (100us); CHECK (i2 == myDummy.invoke()); sleep_for (100us); CHECK (i3 == myDummy.invoke()); }); while (explore(threads).has_any()) yield(); // wait for all threads to finish // ...without any interference with the main thread's instance CHECK (i3 == myDummy.invoke()); } /** @test investigate performance of thread-local dispatch * - subject-1 accesses the increment through the common thread-local dispatch * - subject-2 increments just a local variable in each thread * - run a multithreaded benchmark with 1 million repetitions * - but only small number of threads to avoid OS scheduler congestion * @see test::threadBenchmark() */ void investigatePerformance() { DummySlice threadLocalBackend; auto testThreadLocal = [&]{ auto volatile result = threadLocalBackend->getStep(); return result; }; auto testLocalInc = [cnt=0] mutable { auto volatile result = ++cnt; return result; }; cout << "µ-benchmark :: thread-local" < (testThreadLocal, REPETITIONS_BENCH); cout << "thread-local dispatch: "< (testLocalInc, REPETITIONS_BENCH); cout << "increment local var : "< t2); } }; /** Register this test class... */ LAUNCHER(LocalSlice_test, "unit common"); }} // namespace lib::test