Extending State Pattern - Example Code
This is an example toy implementation of state pattern extended with secondary transition stimuli. We dicussed in the last post that a context is more than a wrapper here, and thus it provides an interface to states to raise secondary events. As handling of the secondary events would vary from concrete context to concrete context, state is provided with an interface of an abstract context, as shown below:
class AbstractContext
{
public:
typedef std::map
InstanceTable_t;
virtual ~AbstractContext();
virtual void
raiseSecondaryEvent(
const SecondaryEvent::SecondaryEvent_t&
) = 0;
void activateState(const AbstractState::State_t& stateType);
void onEvent_1();
void onEvent_2();
void onEvent_3();
protected:
AbstractContext();
AbstractState* getStateInstance(
const AbstractState::State_t& type);
virtual AbstractState*
createStateInstance(
const AbstractState::State_t& type
)const = 0;
AbstractState::State_t getCurrentStateType() const;
private:
AbstractState* state_;
InstanceTable_t instances_;
};
A concrete context will have an implementation of the transition table mapping (current state, secondary event) -> next state; naturally a simple representation of the table would be an stl::map. Notice that states were instantiated with a factory method createStateInstance(), which is defined in concreteContext and used from AbstractContext::getStateInstance(); to use this on demand instantiation pattern properly we always have to use this accessor function getStateInstance() to get any state instance.
#include <map>
#include "AbstractContext.hpp"
class ConcreteContext : public AbstractContext
{
public:
typedef
std::map,
AbstractState::State_t> TransitionTable_t;
ConcreteContext();
virtual void
raiseSecondaryEvent(
const SecondaryEvent::SecondaryEvent_t& event
);
protected:
void populateTransitions();
AbstractState* createStateInstance
(const AbstractState::State_t& type) const;
private:
TransitionTable_t transitionTable;
};
Abstract state interface is typical. See concreteState classes to check how we are raising secondary events.
class AbstractContext;
class AbstractState
{
public:
enum State_t
{
State_1 = 1,
State_2 = 2
};
virtual ~AbstractState(){}
virtual void onEvent_1(AbstractContext* context) = 0;
virtual void onEvent_2(AbstractContext* context) = 0;
virtual void onEvent_3(AbstractContext* context) = 0;
virtual State_t stateType() const = 0;
protected:
AbstractState(){}
};
class ConcreteState_1 : public AbstractState
{
public:
ConcreteState_1(){}
virtual void onEvent_1(AbstractContext* context)
{
std::cout << "Process primary event-1 in ConcreteState-1";
std::cout << std::endl;
}
virtual void onEvent_2(AbstractContext* context)
{
std::cout << "Process primary event-2 in ConcreteState-1";
std::cout << std::endl;
}
virtual void onEvent_3(AbstractContext* context)
{
std::cout << "Process primary event-3 in ConcreteState-1";
std::cout << std::endl;
context->raiseSecondaryEvent(SecondaryEvent::Trigger_1);
}
virtual AbstractState::State_t stateType() const
{
return AbstractState::State_1;
}
};
class ConcreteState_2 : public AbstractState
{
public:
ConcreteState_2(){}
virtual void onEvent_1(AbstractContext* context)
{
std::cout << "Process primary event-1 in ConcreteState-2";
std::cout << std::endl;
}
virtual void onEvent_2(AbstractContext* context)
{
std::cout << "Process primary event-2 in ConcreteState-2";
std::cout << std::endl;
}
virtual void onEvent_3(AbstractContext* context)
{
std::cout << "Process primary event-3 in ConcreteState-2";
std::cout << std::endl;
context->raiseSecondaryEvent(SecondaryEvent::Trigger_2);
}
virtual AbstractState::State_t stateType() const
{
return AbstractState::State_2;
}
};
Now the cpp definitions for AbstractContext and its concrete derived class -
AbstractContext.cpp :
AbstractContext::AbstractContext() : state_(NULL)
{
}
AbstractContext::~AbstractContext()
{
InstanceTable_t::iterator it = instances_.begin();
InstanceTable_t::iterator itEnd = instances_.end();
while(it != itEnd)
{
delete it->second;
++it;
}
instances_.clear();
}
void AbstractContext::onEvent_1()
{
state_->onEvent_1(this);
}
void AbstractContext::onEvent_2()
{
state_->onEvent_2(this);
}
void AbstractContext::onEvent_3()
{
state_->onEvent_3(this);
}
void
AbstractContext::activateState(
const AbstractState::State_t& stateType)
{
state_ = getStateInstance(stateType);
assert(state_ != NULL);
}
AbstractState*
AbstractContext::getStateInstance(const AbstractState::State_t& type)
{
AbstractState* stateInstance = NULL;
InstanceTable_t::const_iterator it =
instances_.find(type);
if(it != instances_.end())
{
stateInstance = it->second;
}
else
{
stateInstance = createStateInstance(type);
instances_[type] = stateInstance;
}
return stateInstance;
}
AbstractState::State_t AbstractContext::getCurrentStateType() const
{
assert(state_ != NULL);
return state_->stateType();
}
ConcreteContext.cpp :
ConcreteContext::ConcreteContext()
{
populateTransitions();
}
void ConcreteContext::raiseSecondaryEvent(
const SecondaryEvent::SecondaryEvent_t& event)
{
TransitionTable_t::iterator it =
transitionTable.find(
std::make_pair(getCurrentStateType(), event));
if(it != transitionTable.end())
{
activateState(it->second);
}
}
void ConcreteContext::populateTransitions()
{
transitionTable[std::make_pair(AbstractState::State_1,
SecondaryEvent::Trigger_1)] = AbstractState::State_2;
transitionTable[std::make_pair(AbstractState::State_2,
SecondaryEvent::Trigger_2)] = AbstractState::State_1;
}
AbstractState*
ConcreteContext::createStateInstance(
const AbstractState::State_t& type) const
{
AbstractState* state = NULL;
switch(type)
{
case AbstractState::State_1:
state = new ConcreteState_1();
break;
case AbstractState::State_2:
state = new ConcreteState_2();
break;
default:
assert(false);
break;
}
return state;
}
Now client code would just create a ConcreteContext, activate initial state and expose itself to primary events - that’s it.
Extending State Pattern with Secondary Transition Stimuli
State machine in procedural programming and corresponding state pattern in OOP has been there and served us well to implement event driven scenarios for some time now. To be accurate, state machine is the architectural pattern which was there even before OO; while ’state pattern’ is really the OO implementation of State Machine. The intent and benefit are clear for the basic state pattern. State pattern lets you implement state specific behaviour without cluttering your code with conditionals. State pattern is based on OO principle of delegation to a polymorphic base class - and it is very clean that way ( yes, just like strategy pattern ). A ‘context’-wrapper object delegates all requests to a ConcreteState class through an abstract state interface. Naturally, ‘the context’ is also responsible for presenting an interface of the state machine to its client.
To use pthreads with C++ :
Yes, it was easy - but even before boost there has always been some C++ wrapper. So when you go to check this piece of code wriiten a couple of years ago using pure pthreads you are not always sure if everything is alright with the code.
So here are some interesting and not so interesting information about POSIX threads :
- POSIX 1003.1c deals with threads.
- The current posix threads library in linux is NPTL ( Native POSIX Thread Library). NPTL is 1:1, kernel supported threading library, it uses clone() system call to create a thread and uses the new system call futex() to resolve contention. By the way, futexes are kind of cool and fast because kernel’s involvement is required only when a contended case requires arbitrattion. On a 2.6.x linux kernel to know the latest pthread library you use
$ getconf GNU_LIBPTHREAD_VERSION
I find it interesting that you use opaque objects everywhere in pthreads library; it is a ‘C’ way to implement API constraints(and interedependency) with data hiding - it is pretty clean. For example when you want to set an attribute (setting an attribute is the way to set the stack size, to make it joinable etc) you initialize the attribute separately, set properties on it and then pass it to pthread_create() - pthread_create() is the function to create a new thread.
So it would look like :
pthread_t tid; int arg = 10; pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); rc = pthread_create(&tid, &attr, startFunction, &arg); pthread_attr_destroy(&attr);
Whenever we are using C++, we have to remember that startFunction is a c function - hence no namemangling is allowed there. Your C++ compiler would always mangle the name to support overloading etc, so the globally visible name of a function may not be same as the name you declared it with.(Since you can have multiple functions with the same name, C++ compiler creates a unique name by using the signature of the function) Naturally, you cannot use a namemangled function as the callback function for C library. The solution is to use extern “C” linkage declaration - it can be done with global and static functions only.
The next point is to remember that you do not know when a thread is going to be scheduled, so manage your resources with care, for example you can pass an argument to the start routine ( as you can see in the above example) but the passed argument is going to be used by the newly created thread only when it is scheduled, so naturally the parent thread must not try to modify the argument before that !
As soon as you learn to use multiple threads sharing the same address space, using each others data freely, you have to learn to use guards to protect your data as well - it comes with the territory. For example you have the case where you want to implement some kind of transactional sanity in a situation where multiple threads are effectively jumbling up each others data to create a confusion … check the following code :
This could be the main.cpp :
#include "transaction.hpp"
using std::cout;
using std::endl;
using std::terminate;
extern "C"
{
void* runTransactionA(void* arg)
{
Transaction* trans = static_cast<Transaction*>(arg);
trans->transactAs();
return 0;
}
void* runTransactionB(void* arg)
{
Transaction* trans = static_cast<Transaction*>(arg);
trans->transactBs();
return 0;
}
}
int main()
{
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_t firstHandle, secondHandle;
Transaction transaction("Transaction");
int rc;
rc = pthread_create(&firstHandle, &attr, runTransactionA,
&transaction);
if(rc != 0)
{
cout << "Error - return code from pthread_create() ";
cout << rc << endl;
terminate();
}
rc = pthread_create(&secondHandle, &attr, runTransactionB,
&transaction);
if(rc != 0)
{
cout << "Error - return code from pthread_create() ";
cout << rc << endl;
terminate();
}
pthread_attr_destroy(&attr);
void* status = 0;
rc = pthread_join(firstHandle, &status);
if(rc != 0)
{
cout << "Error - return code from pthread_join() ";
cout << rc << endl;
terminate();
}
rc = pthread_join(secondHandle, &status);
if(rc != 0)
{
cout << "Error - return code from pthread_join() ";
cout << rc << endl;
terminate();
}
transaction.displayServerLink();
pthread_exit(0);
}
Where the Transaction implementation would look like :
#include
#include
#include
#include
#include
class Transaction
{
public:
//constructor
Transaction(std::string name) : m_name(name)
{
pthread_mutex_init(&serverMutex, 0);
}
//destructor
~Transaction()
{
pthread_mutex_destroy(&serverMutex);
}
void transactAs()
{
struct timespec req;
struct timespec rem;
req.tv_sec = 0;
req.tv_nsec = 10000;
pthread_mutex_lock(&serverMutex);
serverLink << std::endl;
serverLink << m_name << " : ";
for (int step = 0; step < 10; ++step)
{
serverLink << "<< step << ">";
nanosleep(&req, &rem);
}
serverLink << " :" << std::endl;
pthread_mutex_unlock(&serverMutex);
}
void transactBs()
{
struct timespec req;
struct timespec rem;
req.tv_sec = 0;
req.tv_nsec = 10000;
pthread_mutex_lock(&serverMutex);
serverLink << std::endl;
serverLink << m_name << " : ";
for (int step = 0; step < 10; ++step)
{
serverLink << "<< step << ">";
nanosleep(&req, &rem);
}
serverLink << " :" << std::endl;
pthread_mutex_unlock(&serverMutex);
}
void displayServerLink()
{
std::cout << "Current Buffer in ServerLink is";
std::cout << serverLink.str() << std::endl;
}
private:
std::string m_name;
std::stringstream serverLink;
pthread_mutex_t serverMutex;
};
The Monitor pattern vs the Monitor programming construct
Though, the monitor programming construct is always there to support the monitor pattern, it is important to understand the programming construct separately from the pattern. This is necessary because the pattern can be used with all modern threading libraries even if the ’specific programming language’ being used does not have any specific keyword for that.
Monitor as a programming language construct :
Basically, when supported, ‘monitor’ as a programming language construct is there to hide mutual exclusion. So if a procedure is defined to be a monitor then by definition that piece of code is taken to be a critical region. A critical region is a piece of code which can only be executed by one thread at a time. In case of a monitor the programmer himself does not have to put the critical region inside lock-mutex and unlock-mutex, the programming language would take care of it.
[ But this is not there in C++:
For example java has 'synchronized' keyword for monitors, java virtual machine has corresponding monitorentry and monitorexit opcodes, and when java virtual machine encounters monitorentry it acquires a lock, and similarly it releases the lock when encounters monitorexit - the opcodes are not used for monitor procedures but ultimately jvm works with the same principle of locking and unlocking ]
On the other hand the monitor pattern is something like (mutex for critical section) + (wait) + (notify) - the language that has a monitor-like keyword would have all the facilities but the programming language construct is not necessary to implement the pattern.
In C++, with boost 1.34 – replacing the monitor keyword would be as simple as having a scoped mutex :
#include <boost/thread/mutex.hpp>
boost::mutex someMutex;
void someMethod()
{
boost::mutex::scoped_lock lock(someMutex);
// more code
}