Library: gut and remould the existing thread-wrapper

- cut the ties to the old POSIX-based custom threadpool framework
- remove operations deemed no longer necessary
- sync() obsoleted by the new SyncBarrier
- support anything std::invoke supports
This commit is contained in:
Fischlurch 2023-09-25 16:27:38 +02:00
parent 11cb53a406
commit c9a0203492
4 changed files with 289 additions and 146 deletions

View file

@ -23,15 +23,34 @@
/** @file thread.hpp
** Convenience front-end for basic thread handling needs.
** The Lumiera vault contains a dedicated low-level thread handling framework,
** which is relevant for scheduling render activities to make best use of parallelisation
** abilities of the given system. Typically, the upper layers should not have to deal much
** with thread handling, yet at some point there is the need to implement a self contained
** action running within a dedicated thread. The vault::Thread class is a wrapper to
** represent such an parallel action conveniently and safely; together with the object
** monitor, this allows to abstract away intricacies into self contained objects.
**
** Convenience front-end to simplify and codify basic thread handling.
** While the implementation of threading and concurrency support is based on the C++
** standard library, using in-project wrappers as front-end allows to codify some preferences
** and provide simplifications for the prevalent use case. Notably, threads which must be
** _joined_ are qualified as special case, while the standard case will just `detach()`
** at thread end. The main-level of each thread catches exceptions, which are typically
** ignored to keep the application running. Moreover, similar convenience wrappers are
** provided to implement [N-fold synchronisation](\ref lib::SyncBarrier) and to organise
** global locking and waiting in accordance with the _Object Monitor_ pattern. Together,
** these aim at packaging concurrency facilities into self-contained RAII-style objects.
** @remarks
** - Lumiera offered simplified convenience wrappers long before a similar design
** became part of the C++14 standard. These featured the distinction in join-able or
** detached threads, the ability to define the thread main-entry as functor, and a
** two-fold barrier between starter and new thread, which could also be used to define
** a second custom synchronisation point. A similar setup with wrappers was provided
** for locking, exposed in the form of the Object Monitor pattern.
** - The original Render Engine design called for an active thread-pool, which was part
** of a invoker service located in Vault layer; the thread-wrapper could only be used
** in conjunction with this pool, re-using detached and terminated threads. All features
** where implemented in plain-C on top of POSIX, using Mutexes and Condition Variables.
** - In 2023, when actually heading towards integration of the Render Engine, in-depth
** analysis showed that active dispatch into a thread pool would in fact complicate
** the scheduling of Render-Activities leading to a design change towards _pull_
** of work tasks by competing _active workers._ This obsoleted the Thread-pool service
** and paved the way for switch-over to the threading support meanwhile part of the
** C++ standard library. Design and semantics were retained, while implemented
** using modern features, notably the new _Atomics_ synchronisation framework.
** @todo WIP 9/23 about to be replaced by a thin wrapper on top of C++17 threads ///////////////////////TICKET #1279 : consolidate to C++17 features
*/
@ -44,14 +63,11 @@
#include "lib/nocopy.hpp"
#include "include/logging.h"
#include "lib/meta/function.hpp"
#include "lib/format-string.hpp" ///////////////////////////OOO RLY? or maybe into CPP file?
#include "lib/result.hpp"
extern "C" {
#include "vault/threads.h"
}
//#include "vault/threadpool-init.hpp"
#include <type_traits>
#include <thread>
#include <utility>
@ -67,10 +83,10 @@ namespace lib {
/************************************************************************//**
* A thin convenience wrapper for dealing with threads,
* as implemented by the threadpool in the vault (based on pthread).
* A thin convenience wrapper to simplify thread-handling. The implementation
* is backed by the C++ standard library.
* Using this wrapper...
* - helps with passing data to the function executed in the new thread
* - removes the need to join() threads, catches and ignores exceptions.
* - allows to bind to various kinds of functions including member functions
* The new thread starts immediately within the ctor; after returning, the new
* thread has already copied the arguments and indeed actively started to run.
@ -110,81 +126,38 @@ namespace lib {
class Thread
: util::MoveOnly
{
/** @internal perfect forwarding through a C-style `void*` */
template<class FUN>
static FUN&&
forwardInitialiser (void* rawPtr) noexcept
{
REQUIRE (rawPtr);
FUN& initialiser = *reinterpret_cast<FUN*> (rawPtr);
return static_cast<FUN&&> (initialiser);
}
template<class FUN>
static void
threadMain (void* arg)
template<class FUN, typename...ARGS>
void
threadMain (string threadID, FUN&& threadFunction, ARGS&& ...args)
{
using Fun= typename lib::meta::_Fun<FUN>::Functor;
Fun _doIt_{forwardInitialiser<FUN> (arg)};
//lumiera_thread_sync (); // sync point: arguments handed over /////////////////////////////////OOO TOD-oh
try {
_doIt_(); // execute the actual operation in the new thread
}
catch (std::exception& failure)
{
if (!lumiera_error_peek())
LUMIERA_ERROR_SET (sync, STATE
,failure.what());
}
catch (...)
{
LUMIERA_ERROR_SET_ALERT (sync, EXTERNAL
, "Thread terminated abnormally");
markThreadStart (threadID);
// execute the actual operation in this new thread
std::invoke (std::forward<FUN> (threadFunction), std::forward<ARGS> (args)...);
}
ERROR_LOG_AND_IGNORE (thread, "Thread function")
}
void
markThreadStart (string const& threadID)
{
string logMsg = util::_Fmt{"Thread '%s' start..."} % threadID;
TRACE (thread, "%s", logMsg.c_str());
//////////////////////////////////////////////////////////////////////OOO maybe set the the Thread-ID via POSIX ??
}
protected:
LumieraThread threadHandle_;
std::thread threadImpl_;
/** @internal derived classes may create an inactive thread */
Thread() : threadHandle_(0) { }
/** @internal use the Lumiera thread manager to start a new thread and hand over the operation */
template<class FUN>
void
launchThread (Literal purpose, FUN&& operation, NoBugFlag logging_flag, uint additionalFlags =0)
{
REQUIRE (!lumiera_error(), "Error pending at thread start");
using Functor = typename std::remove_reference<FUN>::type;
threadHandle_ =
nullptr; ////////////////////////////////////////////////////////////////////////////////////OOO LaLaLa
// lumiera_thread_run ( LUMIERA_THREADCLASS_INTERACTIVE | additionalFlags
// , &threadMain<Functor>
// , reinterpret_cast<void*> (&operation)
// , purpose.c()
// , logging_flag
// );
if (!threadHandle_)
throw error::State ("Failed to start a new Thread for \"+purpose+\""
, lumiera_error());
// make sure the new thread had the opportunity to take the Operation
// prior to leaving and thereby possibly destroying this local context
//lumiera_thread_sync_other (threadHandle_); //////////////////////////////////////////////////OOO Dadü DaDa
}
Thread() : threadImpl_{} { }
public:
/** Create a new thread to execute the given operation.
* The new thread starts up synchronously, can't be cancelled and it can't be joined.
* @param purpose fixed char string used to denote the thread for diagnostics
* @param threadID human readable descriptor to identify the thread for diagnostics
* @param logging_flag NoBug flag to receive diagnostics regarding the new thread
* @param operation a functor holding the code to execute within the new thread.
* Any function-like entity with signature `void(void)` is acceptable.
@ -193,59 +166,36 @@ namespace lib {
* anything referred through a lambda closure here must stay alive
* until the new thread terminates.
*/
template<class FUN>
Thread (Literal purpose, FUN&& operation, NoBugFlag logging_flag = &NOBUG_FLAG(thread))
: threadHandle_{nullptr}
{
launchThread (purpose, std::forward<FUN> (operation), logging_flag);
}
template<class FUN, typename...ARGS>
Thread (string const& threadID, FUN&& threadFunction, ARGS&& ...args)
: threadImpl_{&Thread::threadMain<FUN,ARGS...>, this
, threadID
, std::forward<FUN> (threadFunction)
, std::forward<ARGS> (args)... }
{ }
/** @note by design there is no possibility to find out
* just based on the thread handle if some thread is alive.
* We define our own accounting here based on the internals
* of the thread wrapper. This will break down, if you mix
* uses of the C++ wrapper with the raw C functions. */
bool
isValid() const
{
return threadHandle_;
}
/** Synchronisation barrier. In the function executing in this thread
* needs to be a corresponding Thread::syncPoint() call. Blocking until
* both the caller and the thread have reached the barrier.
/**
* Is this thread »active« and thus tied to OS resources?
* @note this implies some statefulness, which may contradict the RAII pattern.
* - especially note the possibly for derived classes to create an _empty_ Thread.
* - moreover note that ThreadJoinable may have terminated, but still awaits `join()`.
*/
void
sync()
explicit
operator bool() const
{
REQUIRE (isValid(), "Thread not running");
if (!lumiera_thread_sync_other (threadHandle_))
lumiera::throwOnError();
return threadImpl_.joinable();
}
/** counterpart of the synchronisation barrier, to be called from
* within the thread to be synchronised. Will block until both
* this thread and the outward partner reached the barrier.
* @warning blocks on the _current_ thread's condition var
*/
static void
syncPoint ()
{
lumiera_thread_sync ();
}
protected:
/** determine if the currently executing code runs within this thread */
bool
invokedWithinThread() const
{
REQUIRE (isValid(), "Thread not running");
LumieraThread current = nullptr; // lumiera_thread_self (); /////////////////////////////////OOO
return current
and current == this->threadHandle_;
}
return threadImpl_.get_id() == std::this_thread::get_id();
} // Note: implies get_id() != std::thread::id{} ==> it is running
};
@ -266,8 +216,8 @@ namespace lib {
NoBugFlag logging_flag = &NOBUG_FLAG(thread))
: Thread{}
{
launchThread<FUN> (purpose, std::forward<FUN> (operation), logging_flag,
LUMIERA_THREAD_JOINABLE);
// launchThread<FUN> (purpose, std::forward<FUN> (operation), logging_flag,
// LUMIERA_THREAD_JOINABLE);
}
@ -279,17 +229,17 @@ namespace lib {
lib::Result<void>
join ()
{
if (!isValid())
throw error::Logic ("joining on an already terminated thread");
lumiera_err errorInOtherThread =
"TODO TOD-oh";//lumiera_thread_join (threadHandle_); //////////////////////////////////OOO
threadHandle_ = 0;
if (errorInOtherThread)
return error::State ("Thread terminated with error", errorInOtherThread);
else
return true;
// if (!isValid())
// throw error::Logic ("joining on an already terminated thread");
//
// lumiera_err errorInOtherThread =
// "TODO TOD-oh";//lumiera_thread_join (threadHandle_); //////////////////////////////////OOO
// threadHandle_ = 0;
//
// if (errorInOtherThread)
// return error::State ("Thread terminated with error", errorInOtherThread);
// else
// return true;
}
};

View file

@ -142,12 +142,12 @@ namespace test{
, bind (&HavocThread::doIt, this)
)
{
CHECK (thread_.isValid());
CHECK (thread_);
}
~HavocThread ()
{
if (thread_.isValid())
if (thread_)
thread_.join();
}
};

View file

@ -161,8 +161,8 @@ namespace test{
Thread ping ("SyncWaiting ping", bind (&Token::getIt, &tok));
Thread pong ("SyncWaiting pong", bind (&Token::getIt, &tok));
CHECK (ping.isValid());
CHECK (pong.isValid());
CHECK (ping);
CHECK (pong);
CHECK (0 == tok.result());
usleep (100000); // if the threads don't block correctly, they've missed their chance by now...

View file

@ -79021,7 +79021,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1695314579779" ID="ID_643512170" MODIFIED="1695314602498" TEXT="diese mu&#xdf; initialisiert sein, bevor der Session-Thread ihre Logik verwendet"/>
</node>
</node>
<node COLOR="#338800" CREATED="1695315426871" FOLDED="true" ID="ID_1792980090" MODIFIED="1695484826031" TEXT="&#x27f9; Ersatzkonstrukt zwingend notwendig">
<node COLOR="#338800" CREATED="1695315426871" ID="ID_1792980090" MODIFIED="1695595904577" TEXT="&#x27f9; Ersatzkonstrukt zwingend notwendig">
<node CREATED="1695334520345" ID="ID_1748880887" MODIFIED="1695334539706" TEXT="sollte dann aber eine explizite Lib-Funktionalit&#xe4;t sein"/>
<node CREATED="1695334551413" ID="ID_257643954" LINK="https://stackoverflow.com/a/24218922" MODIFIED="1695334594067" TEXT="man k&#xf6;nnte ein spinning-latch mit yield verwenden">
<icon BUILTIN="idea"/>
@ -79120,7 +79120,17 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="ksmiletris"/>
</node>
<node CREATED="1695580030937" ID="ID_634357060" MODIFIED="1695580179481" TEXT="die Performance von SyncBarrier ist ad&#xe4;quat f&#xfc;r den Einsatzzweck"/>
<node CREATED="1695580048111" ID="ID_784900194" MODIFIED="1695580199504" TEXT="es ist kein Overhead beobachtbar &#x2014; jenseits der typischen Scheduling-Unsch&#xe4;rfe">
<node CREATED="1695580048111" ID="ID_784900194" MODIFIED="1695596554665" TEXT="es ist kein Overhead beobachtbar &#x2014; jenseits der typischen Scheduling-Unsch&#xe4;rfe">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Zum einen handelt es sich um bekannte Erfahrungswerte, zumindest f&#252;r die F&#228;lle mit wenigen Threads: da&#223; zwei zusammen gestartete Threads um bis zu 0.5&#181;s auseinanderlaufen, ist erwartbar, wesentlich mehr aber nicht ohne Weiteres. Und dann l&#228;&#223;t es sich best&#228;tigen, indem die Implementierung versuchsweise auf <b>busy-wait</b>&#160;umgestellt wird &#10233; f&#252;r kleine Anzahl Threads bleiben die Me&#223;werte nahezu unver&#228;ndert (sie sind minimal schlechter, aber das System geht auch in Vollast). Das bedeutet: die beobachteten Werte stellen bereits nahezu optimales Verhalten dar, f&#252;r kleine Anzahl Threads.
</p>
</body>
</html></richcontent>
<arrowlink COLOR="#c6fdd1" DESTINATION="ID_1515850328" ENDARROW="Default" ENDINCLINATION="-864;-29;" ID="Arrow_ID_1385448927" STARTARROW="None" STARTINCLINATION="99;698;"/>
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1695580207961" ID="ID_1915318856" MODIFIED="1695580215897" TEXT="auf C++20 warten....">
@ -79190,11 +79200,184 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695340297038" ID="ID_1888990589" MODIFIED="1695340304054" TEXT="die Threadpool-Library">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1695606751304" ID="ID_933215857" MODIFIED="1695606757488" TEXT="TICKET #844 k&#xf6;nnte obsolet sein">
<icon BUILTIN="help"/>
</node>
</node>
<node COLOR="#338800" CREATED="1695597040159" ID="ID_215388471" MODIFIED="1695597196367" TEXT="eine Kopie der bestehenden L&#xf6;sung &#x27fc; lib/thread.hpp">
<arrowlink COLOR="#6ebe5a" DESTINATION="ID_125138411" ENDARROW="Default" ENDINCLINATION="43;-18;" ID="Arrow_ID_195305087" STARTARROW="None" STARTINCLINATION="-113;6;"/>
<icon BUILTIN="idea"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695394188133" ID="ID_1229328590" MODIFIED="1695394203632" TEXT="Umbau">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695597011139" ID="ID_125138411" MODIFIED="1695597196367" TEXT="Kopie des bestehenden Thread-Wrappers umschreiben">
<linktarget COLOR="#6ebe5a" DESTINATION="ID_125138411" ENDARROW="Default" ENDINCLINATION="43;-18;" ID="Arrow_ID_195305087" SOURCE="ID_215388471" STARTARROW="None" STARTINCLINATION="-113;6;"/>
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695597146529" ID="ID_938038908" MODIFIED="1695651348752" TEXT="std::thread liefert fast alle Funktionalit&#xe4;t">
<icon BUILTIN="idea"/>
<node COLOR="#435e98" CREATED="1695600626208" ID="ID_688465099" MODIFIED="1695602235450" TEXT="als Basisklasse sichtbar?">
<icon BUILTIN="help"/>
<node CREATED="1695600644749" ID="ID_1230170765" MODIFIED="1695600664138" TEXT="damit k&#xf6;nnte man &#xbb;wie mit C++ - Threads&#xab; programmieren"/>
<node CREATED="1695600666050" ID="ID_642355226" MODIFIED="1695600701593" TEXT="das h&#xe4;tte aber auch den Nachteil, da&#xdf; man die join/detach()-Unterscheidung unterl&#xe4;uft"/>
<node CREATED="1695602083733" ID="ID_1989367844" MODIFIED="1695602231389" TEXT="also besser nicht exponieren &#x2014; ggfs Funktionalit&#xe4;t nachr&#xfc;sten">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
YAGNI.
</p>
<p>
Zun&#228;chst einmal so wenig Funktionalit&#228;t wie m&#246;glich durchreichen, und das nur auf den festen Bahnen gem&#228;&#223; Design; im Zweifelsfall ist es besser, spezielle Funktionalit&#228;t im Bedarfsfall in den Wrapper zu packen; genau das ist ja <i>der Vorteil einer Hilfsklasse direkt im Projekt.</i>
</p>
</body>
</html></richcontent>
<icon BUILTIN="yes"/>
</node>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1695602445469" ID="ID_705070648" MODIFIED="1695602458204" TEXT="Problem mit Init / Sync">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1695602459300" ID="ID_1560126243" MODIFIED="1695602546925" TEXT="das ist ein inh&#xe4;rentes Problem mit diesem &#xbb;Thread-Objekt&#xab; - Design">
<icon BUILTIN="clanbomber"/>
</node>
<node CREATED="1695602549712" ID="ID_331491775" MODIFIED="1695602590334" TEXT="tritt nur auf, wenn die Implementierung vom Thread erbt"/>
<node CREATED="1695602592608" ID="ID_207463236" MODIFIED="1695603506751" TEXT="Gefahr: Race zwischen dem abgeleiteten Ctor und der bereits startenden Thread-Funktion">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Zwar sichert der Standard zu, da&#223; das Ende des ctor-Aufrufs <i>synchronizes_with</i>&#160; dem Start der Thread-Funktion. Streng logisch kann das aber nur f&#252;r den std::thread-Konstruktor <i>selber </i>gelten (andernfalls h&#228;tte man mit einem Sequence-Point argumentieren m&#252;ssen, und nicht mit dem ctor selber; das w&#252;rde dann aber auch wieder eine unerw&#252;nschte Statefulness einf&#252;hren, weil dann im gesamten umschlie&#223;enden Ausdruck der Thread <i>eben noch nicht l&#228;uft, </i>was das RAII-Konzept untergraben w&#252;rde).
</p>
<br/>
<p>
Das ist nun zwar ziemlich unwahrscheinlich (weil normalerweise der Scheduler immer eine erhebliche Zeit braucht, bis ein anderer Thread &#252;berhaupt zum Zug kommt), aber leider ist es demzufolge <i>theoretisch m&#246;glich, </i>da&#223; eine im abgeleiteten Objekt definierte Thread-Funktionalit&#228;t bereits auf eine noch nicht vollst&#228;ndig initialisierte Objektinstanz zugreift, oder da&#223; die Initialisierung der abgeleiteten Klasse Werte in lokalen Feldern &#252;berschreibt, die aus der bereits startenden Thread-Funktion gesetzt wurden. Unerkl&#228;rliches und reproduzierbares Verhalten w&#228;re die Folge. Und das l&#228;&#223;t sich aus dem Wrapper heraus nicht beheben (zumindest nicht ohne verwirrende Konventionen einzuf&#252;hren).
</p>
</body>
</html></richcontent>
<icon BUILTIN="broken-line"/>
</node>
<node CREATED="1695603509089" ID="ID_710810951" MODIFIED="1695603513019" TEXT="Abhilfe">
<icon BUILTIN="idea"/>
<node CREATED="1695603514279" ID="ID_1788387529" MODIFIED="1695603548957">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
explizit eine <font face="Monospaced" color="#5e1caa">lib::SyncBarrier</font>&#160;<i>in der Implementierung </i>einbinden
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1695603557370" ID="ID_1092955208" MODIFIED="1695603572524" TEXT="den Thread-Wrapper nur als Member-Feld verwenden, nicht als Basisklasse"/>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1695597148544" ID="ID_1135683799" MODIFIED="1695651329233" TEXT="die sync() / syncThread() -Barrier ersatzlos zur&#xfc;ckbauen">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1695597151336" ID="ID_854108015" MODIFIED="1695651323636" TEXT="Konstruktor-Syntax anpassen">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1695606839044" ID="ID_1024366951" MODIFIED="1695607662788" TEXT="ausweiten auf std::invoke">
<icon BUILTIN="yes"/>
<node CREATED="1695606859838" ID="ID_1502075570" MODIFIED="1695606889162" TEXT="kann damit alles &#xbb;function-like&#xab; ausf&#xfc;hren (auch Member-Functions)">
<icon BUILTIN="idea"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695606903540" ID="ID_1827438483" MODIFIED="1695606934199" TEXT="Textuelle Thread-Kennung setzen">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695606935311" ID="ID_1002758793" MODIFIED="1695606969745" TEXT="nimmt jetzt einen string...">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...damit man auch zusammengesetzte/formatierte Werte bauen kann
</p>
</body>
</html></richcontent>
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1695606971754" ID="ID_1204181207" MODIFIED="1695606985337" TEXT="kann man das per PTHREAD setzen?">
<icon BUILTIN="help"/>
</node>
</node>
<node CREATED="1695607699183" ID="ID_470294434" MODIFIED="1695607712099" TEXT="Ctor-Varianten">
<node CREATED="1695607713041" ID="ID_1988127528" MODIFIED="1695607720299" TEXT="ohne Thread-Kennung"/>
<node CREATED="1695607745859" ID="ID_1296792416" MODIFIED="1695607796305" TEXT="direkt mit einem Member-Fun-Ptr">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
sollte dann den this-Typ extrahieren und den this-Ptr automatisch injizieren
</p>
</body>
</html></richcontent>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1695597337839" ID="ID_1776858144" MODIFIED="1695651315240" TEXT="detach() / join-Differenzierung">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1695647408544" ID="ID_805151848" MODIFIED="1695651308867" TEXT="isValid() bzw. bool-Check">
<icon BUILTIN="button_ok"/>
<node CREATED="1695647421701" ID="ID_1863075325" MODIFIED="1695647426362" TEXT="noch notwendig?">
<node CREATED="1695647427993" ID="ID_1136703438" MODIFIED="1695647736631" TEXT="bisher nur intern verwendet">
<icon BUILTIN="info"/>
<node CREATED="1695647469998" ID="ID_1784422581" MODIFIED="1695647479753" TEXT="invokedWithinThread()"/>
<node CREATED="1695647480310" ID="ID_1790796451" MODIFIED="1695647497911" TEXT="sync() / syncPoint()"/>
</node>
<node CREATED="1695647503234" ID="ID_1607779605" MODIFIED="1695647520450">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
der Name ist <i>nicht besonders klar</i>
</p>
</body>
</html></richcontent>
<icon BUILTIN="messagebox_warning"/>
</node>
<node COLOR="#435e98" CREATED="1695647529423" ID="ID_73599708" MODIFIED="1695647771420" TEXT="wof&#xfc;r sinnvoll?">
<arrowlink COLOR="#5321e5" DESTINATION="ID_1350304000" ENDARROW="Default" ENDINCLINATION="19;1;" ID="Arrow_ID_795204262" STARTARROW="None" STARTINCLINATION="14;24;"/>
<node CREATED="1695647550628" ID="ID_90948508" MODIFIED="1695647582331" TEXT="f&#xfc;r joinable-Threads &#x27fc; sie durchlaufen einen Proze&#xdf;"/>
<node CREATED="1695647620445" ID="ID_1998559139" MODIFIED="1695647729498" TEXT="es gibt die &#xbb;Hintert&#xfc;r&#xab; mit dem protected default-Ctor">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Die hatte ich eingebaut, um f&#252;r spezialisierte abgeleitete Klassen doch noch erweiterte Zustands&#252;berg&#228;nge zu erm&#246;glichen
</p>
</body>
</html></richcontent>
</node>
</node>
</node>
<node CREATED="1695647738890" ID="ID_1350304000" MODIFIED="1695647759234" TEXT="besser nur bool-check">
<linktarget COLOR="#5321e5" DESTINATION="ID_1350304000" ENDARROW="Default" ENDINCLINATION="19;1;" ID="Arrow_ID_795204262" SOURCE="ID_73599708" STARTARROW="None" STARTINCLINATION="14;24;"/>
<icon BUILTIN="yes"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695597475293" ID="ID_1100470933" MODIFIED="1695597482079" TEXT="invokedWithinThread()">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695597447169" ID="ID_35947260" MODIFIED="1695597451881" TEXT="Fehlerbehandlung">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695394205547" ID="ID_1719816317" MODIFIED="1695394259544" TEXT="Tests umstellen und modernisieren">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695394270093" ID="ID_585437655" MODIFIED="1695394271819" TEXT="ThreadWrapper_test">
@ -79503,19 +79686,29 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</body>
</html></richcontent>
<icon BUILTIN="list"/>
<node CREATED="1695576145933" ID="ID_1009935908" MODIFIED="1695578510383" TEXT="emptySetup : 0.6ns"/>
<node CREATED="1695576145934" ID="ID_1350901174" MODIFIED="1695576175936" TEXT="SyncBarrier (2 Thr) : 280ns"/>
<node CREATED="1695576145934" ID="ID_1893777478" MODIFIED="1695576185574" TEXT="SyncBarrier (4 Thr) : 700ns"/>
<node CREATED="1695576145934" ID="ID_517518950" MODIFIED="1695576196685" TEXT="SyncBarrier (8 Thr) : 2&#xb5;s"/>
<node CREATED="1695576145934" ID="ID_786614029" MODIFIED="1695576215763" TEXT="SyncBarrier (16 Thr) : 9&#xb5;s"/>
<node CREATED="1695576145934" ID="ID_1548951986" MODIFIED="1695576221330" TEXT="SyncBarrier (32 Thr) : 21&#xb5;s"/>
<node CREATED="1695576145934" ID="ID_1877591130" MODIFIED="1695576230336" TEXT="SyncBarrier (48 Thr) : 30&#xb5;s"/>
<node CREATED="1695576145935" ID="ID_469124083" MODIFIED="1695576238800" TEXT="SyncBarrier (64 Thr) : 50&#xb5;s"/>
<node CREATED="1695576145935" ID="ID_754356198" MODIFIED="1695576257440" TEXT="SyncBarrier (80 Thr) : 80&#xb5;s"/>
<node CREATED="1695576145933" MODIFIED="1695578510383" TEXT="emptySetup : 0.6ns"/>
<node CREATED="1695576145934" MODIFIED="1695576175936" TEXT="SyncBarrier (2 Thr) : 280ns"/>
<node CREATED="1695576145934" MODIFIED="1695576185574" TEXT="SyncBarrier (4 Thr) : 700ns"/>
<node CREATED="1695576145934" MODIFIED="1695576196685" TEXT="SyncBarrier (8 Thr) : 2&#xb5;s"/>
<node CREATED="1695576145934" MODIFIED="1695576215763" TEXT="SyncBarrier (16 Thr) : 9&#xb5;s"/>
<node CREATED="1695576145934" MODIFIED="1695576221330" TEXT="SyncBarrier (32 Thr) : 21&#xb5;s"/>
<node CREATED="1695576145934" MODIFIED="1695576230336" TEXT="SyncBarrier (48 Thr) : 30&#xb5;s"/>
<node CREATED="1695576145935" MODIFIED="1695576238800" TEXT="SyncBarrier (64 Thr) : 50&#xb5;s"/>
<node CREATED="1695576145935" MODIFIED="1695576257440" TEXT="SyncBarrier (80 Thr) : 80&#xb5;s"/>
<node CREATED="1695583795249" MODIFIED="1695583795249" TEXT="MonitorWait (2 Thr) : 7&#xb5;s"/>
<node CREATED="1695583795249" MODIFIED="1695583795249" TEXT="MonitorWait (4 Thr) : 12&#xb5;s"/>
<node CREATED="1695583795250" MODIFIED="1695583795250" TEXT="MonitorWait (8 Thr) : 27&#xb5;s"/>
<node CREATED="1695583795250" MODIFIED="1695583795250" TEXT="MonitorWait (16 Thr) : 75&#xb5;s"/>
<node CREATED="1695595955182" HGAP="8" ID="ID_1818362689" MODIFIED="1695596168103" TEXT="Erg&#xe4;nzung: busy-wait" VSHIFT="14">
<icon BUILTIN="idea"/>
<node CREATED="1695595970341" MODIFIED="1695595980004" TEXT="degeneriert brutal &gt; 8 Cores"/>
<node CREATED="1695595980802" MODIFIED="1695596009566" TEXT="ist f&#xfc;r die kleinen Zeiten minimal aufwendiger">
<node CREATED="1695596024582" MODIFIED="1695596055154" TEXT="2 Threads &#x27fc; 340ns"/>
<node CREATED="1695596055953" MODIFIED="1695596074107" TEXT="4 Threads &#x27fc; 910ns"/>
<node CREATED="1695596080358" MODIFIED="1695596122796" TEXT="8 Threads &#x27fc; 2200ns"/>
</node>
<node CREATED="1695596136607" MODIFIED="1695596156584" TEXT="&#x27f9; best&#xe4;tigt: wir messen die Scheduler-Unsch&#xe4;rfe"/>
</node>
</node>
<node CREATED="1695576264352" ID="ID_1266717950" MODIFIED="1695578848332" TEXT="Debug-Buid-Werte weichen nur wenig ab">
<icon BUILTIN="idea"/>