Library: prepare switch of Thread-wrapper to C++17

The investigation for #1279 leads to the following conclusions

- the features and the design of our custom thread-wrapper
  almost entirely matches the design chosen meanwhile by the C++ committee

- the implementation provided by the standard library however uses
  modern techniques (especially Atomics) and is more precisely worked out
  than our custom implementation was.

- we do not need an *active* threadpool with work-assignment,
  rather we'll use *active* workers and a *passive* pool,
  which was easy to implement based on C++17 features

==> decision to drop our POSIX based custom implementation
    and to retrofit the Thread-wrapper as a drop-in replacement

+++ start this refactoring by moving code into the Library
+++ create a copy of the Threadwrapper-code to build and test
    the refactorings while the application itself still uses
    existing code, until the transition is complete
This commit is contained in:
Fischlurch 2023-09-21 23:23:55 +02:00
parent 6c17204dad
commit 416895b5b2
12 changed files with 545 additions and 60 deletions

299
src/lib/thread.hpp Normal file
View file

@ -0,0 +1,299 @@
/*
THREAD.hpp - thin convenience wrapper for starting threads
Copyright (C) Lumiera.org
2008, 2010 Hermann Vosseler <Ichthyostega@web.de>
Christian Thaeter <ct@pipapo.org>
This program 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.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/** @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.
**
** @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
*/
#ifndef LIB_THREAD_H
#define LIB_THREAD_H
#include "lib/error.hpp"
#include "lib/nocopy.hpp"
#include "include/logging.h"
#include "lib/meta/function.hpp"
#include "lib/result.hpp"
extern "C" {
#include "vault/threads.h"
}
//#include "vault/threadpool-init.hpp"
#include <type_traits>
#include <utility>
namespace lib {
using lib::Literal;
namespace error = lumiera::error;
using error::LERR_(STATE);
using error::LERR_(EXTERNAL);
typedef struct nobug_flag* NoBugFlag;
/************************************************************************//**
* A thin convenience wrapper for dealing with threads,
* as implemented by the threadpool in the vault (based on pthread).
* Using this wrapper...
* - helps with passing data to the function executed in the new thread
* - 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.
*
* # Joining, cancellation and memory management
* In the basic version (class Thread), the created thread is completely detached
* and not further controllable. There is no way to find out its execution state,
* wait on termination or even cancel it. Client code needs to implement such
* facilities explicitly, if needed. Care has to be taken with memory management,
* as there are no guarantees beyond the existence of the arguments bound into
* the operation functor. If the operation in the started thread needs additional
* storage, it has to manage it actively.
*
* There is an extended version (class ThreadJoinable) to allow at least to wait
* on the started thread's termination (joining). Building on this it is possible
* to create a self-contained "thread in an object"; the dtor of such an class
* must join to prevent pulling away member variables the thread function will
* continue to use.
*
* # failures in the thread function
* The operation started in the new thread is protected by a top-level catch block.
* Error states or caught exceptions can be propagated through the lumiera_error
* state flag, when using ThreadJoinable::join(). By invoking `join().maybeThrow()`
* on a join-able thread, exceptions can be propagated.
* @note any errorstate or caught exception detected on termination of a standard
* async Thread is considered a violation of policy and will result in emergency
* shutdown of the whole application.
*
* # synchronisation barriers
* Lumiera threads provide a low-level synchronisation mechanism, which is used
* to secure the hand-over of additional arguments to the thread function. It
* can be used by client code, but care has to be taken to avoid getting out
* of sync. When invoking the #sync and #syncPoint functions, the caller will
* block until the counterpart has also invoked the corresponding function.
* If this doesn't happen, you'll block forever.
*/
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)
{
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");
}
}
protected:
LumieraThread threadHandle_;
/** @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
}
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 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.
* @warning The operation functor will be forwarded to create a copy residing
* on the stack of the new thread; thus it can be transient, however
* 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);
}
/** @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.
*/
void
sync()
{
REQUIRE (isValid(), "Thread not running");
if (!lumiera_thread_sync_other (threadHandle_))
lumiera::throwOnError();
}
/** 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_;
}
};
/**
* Variant of the standard case, allowing additionally
* to join on the termination of this thread.
*/
class ThreadJoinable
: public Thread
{
public:
template<class FUN>
ThreadJoinable (Literal purpose, FUN&& operation,
NoBugFlag logging_flag = &NOBUG_FLAG(thread))
: Thread{}
{
launchThread<FUN> (purpose, std::forward<FUN> (operation), logging_flag,
LUMIERA_THREAD_JOINABLE);
}
/** put the caller into a blocking wait until this thread has terminated.
* @return token signalling either success or failure.
* The caller can find out by invoking `isValid()`
* or `maybeThrow()` on this result token
*/
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;
}
};
} // namespace lib
#endif /*LIB_THREAD_H*/

View file

@ -32,7 +32,7 @@
** represent such an parallel action conveniently and safely; together with the object
** monitor, this allows to abstract away intricacies into self contained objects.
**
** @note the thread wrapper is not intended for high performance computations.
** @deprecated will be replaced by a thin wrapper on top of C++17 threads //////////////////////////////TICKET #1279 : consolidate to C++17 features
*/
@ -106,6 +106,7 @@ namespace vault {
* of sync. When invoking the #sync and #syncPoint functions, the caller will
* block until the counterpart has also invoked the corresponding function.
* If this doesn't happen, you'll block forever.
* @deprecated will be replaced by a thin wrapper on top of C++17 threads /////////////////////////////TICKET #1279 : consolidate to C++17 features
*/
class Thread
: util::MoveOnly
@ -255,6 +256,7 @@ namespace vault {
/**
* Variant of the standard case, allowing additionally
* to join on the termination of this thread.
* @deprecated will be replaced by a thin wrapper on top of C++17 threads /////////////////////////////TICKET #1279 : consolidate to C++17 features
*/
class ThreadJoinable
: public Thread

44
tests/11concurrency.tests Normal file
View file

@ -0,0 +1,44 @@
TESTING "Library Test Suite: concurrency helpers" ./test-suite --group=common
TEST "Multithread Locking by Monitor" SyncLocking_test <<END
return: 0
END
TEST "Multithread Locking class level" SyncClasslock_test <<END
return: 0
END
TEST "Thread-local diagnostic context" DiagnosticContext_test <<END
return: 0
END
TEST "Wait/Notify on Object Monitor" SyncWaiting_test <<END
return: 0
END
TEST "Wait/Notify with timeout" SyncTimedwait_test <<END
return: 0
END
TEST "Create 20 Threads passing context" ThreadWrapper_test <<END
return: 0
END
TEST "Waiting on Thread termination" ThreadWrapperJoin_test <<END
return: 0
END
TEST "Detect code runing in a given Thread" ThreadWrapperSelfRecognitionTest_test <<END
return: 0
END

View file

@ -115,41 +115,6 @@ return: 0
END
TEST "Multithread Locking by Monitor" SyncLocking_test <<END
return: 0
END
TEST "Multithread Locking whole class" SyncClasslock_test <<END
return: 0
END
TEST "Thread-local diagnostic context" DiagnosticContext_test <<END
return: 0
END
TEST "Wait/Notify on Object Monitor" SyncWaiting_test <<END
return: 0
END
TEST "Create 20 Threads passing context" ThreadWrapper_test <<END
return: 0
END
TEST "Waiting on Thread termination" ThreadWrapperJoin_test <<END
return: 0
END
TEST "Detect code runing in a given Thread" ThreadWrapperSelfRecognitionTest_test <<END
return: 0
END
TEST "Starting and stopping subsystems" SubsystemRunner_test <<END
out: -----singleSubsys_complete_cycle-----
out: -----singleSubsys_start_failure-----

View file

@ -28,10 +28,10 @@
#include "lib/test/run.hpp"
#include "lib/sync-classlock.hpp"
#include "lib/scoped-collection.hpp"
#include "vault/thread-wrapper.hpp"
#include "lib/thread.hpp"
using test::Test;
using vault::ThreadJoinable;
//using vault::ThreadJoinable; //////////////////WIP
namespace lib {
namespace test {

View file

@ -29,7 +29,7 @@
#include "lib/error.hpp"
#include "lib/sync.hpp"
#include "vault/thread-wrapper.hpp"
#include "lib/thread.hpp"
#include <iostream>
#include <functional>
@ -126,7 +126,7 @@ namespace test{
*/
class HavocThread
{
vault::ThreadJoinable thread_;
ThreadJoinable thread_;
void
doIt ()

View file

@ -28,7 +28,7 @@
#include "lib/test/run.hpp"
#include "lib/error.hpp"
#include "vault/thread-wrapper.hpp"
#include "lib/thread.hpp"
#include "lib/sync.hpp"
#include <functional>
@ -156,7 +156,7 @@ namespace test{
void
waitPingPong (Token& tok)
{
typedef vault::ThreadJoinable Thread;
typedef ThreadJoinable Thread; //////////////////////////////////////WIP
Thread ping ("SyncWaiting ping", bind (&Token::getIt, &tok));
Thread pong ("SyncWaiting pong", bind (&Token::getIt, &tok));

View file

@ -28,7 +28,7 @@
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "vault/thread-wrapper.hpp"
#include "lib/thread.hpp"
#include "lib/error.hpp"
#include <functional>
@ -38,7 +38,7 @@ using test::Test;
namespace vault {
namespace lib {
namespace test {
using lumiera::error::LUMIERA_ERROR_LOGIC;
@ -139,4 +139,4 @@ namespace test {
}} // namespace vault::test
}} // namespace lib::test

View file

@ -24,14 +24,14 @@
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "vault/thread-wrapper.hpp"
#include "lib/thread.hpp"
#include "lib/error.hpp"
using test::Test;
namespace vault {
namespace lib {
namespace test {
namespace {
@ -86,4 +86,4 @@ namespace test {
}} // namespace vault::test
}} // namespace lib::test

View file

@ -27,7 +27,7 @@
#include "lib/test/run.hpp"
#include "vault/thread-wrapper.hpp"
#include "lib/thread.hpp"
#include "lib/sync.hpp"
#include "lib/symbol.hpp"
@ -37,7 +37,7 @@ using std::bind;
using test::Test;
namespace vault {
namespace lib {
namespace test {
namespace { // private test classes and data...
@ -145,4 +145,4 @@ namespace vault {
}} // namespace vault::test
}} // namespace lib::test

View file

@ -79544,7 +79544,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="button_ok"/>
</node>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1693787750689" ID="ID_1636630763" MODIFIED="1693787785090" TEXT="lib::Thread wird aufgegeben">
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1693787750689" ID="ID_1636630763" MODIFIED="1695311900187" TEXT="vault::Thread wird aufgegeben">
<icon BUILTIN="yes"/>
<node CREATED="1693787857875" ID="ID_1964973756" MODIFIED="1693787947679">
<richcontent TYPE="NODE"><html>
@ -79559,38 +79559,38 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node CREATED="1693787905997" ID="ID_1862230955" MODIFIED="1693787943205" TEXT="aber: sie verlagern Architektur-Fragen in die LIbrary-Ebene">
<icon BUILTIN="closed"/>
<node CREATED="1693788079957" ID="ID_84816913" MODIFIED="1693788252833" TEXT="das ist bestenfalls wirkungslos">
<node CREATED="1693788079957" ID="ID_84816913" MODIFIED="1695312147060" TEXT="das ist bestenfalls wirkungslos">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
...wenn es sich ohnehin nur um eine Einrichtung innerhalb einer bestimmten Applikation handelt, dann bleibt lediglich die Frage &#252;brig, wohin der Quell/Objektcode gepackt wird
...wenn es sich ohnehin nur um eine Einrichtung innerhalb einer bestimmten Applikation handelt, dann bleibt lediglich die Frage &#252;brig, wohin der Quell/Objektcode gepackt wird &#8212; wenn man ein bestimmtes Feature braucht, kann man es implementieren (und die bestehende Lib-Implementierung kann das nicht verhindern)
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1693788121089" ID="ID_553756560" MODIFIED="1693788325694" TEXT="schafft aber ungesunde Strukturen">
<node CREATED="1693788121089" ID="ID_553756560" MODIFIED="1695312764615" TEXT="schafft aber m&#xf6;glicherweise ungesunde Strukturen">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Da Library-Funktionen ohne Weiteres genutzt werden, zwingt das dazu, statische Instanzen und versteckte magische Initialisierung zu verwenden
Da Library-Funktionen ohne Weiteres genutzt werden, verleiten solche <i>eingebaute H&#252;rden</i>&#160;ehr dazu, schmutzige Workarounds zu machen
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1693788148236" ID="ID_1859841213" MODIFIED="1693788618261" TEXT="und stellt im allgemeineren Fall eine Kompetenz&#xfc;berschreitung dar">
<node CREATED="1693788148236" ID="ID_1859841213" MODIFIED="1695312822573" TEXT="im allgemeineren Fall sollte ein Library sich nicht als Architekt bet&#xe4;tigen">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Sprachmittel sind stets aus gutem Grund da. Entweder es gibt aktuell ad&#228;quaten Nutzen, oder es gab fr&#252;her mal einen relevanten Nutzen. Daher stellt es eine Anma&#223;ung dar, wenn jemand nun einfach rundheraus behauptet, es sei <i>falsch</i>&#160;dieses Sprachmittel zu verwenden. Wenn man einen Anf&#228;nger ohne ausreichende Kompetenzen entlasten m&#246;chte, oder wenn man unter Einschr&#228;nkung ein bestimmtes Nutmuster vorgeben m&#246;chte, dann baut man ein <b>Framework</b>, und keine Library.
Sprachmittel sind stets aus gutem Grund da. Entweder es gibt aktuell ad&#228;quaten Nutzen, oder es gab fr&#252;her mal einen relevanten Nutzen. Daher stellt es eine Kompetenz&#252;berschreitung dar, wenn jemand rundheraus behauptet, es sei <i>falsch</i>&#160;dieses Sprachmittel zu verwenden. Wenn man einen Anf&#228;nger ohne ausreichende Kompetenzen entlasten m&#246;chte, oder wenn man unter Einschr&#228;nkung ein bestimmtes Nutmuster vorgeben m&#246;chte, dann baut man ein <b>Framework</b>, und keine Library.
</p>
</body>
</html></richcontent>
</node>
</node>
<node CREATED="1693788648105" ID="ID_426109467" MODIFIED="1693788808876">
<node CREATED="1693788648105" ID="ID_426109467" MODIFIED="1695312840978">
<richcontent TYPE="NODE"><html>
<head/>
<body>
@ -79601,10 +79601,176 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
das sich nun auch im Standard als angemessen herausgebildet hat &#8212;
</p>
<p>
aber die Standard-L&#246;sung ist ausgereifter und verwendet moderne Sprachmittel
aber die Standard-L&#246;sung ist ausgereifter und verwendet moderne Sprachmittel (Atomics)
</p>
</body>
</html></richcontent>
<icon BUILTIN="idea"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695313755664" HGAP="-12" ID="ID_1075854169" MODIFIED="1695313767920" TEXT="Umstellung der Codebasis" VSHIFT="21">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1695313774171" ID="ID_1529952681" MODIFIED="1695340435812" TEXT="spezielle Abh&#xe4;ngigkeiten">
<icon BUILTIN="help"/>
<node CREATED="1695313807671" ID="ID_1031643072" MODIFIED="1695313818061" TEXT="vault::Thread vs vault::ThreadJoinable">
<node CREATED="1695335467170" ID="ID_920953501" MODIFIED="1695335514560" TEXT="das erweist sich als praktische Abk&#xfc;rzung"/>
<node CREATED="1695335516996" ID="ID_1023062994" MODIFIED="1695335550147" TEXT="der normale Thread w&#xfc;rde sich dann stets am Ende selbst detachen"/>
<node CREATED="1695335555337" ID="ID_712387888" MODIFIED="1695335682482" TEXT="damit f&#xe4;llt ein Anla&#xdf; f&#xfc;r Fl&#xfc;chtigkeitsfehler weg"/>
<node CREATED="1695335705339" ID="ID_164690747" MODIFIED="1695335719461" TEXT="pa&#xdf;t damit zusammen, da&#xdf; wir auch einen try-catch-Block einbauen"/>
</node>
<node CREATED="1695313818614" ID="ID_470584516" MODIFIED="1695313837839" TEXT="Thread::syncPoint() / sync()">
<node CREATED="1695315415143" ID="ID_1102877346" MODIFIED="1695315425412" TEXT="bisherige Verwendungen">
<icon BUILTIN="info"/>
<node CREATED="1695313896379" ID="ID_234820417" MODIFIED="1695313904622" TEXT="CallQueue_test::verify_ThreadSafety">
<node CREATED="1695313928470" ID="ID_1264620312" MODIFIED="1695313943649" TEXT="dient hier zur Versch&#xe4;rfung des Thread"/>
<node CREATED="1695313944237" ID="ID_844947606" MODIFIED="1695313978092" TEXT="da wir &quot;nur&quot; 500 Iterationen fahren"/>
<node CREATED="1695313978944" ID="ID_820170937" MODIFIED="1695314027828" TEXT="...m&#xfc;ssen alle Worker m&#xf6;glichst gleichzeitig losrennen"/>
</node>
<node CREATED="1695315143388" ID="ID_356745606" MODIFIED="1695315156254" TEXT="microbenchmark.hpp : threadBenchmark()">
<node CREATED="1695315157154" ID="ID_1607681300" MODIFIED="1695315164920" TEXT="gleiche Problematik..."/>
<node CREATED="1695315165449" ID="ID_365200218" MODIFIED="1695315179331" TEXT="die Messung soll erst beginnen, wenn alle Threads starten"/>
<node CREATED="1695315180159" ID="ID_669527704" MODIFIED="1695315191154" TEXT="(und die Threads sollen m&#xf6;glichst gleichzeitig unterwegs sein)"/>
</node>
<node CREATED="1695314335069" ID="ID_841603246" MODIFIED="1695314336372" TEXT="SubsystemRunner_test">
<node CREATED="1695314337464" ID="ID_137940927" MODIFIED="1695314376718" TEXT="auch hier: zus&#xe4;tzliche Barriere f&#xfc;r beherrschbaren Test"/>
<node CREATED="1695314377451" ID="ID_1181532265" MODIFIED="1695314403731" TEXT="der Test spielt explizit das Scheitern in verschiedenen Phasen durch"/>
<node CREATED="1695314404385" ID="ID_1452050759" MODIFIED="1695314460156" TEXT="um die F&#xe4;lle klar unterscheiden zu k&#xf6;nnen, mu&#xdf; der Sub-Thread wenigstens laufen">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
sonst k&#246;nnte der Fall nicht zuverl&#228;ssig getestet werden, da&#223; der Test-Runner selber kurz nach dem Start stirbt
</p>
</body>
</html>
</richcontent>
</node>
</node>
<node CREATED="1695315325887" ID="ID_625375733" MODIFIED="1695315328199" TEXT="SessionCommandFunction_test">
<node CREATED="1695315329875" ID="ID_592889751" MODIFIED="1695315363091" TEXT="Threads erst nach Initialisierung der ganzen Klasse starten"/>
<node CREATED="1695315363798" ID="ID_146882783" MODIFIED="1695315376115" TEXT="die Klasse erbt von vault::Thread"/>
<node CREATED="1695315376877" ID="ID_1462759116" MODIFIED="1695315388279" TEXT="f&#xfc;gt aber noch weitere Felder hinzu"/>
<node CREATED="1695315389155" ID="ID_1933290982" MODIFIED="1695315407516" TEXT="und die Logik setzt wohldefinierten Anfangszustand vorraus"/>
</node>
<node CREATED="1695314551763" ID="ID_482710510" MODIFIED="1695314558086" TEXT="steam::control::DispatcherLoop">
<node CREATED="1695314559226" ID="ID_1190495284" MODIFIED="1695314574980" TEXT="verwendet eine eingebettete &#xbb;Looper&#xab;-Komponente"/>
<node CREATED="1695314579779" ID="ID_643512170" MODIFIED="1695314602498" TEXT="diese mu&#xdf; initialisiert sein, bevor der Session-Thread ihre Logik verwendet"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695315426871" ID="ID_1792980090" MODIFIED="1695315442534" TEXT="&#x27f9; Ersatzkonstrukt zwingend notwendig">
<icon BUILTIN="flag-yellow"/>
<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"/>
<node CREATED="1695334637793" ID="ID_681204225" MODIFIED="1695334721438" TEXT="spinning ohne yield w&#xe4;re gef&#xe4;hrlich im allgemeinen Fall">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
ein Spinlock sollte man nur verwenden, wenn man sicher sein kann, da&#223; man (a) fast nicht warten mu&#223; und (b) die CPU f&#252;r sich hat. Andernfalls kann das schlimmstenfalls zum Verhungern des ganzen Systems f&#252;hren...
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1695334757953" ID="ID_1716551234" MODIFIED="1695334788969">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
Mutex + ConditionVar ist auch <i>gar nicht so schlecht</i>&#160;<font size="2">(aber noch schwergewichtiger)</font>
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1695334869074" ID="ID_938671107" MODIFIED="1695335156463" TEXT="yield() gef&#xe4;llt mir &#x2014; weil es typischerweise genau um einen Thread-Schedule-delay geht">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Der Sprachstandard garantiert bereits, da&#223; der Thread startklar ist, wenn der std::thread-Konstruktor verlassen wird. Allerdings kann es immer passieren, da&#223; dummerweise der OS-Scheduler grade Anderes mit dieser CPU vor hat &#8212; und genau dann laufen wir in die Situation, vor der diese Sync-Barriere sch&#252;tzen soll; d.h. <i>sp&#228;testens nach einer Schedule-Runde</i>&#160;sollten alle Threads einmal vorbeigekommen sein.
</p>
</body>
</html>
</richcontent>
</node>
</node>
</node>
</node>
<node CREATED="1695336070266" ID="ID_1892470569" MODIFIED="1695336082188" TEXT="Thread::invokedWithinThread()">
<node CREATED="1695336097750" ID="ID_84784760" MODIFIED="1695336130714" TEXT="es gibt genau einen use-Case (in DispatcherLoop)"/>
<node CREATED="1695336140400" ID="ID_661758467" MODIFIED="1695336193541" TEXT="man k&#xf6;nnte diese Funktion mit Stdlib-Mitteln auch realisieren (std::this_thread::get_id)"/>
<node CREATED="1695336202544" ID="ID_818516761" MODIFIED="1695336271068" TEXT="daf&#xfc;r spricht, da&#xdf; dadurch eine Abstraktion geschaffen wird"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695336491346" ID="ID_940191365" MODIFIED="1695340427992" TEXT="Code-Anordnung">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1695339482171" ID="ID_781380757" MODIFIED="1695339496962" TEXT="Thread-Pool deaktivieren">
<icon BUILTIN="flag-pink"/>
<node CREATED="1695339498617" ID="ID_1329617125" MODIFIED="1695339543619" TEXT="umgezogene Tests gebrochen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
solange sie noch auf den alten Code gehen, der den Threadpool voraussetzt (welcher aber f&#252;r Library-Tests nicht zugelinkt wird)
</p>
</body>
</html>
</richcontent>
<node CREATED="1695339558729" ID="ID_1181374984" MODIFIED="1695339576171" TEXT="SyncLocking_test"/>
<node CREATED="1695339549851" ID="ID_1030547189" MODIFIED="1695339553543" TEXT="SyncClasslock_test"/>
<node CREATED="1695339568255" ID="ID_288867443" MODIFIED="1695339574891" TEXT="ThreadWrapper_test"/>
<node CREATED="1695339580526" ID="ID_592772431" MODIFIED="1695339584105" TEXT="ThreadWrapperJoin_test"/>
<node CREATED="1695339596332" ID="ID_164644014" MODIFIED="1695339607086" TEXT="ThreadWrapperSelfRecognition_test"/>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1695340324306" ID="ID_708290532" MODIFIED="1695340369662" TEXT="threadpool-init">
<icon BUILTIN="messagebox_warning"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1695340372505" ID="ID_115540630" MODIFIED="1695340397384" TEXT="war ohnehin ein Workaround, da backend-init gar nicht mehr l&#xe4;uft">
<icon BUILTIN="clanbomber"/>
</node>
<node COLOR="#338800" CREATED="1695340337153" ID="ID_1494450459" MODIFIED="1695340350326" TEXT="vorerst noch belassen, damit die Applikation selber noch startf&#xe4;hig ist">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695340351463" ID="ID_1816356053" MODIFIED="1695340361785" TEXT="schlie&#xdf;lich zur&#xfc;ckbauen">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695336500985" ID="ID_1001374907" MODIFIED="1695340319832" TEXT="alles wieder in die Support-Library ziehen">
<icon BUILTIN="yes"/>
<node CREATED="1695336524821" ID="ID_625640883" MODIFIED="1695336587478" TEXT="Begr&#xfc;ndung: es ist kein &#xbb;Vault-Feature&#xab; mehr">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Denn wir betreiben hier nun keine System-Einrichtungen mehr, keinen Resource-Collector und keinen Threadpool. Es handelt sich lediglich um Adapter &#252;ber der Std-Lib
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1695336588389" ID="ID_1245830716" MODIFIED="1695336597664" TEXT="au&#xdf;erdem erleichtert das den Schwenk"/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1695340272418" ID="ID_1240703198" MODIFIED="1695340308005" TEXT="Code aus namespace vault zur&#xfc;ckbauen">
<icon BUILTIN="flag-pink"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695340289647" ID="ID_456037035" MODIFIED="1695340305094" TEXT="thread-wrapper.hpp">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695340297038" ID="ID_1888990589" MODIFIED="1695340304054" TEXT="die Threadpool-Library">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1693789156806" ID="ID_128127845" MODIFIED="1693789195596" TEXT="Monitor erhalten und umschreiben">
@ -79612,6 +79778,15 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1693789376921" ID="ID_158507519" MODIFIED="1693789398154" TEXT="ein Monitor geh&#xf6;rt typischerweise in ein Framework"/>
<node CREATED="1693789399454" ID="ID_1417502572" MODIFIED="1693789408745" TEXT="und hier haben wir ein Applicatino-Framework"/>
<node CREATED="1693789410469" ID="ID_1281994248" MODIFIED="1693789423198" TEXT="in diesem Rahmen hat er sich hervorragend bew&#xe4;hrt"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1695337735932" HGAP="-30" ID="ID_293636243" MODIFIED="1695337749093" TEXT="Umarbeiten der Implementierung" VSHIFT="40">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1695337750146" ID="ID_99990028" MODIFIED="1695337765337" TEXT="brauchen wir das timedWait-Feature wirklich?">
<font NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="help"/>
<node CREATED="1695337787989" ID="ID_154693007" MODIFIED="1695337808761" TEXT="ich errinnere mich, das nebenbei mit eingebaut zu haben..."/>
<node CREATED="1695337809498" ID="ID_789251448" MODIFIED="1695337825212" TEXT="inzwischen gibt es da diverse bessere M&#xf6;glichkeiten (mit Futures)"/>
</node>
</node>
</node>
</node>
</node>