201 lines
4.2 KiB
C++
201 lines
4.2 KiB
C++
/*
|
|
* Modification History
|
|
*
|
|
* 2001-January-11 Jason Rohrer
|
|
* Created.
|
|
*
|
|
* 2001-January-11 Jason Rohrer
|
|
* Added a willBlock() function.
|
|
*
|
|
* 2001-February-24 Jason Rohrer
|
|
* Fixed incorrect delete usage.
|
|
*
|
|
* 2002-February-11 Jason Rohrer
|
|
* Fixed a mistake in the signal() comment.
|
|
*
|
|
* 2003-August-26 Jason Rohrer
|
|
* Added support for timeouts on wait.
|
|
*
|
|
* 2003-December-28 Jason Rohrer
|
|
* Fixed a bug in semaphore value when we timeout on wait.
|
|
*
|
|
* 2004-January-9 Jason Rohrer
|
|
* Fixed a preprocessor error.
|
|
*/
|
|
|
|
#include "minorGems/common.h"
|
|
|
|
|
|
|
|
#ifndef SEMAPHORE_CLASS_INCLUDED
|
|
#define SEMAPHORE_CLASS_INCLUDED
|
|
|
|
#include "MutexLock.h"
|
|
#include "BinarySemaphore.h"
|
|
|
|
|
|
/**
|
|
* General semaphore with an unbounded value.
|
|
*
|
|
* This class uses BinarySemaphores to implement general semaphores,
|
|
* so it relies on platform-specific BinarySemaphore implementations,
|
|
* but this class itself is platform-independent.
|
|
*
|
|
* @author Jason Rohrer
|
|
*/
|
|
class Semaphore {
|
|
|
|
public:
|
|
|
|
/**
|
|
* Constructs a semaphore.
|
|
*
|
|
* @param inStartingValue the starting value for this semaphore.
|
|
* Defaults to 0 if unspecified.
|
|
*/
|
|
Semaphore( int inStartingValue = 0 );
|
|
|
|
~Semaphore();
|
|
|
|
|
|
/**
|
|
* If this semaphore's current value is 0, then this call blocks
|
|
* on this semaphore until signal() is called by another thread.
|
|
* If this semaphore's value is >0, then it is decremented by this
|
|
* call.
|
|
*
|
|
* @param inTimeoutInMilliseconds the maximum time to wait in
|
|
* milliseconds, or -1 to wait forever. Defaults to -1.
|
|
*
|
|
* @return 1 if the semaphore was signaled, or 0 if it timed out.
|
|
*/
|
|
int wait( int inTimeoutInMilliseconds = -1 );
|
|
|
|
|
|
|
|
/**
|
|
* If a thread is waiting on this semaphore, then the thread
|
|
* becomes unblocked.
|
|
* If no thread is waiting, then the semaphore is incremented.
|
|
*/
|
|
void signal();
|
|
|
|
|
|
/**
|
|
* Returns true if a call to wait would have blocked.
|
|
*/
|
|
char willBlock();
|
|
|
|
|
|
private:
|
|
|
|
// starts at 0
|
|
int mSemaphoreValue;
|
|
|
|
// mutex semaphore starts at 1
|
|
BinarySemaphore *mMutexSemaphore;
|
|
// blocking semaphore starts at 0
|
|
BinarySemaphore *mBlockingSemaphore;
|
|
};
|
|
|
|
|
|
|
|
inline Semaphore::Semaphore( int inStartingValue )
|
|
: mSemaphoreValue( inStartingValue ),
|
|
mMutexSemaphore( new BinarySemaphore() ),
|
|
mBlockingSemaphore( new BinarySemaphore() ) {
|
|
|
|
// increment the mutex semaphore to 1
|
|
mMutexSemaphore->signal();
|
|
}
|
|
|
|
|
|
|
|
inline Semaphore::~Semaphore() {
|
|
delete mMutexSemaphore;
|
|
delete mBlockingSemaphore;
|
|
}
|
|
|
|
|
|
|
|
inline int Semaphore::wait( int inTimeoutInMilliseconds ) {
|
|
int returnValue;
|
|
// this implementation copied from _Operating System Concepts_, p. 172
|
|
|
|
// lock the mutex
|
|
mMutexSemaphore->wait();
|
|
// decrement the semaphore
|
|
mSemaphoreValue--;
|
|
if( mSemaphoreValue < 0 ) {
|
|
// we should block
|
|
|
|
// release the mutex
|
|
mMutexSemaphore->signal();
|
|
|
|
// block
|
|
returnValue = mBlockingSemaphore->wait( inTimeoutInMilliseconds );
|
|
|
|
if( returnValue != 1 ) {
|
|
// timed out
|
|
|
|
// increment the semaphore, since we never got signaled
|
|
// lock the mutex
|
|
mMutexSemaphore->wait();
|
|
mSemaphoreValue++;
|
|
|
|
// we will unlock the mutex below
|
|
}
|
|
}
|
|
else {
|
|
returnValue = 1;
|
|
}
|
|
|
|
// release the mutex
|
|
// ( if we were signaled, then the signaller left the mutex locked )
|
|
// ( if we timed out, then we re-locked the mutex above )
|
|
mMutexSemaphore->signal();
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
|
|
|
|
inline char Semaphore::willBlock() {
|
|
char returnValue = false;
|
|
|
|
// lock the mutex
|
|
mMutexSemaphore->wait();
|
|
|
|
// check if we will block
|
|
if( mSemaphoreValue <= 0 ) {
|
|
returnValue = true;
|
|
}
|
|
|
|
// release the mutex
|
|
mMutexSemaphore->signal();
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
|
|
|
|
inline void Semaphore::signal() {
|
|
// lock the mutex
|
|
mMutexSemaphore->wait();
|
|
// increment the semaphore
|
|
mSemaphoreValue++;
|
|
if( mSemaphoreValue <= 0 ) {
|
|
// we need to wake up a waiting thread
|
|
mBlockingSemaphore->signal();
|
|
// let the waiting thread unlock the mutex
|
|
}
|
|
else {
|
|
// no threads are waiting, so we need to unlock the mutex
|
|
mMutexSemaphore->signal();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|