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