Preface
I have decided to let my pet project created some years ago to undergo a code review here.
The review will be broken into parts according to meta question Multiple reviews or one big review?.
The overall concept and motivation can be inspected here (be patient, it takes some time to load).
The doxygen generated documentation can be accessed online here (it loads a bit quicker).
- Part I: The basic interfaces
- Part II: Abstraction of asynchronous/concurrency features
Part III: Policy based design of higher level State Machine constructs
As promised earlier here are the higher level state constructs like CompositeState
, concurrent Regions
etc.
The simplest thing is an ActiveState
, that keeps an asynchonous thread running from it's do()
action:
ActiveState.h
#ifndef ACTIVESTATE_H_
#define ACTIVESTATE_H_
#include "State.h"
#include "SttclThread.h"
#include "SttclMutex.h"
#include "SttclSemaphore.h"
#include "SttclTime.h"
namespace sttcl
{
template
< class StateImpl
, class StateMachineImpl
, class IState
, class StateThreadType
, class TimeDurationType
, class EndDoActionSemaphoreType
, class ActiveStateMutexType
>
class ActiveState;
namespace internal
{
/**
* Helper class to call a state thread method.
*/
struct ThreadFunctionHelper
{
/**
* Defines a static method that can be passed to a thread implementation.
* @tparam StateImpl The \link sttcl::ActiveState\endlink<>
* implementation class.
* @tparam StateMachineImpl The \link sttcl::StateMachine\endlink<> implementation
* class that contains the \link sttcl::ActiveState\endlink<>
* implementation.
* @tparam IState The inner event handler interface of the
* \link sttcl::StateMachine\endlink<> implementation
* @tparam StateThreadType The thread implementation class, default is
* \link sttcl::internal::SttclThread\endlink<>.
* @tparam EndDoActionSemaphoreType The semaphore implementation class, default is
* \link sttcl::internal::SttclSemaphore\endlink<>.
* @tparam TimeDurationType The time duration representation implementation class, default
* is \link sttcl::TimeDuration\endlink<>.
* @tparam ActiveStateMutexType The mutex implementation class, default
* is \link sttcl::internal::SttclMutex\endlink<>.
* @param args A pointer to the thread args. Implicitely \em args is casted to
* \link sttcl::ActiveState\endlink and the
* sttcl::ActiveState::doAction method is called.
*/
template
< class StateImpl
, class StateMachineImpl
, class IState
, class StateThreadType
, class TimeDurationType
, class EndDoActionSemaphoreType
, class ActiveStateMutexType
>
static void* stateThreadMethod(void* args)
{
ActiveState<StateImpl
,StateMachineImpl
,IState
,StateThreadType
,EndDoActionSemaphoreType
,TimeDurationType
,ActiveStateMutexType
>* pThis =
reinterpret_cast<ActiveState<StateImpl
,StateMachineImpl
,IState
,StateThreadType
,EndDoActionSemaphoreType
,TimeDurationType
,ActiveStateMutexType
>*>(args);
pThis->runDoAction(pThis->currentContext);
return 0;
}
};
}
/**
* Represents a particular state machines active state implementation base class.
* An ActiveState implementation runs its do action in a separate thread.
*
* @tparam StateImpl The state implementation type.
* @tparam StateMachineImpl The state machine implementation type.
* @tparam IState Specifies the internal interface of state implementations for the state
* machine.
* @tparam StateThreadType The thread implementation class, default is
* \link sttcl::internal::SttclThread\endlink<>.
* @tparam EndDoActionSemaphoreType The semaphore implementation class, default is
* \link sttcl::internal::SttclSemaphore\endlink<>.
* @tparam TimeDurationType The time duration representation implementation class, default
* is \link sttcl::TimeDuration\endlink<>.
* @tparam ActiveStateMutexType The mutex implementation class, default
* is \link sttcl::internal::SttclMutex\endlink<>.
*/
template
< class StateImpl
, class StateMachineImpl
, class IState
, class StateThreadType = sttcl::internal::SttclThread<STTCL_DEFAULT_THREADIMPL>
, class TimeDurationType = TimeDuration<STTCL_DEFAULT_TIMEDURATIONIMPL>
, class EndDoActionSemaphoreType = internal::SttclSemaphore<STTCL_DEFAULT_SEMAPHOREIMPL>
, class ActiveStateMutexType = internal::SttclMutex<STTCL_DEFAULT_MUTEXIMPL,TimeDurationType>
>
class ActiveState
: public sttcl::StateBase<StateMachineImpl,IState>
{
friend class sttcl::internal::ThreadFunctionHelper;
public:
/**
* The state machine implementation type.
*/
typedef StateMachineImpl Context;
/**
* The implementation class type.
*/
typedef StateImpl Implementation;
/**
* The state base class type.
*/
typedef StateBase<StateMachineImpl,IState> StateBaseType;
/**
* The state do action type.
*/
typedef void (Implementation::*StateDoAction)(Context*, bool);
/**
* The thread class type.
*/
typedef StateThreadType StateThreadImpl;
/**
* The semaphore class type to use for ending the do action call loop.
*/
typedef EndDoActionSemaphoreType EndDoActionSemaphoreImpl;
/**
* The time duration class type to use for specifying the do action call rate.
*/
typedef TimeDurationType TimeDurationImpl;
/**
* The mutex class type to use for co9ordinating access to ActiveState internal variables.
*/
typedef ActiveStateMutexType ActiveStateMutexImpl;
/**
* Gets the do action call frequency.
*/
inline const TimeDurationImpl& getDoFrequency() const
{
return doFrequency;
}
/**
* Sets the do action call frequency.
*/
inline void setDoFrequency(const TimeDurationImpl& newValue)
{
doFrequency = newValue;
}
/**
* Default implementation for checking if endDo() was requested.
* @return \c true if the do action loop should be left, \c false otherwise.
*/
bool endDoActionRequestedImpl()
{
bool result = endDoActionSemaphore.try_wait(doFrequency);
return result;
}
/**
* Default implementation for the entry() method.
* @param context A pointer to the containing state machine.
*/
inline void entryImpl(Context* context)
{
}
/**
* Default implementation for the exit() method.
* @param context A pointer to the containing state machine.
*/
inline void exitImpl(Context* context)
{
}
/**
* Default implementation for the exitingDoActionImpl() method.
* The method is called when the do action thread is exiting.
*/
inline void exitingDoActionImpl()
{
}
/**
* Default implementation to join the active state thread.
*/
inline void joinDoActionThreadImpl()
{
if(!StateThreadType::isSelf(stateThread))
{
stateThread.join();
}
}
/**
* Default implementation for the joinDoAction() method.
* @param context A pointer to the containing state machine.
*/
inline void joinDoActionImpl(Context* context)
{
static_cast<StateImpl*>(this)->joinDoActionThreadImpl();
context->unregisterActiveStateRunning(static_cast<StateBaseType*>(this));
}
/**
* Default implementation called to unblock any blocking methods waiting in the do action
* call.
*/
inline void unblockDoActionImpl()
{
}
/**
* ActiveState class default implementation for the endDo() method.
* @param context A pointer to the containing state machine.
*/
inline void endDoImpl(Context* context)
{
endDoActionSemaphore.post();
if(isDoActionRunning())
{
static_cast<StateImpl*>(this)->unblockDoActionImpl();
}
if(!StateThreadType::isSelf(stateThread))
{
joinDoAction(context);
currentContext = 0;
}
}
/**
* Gets the thread instance used to run the ActiveState do action.
* @return
*/
inline const StateThreadType& getStateThread() const
{
return stateThread;
}
/**
* Called after this state was entered.
*
* @param context The state machine context.
*/
inline void startDoImpl(Context* context)
{
if(!isDoActionRunning())
{
currentContext = context;
stateThread.run(this);
}
}
inline bool isDoActionRunning() const
{
sttcl::internal::AutoLocker<ActiveStateMutexType> lock(activeStateMutex);
return doActionRunning;
}
/**
* Default implementation for the finalizeSubStateMachines() method.
* @param recursive If \c true further sub state machines should be finalized
* recursively.
*/
void finalizeSubStateMachinesImpl(bool recursive)
{
}
/**
* Default implementation for the initSubStateMachines() method.
* @param recursive If \c true further sub state machines should be initialized
* recursively.
*/
void initSubStateMachinesImpl(bool recursive)
{
}
/**
* Default implementation for the getDirectTransitionImpl() method.
* @param context A pointer to the containing state machine.
* @param nextState Receives a pointer to the next sibling state to appear on a direct transition.
* @param finalize Receives \c true to finalize the containing state machine.
* @return \c true if a direct transition should be performed, \c false otherwise.
*/
bool checkDirectTransitionImpl(Context* context, bool& finalize, StateBaseType*& nextState)
{
nextState = 0;
finalize = false;
return false;
}
protected:
/**
* Constructor for class ActiveState.
* @param argDoAction The do action that will be called in the ActiveState's thread.
* @param argRunDoActionOnce Indicates to run the do action only once after the state was
* entered.
* @param argDoFrequency The frequency used to call the do action in a loop. You can use this
* to limit the CPU resources given to the do action. If the do action
* uses another blocking mechanism the parameter may be omitted (the
* default is TimeDurationType::Zero).
*/
ActiveState(StateDoAction argDoAction, bool argRunDoActionOnce = false , TimeDurationType argDoFrequency = TimeDurationType::Zero)
: doAction(argDoAction)
, doFrequency(argDoFrequency)
, currentContext(0)
, stateThread(&sttcl::internal::ThreadFunctionHelper::stateThreadMethod
< StateImpl
, StateMachineImpl
, IState
, StateThreadType
, EndDoActionSemaphoreType
, TimeDurationType
, ActiveStateMutexType>)
, endDoActionSemaphore(0)
, doActionRunning(false)
, runDoActionOnce(argRunDoActionOnce)
{
}
/**
* Destructor for class ActiveState.
*/
virtual ~ActiveState()
{
bool joinStateThread = false;
{ sttcl::internal::AutoLocker<ActiveStateMutexType> lock(activeStateMutex);
joinStateThread = doActionRunning;
}
if(joinStateThread && !StateThreadType::isSelf(stateThread))
{
unblockDoActionImpl();
joinDoActionThreadImpl();
}
}
/**
* Implements \c StateBaseType::entry()
* Called by the containing state machine when the state is entered.
* @param context A pointer to the containing state machine.
*/
virtual void entry(Context* context)
{
static_cast<Implementation*>(this)->entryImpl(context);
}
/**
* Implements \c StateBaseType::exit()
* Called by the containing state machine when the state is left.
* @param context A pointer to the containing state machine.
*/
virtual void exit(Context* context)
{
static_cast<Implementation*>(this)->exitImpl(context);
}
/**
* Implements \c StateBaseType::startDo()
* Called by the containing state machine after the state was entered.
* @param context A pointer to the containing state machine.
*/
virtual void startDo(Context* context)
{
static_cast<StateImpl*>(this)->startDoImpl(context);
}
/**
* Implements \c StateBaseType::endDo()
* Called by the containing state machine before the state is left.
* @param context A pointer to the containing state machine.
*/
virtual void endDo(Context* context)
{
static_cast<Implementation*>(this)->endDoImpl(context);
}
/**
* Changes \em context state machine to another sibling state.
* @param context A pointer to the containing state machine.
* @param newState A pointer to the sibling state to change to.
*/
void changeState(Context* context,StateBase<StateMachineImpl,IState>* newState)
{
if(context && context->getState() == this)
{
static_cast<Implementation*>(this)->changeStateImpl(context,newState);
}
}
/**
* Indicates that the do action thread should stop.
* @return \c true if the do action thread should stop, \c false otherwise.
*/
bool endDoActionRequested()
{
return static_cast<Implementation*>(this)->endDoActionRequestedImpl();
}
private:
/**
* Default implementation for the joinDoAction() method.
* @param context A pointer to the containing state machine.
*/
virtual void joinDoAction(Context* context)
{
static_cast<Implementation*>(this)->joinDoActionImpl(context);
}
void setDoActionRunning(bool value)
{
sttcl::internal::AutoLocker<ActiveStateMutexType> lock(activeStateMutex);
doActionRunning = value;
}
void runDoAction(Context* context)
{
bool initialCall = true;
setDoActionRunning(true);
bool directTransitionTriggered = false;
do
{
if(context && (context->isFinalizing() || context->isFinalized()))
{
break;
}
// Run the do action
if(doAction)
{
(static_cast<Implementation*>(this)->*doAction)(context,initialCall);
if(initialCall)
{
initialCall = false;
}
}
// Handle direct transitions
StateBaseType* nextState = 0;
bool finalize = false;
if(static_cast<Implementation*>(this)->checkDirectTransitionImpl(context,finalize,nextState))
{
if(finalize)
{
context->finalize();
}
else if(nextState)
{
changeState(context,nextState);
}
directTransitionTriggered = true;
}
} while(!endDoActionRequested() && !runDoActionOnce && !directTransitionTriggered);
setDoActionRunning(false);
static_cast<Implementation*>(this)->exitingDoActionImpl();
context->unregisterActiveStateRunning(static_cast<StateBaseType*>(this));
}
virtual void finalizeSubStateMachines(bool recursive)
{
static_cast<Implementation*>(this)->finalizeSubStateMachinesImpl(recursive);
}
virtual void initSubStateMachines(bool recursive)
{
static_cast<Implementation*>(this)->initSubStateMachinesImpl(recursive);
}
StateDoAction doAction;
TimeDurationType doFrequency;
Context* currentContext;
StateThreadType stateThread;
EndDoActionSemaphoreType endDoActionSemaphore;
mutable ActiveStateMutexType activeStateMutex;
bool doActionRunning;
bool runDoActionOnce;
};
}
#endif /* ACTIVESTATE_H_ */
The other (orthogonal) policiy is to create composite State
s as done here:
#ifndef COMPOSITESTATE_H_
#define COMPOSITESTATE_H_
#include "State.h"
namespace sttcl
{
/**
* Represents a composite states history pseudo state behavior.
*/
struct CompositeStateHistoryType
{
enum Values
{
/**
* Default behavior of the history pseudo state. No history is tracked.
*/
None ,
/**
* Deep history pseudo state behavior. Nested history pseudo states will be resumed
* on (re-)entering the composite state.
*/
Deep ,
/**
* Shallow history pseudo state behavior. Nested history pseudo states will be ignored
* on (re-)entering the composite state.
*/
Shallow ,
};
};
namespace internal
{
/**
* Represents the default base class for a composite state.
* @tparam InnerStateType The state base class type of the composite states inner states.
*/
template<class InnerStateType>
class CompositeStateBase
{
protected:
/**
* Constructor for class CompositeStateBase.
*/
CompositeStateBase()
{
}
/**
* Destructor for class CompositeStateBase.
*/
~CompositeStateBase()
{
}
/**
* Saves the current inner state of the composite state.
* @param currentState
*/
void saveCurrentState(InnerStateType* currentState)
{
}
/**
* Gets the last saved state.
* @return The last saved state.
*/
InnerStateType* getStateHistory() const
{
return 0;
}
/**
* Resumes the state history.
* @tparam CompositeStateImpl The composite state implementation class.
* @param compositeState A pointer to the composite state implementation.
* @return The current inner state of the composite state.
*/
template<class CompositeStateImpl>
InnerStateType* resumeStateHistory(CompositeStateImpl* compositeState)
{
if(!compositeState->isReady())
{
compositeState->initialize();
compositeState->setReady();
}
InnerStateType* currentState = compositeState->getState();
// if(!currentState)
// {
// currentState = compositeState->getInitialState();
// compositeState->changeState(currentState);
// }
return currentState;
}
/**
* Resets the state history.
* @tparam CompositeStateImpl The composite state implementation class.
* @param compositeState A pointer to the composite state implementation.
* @return The current inner state of the composite state.
*/
template<class CompositeStateImpl>
InnerStateType* finalizeStateHistory(CompositeStateImpl* compositeState)
{
InnerStateType* currentState = compositeState->getState();
if(currentState)
{
currentState->finalizeSubStateMachines(true);
}
return 0;
}
};
/**
* Represents the base class for a composite state with a deep history pseudo state.
* @tparam InnerStateType The state base class type of the composite states inner states.
*/
template<class InnerStateType>
class CompositeStateBaseWithDeepHistory
{
protected:
/**
* Constructor for class CompositeStateBaseWithDeepHistory.
*/
CompositeStateBaseWithDeepHistory()
: lastState(0)
{
}
/**
* Destructor for class CompositeStateBaseWithDeepHistory.
*/
~CompositeStateBaseWithDeepHistory()
{
}
/**
* Saves the current inner state of the composite state.
* @param currentState
*/
void saveCurrentState(InnerStateType* currentState)
{
lastState = currentState;
}
/**
* Gets the last saved state.
* @return
*/
InnerStateType* getStateHistory() const
{
return lastState;
}
/**
* Resumes the state history.
* @tparam CompositeStateImpl The composite state implementation class.
* @param compositeState A pointer to the composite state implementation.
* @return The current inner state of the composite state.
*/
template<class CompositeStateImpl>
InnerStateType* resumeStateHistory(CompositeStateImpl* compositeState)
{
if(lastState != 0)
{
compositeState->changeState(lastState);
InnerStateType* currentState = compositeState->getState();
if(currentState && (currentState != compositeState->getInitialState()))
{
currentState->initSubStateMachines(true);
}
}
else if(!compositeState->isReady())
{
compositeState->initialize(false);
compositeState->setReady();
}
InnerStateType* currentState = compositeState->getState();
return currentState;
}
/**
* Resets the state history.
* @tparam CompositeStateImpl The composite state implementation class.
* @param compositeState A pointer to the composite state implementation.
* @return The current inner state of the composite state.
*/
template<class CompositeStateImpl>
InnerStateType* finalizeStateHistory(CompositeStateImpl* compositeState)
{
InnerStateType* currentState = compositeState->getState();
if(currentState)
{
currentState->finalizeSubStateMachines(true);
}
saveCurrentState(currentState);
return 0;
}
InnerStateType* lastState;
};
/**
* Represents the base class for a composite state with a shallow history pseudo state.
* @tparam InnerStateType The state base class type of the composite states inner states.
*/
template<class InnerStateType>
class CompositeStateBaseWithShallowHistory
{
protected:
/**
* Constructor for class CompositeStateBaseWithShallowHistory.
*/
CompositeStateBaseWithShallowHistory()
: lastState(0)
{
}
/**
* Destructor for class CompositeStateBaseWithShallowHistory.
*/
~CompositeStateBaseWithShallowHistory()
{
}
/**
* Saves the current inner state of the composite state.
* @param currentState
*/
void saveCurrentState(InnerStateType* currentState)
{
lastState = currentState;
}
/**
* Gets the last saved state.
* @return
*/
InnerStateType* getStateHistory() const
{
return lastState;
}
/**
* Resumes the state history.
* @tparam CompositeStateImpl The composite state implementation class.
* @param compositeState A pointer to the composite state implementation.
* @return The current inner state of the composite state.
*/
template<class CompositeStateImpl>
InnerStateType* resumeStateHistory(CompositeStateImpl* compositeState)
{
if(lastState)
{
compositeState->changeState(lastState);
InnerStateType* currentState = compositeState->getState();
if(currentState && (currentState != compositeState->getInitialState()))
{
currentState->initSubStateMachines(false);
}
}
else if(!compositeState->isReady())
{
compositeState->initialize();
compositeState->setReady();
}
InnerStateType* currentState = compositeState->getState();
if(!currentState)
{
compositeState->finalize(false);
}
return currentState;
}
/**
* Resets the state history.
* @tparam CompositeStateImpl The composite state implementation class.
* @param compositeState A pointer to the composite state implementation.
* @return The current inner state of the composite state.
*/
template<class CompositeStateImpl>
InnerStateType* finalizeStateHistory(CompositeStateImpl* compositeState)
{
InnerStateType* currentState = compositeState->getState();
if(currentState)
{
currentState->finalizeSubStateMachines(false);
}
saveCurrentState(currentState);
return 0;
}
InnerStateType* lastState;
};
/**
* Used to select a pseudo state history behavior base class for class CompositeState.
* @tparam HistoryType The history pseudo state type.
* @tparam InnerStateType The state base class type of the composite states inner states.
*/
template<CompositeStateHistoryType::Values HistoryType, class InnerStateType>
struct CompositeStateBaseSelector
{
/**
* The composite state base class implementing the history pseudo state behavior.
*/
typedef sttcl::internal::CompositeStateBase<InnerStateType> RESULT_TYPE;
};
/**
* Specializes CompositeStateBaseSelector for deep history pseudo state behavior.
*/
template<class InnerStateType>
struct CompositeStateBaseSelector<CompositeStateHistoryType::Deep,InnerStateType>
{
/**
* The composite state base class implementing the history pseudo state behavior.
*/
typedef sttcl::internal::CompositeStateBaseWithDeepHistory<InnerStateType> RESULT_TYPE;
};
/**
* Specializes CompositeStateBaseSelector for shallow history pseudo state behavior.
*/
template<class InnerStateType>
struct CompositeStateBaseSelector<CompositeStateHistoryType::Shallow,InnerStateType>
{
/**
* The composite state base class implementing the history pseudo state behavior.
*/
typedef sttcl::internal::CompositeStateBaseWithShallowHistory<InnerStateType> RESULT_TYPE;
};
}
/**
* Represents the base class for a composite state implementation.
* @tparam CompositeStateImpl The implementing class.
* @tparam StateMachineImpl The implementing class.
* @tparam IInnerState The inner state's interface class type.
* @tparam HistoryType The composite states history pseudo state type.
* @tparam StateBaseImpl The composite states base state implementation type.
* @tparam StateMachineBaseImpl The composite states base state machine implementation type.
*/
template
< class CompositeStateImpl
, class StateMachineImpl
, class IInnerState
, CompositeStateHistoryType::Values HistoryType = CompositeStateHistoryType::None
, class StateBaseImpl = State<CompositeStateImpl,StateMachineImpl,typename StateMachineImpl::StateInterface>
#if defined(STTCL_THREADSAFE_IMPL)
, class StateMachineBaseImpl = StateMachine<CompositeStateImpl, IInnerState, typename StateMachineImpl::MutexType>
#else
, class StateMachineBaseImpl = StateMachine<CompositeStateImpl, IInnerState>
#endif
>
class CompositeState
: public StateBaseImpl
, public StateMachineBaseImpl
, public internal::CompositeStateBaseSelector<HistoryType,typename StateMachineBaseImpl::StateBaseClass>::RESULT_TYPE
{
public:
/**
* The implementation class type.
*/
typedef CompositeStateImpl Implementation;
/**
* The state machine implementation type.
*/
typedef StateMachineImpl Context;
/**
* The composite states base state implementation type class.
*/
typedef StateBaseImpl StateImplementationBase;
/**
* The composite states base state machine implementation type class.
*/
typedef StateMachineBaseImpl StateMachineImplementationBase;
/**
* The state do action type.
*/
typedef void (Implementation::*StateDoAction)(Context*,bool);
/**
* The composite state base class regarding state history policy.
*/
typedef typename internal::CompositeStateBaseSelector<HistoryType,typename StateMachineBaseImpl::StateBaseClass>::RESULT_TYPE CompositeStateHistoryBaseClass;
/**
* The inner state class.
*/
typedef typename StateMachineBaseImpl::StateBaseClass InnerStateClass;
/**
* The outer state interface.
*/
typedef typename StateMachineImpl::StateInterface OuterStateInterface;
/**
* The state interface.
*/
typedef typename StateMachineBaseImpl::StateInterface StateInterface;
/**
* Constructor for class CompositeState.
* @param argDoAction The composite state do action.
*/
CompositeState(StateDoAction argDoAction = 0)
: StateBaseImpl(argDoAction)
{
}
/**
* Destructor for class CompositeState.
*/
virtual ~CompositeState()
{
}
/**
* Changes the composite state machine to \em newState.
* @param newState The inner state to change to.
*/
void changeState(InnerStateClass* newState)
{
StateMachineBaseImpl::changeState(newState);
}
/**
* Default implementation for the initialize() method.
* @param force Indicates to finalize the state machine before (re-)initializing.
* @return The ready state of the state machine.
*/
bool initializeImpl(bool force)
{
return static_cast<StateMachineBaseImpl*>(this)->initializeImpl(force);
}
/**
* Default implementation for the finalize() method.
*
* @param finalizeSubStateMachines Indicates to finalize any sub state machines.
*/
inline void finalizeImpl(bool finalizeSubStateMachines)
{
static_cast<StateMachineBaseImpl*>(this)->finalizeImpl(finalizeSubStateMachines);
subStateMachineCompleted();
}
/**
* Default implementation of the subStateMachineCompleted() method.
*/
inline void subStateMachineCompletedImpl()
{
// TODO: Propagate completion events asynchronously (e.g. for Region and ConcurrentCompositeState)
}
/**
* Notifies the implementation class when the sub state machine has finished.
*/
inline void subStateMachineCompleted()
{
static_cast<CompositeStateImpl*>(this)->subStateMachineCompletedImpl();
}
/**
* Overriden implementation of StateBase::entry.
*/
// virtual void entry(Context* context)
// {
// static_cast<CompositeStateImpl*>(this)->entryImpl(context);
// }
/**
* Called when this state is entered.
*
* @param context The state machine context.
*/
void entryImpl(Context* context)
{
StateBaseImpl::entryImpl(context);
// StateMachineBaseImpl::setState(
InnerStateClass* currentState =
this->resumeStateHistory(static_cast<CompositeStateImpl*>(this));
// );
if(!currentState)
{
currentState = static_cast<CompositeStateImpl*>(this)->getInitialState();
static_cast<CompositeStateImpl*>(this)->changeState(currentState);
}
}
/**
* Called when this state is left.
*
* @param context The state machine context.
*/
void exitImpl(Context* context)
{
StateMachineBaseImpl::exitCurrentState();
StateMachineBaseImpl::setState
(this->finalizeStateHistory(static_cast<CompositeStateImpl*>(this)));
StateMachineBaseImpl::finalize(false);
StateBaseImpl::exitImpl(context);
}
/**
* Default implementation for isReady().
*
* @return \c true if the state machine is ready to process events, \c false otherwise.
*/
inline bool isReadyImpl() const
{
return (CompositeStateHistoryBaseClass::getStateHistory() ||
StateMachineImplementationBase::isReadyImpl());
}
/**
* Indicates that the composite state machine is currently initializing.
* @return \c true if the state machine is currently initializing, \c false otherwise.
*/
bool isInitializing() const
{
return StateMachineImplementationBase::isInitializing();
}
protected:
/**
* Changes \em context state machine to another sibling state.
* @param context A pointer to the containing state machine.
* @param newState A pointer to the sibling state to change to.
*/
void changeState(Context* context,StateBase<StateMachineImpl,OuterStateInterface>* newState)
{
StateBaseImpl::changeState(context,newState);
}
/**
* Called by the containing StateMachine to finalize any sub state machines.
* @param recursive If \c true further sub state machines should be finalized
* recursively.
*/
virtual void finalizeSubStateMachines(bool recursive)
{
if(!recursive)
{
static_cast<StateMachineBaseImpl*>(this)->finalize(recursive);
}
}
/**
* Called by the containing StateMachine to initialize any sub state machines.
* @param recursive If \c true further sub state machines should be initialized
* recursively.
*/
virtual void initSubStateMachines(bool recursive)
{
InnerStateClass* currentState = StateMachineBaseImpl::getState();
if(!recursive)
{
StateMachineBaseImpl::initialize(false);
}
else
{
currentState = StateMachineBaseImpl::getState();
if(currentState)
{
currentState->initSubStateMachines(true);
}
else
{
StateMachineBaseImpl::initialize(false);
}
}
}
};
}
#endif /* COMPOSITESTATE_H_ */
The combination of policies (history handling and concurrency) results in
#ifndef CONCURRENTCOMPOSITESTATE_H_
#define CONCURRENTCOMPOSITESTATE_H_
#if defined(STTCL_USE_STL)
#include <set>
#endif
#include "SttclConfig.h"
#include "StateMachine.h"
#include "CompositeState.h"
#include "Region.h"
namespace sttcl
{
namespace internal
{
template
< class CompositeStateImpl
, class IInnerState
, class EventArgs
>
class RegionContainer
{
public:
/**
* The region base class type.
*/
typedef RegionBase<CompositeStateImpl,IInnerState,EventArgs> RegionBaseType;
/**
* Destructor for the RegionContainer class.
*/
virtual ~RegionContainer() {}
/**
* Called when a region sub state machine completes (finalizes).
* @param region A pointer to the finalized region.
*/
virtual void regionCompleted(RegionBaseType* region) = 0;
};
template
< class CompositeStateImpl
, class StateMachineImpl
, class IInnerState
, unsigned int NumOfRegions
, class EventArgs
, class StateBaseImpl
>
class ConcurrentCompositeStateWithEventArgs
: public StateBaseImpl
{
public:
// void changeStateImpl(StateMachineImpl* context,StateBaseImpl* newState)
// {
// StateBaseImpl::changeState(context,newState);
// }
protected:
/**
* The region base class type.
*/
typedef RegionBase<CompositeStateImpl,IInnerState,EventArgs> RegionBaseType;
/**
* The type of the regions array.
*/
typedef RegionBaseType* RegionsArray[NumOfRegions];
/**
* The context state machine implementation type.
*/
typedef StateMachineImpl Context;
typedef sttcl::internal::EventArgsInterfaceSelector<CompositeStateImpl,IInnerState,EventArgs> EventArgsSelectorType;
/**
* The pointer type used to pass event arguments to the contained regions inner states.
*/
typedef typename EventArgsSelectorType::RefCountPtr RefCountPtr;
/**
* The outer event handler signature.
*/
typedef typename StateMachineImpl::StateInterface OuterEventHandler;
/**
* The inner event handler signature.
*/
typedef typename EventArgsSelectorType::InnerEventHandler InnerEventHandler;
ConcurrentCompositeStateWithEventArgs(const RegionsArray& argRegions)
: regions(argRegions)
{
}
/**
* Broadcasts an event to all contained regions.
* @param context A pointer to the containing state machine.
* @param eventHandler The event handler to call inside all region threads.
* @param eventArgs The event arguments to pass to the event handler calls.
*/
void broadcastEvent(Context* context,InnerEventHandler eventHandler,RefCountPtr eventArgs)
{
bool allRegionsFinalized = true;
for(unsigned int i = 0; i < NumOfRegions; ++i)
{
if(regions[i])
{
if(!regions[i]->isRegionFinalized())
{
allRegionsFinalized = false;
regions[i]->handleBroadcastedEvent(static_cast<CompositeStateImpl*>(this),eventHandler,eventArgs);
}
else if(regions[i]->isRegionThreadRunning())
{
regions[i]->endDoRegion(static_cast<CompositeStateImpl*>(this));
regions[i]->exitRegion(static_cast<CompositeStateImpl*>(this));
regions[i]->joinRegionThread();
}
}
}
if(allRegionsFinalized)
{
context->subStateMachineCompleted(this);
}
}
const RegionsArray& regions;
};
template
< class CompositeStateImpl
, class StateMachineImpl
, class IInnerState
, unsigned int NumOfRegions
, class StateBaseImpl
>
class ConcurrentCompositeStateWithoutEventArgs
: public StateBaseImpl
{
public:
// void changeStateImpl(StateMachineImpl* context,StateBaseImpl* newState)
// {
// StateBaseImpl::changeState(context,newState);
// }
protected:
/**
* The region base class type.
*/
typedef RegionBase<CompositeStateImpl,IInnerState,void> RegionBaseType;
/**
* The type of the regions array.
*/
typedef RegionBaseType* RegionsArray[NumOfRegions];
/**
* The context state machine implementation type.
*/
typedef StateMachineImpl Context;
typedef sttcl::internal::EventArgsInterfaceSelector<CompositeStateImpl,IInnerState,void> EventArgsSelectorType;
/**
* The pointer type used to pass event arguments to the contained regions inner states.
*/
typedef typename EventArgsSelectorType::RefCountPtr RefCountPtr;
/**
* The outer event handler signature.
*/
typedef typename StateMachineImpl::StateInterface OuterEventHandler;
/**
* The inner event handler signature.
*/
typedef typename EventArgsSelectorType::InnerEventHandler InnerEventHandler;
ConcurrentCompositeStateWithoutEventArgs(const RegionsArray& argRegions)
: regions(argRegions)
{
}
/**
* Broadcasts an event to all contained regions.
* @param context A pointer to the containing state machine.
* @param eventHandler The event handler to call inside all region threads.
*/
void broadcastEvent(Context* context,InnerEventHandler eventHandler)
{
bool allRegionsFinalized = true;
for(unsigned int i = 0; i < NumOfRegions; ++i)
{
if(regions[i])
{
if(!regions[i]->isRegionFinalized())
{
allRegionsFinalized = false;
regions[i]->handleBroadcastedEvent(static_cast<CompositeStateImpl*>(this),eventHandler);
}
else if(regions[i]->isRegionThreadRunning())
{
regions[i]->endDoRegion(static_cast<CompositeStateImpl*>(this));
regions[i]->joinRegionThread();
}
}
}
if(allRegionsFinalized)
{
context->subStateMachineCompleted(this);
}
}
const RegionsArray& regions;
};
/**
* Used to select the region base implementation.
* @tparam StateMachineImpl
* @tparam StateInterface
* @tparam EventArgs
*/
template
< class CompositeStateImpl
, class StateMachineImpl
, class IInnerState
, unsigned int NumOfRegions
, class EventArgs
, class StateBaseImpl
>
struct ConcurrentCompositeBaseImplementationSelector
{
/**
* The region base implementation type.
*/
typedef ConcurrentCompositeStateWithEventArgs<CompositeStateImpl,StateMachineImpl,IInnerState,NumOfRegions,EventArgs,StateBaseImpl> RESULT_TYPE;
};
/**
* Specializes RegionBaseImplementationSelector for void (i.e. no) event arguments.
*/
template
< class CompositeStateImpl
, class StateMachineImpl
, class IInnerState
, unsigned int NumOfRegions
, class StateBaseImpl
>
struct ConcurrentCompositeBaseImplementationSelector<CompositeStateImpl,StateMachineImpl,IInnerState,NumOfRegions,void,StateBaseImpl>
{
/**
* The region base implementation type.
*/
typedef ConcurrentCompositeStateWithoutEventArgs<CompositeStateImpl,StateMachineImpl,IInnerState,NumOfRegions,StateBaseImpl> RESULT_TYPE;
};
/**
* Represents a concurrent composite state implementation base class.
* @tparam CompositeStateImpl
* @tparam StateMachineImpl
* @tparam IInnerState
* @tparam NumOfRegions
* @tparam EventArgs
* @tparam StateBaseImpl
*/
template
< class CompositeStateImpl
, class StateMachineImpl
, class IInnerState
, unsigned int NumOfRegions
, class EventArgs
, class StateBaseImpl
#if defined(STTCL_THREADSAFE_IMPL)
, class StateMachineMutexType = sttcl::internal::SttclMutex<STTCL_DEFAULT_MUTEXIMPL>
#endif
>
class ConcurrentCompositeStateBase
: public ConcurrentCompositeBaseImplementationSelector
< CompositeStateImpl
, StateMachineImpl
, IInnerState
, NumOfRegions
, EventArgs
, StateBaseImpl
>::RESULT_TYPE
, public RegionContainer<CompositeStateImpl,IInnerState,EventArgs>
{
public:
typedef ConcurrentCompositeBaseImplementationSelector
< CompositeStateImpl
, StateMachineImpl
, IInnerState
, NumOfRegions
, EventArgs
, StateBaseImpl
> BaseClassSelectorType;
typedef typename BaseClassSelectorType::RESULT_TYPE BaseClassType;
#if defined(STTCL_THREADSAFE_IMPL)
typedef StateMachineMutexType MutexType;
#endif
/**
* The region container implementation base class type.
*/
typedef RegionContainer<CompositeStateImpl,IInnerState,EventArgs> RegionContainerBase;
/**
* The state implementation base class type.
*/
typedef StateBaseImpl StateImplementationBase;
/**
* The (outer) state interface class type.
*/
typedef typename StateMachineImpl::StateInterface StateInterface;
/**
* The region state base class type.
*/
typedef sttcl::StateBase<CompositeStateImpl,IInnerState> RegionStateBaseClass;
/**
* The region base class type.
*/
typedef typename BaseClassType::RegionBaseType RegionBaseType;
/**
* The type of the regions array.
*/
typedef typename BaseClassType::RegionsArray RegionsArray;
/**
* The context state machine implementation type.
*/
typedef StateMachineImpl Context;
typedef sttcl::internal::EventArgsInterfaceSelector<StateMachineImpl,IInnerState,EventArgs> EventArgsSelectorType;
/**
* The pointer type used to pass event arguments to the contained regions inner states.
*/
typedef typename EventArgsSelectorType::RefCountPtr RefCountPtr;
/**
* The outer event handler signature.
*/
typedef typename StateMachineImpl::StateInterface OuterEventHandler;
/**
* The inner event handler signature.
*/
typedef typename EventArgsSelectorType::InnerEventHandler InnerEventHandler;
/**
* The internal state implementation base type.
*/
typedef StateBase<CompositeStateImpl,IInnerState> StateBaseClass;
/**
* Constructor for class ConcurrentCompositeState.
* @param argContextStateMachine The containing state machine context.
* @param argRegions A reference to the concrete array of regions in the concurrent composite
* state.
*/
ConcurrentCompositeStateBase(Context* argContextStateMachine, const RegionsArray& argRegions)
: BaseClassType(argRegions)
, flags(StateMachineFlags::Empty())
, contextStateMachine_(argContextStateMachine)
{
}
/**
* Destructor for class ConcurrentCompositeState.
*/
virtual ~ConcurrentCompositeStateBase()
{
}
RegionStateBaseClass* getState() const
{
return 0;
}
void setStateMachineFlags(const StateMachineFlags& value)
{
STTCL_STATEMACHINE_SAFESECTION_START(internalLockGuard);
flags = value;
STTCL_STATEMACHINE_SAFESECTION_END;
}
/**
* Indicates that the state machine is initialized.
* @return \c true if the state machine is initialized, \c false otherwise.
*/
bool isInitialized() const
{
STTCL_STATEMACHINE_SAFE_RETURN(internalLockGuard,flags.initialized);
}
/**
* Indicates that the state machine is finalized.
* @return \c true if the state machine is finalized, \c false otherwise.
*/
bool isFinalized() const
{
STTCL_STATEMACHINE_SAFE_RETURN(internalLockGuard,flags.finalized);
}
/**
* Indicates that the state machine is currently finalizing.
* @return \c true if the state machine is currently finalizing, \c false otherwise.
*/
bool isFinalizing() const
{
STTCL_STATEMACHINE_SAFE_RETURN(internalLockGuard,flags.finalizing);
}
/**
* Indicates that the state machine is currently finalizing.
* @return \c true if the state machine is currently finalizing, \c false otherwise.
*/
bool isInitializing() const
{
STTCL_STATEMACHINE_SAFE_RETURN(internalLockGuard,flags.initializing);
}
/**
* Default entry() implementation.
*
* @param context The state machine context.
*/
inline void entryImpl(Context* context)
{
StateImplementationBase::entryImpl(context);
for(unsigned int i = 0; i < NumOfRegions; ++i)
{
if(BaseClassType::regions[i])
{
if(!BaseClassType::regions[i]->isRegionInitializing() &&
!BaseClassType::regions[i]->isRegionInitialized()
)
{
BaseClassType::regions[i]->initializeRegion
( !context->isInitializing() &&
!context->isInitialized()
);
}
BaseClassType::regions[i]->enterRegion(static_cast<CompositeStateImpl*>(this));
}
}
}
/**
* Default exit() implementation.
*
* @param context The state machine context.
*/
inline void exitImpl(Context* context)
{
for(unsigned int i = 0; i < NumOfRegions; ++i)
{
if(BaseClassType::regions[i] &&
BaseClassType::regions[i]->isRegionInitialized() &&
!BaseClassType::regions[i]->isRegionFinalized())
{
BaseClassType::regions[i]->exitRegion(static_cast<CompositeStateImpl*>(this));
}
}
StateImplementationBase::exitImpl(context);
}
/**
* The StateBase::finalizeSubStateMachines() implementation.
* @param recursive Indicates to finalize nested sub state machines reRegionBaseTypecursively.
*/
virtual void finalizeSubStateMachines(bool recursive)
{
static_cast<CompositeStateImpl*>(this)->finalizeImpl(contextStateMachine_->isFinalizing() && recursive);
}
/**
* The StateBase::initSubStateMachines() implementation.
* @param recursive Indicates to initialize nested sub state machines recursively.
*/
virtual void initSubStateMachines(bool recursive)
{
static_cast<CompositeStateImpl*>(this)->initializeImpl(recursive);
}
/**
* The default initialize() implementation.
* @param recursive Indicates to initialize nested sub state machines recursively.
* @return \c true if all contained regions were successfully initialized, \c false otherwise.
*/
bool initializeImpl(bool recursive)
{
bool result = true;
if(!isInitialized() && !isInitializing())
{
setStateMachineFlags(StateMachineFlags::Initializing());
if(recursive)
{
for(unsigned int i = 0; i < NumOfRegions; ++i)
{
if(BaseClassType::regions[i])
{
result = BaseClassType::regions[i]->initializeRegion(recursive);
if(!result)
{
break;
}
}
}
}
}
if(result)
{
setStateMachineFlags(StateMachineFlags::Initialized());
}
return result;
}
void finalize(bool finalizeSubStateMachines = true)
{
finalizeImpl(finalizeSubStateMachines);
}
/**
* The default finalizeSubStateMachines() implementation.
* @param recursive Indicates to finalize nested sub state machines recursively.
*/
void finalizeImpl(bool recursive)
{
if(!isFinalizing() && !isFinalized())
{
setStateMachineFlags(StateMachineFlags::Finalizing());
for(unsigned int i = 0; i < NumOfRegions; ++i)
{
if(BaseClassType::regions[i] && !BaseClassType::regions[i]->isRegionFinalized())
{
BaseClassType::regions[i]->endDoRegion(static_cast<CompositeStateImpl*>(this));
BaseClassType::regions[i]->exitRegion(static_cast<CompositeStateImpl*>(this));
BaseClassType::regions[i]->finalizeRegion(recursive);
}
}
setStateMachineFlags(StateMachineFlags::Finalized());
}
}
/**
* Called after this state was entered.
*
* @param context The state machine context.
*/
void startDoImpl(Context* context)
{
for(unsigned int i = 0; i < NumOfRegions; ++i)
{
if(BaseClassType::regions[i])
{
BaseClassType::regions[i]->startDoRegion(static_cast<CompositeStateImpl*>(this));
}
}
}
/**
* Called before this state is left.
*
* @param context The state machine context.
*/
void endDoImpl(Context* context)
{
for(unsigned int i = 0; i < NumOfRegions; ++i)
{
if(BaseClassType::regions[i] && BaseClassType::regions[i]->isRegionThreadRunning())
{
BaseClassType::regions[i]->endDoRegion(static_cast<CompositeStateImpl*>(this));
}
}
}
/**
* Called when a containe region is completed (finalized).
* @param region The finalized region.
*/
void regionCompletedImpl(RegionBaseType* region)
{
bool allRegionsCompleted = true;
for(unsigned int i = 0; i < NumOfRegions; ++i)
{
if(BaseClassType::regions[i] && !BaseClassType::regions[i]->isRegionFinalized())
{
allRegionsCompleted = false;
}
}
if(allRegionsCompleted)
{
context()->subStateMachineCompleted(this);
}
}
Context* context() const { return contextStateMachine_; }
inline void registerActiveStateRunning(RegionStateBaseClass* regionState)
{
}
inline void unregisterActiveStateRunning(RegionStateBaseClass* regionState)
{
}
private:
typename sttcl::StateMachineFlags flags;
#if defined(STTCL_THREADSAFE_IMPL)
mutable StateMachineMutexType internalLockGuard;
#endif
virtual void regionCompleted(RegionBaseType* region)
{
static_cast<CompositeStateImpl*>(this)->regionCompletedImpl(region);
}
Context* contextStateMachine_;
};
}
/**
* Represents the base class for an orthogonal composite state implementation.
* @tparam CompositeStateImpl The implementing class.
* @tparam StateMachineImpl The containing state machine class.
* @tparam IInnerState The inner state's interface class type.
* @tparam NumOfRegions The number of orthogonal state regions (default is 1).
* @tparam EventArgs The dispatch event arguments type (default is void).
* @tparam StateBaseImpl The State class base implementation (default is
* State<CompositeStateImpl,StateMachineImpl,StateMachineImpl::StateInterface>)
*/
template
< class CompositeStateImpl
, class StateMachineImpl
, class IInnerState
, unsigned int NumOfRegions = 1
, class EventArgs = void
, class StateBaseImpl = State<CompositeStateImpl,StateMachineImpl,typename StateMachineImpl::StateInterface>
#if defined(STTCL_THREADSAFE_IMPL)
, class StateMachineMutexType = sttcl::internal::SttclMutex<STTCL_DEFAULT_MUTEXIMPL>
#endif
>
class ConcurrentCompositeState
: public sttcl::internal::ConcurrentCompositeStateBase
< CompositeStateImpl
, StateMachineImpl
, IInnerState
, NumOfRegions
, EventArgs
, StateBaseImpl
>
{
public:
typedef sttcl::internal::ConcurrentCompositeStateBase
< CompositeStateImpl
, StateMachineImpl
, IInnerState
, NumOfRegions
, EventArgs
, StateBaseImpl
> ConcurrenCompositeStateBaseType;
typedef StateMachineImpl StateMachineClass;
typedef typename ConcurrenCompositeStateBaseType::RegionBaseType RegionBaseType;
typedef typename ConcurrenCompositeStateBaseType::RegionsArray RegionsArray;
typedef IInnerState InnerStateInterface;
typedef typename StateMachineImpl::StateInterface OuterStateInterface;
typedef typename ConcurrenCompositeStateBaseType::RegionContainerBase RegionContainerBase;
/**
* Constructor for class ConcurrentCompositeState.
* @param context The containing state machine context.
* @param argRegions A reference to the concrete array of regions in the concurrent composite
* state.
*/
ConcurrentCompositeState(StateMachineImpl* context, const RegionsArray& argRegions)
: ConcurrenCompositeStateBaseType(context,argRegions)
{
}
/**
* Destructor for class ConcurrentCompositeState.
*/
virtual ~ConcurrentCompositeState()
{
}
/**
* Changes \em context state machine to another sibling state.
* @param context A pointer to the containing state machine.
* @param newState A pointer to the sibling state to change to.
*/
void changeState(StateMachineImpl* context,sttcl::StateBase<StateMachineImpl,OuterStateInterface>* newState)
{
static_cast<CompositeStateImpl*>(this)->changeStateImpl(context,newState);
}
protected:
};
}
#endif /* CONCURRENTCOMPOSITESTATE_H_ */
Region
s are fairly simple and just implement event delegation received from a queue to inner composite sub StateMachine
s:
The trickier part with this is interface matching as it looks like. That implementation relies on interfaces providing the same virtual function set, will be always layed out with the same (vtable) structure.