2009-06-08 04:46:07 +02:00
/*
ProcDispatcher - Proc - Layer command dispatch and execution
2010-12-17 23:28:49 +01:00
2009-06-08 04:46:07 +02:00
Copyright ( C ) Lumiera . org
2008 , Hermann Vosseler < Ichthyostega @ web . de >
2010-12-17 23:28:49 +01:00
2009-06-08 04:46:07 +02:00
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License as
2010-12-17 23:28:49 +01:00
published by the Free Software Foundation ; either version 2 of
the License , or ( at your option ) any later version .
2009-06-08 04:46:07 +02:00
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
2010-12-17 23:28:49 +01:00
2009-06-08 04:46:07 +02:00
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
2010-12-17 23:28:49 +01:00
2009-06-08 04:46:07 +02:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2016-12-16 23:26:56 +01:00
/** @file proc-dispatcher.cpp
* * Implementation details of running commands and the builder .
* * The ProcDispatcher is at the heart of the session subsystem and implements
* * a ( 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 logic are encapsulated in a [ logic component ] ( \ ref Looper ) .
* *
* * @ todo as of 12 / 2016 , implementation has been drafted and is very much WIP
* * @ todo //////////////////////////////////////////////////////TODO ensure really every state change triggers a wakeup!!!!!!!
* *
* * @ see ProcDispatcher
* * @ see DispatcherLooper_test
* * @ see CommandQueue_test
* *
*/
2009-06-08 04:46:07 +02:00
2016-12-13 04:45:00 +01:00
# include "lib/error.hpp"
# include "include/logging.h"
2009-06-08 04:46:07 +02:00
# include "proc/control/proc-dispatcher.hpp"
2016-12-15 05:21:03 +01:00
# include "proc/control/command-dispatch.hpp"
2016-12-15 20:48:35 +01:00
# include "proc/control/command-queue.hpp"
# include "proc/control/looper.hpp"
2016-12-15 05:38:12 +01:00
# include "proc/control/session-command-service.hpp"
2009-09-29 04:47:09 +02:00
# include "proc/mobject/session.hpp"
2016-12-13 04:34:28 +01:00
# include "backend/thread-wrapper.hpp"
2009-06-08 04:46:07 +02:00
//#include "proc/mobject/mobject-ref.hpp"
//#include "proc/mobject/mobject.hpp"
//#include "proc/mobject/placement.hpp"
2016-12-15 05:54:48 +01:00
# include <memory>
2009-06-08 04:46:07 +02:00
//using boost::str;
2016-12-13 04:34:28 +01:00
using backend : : ThreadJoinable ;
2016-12-14 04:18:58 +01:00
using lib : : Sync ;
using lib : : RecursiveLock_Waitable ;
2016-12-15 05:54:48 +01:00
using std : : unique_ptr ;
2009-06-08 04:46:07 +02:00
2011-12-02 17:50:44 +01:00
namespace proc {
2009-06-08 04:46:07 +02:00
namespace control {
2016-12-16 23:11:19 +01:00
namespace error = lumiera : : error ;
2016-12-13 04:34:28 +01:00
class DispatcherLoop
: ThreadJoinable
2016-12-15 05:21:03 +01:00
, public CommandDispatch
2016-12-14 04:18:58 +01:00
, public Sync < RecursiveLock_Waitable >
2016-12-13 04:34:28 +01:00
{
2016-12-14 04:57:08 +01:00
bool canDispatch_ { false } ;
bool blocked_ { false } ;
2016-12-15 22:15:20 +01:00
bool mustHalt_ { false } ; /////////////////TODO this flag shall be relocated into the Looper!
2016-12-14 04:57:08 +01:00
2016-12-15 05:54:48 +01:00
unique_ptr < SessionCommandService > commandService_ ;
2016-12-15 05:38:12 +01:00
2016-12-15 20:48:35 +01:00
CommandQueue queue_ ;
Looper looper_ ;
2016-12-15 05:38:12 +01:00
2016-12-13 04:34:28 +01:00
public :
DispatcherLoop ( Subsys : : SigTerm notification )
: ThreadJoinable ( " Lumiera Session "
, bind ( & DispatcherLoop : : run , this , notification ) )
2016-12-15 05:54:48 +01:00
, commandService_ ( new SessionCommandService ( * this ) )
2016-12-15 20:48:35 +01:00
, queue_ ( )
2016-12-16 19:21:06 +01:00
, looper_ ( [ & ] ( ) - > bool
{
return not queue_ . empty ( ) ;
} )
2016-12-13 04:34:28 +01:00
{
INFO ( session , " Proc-Dispatcher running... " ) ;
}
~ DispatcherLoop ( )
{
2016-12-15 05:54:48 +01:00
try {
Lock sync ( this ) ;
2016-12-15 06:21:59 +01:00
commandService_ . reset ( ) ; // redundant call, ensure the session interface is reliably closed
this - > join ( ) ; // block until the loop thread terminates and is reaped
2016-12-15 05:54:48 +01:00
INFO ( session , " Proc-Dispatcher stopped. " ) ;
}
ERROR_LOG_AND_IGNORE ( session , " Stopping the Proc-Dispatcher " ) ;
2016-12-13 04:34:28 +01:00
}
2016-12-14 04:57:08 +01:00
void
activateCommandProecssing ( )
{
Lock sync ( this ) ;
canDispatch_ = true ;
INFO ( command , " Session command processing activated. " ) ;
TODO ( " implement command processing queue " ) ;
}
void
deactivateCommandProecssing ( )
{
Lock sync ( this ) ;
canDispatch_ = false ;
INFO ( command , " Session command interface closed. " ) ;
TODO ( " implement command processing queue " ) ;
}
2016-12-15 05:21:03 +01:00
/* === CommandDispatch interface === */
2016-12-16 23:26:56 +01:00
//////////////////////////////////////////TODO notify!!!! on!! every!! state!! changing!! operation!!
2016-12-15 05:21:03 +01:00
void
clear ( ) override
{
Lock sync ( this ) ;
UNIMPLEMENTED ( " clear the queue " ) ;
2016-12-16 23:26:56 +01:00
//////////////////////////////////////////TODO notify!!!!
2016-12-15 05:21:03 +01:00
}
size_t
size ( ) const
{
TODO ( " implement command processing queue " ) ;
return 0 ;
}
2016-12-15 06:21:59 +01:00
void
requestStop ( ) noexcept
{
Lock sync ( this ) ;
commandService_ . reset ( ) ; // closes Session interface
mustHalt_ = true ;
2016-12-16 23:26:56 +01:00
UNIMPLEMENTED ( " *must* notify loop thread " ) ; /////////////////TODO really? YES!!!
//////////////////////////////////////////TODO notify!!!!
2016-12-15 06:21:59 +01:00
}
2016-12-16 23:11:19 +01:00
void
awaitCheckpoint ( )
{
Lock blockWaiting ( this , & DispatcherLoop : : stateIsSynched ) ;
2016-12-21 03:15:36 +01:00
//////////////////////////////////////////TODO find out who will notify us!!!!
2016-12-16 23:11:19 +01:00
}
2016-12-13 04:34:28 +01:00
private :
2016-12-21 03:15:36 +01:00
/** the actual loop running in the Session thread */
2016-12-13 04:34:28 +01:00
void
run ( Subsys : : SigTerm sigTerm )
{
2016-12-13 04:45:00 +01:00
string errorMsg ;
try
{
2016-12-15 22:15:20 +01:00
while ( looper_ . shallLoop ( ) )
{
awaitAction ( ) ;
if ( looper_ . isDying ( ) ) break ;
2016-12-21 03:15:36 +01:00
if ( looper_ . runBuild ( ) )
2016-12-15 22:15:20 +01:00
startBuilder ( ) ;
else
if ( looper_ . isWorking ( ) )
processCommands ( ) ;
2016-12-20 03:53:48 +01:00
looper_ . markStateProcessed ( ) ;
2016-12-15 22:15:20 +01:00
}
2016-12-13 04:45:00 +01:00
}
catch ( lumiera : : Error & problem )
{
errorMsg = problem . what ( ) ;
lumiera_error ( ) ; // clear error flag
}
catch ( . . . )
{
errorMsg = string { lumiera_error ( ) } ;
}
2016-12-15 22:15:20 +01:00
// now leave the Session thread...
// send notification of subsystem shutdown
2016-12-13 04:45:00 +01:00
sigTerm ( & errorMsg ) ;
2016-12-13 04:34:28 +01:00
}
2016-12-15 22:15:20 +01:00
void
awaitAction ( )
{
2016-12-16 19:21:06 +01:00
Lock ( this ) . wait ( looper_ , & Looper : : requireAction ,
2016-12-15 22:15:20 +01:00
looper_ . getTimeout ( ) ) ;
}
2016-12-16 23:11:19 +01:00
bool
stateIsSynched ( )
{
2016-12-20 03:53:48 +01:00
if ( looper_ . hasPendingChanges ( ) and calledFromWithinSessionThread ( ) )
2016-12-16 23:11:19 +01:00
throw error : : Fatal ( " Possible Deadlock. "
" Attempt to synchronise to a command processing check point "
" from within the (single) session thread. "
, error : : LUMIERA_ERROR_LIFECYCLE ) ;
2016-12-21 03:15:36 +01:00
return not looper_ . hasPendingChanges ( ) ;
2016-12-16 23:11:19 +01:00
}
2016-12-15 22:15:20 +01:00
void
processCommands ( )
{
UNIMPLEMENTED ( " pull commands from the queue and dispatch them " ) ;
}
void
startBuilder ( )
{
UNIMPLEMENTED ( " start the Proc-Builder to recalculate render nodes network " ) ;
}
2016-12-16 23:11:19 +01:00
bool
calledFromWithinSessionThread ( )
{
UNIMPLEMENTED ( " how to find out when the session thread attempts to catch its own tail...??? " ) ;
////////////////////////////////////////////////////////////////TODO any idea how to achieve that? The lock does not help us, since it is recursive and
//////////////////////////////////////////////////////////////// ... since command/builder execution itself is not performed in a locked section.
//////////////////////////////////////////////////////////////// ... Possibly we'll just have to plant a ThreadLocal to mark this dangerous situation.
2016-12-20 02:35:45 +01:00
///////////////////////////////////////////////////////////////////////////////TICKET #1054 : can be done by relying on some internals of our thread handling framework
2016-12-16 23:11:19 +01:00
}
2016-12-13 04:34:28 +01:00
} ;
2009-09-29 04:47:09 +02:00
2016-12-15 22:15:20 +01:00
2009-09-29 04:47:09 +02:00
/** storage for Singleton access */
2013-10-20 03:19:36 +02:00
lib : : Depend < ProcDispatcher > ProcDispatcher : : instance ;
2009-09-29 04:47:09 +02:00
2016-12-13 04:34:28 +01:00
/** */
bool
ProcDispatcher : : start ( Subsys : : SigTerm termNotification )
{
2016-12-14 04:18:58 +01:00
Lock sync ( this ) ;
2016-12-13 04:34:28 +01:00
if ( runningLoop_ ) return false ;
runningLoop_ . reset (
new DispatcherLoop (
[ = ] ( string * problemMessage )
{
runningLoop_ . reset ( ) ;
termNotification ( problemMessage ) ;
} ) ) ;
2016-12-14 04:57:08 +01:00
if ( active_ )
runningLoop_ - > activateCommandProecssing ( ) ;
2016-12-13 04:34:28 +01:00
return true ;
}
/** */
bool
ProcDispatcher : : isRunning ( )
{
2016-12-14 04:18:58 +01:00
Lock sync ( this ) ;
2016-12-13 04:34:28 +01:00
return bool ( runningLoop_ ) ;
}
2016-12-15 06:21:59 +01:00
/** 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 ! ! !
*/
2016-12-13 04:34:28 +01:00
void
2016-12-15 06:21:59 +01:00
ProcDispatcher : : requestStop ( ) noexcept
2016-12-13 04:34:28 +01:00
{
2016-12-14 04:18:58 +01:00
Lock sync ( this ) ;
2016-12-15 06:21:59 +01:00
if ( runningLoop_ )
runningLoop_ - > requestStop ( ) ;
2016-12-13 04:34:28 +01:00
}
2016-12-14 04:57:08 +01:00
/** activate processing of enqueued session commands.
* @ remarks command processing serves as public external interface
* to the session . This call is used by the session lifecycle ( SessManagerImpl )
* when the session is brought up ; any other invocation runs danger to mess up
* the session lifecycle state and process commands on a deconfigured session .
* In case the dispatcher loop is not actually running , the activation state
* is stored and applied accordingly later , when the loop is fired up .
*/
2009-09-29 04:47:09 +02:00
void
ProcDispatcher : : activate ( )
{
2016-12-14 04:18:58 +01:00
Lock sync ( this ) ;
2016-12-14 04:57:08 +01:00
active_ = true ;
if ( runningLoop_ )
runningLoop_ - > activateCommandProecssing ( ) ;
2009-09-29 04:47:09 +02:00
}
void
ProcDispatcher : : deactivate ( )
{
2016-12-14 04:18:58 +01:00
Lock sync ( this ) ;
2016-12-14 04:57:08 +01:00
active_ = false ;
if ( runningLoop_ )
runningLoop_ - > deactivateCommandProecssing ( ) ;
2009-09-29 04:47:09 +02:00
}
2016-12-16 23:11:19 +01:00
/** block until the dispatcher has actually reached disabled state.
* @ warning beware of invoking this function from within the session thread ,
* since the waiting relies on the very lock also used to coordinate
* command processing and builder runs within that thread .
* @ throw error : : Fatal when a deadlock due to such a recursive call can be detected
*/
void
ProcDispatcher : : awaitDeactivation ( )
{
Lock sync ( this ) ;
if ( runningLoop_ )
runningLoop_ - > awaitCheckpoint ( ) ;
}
2009-09-29 04:47:09 +02:00
void
ProcDispatcher : : clear ( )
{
2016-12-14 04:18:58 +01:00
Lock sync ( this ) ;
2016-12-14 04:57:08 +01:00
if ( not empty ( ) )
2016-12-15 05:21:03 +01:00
{
WARN ( command , " DISCARDING pending Session commands. " ) ;
REQUIRE ( runningLoop_ ) ;
runningLoop_ - > clear ( ) ;
}
2009-09-29 04:47:09 +02:00
}
bool
ProcDispatcher : : empty ( ) const
{
2016-12-14 04:18:58 +01:00
Lock sync ( this ) ;
2016-12-15 05:21:03 +01:00
return not runningLoop_
or 0 = = runningLoop_ - > size ( ) ;
2009-09-29 04:47:09 +02:00
}
2009-06-08 04:46:07 +02:00
2011-12-02 17:50:44 +01:00
} } // namespace proc::control