Making our customers successful
with what we do best:
Create best-of-class software
for Windows and Linux.

 

About Us Contact Us Free Stuff

Component Software's family of thread classes

A good set of C++ classes makes the use of threads less daunting. This article describes such a set of classes, developed by Component Software, Inc.

This set of classes is constructed so that you can write the majority of your application code in such a way that you can compile them unaltered for several operating systems. This is accomplished as follows:

The behavior of the classes is defined in a set of classes that are independent of any operating system calls. Those classes are the ones that are being used in the application code to deal with its threads. When your application code is linked, the independent classes are supplemented with the specific code for your specific operating system target. This specialization code then makes the operating system-specific API calls to implement the behavior that is defined in the OS-independent classes.
The creation of some of the classes must be done with factories, since the os-independent classes are abstract. The factory is defined in an os-independent fashion, so that these factories can be used in the os-independent code.

This article first describes the OS-independent base classes and their interactions. It then briefly describes the supported OS-specific subclasses.

Thread and Runnable classes

"Thread" is a class that allows the creation of a thread, as well as control over its run status (a thread can be in different states: established, running, suspended, or terminated). The Thread class also provides a Sleep method for convenience. The constructor of a thread requires a Runnable object. A thread will execute the DoStep method in that Runnable class. DoStep is called repeatedly until the thread is terminated. Below are the public interfaces of Thread and Runnable

//***************************************************
class Thread
{
public:
  Thread ( Runnable* rr );
  virtual ~Thread();

  virtual void Resume () = 0 ;
  virtual void Suspend () = 0 ;
  virtual void Terminate () = 0 ;
  virtual bool IsTerminated () = 0 ;
  virtual void Sleep ( int mSec ) = 0 ;

  virtual void AssignToken ( TokenDepot* t ) ;
  virtual void DeassignToken ( TokenDepot* t );
};

//***************************************************

class Runnable 
{
public:
  virtual ~Runnable () {}
  virtual void DoStep () = 0 ;
};

 

Guard and TokenDepot classes

The TokenDepot serves as the holder for a Token. The purpose of the token is to allow a maximum of one thread to obtain the token at a time. A token is obtained by putting the TokenDepot into a Guard. When the Guard is destructed, the Token becomes free and can be claimed by another thread. The public interfaces of the Guard and TokenDepot are shown below.

//***************************************************

class TokenDepot
{
public:
  //needed to call subclass destructor
  virtual ~TokenDepot () {} ; };

//***************************************************

class Guard
{
public:
 Guard ( TokenDepot& dep );
 ~Guard ();
private:
 TokenDepot& m_dep ;
};

"Guard" is a concrete class, and can therefore be constructed directly as an object. A Guard is typically established as a temporary variable that encloses the instructions dealing with variables, which are accessed by multiple threads. The example below illustrates this use:

//*****************************************************
void f () 
//this function is executed by thread 1
{
  //variable m_v is shared by multiple threads.
  {
    Guard g ( dep ); //dep is a Token Depot. 
          //dep now owned by g, until g is destroyed.. 

    int v = m_v ;
               //this instruction is executed knowing that 
               //m_v is not changed by any other thread.
       //all threads must enclose 
       //access to m_v with a Guard ( dep ) 
       //to make this work.

    } //g is destroyed here, releasing dep.
           //dep can now be claimed by another thread.
}

//*****************************************************
void SubClassOfRunnable::DoStep () 
//this function is executed by another thread
{
  {
    Guard g ( dep );//same dep as in f ()
    m_v = 1 ;
  }
}

 

Factories

The Thread and TokenDepot classes are abstract. Os-dependent implementations must be provided. The Thread factory is defined as a class ThrdSvc. To get a pointer to the current ThrdSvc, use GetThrdSvc. For the TokenDepot, a simple function MakeTokenDepot is used as factory. 

The thread class factory has been chosen to be a class so that the os-independent code can be executed with different factories, even in the same program. The most important application of this feature is in testing: Since threads have an indeterminate timing relationship to each other (at least in the general case), it is difficult to write deterministic test cases. To solve this problem, special Thread factory classes with deterministic behavior are installed during testing. Details are outside the scope of this article.

The factories are defined as follows:

//*****************************************************
class ThrdSvc
{
public:
	virtual ~ThrdSvc (){}
	virtual Thread* MakeThread ( Runnable& r ) = 0 ;
};
//*****************************************************
ThrdSvc* GetThrdSvc ();

//*****************************************************
TokenDepot* MakeTokenDepot ();

 

A simple example

Given the above classes, a simple example can be constructed to show their use. Note that this example code builds the functionality of the application without any OS-specific API calls. This code can be run without any modifications on MS Windows platforms and on Unix platforms that provide a pthread library.

The example prints out the time once a second until the user hits the enter key on the keyboard.

The example works with two threads: the first thread (that enters the function main) waits for the user to hit the enter key; the second thread displays the time every second on the terminal. 

The second thread is created in main and associated with class SecondThread ( a subclass of Runnable). SecondThread's DoStep method is executed by the second thread.

#include 
#include 
#include "thread.h"

//*****************************************************
//create a subclass of Runnable. Important is to implement
//DoStep.
//to have access to a thread Sleep function, a member m_myThread
//is provided.
class SecondThread : public Runnable
{
public:
	SecondThread () : m_myThread ( 0 ) {}
	void YourThreadIs ( Thread* t ) { m_myThread = t ;}

private:
	void DoStep ();
	Thread* m_myThread ;
};

//*****************************************************
int main ( int argc , char** argv )
{
	InstallOSThrdSvc ();

	SecondThread sth ;
	Thread* th = GetThrdSvc () ->MakeThread ( sth );
	sth .YourThreadIs ( th );
	th ->Resume ();

	getchar ();

	th ->Terminate ();
	delete th ;

	return 0 ;
}


//*****************************************************
//Implementation of SecondThread::DoStep 
//is called repeatedly by
//its associated thread until thread is terminated.
//method will print time to display, 
//and then wait for 1 second before
//returning
void SecondThread::DoStep ()
{
	time_t t ;
	time ( & t );
	printf ( "current time: %s", asctime ( localtime ( & t )));

	m_myThread ->Sleep ( 1000 );
}

 

Complementary code needed to connect to the platform API.

The os-independent classes can only do the job when the required os-specific code is linked. The os-specific code is supplied in subclasses of the os-independent classes. At link time the appropriate os-specific files are compiled and linked with the os-independent files to build the executable.

The OS-specific implementation of the ThrdSvc factory must be created with the call to InstallOSThrdSvc () which is a function that must be implemented by the OS-dependent code. That function must put a pointer to the OS-specific thread service into the global variable activeThrdSvc.

The public declarations for the OS-specific subclasses of Thread and TokenDepot for MS Windows and for the Pthread API are as follows:

MS Windows subclasses:
//*****************************************************
class W32_Thread : public Thread //, public CWinThread
{
public:
	W32_Thread( Runnable& rr );
	~W32_Thread();

	//implementation of the Thread interface
	void Resume () ;
	void Suspend () ;
	void Terminate () ;
	bool IsTerminated () ;
	void Sleep ( int mSec );
	unsigned threadId ();
};

//*****************************************************
class W32_TokenDepot : public TokenDepot
{
public:
	W32_TokenDepot ();
	~W32_TokenDepot ();

	bool GetToken () ;
	void ReturnToken () ;
};

//*****************************************************
class W32_ThrdSvc : public ThrdSvc
{
public:
	~W32_ThrdSvc () {}

	Thread* MakeThread ( Runnable& r )
	{
		W32_Thread* r1 = new  W32_Thread ( r ) ;
		return r1 ;
	}
};

 

  Posix subclasses:

//*****************************************************
class LxThread : public Thread 
{
public:
LxThread( Runnable& rr );
~LxThread();

//implementation of the Thread interface
void Resume () ;
void Suspend () ;
void Terminate () ;
bool IsTerminated () ;
void Sleep ( int mSec ) ;
};

//*****************************************************
class LxTokenDepot : public TokenDepot
{
public:
LxTokenDepot () ;
~LxTokenDepot () ;
bool GetToken () ;
void ReturnToken () ;
};

//*****************************************************
class LxThrdSvc : public ThrdSvc
{
public:
Thread* MakeThread ( Runnable& r )
{
return new  LxThread ( r ) ;
}
};

Source code

Source code is provided for the above example. Also provided is an implementation of the Thread and TokenDepot classes for MS Windows and PThreads. Also provided are project files for Visual C++ and Unix autotools. These files are made available to you under the terms of the Open Source Software license.

Download the source in zip or gzip format. Instructions for compilation and run are provided in the README file.


Has this page been helpful to you? 
Do you have questions or comments?

We would very much like to hear from you. Just send us your thoughts below, or send us an e-mail at support@componentsw.com

Thank you!

 

Contact Name
Contact E-mail

Your feedback

Copyright © 2002-2005 Component Software, Inc.
All Rights Reserved

 


Helping Build the Tech Oasis - http://www.techoasis.org