142 std::string
sanitise (std::string
const&);
161 using std::invoke_result_t;
162 using std::is_constructible;
163 using std::make_from_tuple;
164 using std::tuple_cat;
176 const string threadID_;
177 std::thread threadImpl_;
179 bool isLive()
const {
return threadImpl_.joinable(); }
184 : threadID_{util::BOTTOM_INDICATOR}
189 : threadID_{isnil(threadID)?
"sub-thread" : util::sanitise (threadID)}
207 template<
class...INVO>
211 ASSERT (not isLive(),
"Thread already running");
212 threadImpl_ = make_from_tuple<std::thread> (invocation);
217 bool invokedWithinThread()
const;
219 void markThreadStart();
220 void markThreadEnd ();
221 void setThreadName ();
222 void waitGracePeriod() noexcept;
229 static string decorate_with_global_count (
string const&);
242 threadImpl_.detach();
255 template<
class BAS,
typename=
void>
261 template<
class FUN,
typename...ARGS>
263 perform_thread_function(FUN&& callable, ARGS&& ...args)
267 std::invoke (forward<FUN> (callable), forward<ARGS> (args)...);
273 handle_after_thread()
275 BAS::detach_thread_from_wrapper();
279 handle_loose_thread()
281 BAS::waitGracePeriod();
292 template<
class BAS,
class TAR>
297 using BasePol::BasePol;
300 using Hook = function<void(Self&)>;
302 Hook hook_beginThread{};
303 Hook hook_afterThread{};
304 Hook hook_looseThread{};
307 handle_begin_thread()
309 if (hook_beginThread)
310 hook_beginThread (*
this);
312 BasePol::handle_begin_thread();
316 handle_after_thread()
318 if (hook_afterThread)
319 hook_afterThread (*
this);
321 BAS::detach_thread_from_wrapper();
325 handle_loose_thread()
327 if (hook_looseThread)
328 hook_looseThread (*
this);
330 BasePol::handle_loose_thread();
343 template<
class BAS,
typename RES>
353 template<
class FUN,
typename...ARGS>
355 perform_thread_function(FUN&& callable, ARGS&& ...args)
357 static_assert (__or_<is_same<RES,void>
358 ,is_constructible<RES, invoke_result_t<FUN,ARGS...>>>());
361 result_ = std::move (
363 ,forward<ARGS>(args)...});
367 handle_after_thread()
373 handle_loose_thread()
375 ALERT (thread,
"Thread '%s' was not joined. Abort.", BAS::threadID_.c_str());
384 template<
template<
class,
class>
class POL,
typename RES =
void>
386 :
protected POL<ThreadWrapper, RES>
388 using Policy = POL<ThreadWrapper,RES>;
390 template<
typename...ARGS>
392 invokeThreadFunction (ARGS&& ...args)
394 while (not Policy::isLive())
395 std::this_thread::yield();
396 Policy::handle_begin_thread();
397 Policy::markThreadStart();
398 Policy::perform_thread_function (forward<ARGS> (args)...);
399 Policy::markThreadEnd();
400 Policy::handle_after_thread();
407 if (Policy::isLive())
408 Policy::handle_loose_thread();
421 template<
class W,
class...INVO>
425 return tuple_cat (tuple{&ThreadLifecycle::invokeThreadFunction<INVO...>
443 template<
class...INVO>
447 tuple<decay_t<INVO>...> argCopy{forward<INVO> (args)...};
448 return [invocation = move(argCopy)]
451 auto boundInvocation = lateBindInstance (wrapper, move (invocation));
452 wrapper.launchThread (buildInvocation (wrapper, move(boundInvocation)));
466 using Act = function<void(ThreadLifecycle&)>;
471 template<
class FUN,
typename...ARGS>
472 Launch (FUN&& threadFunction, ARGS&& ...args)
473 : launch{buildLauncher (forward<FUN>(threadFunction), forward<ARGS>(args)...)}
476 template<
class TAR,
typename...ARGS>
477 Launch (RES (TAR::*memFun) (ARGS...), ARGS ...args)
480 ,forward<ARGS> (args)... }
484 threadID (
string const& threadID)
493 id = Policy::decorate_with_global_count (
id);
497 template<
typename HOOK>
499 atStart (HOOK&& hook)
501 return addHook (&Policy::hook_beginThread, forward<HOOK> (hook));
504 template<
typename HOOK>
508 return addHook (&Policy::hook_afterThread, forward<HOOK> (hook));
511 template<
typename HOOK>
513 onOrphan (HOOK&& hook)
515 return addHook (&Policy::hook_looseThread, forward<HOOK> (hook));
527 template<
typename HOOK,
class FUN>
537 return [hook = forward<HOOK>(hook)](Arg){ hook(); };
541 return [hook = forward<HOOK>(hook)]
545 Target& target =
static_cast<Target&
> (base);
552 template<
typename HOOK,
class FUN>
554 addHook (FUN Policy::*storedHook, HOOK&& hook)
556 return addLayer ([storedHook, hook = adaptedHook (storedHook, forward<HOOK> (hook))]
559 wrapper.*storedHook = move (hook);
567 launch = [action=move(action), chain=move(launch)]
586 launcher.launch (*
this);
598 template<
class FUN,
typename...ARGS>
601 Launch{forward<FUN> (threadFunction), forward<ARGS> (args)...}
609 template<
class SUB,
typename...ARGS>
612 Launch{std::move (memFun)
613 ,
static_cast<SUB*
> (
this)
614 ,forward<ARGS> (args)...
616 .threadID(util::joinDash (typeSymbol<SUB>(), args...))}
628 operator bool()
const 630 return Policy::isLive();
634 using Policy::invokedWithinThread;
660 using ThreadLifecycle::ThreadLifecycle;
675 template<
typename RES =
void>
695 if (not Impl::threadImpl_.joinable())
698 Impl::threadImpl_.join();
700 return Impl::result_;
705 template<
typename FUN,
typename...ARGS>
728 using ThreadLifecycle::ThreadLifecycle;
748 template<
class TAR = ThreadHookable>
754 new TAR{move(launchBuilder)
755 .atExit([](TAR& selfAllocation)
757 delete &selfAllocation;
775 using Launch =
typename TAR::Launch;
776 launchDetached<TAR> (Launch{forward<INVO> (args)...}
777 .threadID (threadID));
781 template<
class TAR,
typename...ARGS>
783 launchDetached (
string const& threadID,
void (TAR::*memFun) (ARGS...), ARGS ...args)
785 using Launch =
typename TAR::Launch;
786 launchDetached<TAR> (Launch{std::move (memFun)
788 ,forward<ARGS> (args)...
790 .threadID (threadID));
794 template<
class TAR,
typename...ARGS>
798 launchDetached (util::joinDash (lib::meta::typeSymbol<TAR>(), args...)
800 ,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.