LibraryApplication: tie DispatcherLoop to thread lifecycle

This solution is basically equivalent to the version implemented directly,
but uses the lifecycle-Hooks available through `ThreadHookable`
to structure the code and separate the concerns better.

This largely completes the switch to the new thread-wrapper..

**the old implementation is not referenced anymore**
This commit is contained in:
Fischlurch 2023-10-12 20:23:59 +02:00
parent 184c93750a
commit 1ffee39b23
8 changed files with 149 additions and 98 deletions

View file

@ -183,7 +183,11 @@ namespace lumiera {
INFO (subsystem, "Triggering startup of subsystem \"%s\"", cStr(*susy)); INFO (subsystem, "Triggering startup of subsystem \"%s\"", cStr(*susy));
for_each (susy->getPrerequisites(), start_); for_each (susy->getPrerequisites(), start_);
bool started = susy->start (opts_, bind (&SubsystemRunner::sigTerm, this, susy, _1)); bool started = susy->start (opts_, [this,susy]
(string* problem)
{
this->sigTerm (susy, problem);
});
if (started) if (started)
{ {
@ -204,9 +208,9 @@ namespace lumiera {
{ {
REQUIRE (susy); REQUIRE (susy);
Lock sync (this); Lock sync (this);
triggerEmergency(!isnil (problem)); triggerEmergency(not isnil (problem));
INFO (subsystem, "Subsystem '%s' terminated.", cStr(*susy)); INFO (subsystem, "Subsystem '%s' terminated.", cStr(*susy));
WARN_IF (!isnil(problem), subsystem, "Irregular shutdown caused by: %s", cStr(*problem)); WARN_IF (not isnil(problem), subsystem, "Irregular shutdown caused by: %s", cStr(*problem));
ERROR_IF (susy->isRunning(), subsystem, "Subsystem '%s' signals termination, " ERROR_IF (susy->isRunning(), subsystem, "Subsystem '%s' signals termination, "
"without resetting running state", cStr(*susy)); "without resetting running state", cStr(*susy));
removeall (running_, susy); removeall (running_, susy);

View file

@ -77,7 +77,7 @@ namespace thread{
void void
ThreadWrapper::markThreadEnd() ThreadWrapper::markThreadEnd()
{ {
TRACE (thread, "%s", lifecycleMsg ("finished.", threadID_).c_str()); TRACE (thread, "%s", lifecycleMsg ("terminates.", threadID_).c_str());
} }

View file

@ -270,8 +270,7 @@ namespace lib {
void void
handle_after_thread() handle_after_thread()
{ {
if (BAS::isLive()) BAS::detach_thread_from_wrapper();
BAS::threadImpl_.detach();
} }
void void
@ -316,8 +315,8 @@ namespace lib {
{ {
if (hook_afterThread) if (hook_afterThread)
hook_afterThread (*this); hook_afterThread (*this);
if (BAS::isLive()) // Note: ensure thread is detached at end // Note: ensure thread is detached at end
BAS::threadImpl_.detach(); BAS::detach_thread_from_wrapper();
} }
void void
@ -649,16 +648,6 @@ namespace lib {
{ {
public: public:
using ThreadLifecycle::ThreadLifecycle; using ThreadLifecycle::ThreadLifecycle;
/** allow to detach explicitly — independent from thread-function's state
* @warning this function is borderline dangerous; it might be acceptable
* in a situation where the thread totally manages itself and the
* thread object is maintained in a unique_ptr. You must ensure that
* the thread function only uses storage within its own scope.
* @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_after_thread(); }
}; };

View file

@ -92,7 +92,7 @@ namespace stage {
class GtkLumiera class GtkLumiera
: util::NonCopyable : util::NonCopyable
{ {
UiBus uiBus_; UiBus uiBus_;
UiManager uiManager_; UiManager uiManager_;
public: public:

View file

@ -26,7 +26,7 @@
** The SteamDispatcher is at the heart of the session subsystem and implements a ** The SteamDispatcher is at the heart of the session subsystem and implements a
** (single) session thread to perform commands and trigger builder runs. New commands ** (single) session thread to perform commands and trigger builder runs. New commands
** can be enqueued with a dedicated CommandQueue, while the details of operation control ** can be enqueued with a dedicated CommandQueue, while the details of operation control
** logic are encapsulated in a [logic component](\ref Looper). ** logic are encapsulated in a [processing logic component](\ref Looper).
** **
** # Operational Semantics ** # Operational Semantics
** We need to distinguish between the SteamDispatcher itself, which is a static (singleton) service, ** We need to distinguish between the SteamDispatcher itself, which is a static (singleton) service,
@ -34,15 +34,15 @@
** while the Session itself is a data structure and can be closed, opened or re-loaded. There is a singular ** while the Session itself is a data structure and can be closed, opened or re-loaded. There is a singular
** transactional access point to the Session datastructure, which can be switched to new session contents. ** transactional access point to the Session datastructure, which can be switched to new session contents.
** But external manipulation of the session contents is performed by commands, which are _dispatched_ -- ** But external manipulation of the session contents is performed by commands, which are _dispatched_ --
** to manage this process is the concern of the »Session Subsystem«. ** and the management of this process is the concern served by the »Session Subsystem«.
** **
** Closing a session blocks further command processing, while the lifecycle of the _Session Subsystem_ is ** Closing a session blocks further command processing, while the lifecycle of the _Session Subsystem_ is
** actually linked to _running the \ref DispatcherLoop_ -- implementation logic defined within this ** actually linked to _running the \ref DispatcherLoop_ -- a piece of implementation logic defined within this
** translation unit. This loop implementation is performed in a dedicated thread, _the Session Loop Thread._ ** translation unit. The loop implementation is performed within a dedicated thread, _the Session Loop Thread._
** And this also entails opening the public SessionCommandService interface. ** And this also entails opening the public SessionCommandService interface.
** **
** ## Loop operation control ** ## Loop operation control
** The loop starts with a blocking wait state, bound to the condition Looper::requireAction. Here, Looper ** The loop starts with a blocking wait state, bound to the condition Looper::requireAction. The Looper
** is a helper to encapsulate the control logic, separated from the actual control flow. In the loop body, ** is a helper to encapsulate the control logic, separated from the actual control flow. In the loop body,
** depending on the Looper's decision, either the next command is fetched from the CommandQueue and dispatched, ** depending on the Looper's decision, either the next command is fetched from the CommandQueue and dispatched,
** or a builder run is triggered, rebuilding the »Low-Level-Model« to reflect the executed command's effects. ** or a builder run is triggered, rebuilding the »Low-Level-Model« to reflect the executed command's effects.
@ -53,21 +53,21 @@
** - yet we continue to dispatch further commands, until the queue is emptied ** - yet we continue to dispatch further commands, until the queue is emptied
** - and only after a further small latency wait, the builder run is triggered ** - and only after a further small latency wait, the builder run is triggered
** - but we _enforce a builder run_ after some extended timeout period, even ** - but we _enforce a builder run_ after some extended timeout period, even
** when the command queue is not yet emptied ** when the command queue is not emptied yet
** - from the outside, it is possible to deactivate processing and place the ** - from the outside, it is possible to deactivate processing and place the
** loop into dormant state. This is used while closing or loading the Session ** loop into dormant state. This is used while closing or loading the Session
** - and of course we can request the Session Loop Thread to stop, for shutting ** - and of course we can request the Session Loop Thread to stop, for shutting
** down the »Session Subsystem« as a whole ** down the »Session Subsystem« as a whole
** - in both cases the currently performed action (command or builder) is ** - in both cases the currently performed action (command or builder) is
** finished, without interrupt ** finished, without interruption.
** **
** ## Locking ** ## Locking
** The SteamDispatcher uses an "inner and outer capsule" design, and both layers are locked independently. ** The SteamDispatcher uses an "inner and outer capsule" design, and both layers are locked independently.
** On the outer layer, locking ensures sanity of the control data structures, while locking on the inner ** On the outer layer, locking ensures sanity of the control data structures, while locking on the inner
** layer guards the communication with the Session Loop Thread, and coordinates sleep wait and notification. ** layer guards the communication with the Session Loop Thread, and coordinates sleep wait and notification.
** As usual with Lumiera's Thread wrapper, the management of the thread's lifecycle itself, hand-over of ** As usual with Lumiera's Thread wrapper, the management of the thread's lifecycle itself, hand-over of
** parameters, and starting / joining of the thread operation is protected by separate locking embedded ** parameters, and starting / joining of the thread operation is protected by means of synchronisation
** into the thread and threadpool handling code. ** embedded into the underlying implementation (C++ standard library thread support).
** @note most of the time, the Session Loop Thread does not hold any lock, most notably while performing ** @note most of the time, the Session Loop Thread does not hold any lock, most notably while performing
** a command or running the builder. Likewise, evaluation of the control logic in the Looper helper ** a command or running the builder. Likewise, evaluation of the control logic in the Looper helper
** is a private detail of the performing thread. The lock is acquired solely for checking or leaving ** is a private detail of the performing thread. The lock is acquired solely for checking or leaving
@ -93,13 +93,15 @@
#include "lib/thread.hpp" #include "lib/thread.hpp"
#include "lib/util.hpp" ///////////////TODO for test command invocation #include "lib/util.hpp" ///////////////TODO for test command invocation
#include <utility>
#include <memory> #include <memory>
using lib::Sync; using lib::Sync;
using lib::RecursiveLock_Waitable;
using lib::SyncBarrier; using lib::SyncBarrier;
using lib::Thread; using lib::ThreadHookable;
using std::unique_ptr; using lib::RecursiveLock_Waitable;
using std::make_unique;
using std::move;
namespace steam { namespace steam {
namespace control { namespace control {
@ -119,14 +121,16 @@ namespace control {
, public Sync<RecursiveLock_Waitable> , public Sync<RecursiveLock_Waitable>
{ {
using ServiceHandle = lib::DependInject<SessionCommandService>::ServiceInstance<>; using ServiceHandle = lib::DependInject<SessionCommandService>::ServiceInstance<>;
using Launch = lib::ThreadHookable::Launch;
/** manage the primary public Session interface */ /** manage the primary public Session interface */
ServiceHandle commandService_; ServiceHandle commandService_;
SyncBarrier init_; SyncBarrier init_;
CommandQueue queue_; CommandQueue queue_;
Looper looper_; string error_;
Thread thread_; Looper looper_;
ThreadHookable thread_;
public: public:
/** start the session loop thread /** start the session loop thread
@ -137,16 +141,24 @@ namespace control {
* in case it does, the main application thread will be deadlocked on startup. * in case it does, the main application thread will be deadlocked on startup.
* Such might happen indirectly, when something depends on "the Session" * Such might happen indirectly, when something depends on "the Session"
*/ */
DispatcherLoop (Subsys::SigTerm notification) template<typename FUN>
DispatcherLoop (FUN&& atExit)
: commandService_{ServiceHandle::NOT_YET_STARTED} : commandService_{ServiceHandle::NOT_YET_STARTED}
, queue_{} , queue_{}
, error_{}
, looper_([&]() -> bool , looper_([&]() -> bool
{ {
return not queue_.empty(); return not queue_.empty();
}) })
, thread_{"Session" , thread_{ //----the-Session-Thread---------------
,&DispatcherLoop::runSessionThread Launch{&DispatcherLoop::runSessionThread, this}
, this, notification} .threadID("Session")
.atExit([this, signalEndOfThread = move(atExit)]
(lib::thread::ThreadWrapper& handle)
{
handle.detach_thread_from_wrapper();
signalEndOfThread (&error_);
})}
{ {
init_.sync(); // done with setup; loop may run now.... init_.sync(); // done with setup; loop may run now....
INFO (session, "Steam-Dispatcher running..."); INFO (session, "Steam-Dispatcher running...");
@ -161,6 +173,8 @@ namespace control {
try { try {
commandService_.shutdown(); // redundant call, to ensure session interface is closed reliably commandService_.shutdown(); // redundant call, to ensure session interface is closed reliably
INFO (session, "Steam-Dispatcher stopped."); INFO (session, "Steam-Dispatcher stopped.");
if (thread_)
ALERT (session, "Dispatcher destroyed while Session thread is running. The rest is silence.");
} }
ERROR_LOG_AND_IGNORE(session, "Stopping the Steam-Dispatcher"); ERROR_LOG_AND_IGNORE(session, "Stopping the Steam-Dispatcher");
} }
@ -235,9 +249,8 @@ namespace control {
* to invoke SteamDispatcher::endRunningLoopState(). * to invoke SteamDispatcher::endRunningLoopState().
*/ */
void void
runSessionThread (Subsys::SigTerm notifyEnd) runSessionThread ()
{ {
string errorMsg;
init_.sync(); init_.sync();
try try
{ {
@ -256,18 +269,15 @@ namespace control {
} }
catch (std::exception& problem) catch (std::exception& problem)
{ // could also be lumiera::Error { // could also be lumiera::Error
errorMsg = problem.what(); error_ = problem.what();
lumiera_error(); // clear error flag lumiera_error(); // clear error flag
} }
catch (...) catch (...)
{ {
errorMsg = string{lumiera_error()}; error_ = string{lumiera_error()};
} }
// leave the Session thread... // Session thread terminates...
// send notification of subsystem shutdown } // atExit-λ will invoke ~DispatcherLoop()
thread_.detach();/////////////////////////////////////////////////////////////OOO while this case is exceptional, it still mandates better framework support
notifyEnd (&errorMsg); // invokes ~DispatcherLoop()
}
void void
awaitAction() ///< at begin of loop body... awaitAction() ///< at begin of loop body...
@ -354,19 +364,41 @@ namespace control {
Lock sync(this); Lock sync(this);
if (runningLoop_) return false; if (runningLoop_) return false;
runningLoop_.reset ( runningLoop_ =
new DispatcherLoop ( make_unique<DispatcherLoop>(
[=] (string* problemMessage) [=](string* problemIndicator)
{ { // when the Session thread ends....
SteamDispatcher::endRunningLoopState(); SteamDispatcher::endRunningLoopState();
termNotification(problemMessage); termNotification (problemIndicator);
})); });
if (active_) if (active_)
runningLoop_->activateCommandProecssing(); runningLoop_->activateCommandProecssing();
return true; return true;
} }
/** @internal clean-up when leaving the session loop thread.
* This function is hooked up in to the termination callback,
* and is in fact the only one to delete the loop PImpl. We
* take the (outer) lock on SteamDispatcher to ensure no one
* commits anything to the DispatcherLoop object while being
* deleted. The call itself, while technically originating
* from within DispatcherLoop::runSessionThread(), relies
* solely on stack based context data and is a tail call.
*/
void
SteamDispatcher::endRunningLoopState()
{
Lock sync(this);
if (runningLoop_)
runningLoop_.reset(); // delete DispatcherLoop object
else
WARN (command, "clean-up of DispatcherLoop invoked, "
"while SteamDispatcher is not marked as 'running'. "
"Likely an error in lifecycle logic, as the only one "
"intended to delete this object is the loop thread itself.");
}
/** whether the »session subsystem« is operational. /** whether the »session subsystem« is operational.
* @return `true` if the session loop thread has been fully * @return `true` if the session loop thread has been fully
@ -395,28 +427,6 @@ namespace control {
} }
/** @internal clean-up when leaving the session loop thread.
* This function is hooked up in to the termination callback,
* and is in fact the only one to delete the loop PImpl. We
* take the (outer) lock on SteamDispatcher to ensure no one
* commits anything to the DispatcherLoop object while being
* deleted. The call itself, while technically originating
* from within DispatcherLoop::runSessionThread(), relies
* solely on stack based context data and is a tail call.
*/
void
SteamDispatcher::endRunningLoopState()
{
Lock sync(this);
if (runningLoop_)
runningLoop_.reset(); // delete DispatcherLoop object
else
WARN (command, "clean-up of DispatcherLoop invoked, "
"while SteamDispatcher is not marked as 'running'. "
"Likely an error in lifecycle logic, as the only one "
"intended to delete this object is the loop thread itself.");
}
/** activate processing of enqueued session commands. /** activate processing of enqueued session commands.
* @remarks command processing serves as public external interface * @remarks command processing serves as public external interface

View file

@ -73,13 +73,15 @@ namespace control {
/** /**
* Guard to manage processing commands working on the session. * Guard to manage processing commands to operate on the session.
* A static application facility, actually backing and implementing * A static application facility, actually backing and implementing
* the »session subsystem«. Embedded within the implementation of this * the »session subsystem«. Embedded within the implementation of this
* class is the _»session loop thread«_ to perform any session mutation * class is the _»session loop thread«_ to perform any session mutation
* commands and to operate the Builder, which translates the session * commands and to activate the Builder, which translates the session
* contents into a render nodes network. Also embedded herein is * contents into a render nodes network. Also embedded herein is
* the implementation of steam::control::SessionCommandService * the implementation of steam::control::SessionCommandService
* @warning destroying this object while #isRunning() will
* terminate the Application unconditionally.
*/ */
class SteamDispatcher class SteamDispatcher
: public lib::Sync<> : public lib::Sync<>

View file

@ -47,7 +47,7 @@ return: 0
END END
PLANNED "Detect Thread lifecycle state changes" ThreadWrapperLifecycle_test <<END TEST "Detect Thread lifecycle state changes" ThreadWrapperLifecycle_test <<END
return: 0 return: 0
END END

View file

@ -65009,9 +65009,9 @@
<node COLOR="#338800" CREATED="1695597447169" ID="ID_35947260" MODIFIED="1695859231223" TEXT="Fehlerbehandlung"> <node COLOR="#338800" CREATED="1695597447169" ID="ID_35947260" MODIFIED="1695859231223" TEXT="Fehlerbehandlung">
<icon BUILTIN="button_ok"/> <icon BUILTIN="button_ok"/>
</node> </node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1696468379848" ID="ID_1146069423" MODIFIED="1697054187170" TEXT="Erg&#xe4;nzung: autonomous Thread"> <node COLOR="#338800" CREATED="1696468379848" FOLDED="true" ID="ID_1146069423" MODIFIED="1697133540550" TEXT="Erg&#xe4;nzung: autonomous Thread">
<linktarget COLOR="#5a83aa" DESTINATION="ID_1146069423" ENDARROW="Default" ENDINCLINATION="444;909;" ID="Arrow_ID_516910789" SOURCE="ID_137837629" STARTARROW="None" STARTINCLINATION="-1280;-32;"/> <linktarget COLOR="#5a83aa" DESTINATION="ID_1146069423" ENDARROW="Default" ENDINCLINATION="444;909;" ID="Arrow_ID_516910789" SOURCE="ID_137837629" STARTARROW="None" STARTINCLINATION="-1280;-32;"/>
<icon BUILTIN="pencil"/> <icon BUILTIN="button_ok"/>
<node COLOR="#435e98" CREATED="1696468398477" ID="ID_1775921981" MODIFIED="1696966299356" TEXT="neue Anforderung: komplett abgekoppelt"> <node COLOR="#435e98" CREATED="1696468398477" ID="ID_1775921981" MODIFIED="1696966299356" TEXT="neue Anforderung: komplett abgekoppelt">
<icon BUILTIN="yes"/> <icon BUILTIN="yes"/>
<node CREATED="1696468424018" ID="ID_1127252428" MODIFIED="1696468584146" TEXT="ergibt sich aus dem Protokoll der C++14 - Threads"> <node CREATED="1696468424018" ID="ID_1127252428" MODIFIED="1696468584146" TEXT="ergibt sich aus dem Protokoll der C++14 - Threads">
@ -66291,8 +66291,8 @@
</node> </node>
</node> </node>
</node> </node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1696893013891" ID="ID_1856740667" MODIFIED="1697068782982" TEXT="damit bestehende Probleme im Applikations-Code l&#xf6;sen"> <node COLOR="#338800" CREATED="1696893013891" ID="ID_1856740667" MODIFIED="1697133524821" TEXT="damit bestehende Probleme im Applikations-Code l&#xf6;sen">
<icon BUILTIN="pencil"/> <icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1696893026770" ID="ID_253995958" MODIFIED="1697079847058" TEXT="GtkLumiera"> <node COLOR="#338800" CREATED="1696893026770" ID="ID_253995958" MODIFIED="1697079847058" TEXT="GtkLumiera">
<icon BUILTIN="button_ok"/> <icon BUILTIN="button_ok"/>
<node CREATED="1696893056374" MODIFIED="1696893056374" TEXT="Variante-1"/> <node CREATED="1696893056374" MODIFIED="1696893056374" TEXT="Variante-1"/>
@ -66301,7 +66301,8 @@
<icon BUILTIN="button_ok"/> <icon BUILTIN="button_ok"/>
<node CREATED="1696893058148" MODIFIED="1696893058148" TEXT="Variante-1"/> <node CREATED="1696893058148" MODIFIED="1696893058148" TEXT="Variante-1"/>
</node> </node>
<node CREATED="1696893032057" ID="ID_926131453" MODIFIED="1696893046381" TEXT="SteamDispatcher"> <node COLOR="#338800" CREATED="1696893032057" ID="ID_926131453" MODIFIED="1697133522544" TEXT="SteamDispatcher">
<icon BUILTIN="button_ok"/>
<node CREATED="1696893059260" ID="ID_109825250" MODIFIED="1696893063103" TEXT="Variante-2"/> <node CREATED="1696893059260" ID="ID_109825250" MODIFIED="1696893063103" TEXT="Variante-2"/>
</node> </node>
</node> </node>
@ -82276,9 +82277,10 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node> </node>
</node> </node>
</node> </node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1695394237210" ID="ID_11553358" MODIFIED="1696029630733" TEXT="Applikation umstellen"> <node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1695394237210" ID="ID_11553358" MODIFIED="1697133471895" TEXT="Applikation umstellen">
<icon BUILTIN="flag-yellow"/> <icon BUILTIN="flag-yellow"/>
<icon BUILTIN="pencil"/> <icon BUILTIN="pencil"/>
<icon BUILTIN="hourglass"/>
<node CREATED="1696029414385" ID="ID_140265798" MODIFIED="1696029417676" TEXT="weitere Tests..."> <node CREATED="1696029414385" ID="ID_140265798" MODIFIED="1696029417676" TEXT="weitere Tests...">
<node COLOR="#338800" CREATED="1696029465122" ID="ID_1515483274" MODIFIED="1696357491529" TEXT="SubsystemRunner_test"> <node COLOR="#338800" CREATED="1696029465122" ID="ID_1515483274" MODIFIED="1696357491529" TEXT="SubsystemRunner_test">
<icon BUILTIN="button_ok"/> <icon BUILTIN="button_ok"/>
@ -82465,7 +82467,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node> </node>
</node> </node>
</node> </node>
<node COLOR="#338800" CREATED="1696029465121" ID="ID_786350998" MODIFIED="1697079789466" TEXT="gtk-lumiera.cpp"> <node COLOR="#338800" CREATED="1696029465121" FOLDED="true" ID="ID_786350998" MODIFIED="1697079789466" TEXT="gtk-lumiera.cpp">
<icon BUILTIN="button_ok"/> <icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1696468041645" ID="ID_137837629" MODIFIED="1697079810297" TEXT="brauche einen Thread, den man in den Hintergrund schieben kann"> <node COLOR="#338800" CREATED="1696468041645" ID="ID_137837629" MODIFIED="1697079810297" TEXT="brauche einen Thread, den man in den Hintergrund schieben kann">
<arrowlink COLOR="#5a83aa" DESTINATION="ID_1146069423" ENDARROW="Default" ENDINCLINATION="444;909;" ID="Arrow_ID_516910789" STARTARROW="None" STARTINCLINATION="-1280;-32;"/> <arrowlink COLOR="#5a83aa" DESTINATION="ID_1146069423" ENDARROW="Default" ENDINCLINATION="444;909;" ID="Arrow_ID_516910789" STARTARROW="None" STARTINCLINATION="-1280;-32;"/>
@ -82529,7 +82531,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<arrowlink COLOR="#1f9fe7" DESTINATION="ID_871343805" ENDARROW="Default" ENDINCLINATION="173;10;" ID="Arrow_ID_1685700346" STARTARROW="None" STARTINCLINATION="-8;8;"/> <arrowlink COLOR="#1f9fe7" DESTINATION="ID_871343805" ENDARROW="Default" ENDINCLINATION="173;10;" ID="Arrow_ID_1685700346" STARTARROW="None" STARTINCLINATION="-8;8;"/>
</node> </node>
</node> </node>
<node COLOR="#338800" CREATED="1696029465122" ID="ID_871343805" MODIFIED="1697068873667" TEXT="output-director.cpp"> <node COLOR="#338800" CREATED="1696029465122" FOLDED="true" ID="ID_871343805" MODIFIED="1697068873667" TEXT="output-director.cpp">
<linktarget COLOR="#1f9fe7" DESTINATION="ID_871343805" ENDARROW="Default" ENDINCLINATION="173;10;" ID="Arrow_ID_1685700346" SOURCE="ID_1299573982" STARTARROW="None" STARTINCLINATION="-8;8;"/> <linktarget COLOR="#1f9fe7" DESTINATION="ID_871343805" ENDARROW="Default" ENDINCLINATION="173;10;" ID="Arrow_ID_1685700346" SOURCE="ID_1299573982" STARTARROW="None" STARTINCLINATION="-8;8;"/>
<icon BUILTIN="button_ok"/> <icon BUILTIN="button_ok"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1696888656242" HGAP="36" ID="ID_1500509085" MODIFIED="1696888747350" STYLE="fork" TEXT="Symptom hier: bad_alloc im Shutdown" VSHIFT="25"> <node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1696888656242" HGAP="36" ID="ID_1500509085" MODIFIED="1696888747350" STYLE="fork" TEXT="Symptom hier: bad_alloc im Shutdown" VSHIFT="25">
@ -82542,15 +82544,15 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="button_ok"/> <icon BUILTIN="button_ok"/>
</node> </node>
</node> </node>
<node BACKGROUND_COLOR="#eee5a3" COLOR="#883300" CREATED="1696029465122" ID="ID_556208204" MODIFIED="1696539012684" TEXT="steam-dispatcher.cpp"> <node COLOR="#338800" CREATED="1696029465122" FOLDED="true" ID="ID_556208204" MODIFIED="1697133495447" 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;"/> <linktarget COLOR="#71be6f" DESTINATION="ID_556208204" ENDARROW="Default" ENDINCLINATION="1038;-401;" ID="Arrow_ID_1257115855" SOURCE="ID_1718267866" STARTARROW="None" STARTINCLINATION="602;40;"/>
<icon BUILTIN="flag-yellow"/> <icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1696039703074" ID="ID_796238065" MODIFIED="1696171045349" TEXT="Refactoring wie &#xfc;blich"> <node COLOR="#338800" CREATED="1696039703074" ID="ID_796238065" MODIFIED="1696171045349" TEXT="Refactoring wie &#xfc;blich">
<icon BUILTIN="button_ok"/> <icon BUILTIN="button_ok"/>
<node CREATED="1696039723008" ID="ID_932291373" MODIFIED="1696039728651" TEXT="Thread wird ein Member-Feld"/> <node CREATED="1696039723008" ID="ID_932291373" MODIFIED="1696039728651" TEXT="Thread wird ein Member-Feld"/>
<node CREATED="1696039729255" ID="ID_249325323" MODIFIED="1696039735587" TEXT="Sync-Barrier kommt hinzu"/> <node CREATED="1696039729255" ID="ID_249325323" MODIFIED="1696039735587" TEXT="Sync-Barrier kommt hinzu"/>
</node> </node>
<node COLOR="#435e98" CREATED="1696171047044" ID="ID_964609053" MODIFIED="1696179878653" TEXT="Problem beim Shutdown"> <node COLOR="#435e98" CREATED="1696171047044" FOLDED="true" ID="ID_964609053" MODIFIED="1696179878653" TEXT="Problem beim Shutdown">
<icon BUILTIN="messagebox_warning"/> <icon BUILTIN="messagebox_warning"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1696171082115" ID="ID_150270315" MODIFIED="1696171100640" TEXT="Session-Thread endet nicht rechtzeitig"> <node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1696171082115" ID="ID_150270315" MODIFIED="1696171100640" TEXT="Session-Thread endet nicht rechtzeitig">
<richcontent TYPE="NOTE"><html> <richcontent TYPE="NOTE"><html>
@ -82630,6 +82632,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="ksmiletris"/> <icon BUILTIN="ksmiletris"/>
</node> </node>
<node CREATED="1696174292902" ID="ID_866561914" MODIFIED="1696174342106" TEXT="aber vom SteamDispatcher-dtor g&#xe4;be es eine L&#xfc;cke"> <node CREATED="1696174292902" ID="ID_866561914" MODIFIED="1696174342106" TEXT="aber vom SteamDispatcher-dtor g&#xe4;be es eine L&#xfc;cke">
<linktarget COLOR="#7c8d9d" DESTINATION="ID_866561914" ENDARROW="Default" ENDINCLINATION="-348;479;" ID="Arrow_ID_1571953987" SOURCE="ID_1682021227" STARTARROW="None" STARTINCLINATION="-215;7;"/>
<icon BUILTIN="messagebox_warning"/> <icon BUILTIN="messagebox_warning"/>
<node CREATED="1696174343863" ID="ID_210073839" MODIFIED="1696174470614" TEXT="ebenfalls ehr unwahrscheinlich (Singleton!)"> <node CREATED="1696174343863" ID="ID_210073839" MODIFIED="1696174470614" TEXT="ebenfalls ehr unwahrscheinlich (Singleton!)">
<richcontent TYPE="NOTE"><html> <richcontent TYPE="NOTE"><html>
@ -82719,9 +82722,9 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node> </node>
</node> </node>
</node> </node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696539035824" ID="ID_1472214443" MODIFIED="1696539109792" TEXT="generische L&#xf6;sung f&#xfc;r dieses Nutz-Muster"> <node COLOR="#338800" CREATED="1696539035824" ID="ID_1472214443" MODIFIED="1697133350320" TEXT="generische L&#xf6;sung f&#xfc;r dieses Nutz-Muster">
<linktarget COLOR="#696992" DESTINATION="ID_1472214443" ENDARROW="Default" ENDINCLINATION="-24;-75;" ID="Arrow_ID_409292846" SOURCE="ID_1071243890" STARTARROW="None" STARTINCLINATION="-43;3;"/> <linktarget COLOR="#696992" DESTINATION="ID_1472214443" ENDARROW="Default" ENDINCLINATION="-24;-75;" ID="Arrow_ID_409292846" SOURCE="ID_1071243890" STARTARROW="None" STARTINCLINATION="-43;3;"/>
<icon BUILTIN="flag-yellow"/> <icon BUILTIN="button_ok"/>
<node CREATED="1696539114617" ID="ID_104020495" MODIFIED="1696620523042" TEXT="ThreadHookable"> <node CREATED="1696539114617" ID="ID_104020495" MODIFIED="1696620523042" TEXT="ThreadHookable">
<arrowlink COLOR="#898bab" DESTINATION="ID_1917558827" ENDARROW="Default" ENDINCLINATION="-1027;99;" ID="Arrow_ID_1553665185" STARTARROW="None" STARTINCLINATION="532;77;"/> <arrowlink COLOR="#898bab" DESTINATION="ID_1917558827" ENDARROW="Default" ENDINCLINATION="-1027;99;" ID="Arrow_ID_1553665185" STARTARROW="None" STARTINCLINATION="532;77;"/>
<icon BUILTIN="info"/> <icon BUILTIN="info"/>
@ -82729,18 +82732,61 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="idea"/> <icon BUILTIN="idea"/>
</node> </node>
</node> </node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696539286066" ID="ID_53739973" MODIFIED="1696539300376" TEXT="bestehenden Code in DispatcherLoop umschreiben"> <node COLOR="#338800" CREATED="1696539286066" ID="ID_53739973" MODIFIED="1697133342508" TEXT="bestehenden Code in DispatcherLoop umschreiben">
<icon BUILTIN="flag-yellow"/> <icon BUILTIN="button_ok"/>
<node CREATED="1696539301472" ID="ID_938150628" MODIFIED="1696539312778" TEXT="sollte weitgehend ein drop-in-replacement sein"/> <node CREATED="1696539301472" ID="ID_938150628" MODIFIED="1696539312778" TEXT="sollte weitgehend ein drop-in-replacement sein"/>
<node CREATED="1696539313368" ID="ID_1530319321" MODIFIED="1696539338246" TEXT="nur da&#xdf; nun keine Heap-Allokation mehr stattfindet"/> <node CREATED="1696539313368" ID="ID_1530319321" MODIFIED="1697124202275" TEXT="auch die Allokation erfolgt wie bisher direkt in den unique-ptr"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1696539414089" ID="ID_1532143583" MODIFIED="1696539433399" TEXT="Problem mit geeigneter Wrapper-Basisklasse kl&#xe4;ren"> <node COLOR="#338800" CREATED="1696539414089" ID="ID_1532143583" MODIFIED="1697133332702" TEXT="Problem mit geeigneter Wrapper-Basisklasse kl&#xe4;ren">
<icon BUILTIN="flag-yellow"/> <icon BUILTIN="button_ok"/>
<node CREATED="1697128200944" ID="ID_1946980837" MODIFIED="1697128216272" TEXT="in diesem Fall mu&#xdf; es ein Lifecycle-Hook sein"/>
<node CREATED="1697128241409" ID="ID_383238232" MODIFIED="1697128271254">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
au&#223;erdem ist das thread-Objekt (nach dem Refactoring) nur noch <i>Member</i>
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1697128310456" ID="ID_1682021227" MODIFIED="1697128572722">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
zudem mu&#223; <b>zwingend</b>&#160;<font face="Monospaced" color="#341189">detach()</font>&#160;aufgerufen werden &#8212; <i>hier</i>&#160; und <i>nicht</i>&#160;im Destruktor
</p>
</body>
</html></richcontent>
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Und zwar addressiert das hier das subtile Problem, da&#223; der umschlie&#223;ende Kontext auch auf au&#223;ergew&#246;hnlichem Weg und vorzeitig zerst&#246;rt werden kann, obwohl der Thread noch l&#228;uft. Daher ist es w&#252;nschenswert, da&#223; der Destruktor bei noch laufendem Thread die Applikation hart terminiert
</p>
</body>
</html></richcontent>
<arrowlink COLOR="#7c8d9d" DESTINATION="ID_866561914" ENDARROW="Default" ENDINCLINATION="-348;479;" ID="Arrow_ID_1571953987" STARTARROW="None" STARTINCLINATION="-215;7;"/>
</node>
</node>
<node COLOR="#338800" CREATED="1697128673738" ID="ID_1921134580" MODIFIED="1697133338424" TEXT="L&#xf6;sung: &#x3bb;-Kette">
<icon BUILTIN="idea"/>
<node CREATED="1697128706291" ID="ID_177194465" MODIFIED="1697128761575" TEXT="der TerminationSignal-Funktor steht dort bereit, wo auch die DispatcherLoop konstruiert wird"/>
<node CREATED="1697128762283" ID="ID_743745630" MODIFIED="1697128801666" TEXT="genau dort sollte auch ein &#x3bb; gebunden werden, das den Pointer am Ende wieder leert"/>
<node CREATED="1697129347357" ID="ID_230048402" MODIFIED="1697129378773" TEXT="aber im DispatcherLoop-Objekt wird dieses &#x3bb; in das eigentliche hook-&#x3bb; gepackt"/>
<node CREATED="1697129379529" ID="ID_1027277309" MODIFIED="1697129395410" TEXT="denn dieses nimmt eine ThreadWrapper-Instanz, um darauf detach() aufrufen zu k&#xf6;nnen"/>
<node CREATED="1697129400411" ID="ID_968891728" MODIFIED="1697129448246" TEXT="detach() habe ich bewu&#xdf;t protected gemacht &#x27fc; detach_thread_from_wrapper()">
<icon BUILTIN="yes"/>
</node> </node>
</node> </node>
</node> </node>
</node> </node>
</node> </node>
</node> </node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1697133453673" ID="ID_1053950423" MODIFIED="1697133470456" TEXT="R&#xfc;ckbau alter code">
<icon BUILTIN="hourglass"/>
</node>
</node>
</node> </node>
</node> </node>
</node> </node>