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:
parent
11cb53a406
commit
c9a0203492
4 changed files with 289 additions and 146 deletions
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -142,12 +142,12 @@ namespace test{
|
|||
, bind (&HavocThread::doIt, this)
|
||||
)
|
||||
{
|
||||
CHECK (thread_.isValid());
|
||||
CHECK (thread_);
|
||||
}
|
||||
|
||||
~HavocThread ()
|
||||
{
|
||||
if (thread_.isValid())
|
||||
if (thread_)
|
||||
thread_.join();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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...
|
||||
|
|
|
|||
|
|
@ -79021,7 +79021,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<node CREATED="1695314579779" ID="ID_643512170" MODIFIED="1695314602498" TEXT="diese muß initialisiert sein, bevor der Session-Thread ihre Logik verwendet"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1695315426871" FOLDED="true" ID="ID_1792980090" MODIFIED="1695484826031" TEXT="⟹ Ersatzkonstrukt zwingend notwendig">
|
||||
<node COLOR="#338800" CREATED="1695315426871" ID="ID_1792980090" MODIFIED="1695595904577" TEXT="⟹ Ersatzkonstrukt zwingend notwendig">
|
||||
<node CREATED="1695334520345" ID="ID_1748880887" MODIFIED="1695334539706" TEXT="sollte dann aber eine explizite Lib-Funktionalität sein"/>
|
||||
<node CREATED="1695334551413" ID="ID_257643954" LINK="https://stackoverflow.com/a/24218922" MODIFIED="1695334594067" TEXT="man könnte ein spinning-latch mit yield verwenden">
|
||||
<icon BUILTIN="idea"/>
|
||||
|
|
@ -79120,7 +79120,17 @@ Date:   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äquat für den Einsatzzweck"/>
|
||||
<node CREATED="1695580048111" ID="ID_784900194" MODIFIED="1695580199504" TEXT="es ist kein Overhead beobachtbar — jenseits der typischen Scheduling-Unschärfe">
|
||||
<node CREATED="1695580048111" ID="ID_784900194" MODIFIED="1695596554665" TEXT="es ist kein Overhead beobachtbar — jenseits der typischen Scheduling-Unschärfe">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Zum einen handelt es sich um bekannte Erfahrungswerte, zumindest für die Fälle mit wenigen Threads: daß zwei zusammen gestartete Threads um bis zu 0.5µs auseinanderlaufen, ist erwartbar, wesentlich mehr aber nicht ohne Weiteres. Und dann läßt es sich bestätigen, indem die Implementierung versuchsweise auf <b>busy-wait</b> umgestellt wird ⟹ für kleine Anzahl Threads bleiben die Meßwerte nahezu unverändert (sie sind minimal schlechter, aber das System geht auch in Vollast). Das bedeutet: die beobachteten Werte stellen bereits nahezu optimales Verhalten dar, fü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:   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önnte obsolet sein">
|
||||
<icon BUILTIN="help"/>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1695597040159" ID="ID_215388471" MODIFIED="1695597196367" TEXT="eine Kopie der bestehenden Lösung ⟼ 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ä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önnte man »wie mit C++ - Threads« programmieren"/>
|
||||
<node CREATED="1695600666050" ID="ID_642355226" MODIFIED="1695600701593" TEXT="das hätte aber auch den Nachteil, daß man die join/detach()-Unterscheidung unterläuft"/>
|
||||
<node CREATED="1695602083733" ID="ID_1989367844" MODIFIED="1695602231389" TEXT="also besser nicht exponieren — ggfs Funktionalität nachrüsten">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
YAGNI.
|
||||
</p>
|
||||
<p>
|
||||
Zunächst einmal so wenig Funktionalität wie möglich durchreichen, und das nur auf den festen Bahnen gemäß Design; im Zweifelsfall ist es besser, spezielle Funktionalitä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ärentes Problem mit diesem »Thread-Objekt« - 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ß das Ende des ctor-Aufrufs <i>synchronizes_with</i>  dem Start der Thread-Funktion. Streng logisch kann das aber nur für den std::thread-Konstruktor <i>selber </i>gelten (andernfalls hätte man mit einem Sequence-Point argumentieren müssen, und nicht mit dem ctor selber; das würde dann aber auch wieder eine unerwünschte Statefulness einführen, weil dann im gesamten umschließenden Ausdruck der Thread <i>eben noch nicht läuft, </i>was das RAII-Konzept untergraben würde).
|
||||
</p>
|
||||
<br/>
|
||||
<p>
|
||||
Das ist nun zwar ziemlich unwahrscheinlich (weil normalerweise der Scheduler immer eine erhebliche Zeit braucht, bis ein anderer Thread überhaupt zum Zug kommt), aber leider ist es demzufolge <i>theoretisch möglich, </i>daß eine im abgeleiteten Objekt definierte Thread-Funktionalität bereits auf eine noch nicht vollständig initialisierte Objektinstanz zugreift, oder daß die Initialisierung der abgeleiteten Klasse Werte in lokalen Feldern überschreibt, die aus der bereits startenden Thread-Funktion gesetzt wurden. Unerklärliches und reproduzierbares Verhalten wäre die Folge. Und das läßt sich aus dem Wrapper heraus nicht beheben (zumindest nicht ohne verwirrende Konventionen einzufü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> <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ü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 »function-like« ausfü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ü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ür joinable-Threads ⟼ sie durchlaufen einen Prozeß"/>
|
||||
<node CREATED="1695647620445" ID="ID_1998559139" MODIFIED="1695647729498" TEXT="es gibt die »Hintertür« mit dem protected default-Ctor">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Die hatte ich eingebaut, um für spezialisierte abgeleitete Klassen doch noch erweiterte Zustandsübergänge zu ermö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:   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µs"/>
|
||||
<node CREATED="1695576145934" ID="ID_786614029" MODIFIED="1695576215763" TEXT="SyncBarrier (16 Thr) : 9µs"/>
|
||||
<node CREATED="1695576145934" ID="ID_1548951986" MODIFIED="1695576221330" TEXT="SyncBarrier (32 Thr) : 21µs"/>
|
||||
<node CREATED="1695576145934" ID="ID_1877591130" MODIFIED="1695576230336" TEXT="SyncBarrier (48 Thr) : 30µs"/>
|
||||
<node CREATED="1695576145935" ID="ID_469124083" MODIFIED="1695576238800" TEXT="SyncBarrier (64 Thr) : 50µs"/>
|
||||
<node CREATED="1695576145935" ID="ID_754356198" MODIFIED="1695576257440" TEXT="SyncBarrier (80 Thr) : 80µ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µs"/>
|
||||
<node CREATED="1695576145934" MODIFIED="1695576215763" TEXT="SyncBarrier (16 Thr) : 9µs"/>
|
||||
<node CREATED="1695576145934" MODIFIED="1695576221330" TEXT="SyncBarrier (32 Thr) : 21µs"/>
|
||||
<node CREATED="1695576145934" MODIFIED="1695576230336" TEXT="SyncBarrier (48 Thr) : 30µs"/>
|
||||
<node CREATED="1695576145935" MODIFIED="1695576238800" TEXT="SyncBarrier (64 Thr) : 50µs"/>
|
||||
<node CREATED="1695576145935" MODIFIED="1695576257440" TEXT="SyncBarrier (80 Thr) : 80µs"/>
|
||||
<node CREATED="1695583795249" MODIFIED="1695583795249" TEXT="MonitorWait (2 Thr) : 7µs"/>
|
||||
<node CREATED="1695583795249" MODIFIED="1695583795249" TEXT="MonitorWait (4 Thr) : 12µs"/>
|
||||
<node CREATED="1695583795250" MODIFIED="1695583795250" TEXT="MonitorWait (8 Thr) : 27µs"/>
|
||||
<node CREATED="1695583795250" MODIFIED="1695583795250" TEXT="MonitorWait (16 Thr) : 75µs"/>
|
||||
<node CREATED="1695595955182" HGAP="8" ID="ID_1818362689" MODIFIED="1695596168103" TEXT="Ergänzung: busy-wait" VSHIFT="14">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1695595970341" MODIFIED="1695595980004" TEXT="degeneriert brutal > 8 Cores"/>
|
||||
<node CREATED="1695595980802" MODIFIED="1695596009566" TEXT="ist für die kleinen Zeiten minimal aufwendiger">
|
||||
<node CREATED="1695596024582" MODIFIED="1695596055154" TEXT="2 Threads ⟼ 340ns"/>
|
||||
<node CREATED="1695596055953" MODIFIED="1695596074107" TEXT="4 Threads ⟼ 910ns"/>
|
||||
<node CREATED="1695596080358" MODIFIED="1695596122796" TEXT="8 Threads ⟼ 2200ns"/>
|
||||
</node>
|
||||
<node CREATED="1695596136607" MODIFIED="1695596156584" TEXT="⟹ bestätigt: wir messen die Scheduler-Unschärfe"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1695576264352" ID="ID_1266717950" MODIFIED="1695578848332" TEXT="Debug-Buid-Werte weichen nur wenig ab">
|
||||
<icon BUILTIN="idea"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue