1 /*
2  * Modification History
3  *
4  * 2001-January-11		Jason Rohrer
5  * Created.
6  *
7  * 2001-January-11		Jason Rohrer
8  * Added a willBlock() function.
9  *
10  * 2001-February-24		Jason Rohrer
11  * Fixed incorrect delete usage.
12  *
13  * 2002-February-11		Jason Rohrer
14  * Fixed a mistake in the signal() comment.
15  *
16  * 2003-August-26   Jason Rohrer
17  * Added support for timeouts on wait.
18  *
19  * 2003-December-28   Jason Rohrer
20  * Fixed a bug in semaphore value when we timeout on wait.
21  *
22  * 2004-January-9   Jason Rohrer
23  * Fixed a preprocessor error.
24  */
25 
26 #include "minorGems/common.h"
27 
28 
29 
30 #ifndef SEMAPHORE_CLASS_INCLUDED
31 #define SEMAPHORE_CLASS_INCLUDED
32 
33 #include "MutexLock.h"
34 #include "BinarySemaphore.h"
35 
36 
37 /**
38  * General semaphore with an unbounded value.
39  *
40  * This class uses BinarySemaphores to implement general semaphores,
41  * so it relies on platform-specific BinarySemaphore implementations,
42  * but this class itself is platform-independent.
43  *
44  * @author Jason Rohrer
45  */
46 class Semaphore {
47 
48 	public:
49 
50 		/**
51 		 * Constructs a semaphore.
52 		 *
53 		 * @param inStartingValue the starting value for this semaphore.
54 		 *   Defaults to 0 if unspecified.
55 		 */
56 		Semaphore( int inStartingValue = 0 );
57 
58 		~Semaphore();
59 
60 
61 		/**
62 		 * If this semaphore's current value is 0, then this call blocks
63 		 * on this semaphore until signal() is called by another thread.
64 		 * If this semaphore's value is >0, then it is decremented by this
65          * call.
66          *
67          * @param inTimeoutInMilliseconds the maximum time to wait in
68          *   milliseconds, or -1 to wait forever.  Defaults to -1.
69          *
70          * @return 1 if the semaphore was signaled, or 0 if it timed out.
71 		 */
72 		int wait( int inTimeoutInMilliseconds = -1 );
73 
74 
75 
76 		/**
77 		 * If a thread is waiting on this semaphore, then the thread
78 		 * becomes unblocked.
79 		 * If no thread is waiting, then the semaphore is incremented.
80 		 */
81 		void signal();
82 
83 
84 		/**
85 		 * Returns true if a call to wait would have blocked.
86 		 */
87 		char willBlock();
88 
89 
90 	private:
91 
92 		// starts at 0
93 		int mSemaphoreValue;
94 
95 		// mutex semaphore starts at 1
96 		BinarySemaphore *mMutexSemaphore;
97 		// blocking semaphore starts at 0
98 		BinarySemaphore *mBlockingSemaphore;
99 	};
100 
101 
102 
Semaphore(int inStartingValue)103 inline Semaphore::Semaphore( int inStartingValue )
104 	: mSemaphoreValue( inStartingValue ),
105 	mMutexSemaphore( new BinarySemaphore() ),
106 	mBlockingSemaphore(  new BinarySemaphore() ) {
107 
108 	// increment the mutex semaphore to 1
109 	mMutexSemaphore->signal();
110 	}
111 
112 
113 
~Semaphore()114 inline Semaphore::~Semaphore() {
115 	delete mMutexSemaphore;
116 	delete mBlockingSemaphore;
117 	}
118 
119 
120 
wait(int inTimeoutInMilliseconds)121 inline int Semaphore::wait( int inTimeoutInMilliseconds ) {
122     int returnValue;
123     // this implementation copied from _Operating System Concepts_, p. 172
124 
125 	// lock the mutex
126 	mMutexSemaphore->wait();
127 	// decrement the semaphore
128 	mSemaphoreValue--;
129 	if( mSemaphoreValue < 0 ) {
130 		// we should block
131 
132 		// release the mutex
133 		mMutexSemaphore->signal();
134 
135 		// block
136 		returnValue = mBlockingSemaphore->wait( inTimeoutInMilliseconds );
137 
138         if( returnValue != 1 ) {
139             // timed out
140 
141             // increment the semaphore, since we never got signaled
142             // lock the mutex
143             mMutexSemaphore->wait();
144             mSemaphoreValue++;
145 
146             // we will unlock the mutex below
147             }
148 		}
149     else {
150         returnValue = 1;
151         }
152 
153 	// release the mutex
154 	// ( if we were signaled, then the signaller left the mutex locked )
155     // ( if we timed out, then we re-locked the mutex above )
156 	mMutexSemaphore->signal();
157 
158     return returnValue;
159 	}
160 
161 
162 
willBlock()163 inline char Semaphore::willBlock() {
164 	char returnValue = false;
165 
166 	// lock the mutex
167 	mMutexSemaphore->wait();
168 
169 	// check if we will block
170 	if( mSemaphoreValue <= 0 ) {
171 		returnValue = true;
172 		}
173 
174 	// release the mutex
175 	mMutexSemaphore->signal();
176 
177 	return returnValue;
178 	}
179 
180 
181 
signal()182 inline void Semaphore::signal() {
183 	// lock the mutex
184 	mMutexSemaphore->wait();
185 	// increment the semaphore
186 	mSemaphoreValue++;
187 	if( mSemaphoreValue <= 0 ) {
188 		// we need to wake up a waiting thread
189 		mBlockingSemaphore->signal();
190 		// let the waiting thread unlock the mutex
191 		}
192 	else {
193 		// no threads are waiting, so we need to unlock the mutex
194 		mMutexSemaphore->signal();
195 		}
196 	}
197 
198 
199 
200 
201 #endif
202