passage/minorGems/system/Semaphore.h
2025-10-03 02:19:59 -04:00

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