134 std::string
sanitise (std::string
const&);
153 using std::invoke_result_t;
154 using std::is_constructible;
155 using std::make_from_tuple;
156 using std::tuple_cat;
168 const string threadID_;
169 std::thread threadImpl_;
171 bool isLive()
const {
return threadImpl_.joinable(); }
176 : threadID_{util::BOTTOM_INDICATOR}
181 : threadID_{isnil(threadID)?
"sub-thread" : util::sanitise (threadID)}
199 template<
class...INVO>
203 ASSERT (not isLive(),
"Thread already running");
204 threadImpl_ = make_from_tuple<std::thread> (invocation);
209 bool invokedWithinThread()
const;
211 void markThreadStart();
212 void markThreadEnd ();
213 void setThreadName ();
214 void waitGracePeriod() noexcept;
221 static string decorate_with_global_count (
string const&);
234 threadImpl_.detach();
247 template<
class BAS,
typename=
void>
253 template<
class FUN,
typename...ARGS>
255 perform_thread_function(FUN&& callable, ARGS&& ...args)
259 std::invoke (forward<FUN> (callable), forward<ARGS> (args)...);
265 handle_after_thread()
267 BAS::detach_thread_from_wrapper();
271 handle_loose_thread()
273 BAS::waitGracePeriod();
284 template<
class BAS,
class TAR>
289 using BasePol::BasePol;
292 using Hook = function<void(Self&)>;
294 Hook hook_beginThread{};
295 Hook hook_afterThread{};
296 Hook hook_looseThread{};
299 handle_begin_thread()
301 if (hook_beginThread)
302 hook_beginThread (*
this);
304 BasePol::handle_begin_thread();
308 handle_after_thread()
310 if (hook_afterThread)
311 hook_afterThread (*
this);
313 BAS::detach_thread_from_wrapper();
317 handle_loose_thread()
319 if (hook_looseThread)
320 hook_looseThread (*
this);
322 BasePol::handle_loose_thread();
335 template<
class BAS,
typename RES>
345 template<
class FUN,
typename...ARGS>
347 perform_thread_function(FUN&& callable, ARGS&& ...args)
349 static_assert (__or_<is_same<RES,void>
350 ,is_constructible<RES, invoke_result_t<FUN,ARGS...>>>());
353 result_ = std::move (
355 ,forward<ARGS>(args)...});
359 handle_after_thread()
365 handle_loose_thread()
367 ALERT (thread,
"Thread '%s' was not joined. Abort.", BAS::threadID_.c_str());
376 template<
template<
class,
class>
class POL,
typename RES =
void>
378 :
protected POL<ThreadWrapper, RES>
380 using Policy = POL<ThreadWrapper,RES>;
382 template<
typename...ARGS>
384 invokeThreadFunction (ARGS&& ...args)
386 while (not Policy::isLive())
387 std::this_thread::yield();
388 Policy::handle_begin_thread();
389 Policy::markThreadStart();
390 Policy::perform_thread_function (forward<ARGS> (args)...);
391 Policy::markThreadEnd();
392 Policy::handle_after_thread();
399 if (Policy::isLive())
400 Policy::handle_loose_thread();
413 template<
class W,
class...INVO>
417 return tuple_cat (tuple{&ThreadLifecycle::invokeThreadFunction<INVO...>
435 template<
class...INVO>
439 tuple<decay_t<INVO>...> argCopy{forward<INVO> (args)...};
440 return [invocation = move(argCopy)]
443 auto boundInvocation = lateBindInstance (wrapper, move (invocation));
444 wrapper.launchThread (buildInvocation (wrapper, move(boundInvocation)));
458 using Act = function<void(ThreadLifecycle&)>;
463 template<
class FUN,
typename...ARGS>
464 Launch (FUN&& threadFunction, ARGS&& ...args)
465 : launch{buildLauncher (forward<FUN>(threadFunction), forward<ARGS>(args)...)}
468 template<
class TAR,
typename...ARGS>
469 Launch (RES (TAR::*memFun) (ARGS...), ARGS ...args)
472 ,forward<ARGS> (args)... }
476 threadID (
string const& threadID)
485 id = Policy::decorate_with_global_count (
id);
489 template<
typename HOOK>
491 atStart (HOOK&& hook)
493 return addHook (&Policy::hook_beginThread, forward<HOOK> (hook));
496 template<
typename HOOK>
500 return addHook (&Policy::hook_afterThread, forward<HOOK> (hook));
503 template<
typename HOOK>
505 onOrphan (HOOK&& hook)
507 return addHook (&Policy::hook_looseThread, forward<HOOK> (hook));
519 template<
typename HOOK,
class FUN>
529 return [hook = forward<HOOK>(hook)](Arg){ hook(); };
533 return [hook = forward<HOOK>(hook)]
537 Target& target =
static_cast<Target&
> (base);
544 template<
typename HOOK,
class FUN>
546 addHook (FUN Policy::*storedHook, HOOK&& hook)
548 return addLayer ([storedHook, hook = adaptedHook (storedHook, forward<HOOK> (hook))]
551 wrapper.*storedHook = move (hook);
559 launch = [action=move(action), chain=move(launch)]
578 launcher.launch (*
this);
590 template<
class FUN,
typename...ARGS>
593 Launch{forward<FUN> (threadFunction), forward<ARGS> (args)...}
601 template<
class SUB,
typename...ARGS>
604 Launch{std::move (memFun)
605 ,
static_cast<SUB*
> (
this)
606 ,forward<ARGS> (args)...
608 .threadID(util::joinDash (typeSymbol<SUB>(), args...))}
620 operator bool()
const 622 return Policy::isLive();
626 using Policy::invokedWithinThread;
652 using ThreadLifecycle::ThreadLifecycle;
667 template<
typename RES =
void>
687 if (not Impl::threadImpl_.joinable())
690 Impl::threadImpl_.join();
692 return Impl::result_;
697 template<
typename FUN,
typename...ARGS>
720 using ThreadLifecycle::ThreadLifecycle;
740 template<
class TAR = ThreadHookable>
746 new TAR{move(launchBuilder)
747 .atExit([](TAR& selfAllocation)
749 delete &selfAllocation;
767 using Launch =
typename TAR::Launch;
768 launchDetached<TAR> (Launch{forward<INVO> (args)...}
769 .threadID (threadID));
773 template<
class TAR,
typename...ARGS>
775 launchDetached (
string const& threadID,
void (TAR::*memFun) (ARGS...), ARGS ...args)
777 using Launch =
typename TAR::Launch;
778 launchDetached<TAR> (Launch{std::move (memFun)
780 ,forward<ARGS> (args)...
782 .threadID (threadID));
786 template<
class TAR,
typename...ARGS>
790 launchDetached (util::joinDash (lib::meta::typeSymbol<TAR>(), args...)
792 ,forward<ARGS> (args)...
Launch && addLayer(Act action)
generic helper to add another »onion layer« to this config builder
Variant of the standard case, requiring to wait and join() on the termination of this thread...
void launchThread(tuple< INVO... > &&invocation)
constexpr auto lateBindInstance(W &instance, TUP &&invocation)
Fix-up the arguments for a member-function invocation, allowing to inject the actual this instance in...
void handle_after_thread()
called immediately before end of thread
Representation of the result of some operation, EITHER a value or a failure.
Launch && addHook(FUN Policy::*storedHook, HOOK &&hook)
add a config layer to store a user-provided functor into the polic baseclass(es)
#define ERROR_LOG_AND_IGNORE(_FLAG_, _OP_DESCR_)
convenience shortcut for a sequence of catch blocks just logging and consuming an error...
static auto buildInvocation(W &wrapper, tuple< INVO... > &&invocation)
Build the invocation tuple, using #invokeThreadFunction to delegate to the user-provided functor and ...
Launch(RES(TAR::*memFun)(ARGS...), ARGS ...args)
Types marked with this mix-in may be moved but not copied.
ThreadLifecycle(RES(SUB::*memFun)(ARGS...), ARGS ...args)
Special variant to bind a subclass member function as thread operation.
std::string sanitise(std::string const &)
produce an identifier based on the given string.
Intermediary value object to represent »either« an operation result or a failure. ...
This header is for including and configuring NoBug.
static auto buildLauncher(INVO &&...args)
Build a λ actually to launch the given thread operation later, after the thread-wrapper-object is ful...
Implementation namespace for support and library code.
ThreadLifecycle()
derived classes may create a disabled thread
ThreadLifecycle(string const &threadID, FUN &&threadFunction, ARGS &&...args)
Create a new thread to execute the given operation.
ThreadJoinable(string const &, FUN &&, ARGS &&...) -> ThreadJoinable< std::invoke_result_t< FUN, ARGS... >>
deduction guide: find out about result value to capture from a generic callable.
Derived specific exceptions within Lumiera's exception hierarchy.
Mix-Ins to allow or prohibit various degrees of copying and cloning.
Metaprogramming tools for transforming functor types.
Configuration builder to define the operation running within the thread, and possibly configure furth...
lib::Result< RES > join()
put the caller into a blocking wait until this thread has terminated
Thread Lifecycle Policy Extension: invoke user-provided callbacks from within thread lifecycle...
Helpers for type detection, type rewriting and metaprogramming.
Lumiera error handling (C++ interface).
string typeSymbol()
Short readable type identifier, not necessarily unique or complete.
auto adaptedHook(FUN Policy::*, HOOK &&hook)
Helper to adapt a user provided hook to be usable as lifecycle hook.
void handle_begin_thread()
called immediately at start of thread
Extended variant of the standard case, allowing to install callbacks (hook functions) to be invoked d...
void launchDetached(void(TAR::*memFun)(ARGS...), ARGS ...args)
Special variant without explicitly given thread-ID.
A thin convenience wrapper to simplify thread-handling.
void handle_loose_thread()
called when destroying wrapper on still running thread
Policy-based configuration of thread lifecycle.
ThreadLifecycle(Launch launcher)
Primary constructor: Launch the new thread with flexible configuration.
void detach_thread_from_wrapper()
allow to detach explicitly — independent from thread-function's state.