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