I maintain a couple of objects at work which implement various behavior that requires a separate thread of execution. Since recently there were really only two of them but now they have multiplied beyond that so I realized that it would be appropriate to design a reusable framework in order to reduce unnecessary code duplication. The design I came up with is based around an interface which defines the API of any “threadable” object and an abstract implementation which provides the means to synchronize the threads. Threads in our systems tend to have a very long life span and we avoid any kind of resource allocation (including launching threads) which are soon thereafter released as that can lead to fragmentation.
Although I’m fairly satisfied with this implementation there is at least one flaw in it. I’ll leave it to my readers to find it.
The interface is fairly simple and provides three methods. kill() which is used to signal thread termination, running() which is used to query whether the thread is actually up and running and finally the actual thread function implemented as operator()().
class IThreadable {
public:
virtual ~IThreadable() throw() { }
virtual void kill() = 0;
virtual bool running() const = 0;
virtual void operator()() throw() = 0;
};
The AbstractThreadable object implements the kill() and running() methods which should look the same across all kinds of threadable objects. It also implements two protected methods which must be called in the threadable object to signal that it has enter or exited its thread function, these are known as signalThreadRunning() and signalThreadExit().
I’m using a tristate variable to keep track of the threads current state of execution. The reason behind this is that I want to differentiate between a thread that has not yet been started. If someone calls kill() and never actually launches the thread the kill() method will block on the semaphore unless we can detect this state.
enum ThreadState {
ThreadState_NONE,
ThreadState_RUNNING,
ThreadState_KILLED,
};
class AbstractThreadable : public IThreadable {
public:
AbstractThreadable() : m_state(ThreadState_NONE) { }
virtual void kill() {
scoped_lock stateLock(m_stateMutex);
if (m_state != ThreadState_KILLED) {
ThreadState prevState(m_state);
m_state = ThreadState_KILLED;
stateLock.unlock();
if (prevState == ThreadState_RUNNING) {
m_threadExitSemaphore.p();
}
}
}
virtual bool running() const {
if (scoped_lock(m_stateMutex), m_state != ThreadState_KILLED) {
return true;
}
return false;
}
virtual void operator()() throw() = 0;
protected:
virtual void signalThreadEnter() {
scoped_lock stateLock(m_stateMutex);
m_state = ThreadState_RUNNING;
}
virtual void signalThreadExit() {
m_threadExitSemaphore.v();
}
private:
ThreadState m_state
mutable mutex m_stateMutex;
semaphore m_threadExitSemaphore;
};
Finally I will present a simple object using the above implementation in order to implement a threadable object which just sleeps until someone signals it to terminate via kill(). Notice how signalThreadRunning() and signalThreadExit() are used in order to notify the AbstractThreadable implementation of the current thread state.
Note: Calling running() requires locking a mutex so if the thread loop is very tight it might be wise to unroll it.
class Sleeper : public AbstractThreadable {
public:
void operator()() throw() {
signalThreadEnter();
while (running()) {
sleep(1);
}
signalThreadExit();
}
};
This is a simple example showing how to use the sleeper thread. I’m using Boost to create the thread. thread is a thread object and ref makes sure the sleeper object is passed by ref rather than being copied.
int main(int argc, char **argv)
{
Sleeper sleeper;
thread sleeperThread(ref(sleeper));
...
sleeper.kill();
return sleeperThread.join();
}
I was a bit tired when I wrote the initial post and I didn’t really have time to compile it so a few bugs were lurking inside. I made some changes to the code today so please refresh your caches in case you intend to take it for a spin.