1 /*
2  * Copyright (c) 2005, Eric Crahen
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is furnished
9  * to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20  *
21  */
22 
23 #ifndef __ZTSEMAPHOREIMPL_H__
24 #define __ZTSEMAPHOREIMPL_H__
25 
26 #include "zthread/Guard.h"
27 
28 #include "Debug.h"
29 #include "FastLock.h"
30 #include "Scheduling.h"
31 
32 #include <assert.h>
33 
34 namespace ZThread {
35 
36   class Monitor;
37 
38   /**
39    * @class SemaphoreImpl
40    * @author Eric Crahen <http://www.code-foo.com>
41    * @date <2003-07-16T20:03:20-0400>
42    * @version 2.2.11
43    *
44    * The SemaphoreImpl template allows how waiter lists are sorted
45    * to be parameteized
46    */
47   template <typename List>
48     class SemaphoreImpl {
49 
50     //! List of waiting events
51     List _waiters;
52 
53     //! Serialize access to this object
54     FastLock _lock;
55 
56     //! Current count
57     volatile int _count;
58 
59     //! Maximum count if any
60     volatile int _maxCount;
61 
62     //! Flag for bounded or unbounded count
63     volatile bool _checked;
64 
65     //! Entry count
66     volatile int _entryCount;
67 
68     public:
69 
70 
71     /**
72      * Create a new SemaphoreImpl. Initialzes one pthreads mutex for
73      * internal use.
74      *
75      * @exception Initialization_Exception thrown if resources could not be
76      * properly allocated
77      */
SemaphoreImpl(int count,unsigned int maxCount,bool checked)78     SemaphoreImpl(int count, unsigned int maxCount, bool checked)
79       : _count(count), _maxCount(maxCount), _checked(checked), _entryCount(0) { }
80 
81 
82     ~SemaphoreImpl();
83 
84     void acquire();
85 
86     void release();
87 
88     bool tryAcquire(unsigned long timeout);
89 
90     int count();
91 
92   };
93 
94 
95   /**
96    * Destroy this SemaphoreImpl and release its resources.
97    */
98   template <typename List>
~SemaphoreImpl()99     SemaphoreImpl<List>::~SemaphoreImpl() {
100 
101 #ifndef NDEBUG
102 
103     if(_waiters.size() > 0) {
104 
105       ZTDEBUG("** You are destroying a semaphore which is blocking %d threads. **\n", _waiters.size());
106       assert(0); // Destroyed semaphore while in use
107 
108     }
109 
110 #endif
111 
112   }
113 
114 
115   /**
116    * Get the count for the Semaphore
117    *
118    * @return int
119    */
120   template <typename List>
count()121     int SemaphoreImpl<List>::count() {
122 
123     Guard<FastLock> g(_lock);
124     return _count;
125 
126   }
127 
128   /**
129    * Decrement the count, blocking when that count becomes 0 or less.
130    *
131    * @exception Interrupted_Exception thrown when the caller status is interrupted
132    * @exception Synchronization_Exception thrown if there is some other error.
133    */
134   template <typename List>
acquire()135     void SemaphoreImpl<List>::acquire() {
136 
137     // Get the monitor for the current thread
138     ThreadImpl* self = ThreadImpl::current();
139     Monitor& m = self->getMonitor();
140 
141     Monitor::STATE state;
142 
143     Guard<FastLock> g1(_lock);
144 
145     // Update the count without waiting if possible.
146     if(_count > 0 && _entryCount == 0)
147       _count--;
148 
149     // Otherwise, wait() for the lock by placing the waiter in the list
150     else {
151 
152       ++_entryCount;
153       _waiters.insert(self);
154 
155       m.acquire();
156 
157       {
158 
159         Guard<FastLock, UnlockedScope> g2(g1);
160         state = m.wait();
161 
162       }
163 
164       m.release();
165 
166       // Remove from waiter list, regarless of weather release() is called or
167       // not. The monitor is sticky, so its possible a state 'stuck' from a
168       // previous operation and will leave the wait() w/o release() having
169       // been called.
170       typename List::iterator i = std::find(_waiters.begin(), _waiters.end(), self);
171       if(i != _waiters.end())
172         _waiters.erase(i);
173 
174       --_entryCount;
175 
176       switch(state) {
177         // If awoke due to a notify(), update the count
178         case Monitor::SIGNALED:
179 
180           _count--;
181           break;
182 
183         case Monitor::INTERRUPTED:
184           throw Interrupted_Exception();
185 
186         default:
187           throw Synchronization_Exception();
188       }
189 
190     }
191 
192   }
193 
194   /**
195    * Decrement the count, blocking when it that count is 0 or less. If the timeout
196    * expires before the count is raise above 0, the thread will stop blocking
197    * and return.
198    *
199    * @exception Interrupted_Exception thrown when the caller status is interrupted
200    * @exception Synchronization_Exception thrown if there is some other error.
201    */
202   template <typename List>
tryAcquire(unsigned long timeout)203     bool SemaphoreImpl<List>::tryAcquire(unsigned long timeout) {
204 
205     // Get the monitor for the current thread
206     ThreadImpl* self = ThreadImpl::current();
207     Monitor& m = self->getMonitor();
208 
209     Guard<FastLock> g1(_lock);
210 
211     // Update the count without waiting if possible.
212     if(_count > 0 && _entryCount == 0)
213       _count--;
214 
215     // Otherwise, wait() for the lock by placing the waiter in the list
216     else {
217 
218       ++_entryCount;
219       _waiters.push_back(self);
220 
221       Monitor::STATE state = Monitor::TIMEDOUT;
222 
223       // Don't bother waiting if the timeout is 0
224       if(timeout) {
225 
226         m.acquire();
227 
228         {
229 
230           Guard<FastLock, UnlockedScope> g2(g1);
231           state = m.wait(timeout);
232 
233         }
234 
235         m.release();
236 
237       }
238 
239       // Remove from waiter list, regarless of weather release() is called or
240       // not. The monitor is sticky, so its possible a state 'stuck' from a
241       // previous operation and will leave the wait() w/o release() having
242       // been called.
243       typename List::iterator i = std::find(_waiters.begin(), _waiters.end(), self);
244       if(i != _waiters.end())
245         _waiters.erase(i);
246 
247       --_entryCount;
248 
249       switch(state) {
250         // If awoke due to a notify(), update the count
251         case Monitor::SIGNALED:
252 
253           _count--;
254           break;
255 
256         case Monitor::INTERRUPTED:
257           throw Interrupted_Exception();
258 
259         case Monitor::TIMEDOUT:
260           return false;
261 
262         default:
263           throw Synchronization_Exception();
264       }
265 
266     }
267 
268     return true;
269 
270   }
271 
272   /**
273    * Increment the count and release a waiter if there are any. If the semaphore
274    * is checked, then an exception will be raised if the maximum count is about to
275    * be exceeded.
276    *
277    * @exception InvalidOp_Exception thrown if the maximum count is exceeded while
278    * the checked flag is set.
279    */
280   template <typename List>
release()281     void SemaphoreImpl<List>::release()  {
282 
283     Guard<FastLock> g1(_lock);
284 
285     // Make sure the operation is valid
286     if(_checked && _count == _maxCount)
287       throw InvalidOp_Exception();
288 
289     // Increment the count
290     _count++;
291 
292     // Try to find a waiter with a backoff & retry scheme
293     for(;;) {
294 
295       // Go through the list, attempt to notify() a waiter.
296       for(typename List::iterator i = _waiters.begin(); i != _waiters.end();) {
297 
298         // Try the monitor lock, if it cant be locked skip to the next waiter
299         ThreadImpl* impl = *i;
300         Monitor& m = impl->getMonitor();
301 
302         if(m.tryAcquire()) {
303 
304           // Notify the monitor & remove from the waiter list so time isn't
305           // wasted checking it again.
306           i = _waiters.erase(i);
307 
308           // If notify() is not sucessful, it is because the wait() has already
309           // been ended (killed/interrupted/notify'd)
310           bool woke = m.notify();
311 
312           m.release();
313 
314           // Once notify() succeeds, return
315           if(woke)
316             return;
317 
318         } else ++i;
319 
320       }
321 
322       if(_waiters.empty())
323         return;
324 
325       { // Backoff and try again
326 
327         Guard<FastLock, UnlockedScope> g2(g1);
328         ThreadImpl::yield();
329 
330       }
331 
332     }
333 
334   }
335 
336   class FifoSemaphoreImpl : public SemaphoreImpl<fifo_list> {
337   public:
338 
FifoSemaphoreImpl(int count,unsigned int maxCount,bool checked)339     FifoSemaphoreImpl(int count, unsigned int maxCount, bool checked)
340       /* throw(Synchronization_Exception) */
341       : SemaphoreImpl<fifo_list>(count, maxCount, checked) { }
342 
343   };
344 
345 
346 } // namespace ZThread
347 
348 #endif //  __ZTSEMAPHOREIMPL_H__
349