C++ – Thread, Mutex, Semaphore, Sleep a Cross platform Framework (for Windows & POSIX)
In this post, I describe and give the source to create cross platform Thread, Mutex, Semaphore or Sleep that work on Windows and POSIX.
Introduction
There are several source and framework that propose solutions to create a cross-os library. And the main differences between these solutions are the interface (C or C++, mutex.IWantToLock() or mutex.lock() or mutex.Lock()) and how to set the OS (define, comments,inherited). So you may prefer another solution, or think that mine is not the best. Anyway, in this post I will describe my choice and even if your are not convened … you can read it to understand how mutex/thread work on windows or POSIX.
Specification
In my solution, you need to define WINDOWS or define POSIX (if you do not define anything, POSIX will be used and a warning will be printed). It is possible to detect the OS by asking the compiler, but according to me this solution is not the best because you need to check every possibility (and you may forget some compiler that can be a problem) also if some compiler are updated it can be a problem to.
All the methods are in hpp/header files. This choice will make the methods inline. In fact, putting the code in cpp can also be a great solution : giving the choice to the compiler if a method need to be inline or not, better compiling performance (because each class will be compile only one time). But according to me because the methods are never longer than 5 lines putting the methods inline is a good solution, but you can put the code in the cpp if you prefer.
Let start with the mutex.
MUTEX
#ifndef BMUTEX_HPP
#define BMUTEX_HPP
// Use Window or Posix
#ifdef WINDOWS
#include <windows.h>
#else
#ifndef POSIX
#warning POSIX will be used (but you did not define it)
#endif
#include <pthread.h>
#endif
/**
* @author Berenger
* @version 0.5
* @date February 15 2010
* @file BMutex.hpp
* @package Package-OS specific (POSS)
* @brief Mutex
*
*
* This class represent a simple way to use mutex
*
* @example BMutex mut;
* @example mut.lock(); // lock
* @example ...
* @example mut.islocked(); // fast look up
* @example ...
* @example mut.unlock(); // unlock
* @example mut.tryLock(); // try lock
*
* Ressources : http://www.codeproject.com/KB/threads/thread_class.aspx
*
* @must You may have to change this class if you are not on Windows
* @must or Posix OS
*
* All methods may be inline by the compiler
* @copyright Brainable.Net
*/
class BMutex{
private:
#ifdef WINDOWS
CRITICAL_SECTION _mutex; /**< Window mutex */
#else
pthread_mutex_t _mutex; /**< posix mutex */
#endif
bool _locked; /**< Fast locked look up used for copying */
void init(){
#ifdef WINDOWS
InitializeCriticalSection(&_mutex);
#else
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&_mutex,&attr);
pthread_mutexattr_destroy(&attr);
#endif
_locked = false;
}
public:
/**
* @brief Construct a BMutex
* @brief Posix and Win mutex
*/
BMutex(){
init();
}
/**
* @brief Copy Constructor a mutex (copy the locked state only)
* @param Based mutex
*
*/
BMutex( const BMutex &in_mutex ) {
init();
if(in_mutex._locked && !_locked) lock();
else if(!in_mutex._locked && _locked) unlock();
}
/**
* @brief Copy a mutex (copy the locked state only)
* @param Based mutex
* @return Current mutex
*/
BMutex& operator=(const BMutex &in_mutex) {
if(in_mutex._locked && !_locked) lock();
else if(!in_mutex._locked && _locked) unlock();
return *this;
}
/**
* @brief Destructor
*/
virtual ~BMutex(){
#ifdef WINDOWS
DeleteCriticalSection(&_mutex);
#else
pthread_mutex_unlock(&_mutex);
pthread_mutex_destroy(&_mutex);
#endif
}
/**
* @brief lock a mutex
* @return WIN true
* @return POSIX true if success
*/
bool lock(){
_locked = true;
#ifdef WINDOWS
EnterCriticalSection(&_mutex);
return true;
#else
return pthread_mutex_lock(&_mutex) == 0;
#endif
}
/**
* @brief lock a mutex
* @return true if success else false (if busy or error)
*/
bool tryLock(){
_locked = true;
#ifdef WINDOWS
return TryEnterCriticalSection(&_mutex);
#else
return pthread_mutex_trylock(&_mutex) == 0;
#endif
}
/**
* @brief unlock a mutex
* @return WIN true in every cases
* @return POSIX true if success
*/
bool unlock(){
_locked = false;
#ifdef WINDOWS
LeaveCriticalSection(&_mutex);
return true;
#else
return pthread_mutex_unlock(&_mutex) == 0;
#endif
}
/**
* @brief Fast locked look up
* @return true if locked else false
* This methode use the fast look up variable but still CONST
* if you want to test perfectly do :
* if(myMutex.tryLock()){
* myMutex.unlock();
* // I am sure that the mutex is not locked
* }
* else{
* // The mutex is locked
* }
*/
bool isLocked() const{
return _locked;
}
};
#endif
Thread
#ifndef BTHREAD_HPP
#define BTHREAD_HPP
// Use Win or Posix
#ifdef WINDOWS
#include <windows.h>
#else
#ifndef POSIX
#warning POSIX will be used (but you did not define it)
#endif
#include <pthread.h>
#include <signal.h>
#endif
/**
* @author Berenger
* @version 0.5
* @date February 15 2010
* @file BThread.hpp
* @package Package-OS specific (POSS)
* @brief Thread
*
*
* This class represent a simple way to use thread. You must
* inherit and implement "run".
*
*
* @example BThread th;
* @example th.start(); // start thread
* @example ...
* @example th.isRunning(); // fast look up
* @example ...
* @example th.wait(); // wait thread end
* @example
*
* Ressources : http://www.relisoft.com/Win32/active.html
*
* @must You may have to change this class if you are not on Windows
* @must or Posix OS
*
* All methods may be inline by the compiler
* @copyright Brainable.Net
*/
class BThread{
private:
/**
* @brief Denied equality operator
* @param none
*/
void operator=(const BThread &){}
/**
* @brief Denied copy constructor
* @param none
*/
BThread(const BThread &){}
#ifdef WINDOWS
HANDLE _handle;
#else
pthread_t _thread; /**< Posix Thread*/
#endif
bool _isRunning; /**< Fast bool lookup */
/**
* @brief Static starter function to execute posix thread
* @brief This function set thread->isRunning to false
*/
#ifdef WINDOWS
static DWORD WINAPI Starter(LPVOID in_thread){
#else
static void* Starter(void* in_thread){
#endif
BThread * thread = static_cast< BThread * >(in_thread);
thread->_isRunning = true;
thread->run();
thread->_isRunning = false;
return 0x00;
}
public:
/**
* @brief Constructor
*/
BThread(){
#ifdef WINDOWS
_handle = 0x00;
#else
#endif
_isRunning = false;
}
/**
* @brief Destructor, Warning, it waits the end of the current thread
*/
virtual ~BThread(){
if(!_isRunning) return;
// if we destroy the thread until it has finished
// there is a problem in your implementation algorithm
// So we wait before destroying the thread!
wait();
#ifdef WINDOWS
CloseHandle (_handle);
#else
#endif
}
/**
* @brief start the thread
* @return true if success else false
*/
bool start(){
if(_isRunning) return false;
#ifdef WINDOWS
_handle = CreateThread( 0x00, 0x00,BThread::Starter, static_cast< void* >(this), 0x00, 0x00);
return _handle != NULL;
#else
return pthread_create(&_thread, NULL, BThread::Starter, static_cast< void* >(this)) == 0;
#endif
}
/**
* @brief Fast look up to know if a thread is running
* @return true if running else false
*/
bool isRunning() const{
return _isRunning;
}
/**
* @brief Wait the end of a thread
* @return false in case of error, true if all right
*/
bool wait() const{
if(!_isRunning) return false;
#ifdef WINDOWS
return WaitForSingleObject(_handle,INFINITE) == 0x00000000L;
#else
return pthread_join(_thread, NULL) == 0;
#endif
}
/**
* @brief the function is called when thread is starting
* @must You must implement this methode!
*/
virtual void run() = 0;
/**
*
*/
bool kill(){
if(!_isRunning) return false;
_isRunning = false;
#ifdef WINDOWS
bool success = TerminateThread(_handle,1) && CloseHandle(_handle);
_handle = 0x00;
return success;
#else
return pthread_kill( _thread, SIGKILL) == 0;
#endif
}
};
#endif
Semaphore
#ifndef BSEMAPHORE_HPP
#define BSEMAPHORE_HPP
// Use Win or Posix
#ifdef WINDOWS
#include <windows.h>
#define LMAXIMUMCOUNT 99999999 /**< Maximum semaphore value in Windows*/
#else
#ifndef POSIX
#warning POSIX will be used (but you did not define it)
#endif
#include <semaphore.h>
#endif
/**
* @author Berenger
* @version 0.5
* @date February 15 2010
* @file BSemaphore.hpp
* @package Package-OS specific (POSS)
* @brief Semaphore
*
*
* This class represent a simple way to use semaphore
*
* @must You may have to change this class if you are not on Windows
* @must or Posix OS
*
* All methods may be inline by the compiler
* @copyright Brainable.Net
*/
class BSemaphore{
protected:
#ifdef WINDOWS
HANDLE _sem; /**< Win semaphore*/
#else
sem_t _sem; /**< Posix semaphore*/
#endif
public:
/**
* @brief Constructor
* @param in_init original value
*/
BSemaphore( int in_init = 0 ){
#ifdef WINDOWS
_sem = CreateSemaphore(0,in_init,LMAXIMUMCOUNT,0);
#else
sem_init(&_sem,0,in_init);
#endif
}
/**
* @brief Copy constructor
* @param in_sem original semaphore
*/
BSemaphore(const BSemaphore &in_sem){
int value = in_sem.value();
#ifdef WINDOWS
_sem = CreateSemaphore(0,value,LMAXIMUMCOUNT,0);
#else
sem_init(&_sem,0,value);
#endif
}
/**
* @brief Copy method
* @param in_sem original semaphore
*/
void operator=(const BSemaphore &in_sem){
reset(in_sem.value());
}
/**
* @brief destroy semaphore
*/
virtual ~BSemaphore(){
#ifdef WINDOWS
CloseHandle(_sem);
#else
sem_destroy(&_sem);
#endif
}
/**
* @brief Wait until the semaphore is called by another thread
* @return true if success or false if timeout or error
*/
bool wait() const {
#ifdef WINDOWS
return WaitForSingleObject(static_cast< HANDLE >(_sem),INFINITE) == 0x00000000L;
#else
return sem_wait(const_cast<sem_t*>(&_sem)) == 0;
#endif
}
/**
* @brief post a token
* @return true if success or false if error
*/
bool post(){
#ifdef WINDOWS
return ReleaseSemaphore(static_cast< HANDLE >(_sem),1,0) != 0;
#else
return sem_post(&_sem) == 0;
#endif
}
/**
* @brief get current value
* @return value
*/
int value() const{
#ifdef WINDOWS
long value = -1;
ReleaseSemaphore(static_cast<const HANDLE>(_sem),0,&value);
return value;
#else
int value = -1;
sem_getvalue(const_cast<sem_t*>(&_sem),&value);
return value;
#endif
}
/**
* @brief release current semaphore and create a new one
* @param init the value after reset
*/
void reset( int in_init = 0 ){
#ifdef WINDOWS
CloseHandle(_sem);
_sem = CreateSemaphore(0,in_init,LMAXIMUMCOUNT,0);
#else
sem_destroy(&_sem);
sem_init(&_sem,0,in_init);
#endif
}
};
#endif
Sleep
#ifndef BSLEEP_HPP
#define BSLEEP_HPP
// Use Win or Posix
#ifdef WINDOWS
#include <windows.h>
#else
#ifndef POSIX
#warning POSIX will be used (but you did not define it)
#endif
#include <unistd.h>
#endif
/**
* @author Berenger
* @version 0.5
* @date February 15 2010
* @file BSleep.hpp
* @package Package-OS specific (POSS)
* @brief Cross platform sleep functions
*
* @example BSleep::sleep(50); // Sleep 50 ms
*
* @must You may have to change this class if you are not on Windows
* @must or Posix OS
*
* All methods may be inline by the compiler
* @copyright Brainable.Net
*/
namespace BSleep{
/**
* @brief sleep in milli seconds
* @param in_mseconds the number of milli seconds
*/
void sleep( unsigned int in_mseconds ){
#ifdef WINDOWS
Sleep( in_mseconds );
#else
usleep( in_mseconds * 1000 );
#endif
}
/**
* @brief sleep in seconds
* @param in_mseconds the number of seconds
*/
void ssleep( unsigned int in_seconds ){
#ifdef WINDOWS
Sleep(in_seconds * 1000);
#else
sleep(in_seconds);
#endif
}
}
#endif
Example
In this test, I create two Thread classes. The first one shows how “wait” and “sleep” work. The second, shows how to use Mutex.
//#define WINDOWS
//#define POSIX
#include "BMutex.hpp"
#include "BThread.hpp"
#include "BSemaphore.hpp"
#include "BSleep.hpp"
#include <iostream>
//
// This file shows how to use thread and mutex
//
// A simple thread with 1s sleep
class BTestThread : public BThread{
public:
void run(){
std::cout<<"\t\t [T]I am going to Sleep"<<std::endl;
BSleep::sleep(2000);
std::cout<<"\t\t [T]Okay I quit now"<<std::endl;
}
};
// A simple thread which waits a mutex
class BMutexThread : public BThread{
public:
BMutex *tex;
BMutexThread(BMutex* in_tex){
tex = in_tex;
}
void run(){
std::cout<<"\t\t [T]Lock Mutex"<<std::endl;
tex->lock();
std::cout<<"\t\t [T]Mutex Unlocked -> exit"<<std::endl;
}
};
int main(){
BTestThread th;
std::cout<<"Execute sleep-thread"<<std::endl;
th.start();
std::cout<<"Wait thread to finish"<<std::endl;
BSleep::sleep(50); // To be sure that the thread has started before I wait!
th.wait();
std::cout<<"Thread has Finished"<<std::endl;
std::cout<<"-----------------------------------------"<<std::endl;
std::cout<<"Create and lock mutex"<<std::endl;
BMutex tex;
tex.lock();
BMutexThread mth(&tex);
std::cout<<"Start the second thread"<<std::endl;
mth.start();
std::cout<<"Sleep (the thread is waiting the mutex
"<<std::endl;
BSleep::sleep(1000);
std::cout<<"Release the mutex"<<std::endl;
tex.unlock();
#ifdef WINDOWS
char c;
std::cin>>c;
#endif
// In POSIX
// To do the thing correctly we need to quit with pthread_exit(NULL);
// But there is a wait() in the thread destructor so it is okay!
return 0;
}
/*
RESULT :
Mac OS :
$ ./test
Execute sleep-thread
Wait thread to finish
[T]I am going to Sleep
[T]Okay I quit now
Thread has Finished
-----------------------------------------
Create and lock mutex
Start the second thread
Sleep (the thread is waiting the mutex )
[T]Lock Mutex
Release the mutex
[T]Mutex Unlocked -> exit
Windows (Notice the 2nd Line they have written in the same time):
Execute sleep-thread
Wait thread to finish [T]I am going to Sleep
[T]Okay I quit now
Thread has Finished
-----------------------------------------
Create and lock mutex
Start the second thread
Sleep (the thread is waiting the mutex ) [T]Lock Mutex
Release the mutex
[T]Mutex Unlocked -> exit
*/
Source
You can download the source : POSS Cross Platform thread mutex.
The sources include the test and a Makefile.
Subscribe to the RSS feed and have all new posts delivered straight to you.
