diff --git a/src/proc/control/proc-dispatcher.cpp b/src/proc/control/proc-dispatcher.cpp index 1bb4fad2a..ec7da52a1 100644 --- a/src/proc/control/proc-dispatcher.cpp +++ b/src/proc/control/proc-dispatcher.cpp @@ -338,7 +338,7 @@ namespace control { new DispatcherLoop ( [=] (string* problemMessage) { - runningLoop_.reset(); //////////////////////TODO: Race in ProcDispatcher + ProcDispatcher::endRunningLoopState(); termNotification(problemMessage); })); @@ -361,16 +361,40 @@ namespace control { /** signal to the loop thread that it needs to terminate. - * @warning dangerous operation; must not block nor throw - * - * @todo need to re-check the logic, once the loop is fully implemented; ensure there is nothing on this call path that can block or throw!!! + * @note the immediate consequence is to close SessionCommandService */ void ProcDispatcher::requestStop() noexcept + { + try { + Lock sync(this); + if (runningLoop_) + runningLoop_->requestStop(); + } + ERROR_LOG_AND_IGNORE (command, "Request for Session Loop Thread to terminate"); + } + + + /** @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 ProcDispatcher 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 + ProcDispatcher::endRunningLoopState() { Lock sync(this); if (runningLoop_) - runningLoop_->requestStop(); + runningLoop_.reset(); // delete DispatcherLoop object + else + WARN (command, "clean-up of DispatcherLoop invoked, " + "while ProcDispatcher 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."); } @@ -415,8 +439,9 @@ namespace control { if (runningLoop_) runningLoop_->awaitStateProcessed(); } - + + /** discard any commands waiting in the dispatcher queue */ void ProcDispatcher::clear() { diff --git a/src/proc/control/proc-dispatcher.hpp b/src/proc/control/proc-dispatcher.hpp index 37a464bd5..7ffbfc209 100644 --- a/src/proc/control/proc-dispatcher.hpp +++ b/src/proc/control/proc-dispatcher.hpp @@ -102,6 +102,8 @@ namespace control { bool empty() const ; + private: + void endRunningLoopState(); }; diff --git a/src/proc/facade.cpp b/src/proc/facade.cpp index 9bdfc328e..6475ef457 100644 --- a/src/proc/facade.cpp +++ b/src/proc/facade.cpp @@ -21,14 +21,35 @@ * *****************************************************/ +/** @file facade.cpp + ** Implementation of subsystem lifecycle behaviour for the core parts of Proc-Layer. + ** - The »session subsystem« is responsible for accepting operations to work on the + ** session datastructure, and it will trigger the Builder to reflect those changes + ** into a suitable render nodes network + ** - The »play out subsystem« is able to _perform_ such a render nodes network + ** for video playback and rendering. + ** + ** lumiera::Subsys and lumiera::SubsystemRunner together define a protocol for some + ** large scale building blocks of the whole application to be started and terminated. + ** Typically this entails to create a dedicated thread to manage the top level concerns + ** for the given subsystem, and to create registration and services for public access: + ** - to operate on the session, use the proc::control::SessionCommand facade + ** - playback and render operations can be operated by the lumiera::Play facade + ** + ** @see ProcDispatcher + ** @see OutputDirector + ** @see subsys.hpp + ** @see main.cpp + ** + */ + + #include "proc/facade.hpp" #include "lib/depend.hpp" -//#include "lib/sync.hpp" #include "proc/control/proc-dispatcher.hpp" #include "proc/play/output-director.hpp" #include -//#include namespace proc { @@ -37,8 +58,6 @@ namespace proc { using std::unique_ptr; using lumiera::Subsys; using lumiera::Option; - using lib::Sync; -// using lib::RecursiveLock_NoWait; using proc::control::ProcDispatcher; diff --git a/src/proc/facade.hpp b/src/proc/facade.hpp index 534c4c5da..f32fa46e5 100644 --- a/src/proc/facade.hpp +++ b/src/proc/facade.hpp @@ -22,11 +22,12 @@ /** @file facade.hpp ** Top level entrance point and facade for the Proc-Layer. - ** The middle layer of the application holds a session with the - ** high-level model, to be translated by the Builder into a node network, - ** which can be \em performed by the Engine to render output. + ** The middle layer of the application holds a session with the high-level model, + ** to be translated by the Builder into a node network, which can be _performed_ + ** by the Engine to render output. ** ** @see common.hpp + ** @see main.cpp ** */ @@ -49,8 +50,12 @@ namespace proc { * @todo this is a dummy placeholder as of 1/2009. Currently, there * is only implementation-level code within the Proc-Layer and * the interfaces need to be worked out. - * @note at least the Play/Output subsystem slowly turns into + * @todo at least the Play/Output subsystem slowly turns into * something real, as of 6/2011 + * @todo and as of 1/2017, the Session subsystem is basically + * implemented and it became clear that there won't be + * a "Builder subsystem". So this interface might now + * be considered roughly complete... * */ struct Facade diff --git a/wiki/renderengine.html b/wiki/renderengine.html index a9c2080b9..45f56eabe 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -6410,17 +6410,23 @@ And last but not least: the difficult part of this whole concept is encapsulated {{red{WIP ... draft}}} -
+
//A subsystem within Proc-Layer, responsible for lifecycle and access to the editing [[Session]].//
 [img[Structure of the Session Subsystem|uml/Session-subsystem.png]]
 
 !Structure
-The ProcDispatcher is at the heart of the //Session Subsystem.// Because the official interface for working on the session, the {{{SessionCommand}}} façade, is expressed in terms of sending command messages to invoke predefined [[commands|CommandHandling]] to operate on the SessionInterface, the actual implementation of such a {{{SessionCommandService}}} needs a component actually to enqueue and dispatch those commands -- which is the {{{DispatcherLoop}}} within the ProcDispatcher. As usual, the ''lifecycle'' is controlled by a subsystem descriptor, which starts and stopps the whole subsystem; and this starting and stopping in turn translates into starting/stopping of the dispatcher loop. On the other hand, //activation of the dispatcher,// which means actively to dispatch commands, is controlled by the lifecycle of the session proper. The latter is just a data structure, and can be loaded / saved and rebuilt through the ''session manager''.
+The ProcDispatcher is at the heart of the //Session Subsystem.// Because the official interface for working on the session, the {{{SessionCommand}}} façade, is expressed in terms of sending command messages to invoke predefined [[commands|CommandHandling]] to operate on the SessionInterface, the actual implementation of such a {{{SessionCommandService}}} needs a component actually to enqueue and dispatch those commands -- which is the {{{DispatcherLoop}}} within the ProcDispatcher. As usual, the ''lifecycle'' is controlled by a subsystem descriptor, which starts and stops the whole subsystem; and this starting and stopping in turn translates into starting/stopping of the dispatcher loop. On the other hand, //activation of the dispatcher,// which means actively to dispatch commands, is controlled by the lifecycle of the session proper. The latter is just a data structure, and can be loaded / saved and rebuilt through the ''session manager''.
 
 !Lifecycle
+As far as lifecycle is concerned, the »session subsystem« has to be distinguished from the //session proper,// which is just a data structure with its own, separate lifecycle considerations. Accessing the session data only makes sense when this data structure is fully loaded, while the //session subsystem,// deals with performing commands on the session and with triggering the builder runs.
+
+!!!start-up
+The session subsystem lifecycle translates into method invocations on the {{{ProcDispatcher}}}, which in turn manages the parts actually implementing the session command processing and builder operations. This relation is expressed by holding onto the implementation as a PImpl. As long as the {{{DispatcherLoop}}} object exists, the session subsystem can be considered in //running state.// This is equivalent to the following
+* the ''session loop thread'' is spawned. This thread performs all of the session and builder operations (single-threaded).
+* the {{{SessionCommandService}}} is started and connected as implementation of the {{{SessionCommand}}} façade.
 
 !!!shutdown
-Shutdown is initiated by sending a message to the dispatcher loop. {{red{TODO 12/2016}}} need to ensure the loop terminates reliably then! When loop terminates, the {{{sigTerm}}} of the SessionSubsystem is invoked, which then in turn causes the DispatcherLoop thread to be reaped.
+Shutdown is initiated by sending a message to the dispatcher loop. This causes the internal loop control to wake up and leave the loop, possibly after finishing a command or builder run currently in progress. When leaving the loop, the {{{sigTerm}}} of the SessionSubsystem is invoked, which then in turn causes the {{{DispatcherLoop}}} object to be deleted and the ProcDispatcher thus returned into halted state. 
 
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 1333588e5..b6b8fca23 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -503,14 +503,26 @@ - - + + - + + + + + + + + + + + + + + + - - @@ -518,12 +530,13 @@ - + + - - + + @@ -567,16 +580,27 @@ - - + - + + + + + + + + + + + + - + + @@ -597,9 +621,38 @@ - + + + + + + +

+ 1/2017 Review durchgeführt und Logik überarbeitet. +

+

+ Einziger Risikofaktor ist nun, wenn beim Schließen des SessionCommand-Intertfaces +

+

+ oder beim Signalisieren an den Thread eine Exception fliegt; denn dann loggen wir zwar, +

+

+ aber die Shutdown-Rückmeldung kommt u.U niemals an, und damit bleiben wir +

+

+ am Ende von main() einfach hängen. +

+

+ +

+

+ Ich halte diese Fälle aber für in der Praxis nicht relevant,  und verzichte daher auf eine Spezialbehandlung +

+ + +
- +