Library: policy for self-managed thread
...after resolving the fundamental design problems, a policy mix-in can be defined now for a thread that deletes its own wrapper at the end of the thread-function. Such a setup would allow for »fire-and-forget« threads, but with wrapper and ensuring safe allocations. The prominent use case for such a setup would be the GUI-Thread.
This commit is contained in:
parent
dd2fe7da59
commit
5f9683ef10
5 changed files with 197 additions and 63 deletions
|
|
@ -67,17 +67,17 @@ namespace thread{
|
|||
|
||||
|
||||
void
|
||||
ThreadWrapper::markThreadStart (string id)
|
||||
ThreadWrapper::markThreadStart()
|
||||
{
|
||||
TRACE (thread, "%s", lifecycleMsg ("start...", id).c_str());
|
||||
TRACE (thread, "%s", lifecycleMsg ("start...", threadID_).c_str());
|
||||
setThreadName();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ThreadWrapper::markThreadEnd(string id)
|
||||
ThreadWrapper::markThreadEnd()
|
||||
{
|
||||
TRACE (thread, "%s", lifecycleMsg ("finished.", id).c_str());
|
||||
TRACE (thread, "%s", lifecycleMsg ("finished.", threadID_).c_str());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@
|
|||
#include "lib/error.hpp"
|
||||
#include "lib/nocopy.hpp"
|
||||
#include "include/logging.h"
|
||||
#include "lib/meta/trait.hpp"
|
||||
#include "lib/format-util.hpp"
|
||||
#include "lib/result.hpp"
|
||||
|
||||
|
|
@ -207,10 +208,15 @@ namespace lib {
|
|||
/** detect if the currently executing code runs within this thread */
|
||||
bool invokedWithinThread() const;
|
||||
|
||||
void markThreadStart(string);
|
||||
void markThreadEnd (string);
|
||||
void markThreadStart();
|
||||
void markThreadEnd ();
|
||||
void setThreadName ();
|
||||
void waitGracePeriod() noexcept;
|
||||
|
||||
/* empty implementation for some policy methods */
|
||||
void handle_begin_thread() { }
|
||||
void handle_after_thread() { }
|
||||
void handle_thread_still_running() { }
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -222,7 +228,7 @@ namespace lib {
|
|||
* - thread detaches before terminating
|
||||
* - »grace period« for thread to terminate on shutdown
|
||||
*/
|
||||
template<class BAS, typename>
|
||||
template<class BAS, typename=void>
|
||||
struct PolicyLaunchOnly
|
||||
: BAS
|
||||
{
|
||||
|
|
@ -240,7 +246,7 @@ namespace lib {
|
|||
}
|
||||
|
||||
void
|
||||
handle_end_of_thread()
|
||||
handle_after_thread()
|
||||
{
|
||||
if (BAS::isLive())
|
||||
BAS::threadImpl_.detach();
|
||||
|
|
@ -254,6 +260,36 @@ namespace lib {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* Thread Lifecycle Policy Extension:
|
||||
* additionally self-manage the thread-wrapper allocation.
|
||||
* @warning the thread-wrapper must have been heap-allocated.
|
||||
*/
|
||||
template<class BAS, class TAR>
|
||||
struct PolicySelfManaged
|
||||
: PolicyLaunchOnly<BAS>
|
||||
{
|
||||
using BasePol = PolicyLaunchOnly<BAS>;
|
||||
using BasePol::BasePol;
|
||||
|
||||
void
|
||||
handle_after_thread()
|
||||
{
|
||||
TAR* selfAllocation = static_cast<TAR*>(
|
||||
static_cast<void*> (this));
|
||||
if (BAS::isLive())
|
||||
BAS::threadImpl_.detach();
|
||||
delete selfAllocation;
|
||||
}
|
||||
|
||||
void
|
||||
handle_thread_still_running()
|
||||
{
|
||||
ALERT (thread, "Self-managed thread was deleted from outside. Abort.");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Thread Lifecycle Policy:
|
||||
* - thread with the ability to publish results
|
||||
|
|
@ -286,7 +322,7 @@ namespace lib {
|
|||
}
|
||||
|
||||
void
|
||||
handle_end_of_thread()
|
||||
handle_after_thread()
|
||||
{
|
||||
/* do nothing -- thread must be joined manually */;
|
||||
}
|
||||
|
|
@ -312,11 +348,11 @@ namespace lib {
|
|||
void
|
||||
invokeThreadFunction (ARGS&& ...args)
|
||||
{
|
||||
string id{Policy::threadID_}; // local copy
|
||||
Policy::markThreadStart(id);
|
||||
Policy::handle_begin_thread();
|
||||
Policy::markThreadStart();
|
||||
Policy::perform_thread_function (forward<ARGS> (args)...);
|
||||
Policy::markThreadEnd(id);
|
||||
Policy::handle_end_of_thread();
|
||||
Policy::markThreadEnd();
|
||||
Policy::handle_after_thread();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -490,7 +526,7 @@ namespace lib {
|
|||
* @deprecated can't sleep well while this function is exposed;
|
||||
* need a prime solution to address this relevant use case ////////////////////////////////////////OOO allow for a thread with explicit lifecycle
|
||||
*/
|
||||
void detach() { ThreadLifecycle::handle_end_of_thread(); }
|
||||
void detach() { ThreadLifecycle::handle_after_thread(); }
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -541,5 +577,34 @@ namespace lib {
|
|||
|
||||
|
||||
|
||||
/************************************************************************//**
|
||||
* Special configuration for a »fire-and-forget«-Thread.
|
||||
* @internal this class is meant for subclassing. Start with #launchDetached()
|
||||
* @tparam TAR the concrete type of the subclass to be started as autonomous,
|
||||
* self-managed thread. Must be passed down since thread deletes itself.
|
||||
*/
|
||||
class ThreadAutonomous
|
||||
: public thread::ThreadLifecycle<thread::PolicySelfManaged, ThreadAutonomous>
|
||||
{
|
||||
using Impl = thread::ThreadLifecycle<thread::PolicySelfManaged, ThreadAutonomous>;
|
||||
public:
|
||||
using Impl::Impl;
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch an autonomous self-managing thread (and forget about it).
|
||||
* The thread-wrapper is allocated to the heap and will delete itself on termination.
|
||||
* @tparam TAR concrete type of the subclass to be started as autonomous detached thread.
|
||||
* @param args a valid argument list to call the ctor of thread::ThreadLifecycle
|
||||
*/
|
||||
template<typename...INVO>
|
||||
inline void
|
||||
launchDetached (INVO&& ...args)
|
||||
{
|
||||
new ThreadAutonomous{forward<INVO> (args)...}; // Thread will pick up and manage *this
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace lib
|
||||
#endif /*LIB_THREAD_H*/
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ namespace control {
|
|||
{
|
||||
return not queue_.empty();
|
||||
})
|
||||
, thread_{"Lumiera Session"
|
||||
, thread_{"Session"
|
||||
,&DispatcherLoop::runSessionThread
|
||||
, this, notification}
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,19 +27,15 @@
|
|||
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/thread.hpp"
|
||||
#include "lib/iter-explorer.hpp"
|
||||
#include "lib/scoped-collection.hpp"
|
||||
#include "lib/test/microbenchmark.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
|
||||
using test::Test;
|
||||
using lib::explore;
|
||||
using std::atomic_uint;
|
||||
using std::this_thread::yield;
|
||||
using std::this_thread::sleep_for;
|
||||
using std::chrono::microseconds;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
|
@ -74,11 +70,10 @@ namespace test{
|
|||
demonstrateSimpleUsage()
|
||||
{
|
||||
atomic_uint i{0};
|
||||
Thread thread("counter", [&]{ ++i; }); // bind a λ and launch thread
|
||||
while (thread) yield(); // ensure thread has finished and detached
|
||||
launchDetached ("anarchy", [&]{ ++i; });
|
||||
|
||||
sleep_for(1ms);
|
||||
CHECK (i == 1); // verify the effect has taken place
|
||||
UNIMPLEMENTED ("actually launch detached");
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -88,6 +83,7 @@ namespace test{
|
|||
void
|
||||
verifyMemoryManagement()
|
||||
{
|
||||
UNIMPLEMENTED ("verify thread manages itself");
|
||||
struct TestThread
|
||||
: Thread
|
||||
{
|
||||
|
|
@ -99,39 +95,10 @@ namespace test{
|
|||
doIt (uint a, uint b) ///< the actual operation running in a separate thread
|
||||
{
|
||||
uint sum = a + b;
|
||||
sleep_for (microseconds{sum}); // Note: explicit random delay before local store
|
||||
// sleep_for (microseconds{sum}); // Note: explicit random delay before local store
|
||||
local = sum;
|
||||
}
|
||||
};
|
||||
|
||||
// prepare Storage for these objects (not created yet)
|
||||
lib::ScopedCollection<TestThread> threads{NUM_THREADS};
|
||||
|
||||
size_t checkSum = 0;
|
||||
size_t globalSum = 0;
|
||||
auto launchThreads = [&]
|
||||
{
|
||||
for (uint i=1; i<=NUM_THREADS; ++i)
|
||||
{
|
||||
uint x = rand() % 1000;
|
||||
globalSum += (i + x);
|
||||
threads.emplace (&TestThread::doIt, i, x);
|
||||
} // Note: bind to member function, copying arguments
|
||||
|
||||
while (explore(threads).has_any())
|
||||
yield(); // wait for all threads to have detached
|
||||
|
||||
for (auto& t : threads)
|
||||
{
|
||||
CHECK (0 < t.local);
|
||||
checkSum += t.local;
|
||||
}
|
||||
};
|
||||
|
||||
double runTime = benchmarkTime (launchThreads, REPETITIONS);
|
||||
|
||||
CHECK (checkSum == globalSum); // sum of precomputed random numbers matches sum from threads
|
||||
CHECK (runTime < NUM_THREADS * 1000/2); // random sleep time should be > 500ms on average
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -65196,6 +65196,13 @@
|
|||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<node CREATED="1696890454577" ID="ID_430553780" MODIFIED="1696890460170" TEXT="geht so gar nicht">
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
</node>
|
||||
<node CREATED="1696890461256" ID="ID_1183918146" MODIFIED="1696890497276" TEXT="ein unique_ptr würde ja nie gelöscht">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node CREATED="1696890472062" ID="ID_484865968" MODIFIED="1696890493175" TEXT="⟹ vielmehr muß sich das Objekt selber einfach vom Heap löschen"/>
|
||||
</node>
|
||||
<node CREATED="1696529718984" ID="ID_1539716722" MODIFIED="1696529944892" TEXT="zusätzlichen Erweiterungspunkt nutzen">
|
||||
<arrowlink COLOR="#fdfcc6" DESTINATION="ID_1373519021" ENDARROW="Default" ENDINCLINATION="-114;10;" ID="Arrow_ID_736802000" STARTARROW="None" STARTINCLINATION="116;8;"/>
|
||||
|
|
@ -65234,10 +65241,76 @@
|
|||
<icon BUILTIN="forward"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1696532299942" ID="ID_596357210" MODIFIED="1696532380050" TEXT="die jeweilige Front-End-Klasse nur in der Factory definieren">
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696898745626" ID="ID_1404625774" MODIFIED="1696898766166" TEXT="muß einen gewaltsamen Upcast machen">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1696898767838" ID="ID_982197270" MODIFIED="1696898773034" TEXT="und zwar über void*"/>
|
||||
<node CREATED="1696898773597" ID="ID_1027296232" MODIFIED="1696898787176" TEXT="weil wir einmal protected-Inheritance verwenden"/>
|
||||
<node CREATED="1696898788100" ID="ID_229678951" MODIFIED="1696898804634" TEXT=""PolicyAutonomous" is inaccessible base">
|
||||
<font ITALIC="true" NAME="SansSerif" SIZE="12"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696898808050" ID="ID_649658845" MODIFIED="1696898912722" TEXT="wäre potentiell gefährlich">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
und zwar, sofern oben in der konkreten Subklasse irgendwo ein mix-in verwendet wird — in dem Fall müßte nämlich der Pointer nachjustiert werden
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="clanbomber"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696532299942" ID="ID_596357210" MODIFIED="1696898738780" TEXT="die jeweilige Front-End-Klasse nur in der Factory definieren">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1696897248135" ID="ID_433063268" MODIFIED="1696897258683" TEXT="Subklassen ermöglichen?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1696897270002" ID="ID_944165024" MODIFIED="1696897420063">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<i>ohne Subclassing</i> ist die ganze Übung etwas überzogen
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...es ist ja nett, daß der Thread-Wrapper sich selbst verwaltet — und der ganze Aufwand bloß für zwei interne Datenfelder?  wäre da nicht ein »workaround« angemessener?
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1696897426970" ID="ID_419436552" MODIFIED="1696898701788" TEXT="mit Subklasse wird aber das Front-End fragiler — und beliebiger">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
wozu dann überhaupt noch das Front-End? Um einen etwas sonderbaren new-Aufruf einzupacken?
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1696897485658" ID="ID_885031549" MODIFIED="1696897505971" TEXT="Problem: muß die konkrete Klasse als Template-Parameter in die Policy einführen">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1696897535396" ID="ID_1827915916" MODIFIED="1696897641026" TEXT="das macht die ganze Kette ziemlich komplex"/>
|
||||
<node CREATED="1696897641633" ID="ID_309942358" MODIFIED="1696897719398" TEXT="auch das Front-End braucht nun einen Template-Parameter"/>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1696897720103" ID="ID_785903718" MODIFIED="1696898648512" TEXT="noch schlimmer: das verhindert Verwendung ohne Subclassing">
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1696532404400" ID="ID_1917558827" MODIFIED="1696539443047" TEXT="Variante-2 : optional-Lifecycle">
|
||||
<linktarget COLOR="#898bab" DESTINATION="ID_1917558827" ENDARROW="Default" ENDINCLINATION="-1027;99;" ID="Arrow_ID_1553665185" SOURCE="ID_104020495" STARTARROW="None" STARTINCLINATION="532;77;"/>
|
||||
|
|
@ -65571,7 +65644,7 @@
|
|||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1696534813654" ID="ID_1412510556" MODIFIED="1696535910711" TEXT="was aber exterm unwahrscheinlich ist">
|
||||
<node CREATED="1696534813654" ID="ID_1412510556" MODIFIED="1696890781755" TEXT="was aber exterm unwahrscheinlich ist">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
|
|
@ -65586,11 +65659,11 @@
|
|||
das Kopieren des Funktors und der Argumente ebenfalls ohne Probleme erfolgt sein
|
||||
</li>
|
||||
<li>
|
||||
aber <i>danach</i> ein Problem <i>im bereits eingerichteten Thread</i> auftreten, bevor unser Code den Manager (smart-ptr) aktivieren kann.
|
||||
aber <i>danach</i> ein Problem <i>im bereits eingerichteten Thread</i> auftreten, bevor unser in den try-catch-Block eintritt.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
Hierzu kommt nur wenig in Frage: einmal die <i>Invocation</i> des Funktors selber mit als Wert vorliegenden Parametern, sowie dann unser eigener Library-Code bis zu der Stelle, an der der Pointer <i>gesichert</i> ist. Da sehe ich im Moment wenig Raum für Fehler. Funktoren sind ja entweder Pointer oder Objekte, und durch die erzwungene Kopie sind die tatsächlich dann aktiven Instanzen bereits konstruiert. Ein std::function verwendet zwar einen <i>Invoker, </i>aber das ist ein Trampolin, um die verschiedenen Aufruf-Technologien zu nivellieren; mir ist nicht bekannt, daß das <i>für den Aufruf</i> noch irgend etwas macht, was scheitern könnte. Bleiben also nur noch <b>spezielle esoterische Argument-Typen</b>. Und außerdem müßte der Optimiser so dämlich sein, ein bereits kopiertes Argument<i> noch einmal zu kopieren</i> nur für den Aufruf. Abgesehen davon könnten diese »esoterischen« Typen nur bei den weiteren Funktions-Argumenten auftreten (nicht der Funktor, nicht der this-Pointer). Danach haben wir nur noch einen nicht-virtuellen Aufruf in die Policy (ist inline) und das speichern des this-Pointers in den smart-ptr. Wenn <i>danach</i> noch was passiert, terminiert der Thread, der Funktor wird de-alloziert, und der darin eingebettete smart-ptr waltet seines Amtes
|
||||
Hierzu kommt nur wenig in Frage: einmal die <i>Invocation</i> des Funktors selber mit als Wert vorliegenden Parametern, sowie dann unser eigener Library-Code bis zu der Stelle, an der der control-flow in den try-catch eintritt, sowie das Logging danach. Da sehe ich im Moment wenig Raum für Fehler. Funktoren sind ja entweder Pointer oder Objekte, und durch die erzwungene Kopie sind die tatsächlich dann aktiven Instanzen bereits konstruiert. Ein std::function verwendet zwar einen <i>Invoker, </i>aber das ist ein Trampolin, um die verschiedenen Aufruf-Technologien zu nivellieren; mir ist nicht bekannt, daß das <i>für den Aufruf</i> noch irgend etwas macht, was scheitern könnte. Bleiben also nur noch <b>spezielle esoterische Argument-Typen</b>. Und außerdem müßte der Optimiser so dämlich sein, ein bereits kopiertes Argument<i> noch einmal zu kopieren</i> nur für den Aufruf. Abgesehen davon könnten diese »esoterischen« Typen nur bei den weiteren Funktions-Argumenten auftreten (nicht der Funktor, nicht der this-Pointer). Danach haben wir nur noch nicht-virtuelle Aufrufe in die Policy (ist inline) und das Logging — <i>dieses</i>  stellt vermutlich <i>die größte Gefahr dar; </i>da hier aber kein schützender try-catch mehr darüber liegt, führen Fehler hier sofort zu std::terminate.
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
|
|
@ -65769,8 +65842,7 @@
|
|||
<font color="#1b0d7b" face="Monospaced" size="2">                           .atEnd(terminationHook)}); </font>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1696774321215" ID="ID_1696275940" MODIFIED="1696774330486" TEXT="Suche nach sonstigen Alternativen vertagt">
|
||||
|
|
@ -65829,8 +65901,7 @@
|
|||
wenn halt Argument-Packs einfach als Typ repräsentierbar wären — aber dem ist nicht so; man muß gegen ein getemplatetes Argument matchen, um aus einem Argument-Pack einen anderen Argument-Pack zu konstruieren
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1696812473885" ID="ID_1487641552" MODIFIED="1696812488838" TEXT="wie üblich: std::index_sequence_for<TYPES...>"/>
|
||||
</node>
|
||||
|
|
@ -65896,7 +65967,17 @@
|
|||
<node CREATED="1696538729479" ID="ID_1821271950" MODIFIED="1696538732072" TEXT="handle_begin_thread"/>
|
||||
<node CREATED="1696538713759" ID="ID_743139495" MODIFIED="1696538713759" TEXT="handle_after_thread"/>
|
||||
</node>
|
||||
<node CREATED="1696538607372" ID="ID_479618351" MODIFIED="1696538611095" TEXT="für Variante-1"/>
|
||||
<node CREATED="1696538607372" ID="ID_479618351" MODIFIED="1696538611095" TEXT="für Variante-1">
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1696890808413" ID="ID_569216542" LINK="#ID_1183918146" MODIFIED="1696890916407" TEXT="es zeigt sich: ein smart-ptr löst das Problem gar nicht">
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
</node>
|
||||
<node CREATED="1696890822723" ID="ID_1039222161" MODIFIED="1696890919584" TEXT="damit ist aber auch keine eigene Storage mehr notwendig">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1696890851961" ID="ID_397553721" MODIFIED="1696890865991" TEXT="und auch kein pre-Hook; einfach sich selbst wegschießen und gut is">
|
||||
<icon BUILTIN="ksmiletris"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1696538612261" ID="ID_661367597" MODIFIED="1696538614839" TEXT="für Variante-2"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1696690988844" ID="ID_480585061" MODIFIED="1696861459249" TEXT="das Race-Problem addressieren">
|
||||
|
|
@ -65955,6 +66036,18 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696893013891" ID="ID_1856740667" MODIFIED="1696893025601" TEXT="damit bestehende Probleme im Applikations-Code lösen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1696893026770" ID="ID_253995958" MODIFIED="1696893031336" TEXT="GtkLumiera">
|
||||
<node CREATED="1696893056374" MODIFIED="1696893056374" TEXT="Variante-1"/>
|
||||
</node>
|
||||
<node CREATED="1696893037535" ID="ID_1340852722" MODIFIED="1696893043218" TEXT="OutputDirector">
|
||||
<node CREATED="1696893058148" MODIFIED="1696893058148" TEXT="Variante-1"/>
|
||||
</node>
|
||||
<node CREATED="1696893032057" ID="ID_926131453" MODIFIED="1696893046381" TEXT="SteamDispatcher">
|
||||
<node CREATED="1696893059260" ID="ID_109825250" MODIFIED="1696893063103" TEXT="Variante-2"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1696538743210" ID="ID_1370366397" MODIFIED="1696538750437" TEXT="Tests ergänzen">
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696538756712" ID="ID_1688404846" MODIFIED="1696541608160" TEXT="ThreadWrapperAutonomous_test">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
|
|
@ -82097,10 +82190,19 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<arrowlink COLOR="#bc3562" DESTINATION="ID_1146069423" ENDARROW="Default" ENDINCLINATION="444;909;" ID="Arrow_ID_516910789" STARTARROW="None" STARTINCLINATION="-1280;-32;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node CREATED="1696468692302" ID="ID_1299573982" MODIFIED="1696468713991" TEXT="SteamDispatcher und OutputDirector brauchen das ebenso"/>
|
||||
<node CREATED="1696468692302" ID="ID_1299573982" MODIFIED="1696468713991" TEXT="SteamDispatcher und OutputDirector brauchen das ebenso">
|
||||
<arrowlink COLOR="#e71f4f" DESTINATION="ID_871343805" ENDARROW="Default" ENDINCLINATION="173;10;" ID="Arrow_ID_1685700346" STARTARROW="None" STARTINCLINATION="-14;8;"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696029465122" ID="ID_871343805" MODIFIED="1696541248860" TEXT="output-director.cpp">
|
||||
<linktarget COLOR="#e71f4f" DESTINATION="ID_871343805" ENDARROW="Default" ENDINCLINATION="173;10;" ID="Arrow_ID_1685700346" SOURCE="ID_1299573982" STARTARROW="None" STARTINCLINATION="-14;8;"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1696888656242" HGAP="33" ID="ID_1500509085" MODIFIED="1696888747350" STYLE="fork" TEXT="Symptom hier: bad_alloc im Shutdown" VSHIFT="17">
|
||||
<icon BUILTIN="broken-line"/>
|
||||
<node CREATED="1696888694193" ID="ID_1029299240" MODIFIED="1696888746935" TEXT="dtor des Threads stellt isLife() fest"/>
|
||||
<node CREATED="1696888714635" ID="ID_1361206868" MODIFIED="1696888746935" TEXT="versucht Warnung ausgzugeben"/>
|
||||
<node CREATED="1696888722382" ID="ID_1232713816" MODIFIED="1696888746935" TEXT="aber der string threadID ist bereits korrumpiert"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5a3" COLOR="#883300" CREATED="1696029465122" ID="ID_556208204" MODIFIED="1696539012684" TEXT="steam-dispatcher.cpp">
|
||||
<linktarget COLOR="#71be6f" DESTINATION="ID_556208204" ENDARROW="Default" ENDINCLINATION="1038;-401;" ID="Arrow_ID_1257115855" SOURCE="ID_1718267866" STARTARROW="None" STARTINCLINATION="602;40;"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue