diff --git a/src/lib/meta/function.hpp b/src/lib/meta/function.hpp index 4f5617601..13ae3d097 100644 --- a/src/lib/meta/function.hpp +++ b/src/lib/meta/function.hpp @@ -128,6 +128,7 @@ namespace meta{ using Args = Types; using Sig = RET(ARGS...); using Functor = std::function; + enum { ARITY = sizeof...(ARGS) }; }; /** Specialisation to strip `noexcept` from the signature */ diff --git a/src/lib/thread.hpp b/src/lib/thread.hpp index 70e4ee91f..d66b1a2d6 100644 --- a/src/lib/thread.hpp +++ b/src/lib/thread.hpp @@ -124,6 +124,7 @@ #include "lib/nocopy.hpp" #include "include/logging.h" #include "lib/meta/trait.hpp" +#include "lib/meta/function.hpp" #include "lib/format-util.hpp" #include "lib/result.hpp" @@ -146,6 +147,7 @@ namespace lib { namespace thread {// Thread-wrapper base implementation... using lib::meta::typeSymbol; + using lib::meta::_Fun; using std::function; using std::forward; using std::move; @@ -302,24 +304,18 @@ namespace lib { using BasePol = PolicyLaunchOnly; using BasePol::BasePol; - using Hook = function; + using Self = PolicyLifecycleHook; + using Hook = function; Hook hook_beginThread{}; Hook hook_afterThread{}; Hook hook_looseThread{}; - TAR& - castInstance() - { - return static_cast( - static_cast (this)); - } - void handle_begin_thread() { if (hook_beginThread) - hook_beginThread (castInstance()); + hook_beginThread (*this); else BasePol::handle_begin_thread(); } @@ -328,16 +324,16 @@ namespace lib { handle_after_thread() { if (hook_afterThread) - hook_afterThread (castInstance()); - else - BasePol::handle_after_thread(); + hook_afterThread (*this); + if (BAS::isLive()) // Note: ensure thread is detached at end + BAS::threadImpl_.detach(); } void handle_loose_thread() { if (hook_looseThread) - hook_looseThread (castInstance()); + hook_looseThread (*this); else BasePol::handle_loose_thread(); } @@ -486,7 +482,7 @@ namespace lib { template Launch&& - atEnd (HOOK&& hook) + atExit (HOOK&& hook) { return addHook (&Policy::hook_afterThread, forward (hook)); } @@ -503,11 +499,29 @@ namespace lib { Launch&& addHook (FUN Policy::*storedHook, HOOK&& hook) { - return addLayer ([storedHook, hook = forward(hook)] + static_assert(1 == _Fun::ARITY); + static_assert(1 >= _Fun::ARITY); + using Arg = typename _Fun::Args::List::Head; + FUN adapted; + if constexpr (0 == _Fun::ARITY) + { + adapted = [hook = forward(hook)](Arg){ hook(); }; + } + else + { + using Target = typename _Fun::Args::List::Head; + adapted = [hook = forward(hook)] + (Arg& threadWrapper) + { + ThreadLifecycle& base = static_cast (threadWrapper); + Target& target = static_cast (base); + hook (target); + }; + } + return addLayer ([storedHook, hook = move(adapted)] (ThreadLifecycle& wrapper) { - wrapper.*storedHook = move (hook); - chain (wrapper); + wrapper.*storedHook = move(hook); }); } @@ -665,6 +679,29 @@ namespace lib { + /************************************************************************//** + * Extended variant of the [standard case](\ref Thread), allowing to install + * callbacks (hook functions) to be invoked during thread lifecycle: + * - `atStart` : invoked as first user code in the new thread + * - `atExit` : invoked as the last user code prior to detaching and thread end + * - `onOrphan` : invoked from the thread-wrapper destructor, when the actual + * thread is detected as still running (according to the thread handle) + * By default, these callbacks are empty; custom callbacks can be installed + * through the ThreadLifecycle::Launch configuration builder using the + * corresponding builder functions (e.g. `.atExit(λ)`). The passed functor + * can either take no argument, or a single argument with a reference to + * some `*this` subtype, which must be reachable by static downcast from + * the ThreadLifecycle base type. + */ + class ThreadHookable + : public thread::ThreadLifecycle + { + public: + using ThreadLifecycle::ThreadLifecycle; + }; + + + /************************************************************************//** * Special configuration for a »fire-and-forget«-Thread. * @internal this class is meant for subclassing. Start with #launchDetached() diff --git a/tests/library/thread-wrapper-lifecycle-test.cpp b/tests/library/thread-wrapper-lifecycle-test.cpp index 6e16b5be5..b6be52b3e 100644 --- a/tests/library/thread-wrapper-lifecycle-test.cpp +++ b/tests/library/thread-wrapper-lifecycle-test.cpp @@ -40,7 +40,7 @@ using lib::explore; using std::atomic_uint; using std::this_thread::yield; using std::this_thread::sleep_for; -using std::chrono::microseconds; +using namespace std::chrono_literals; using std::chrono::system_clock; @@ -69,7 +69,8 @@ namespace test{ run (Arg) { defaultWrapperLifecycle(); - verifyExplicitLifecycleState(); + verifyThreadLifecycleHooks(); + demonstrateExplicitThreadLifecycle(); } @@ -90,14 +91,6 @@ namespace test{ double offset = Dur{threadStart - afterCtor}.count(); SHOW_EXPR(offset) CHECK (offset > 0); - - Thread murks{Thread::Launch([&](uint scope) - { - cout << "Hello nested world "< threads{NUM_THREADS}; - - size_t checkSum = 0; - size_t globalSum = 0; - auto launchThreads = [&] - { - for (uint i=1; i<=NUM_THREADS; ++i) - { - uint x = rand() % 1000; - globalSum += (i + x); - threads.emplace (&TestThread::doIt, i, x); - } // Note: bind to member function, copying arguments - - while (explore(threads).has_any()) - yield(); // wait for all threads to have detached - - for (auto& t : threads) - { - CHECK (0 < t.local); - checkSum += t.local; - } - }; - - double runTime = benchmarkTime (launchThreads, REPETITIONS); - - CHECK (checkSum == globalSum); // sum of precomputed random numbers matches sum from threads - CHECK (runTime < NUM_THREADS * 1000/2); // random sleep time should be > 500ms on average } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 056657e52..6d5ed6361 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -65242,7 +65242,7 @@ - + @@ -65262,6 +65262,10 @@ + + + + @@ -65386,7 +65390,8 @@ - + + @@ -65395,7 +65400,7 @@ - + @@ -65403,6 +65408,37 @@ + + + + + + + + + + + + +

+ ganz elegant erst mal auf TheadLifecycle downcasten +

+ +
+ + + + + +

+ was „rein zufällig“ hier in diesem nested scope in ThreadLifecycle erlaubt und auch sicher ist, denn hier haben wir noch Zugriff auf die protected geerbten Policy-Klassen... +

+ +
+
+ + + @@ -66022,9 +66058,12 @@ - + + + +