wibble 0.1.28
|
00001 #ifndef WIBBLE_SYS_MUTEX_H 00002 #define WIBBLE_SYS_MUTEX_H 00003 00004 /* 00005 * Encapsulated pthread mutex and condition 00006 * 00007 * Copyright (C) 2003--2006 Enrico Zini <enrico@debian.org> 00008 * 00009 * This library is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU Lesser General Public 00011 * License as published by the Free Software Foundation; either 00012 * version 2.1 of the License, or (at your option) any later version. 00013 * 00014 * This library is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00017 * Lesser General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU Lesser General Public 00020 * License along with this library; if not, write to the Free Software 00021 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00022 */ 00023 00024 #include <wibble/sys/macros.h> 00025 #include <wibble/exception.h> 00026 #ifdef POSIX 00027 #include <pthread.h> 00028 #endif 00029 00030 #ifdef _WIN32 00031 #include <windows.h> 00032 #include <queue> 00033 #include <time.h> 00034 struct timespec 00035 { 00036 time_t tv_sec; /* seconds */ 00037 long tv_nsec; /* nanoseconds */ 00038 }; 00039 #endif 00040 #include <errno.h> 00041 00042 namespace wibble { 00043 namespace sys { 00044 00051 class Mutex 00052 { 00053 protected: 00054 #ifdef POSIX 00055 pthread_mutex_t mutex; 00056 #endif 00057 00058 #ifdef _WIN32 00059 HANDLE mutex; 00060 bool singlylocking; 00061 #endif 00062 00063 public: 00064 Mutex(bool recursive = false) 00065 { 00066 int res = 0; 00067 #ifdef POSIX 00068 pthread_mutexattr_t attr; 00069 pthread_mutexattr_init( &attr ); 00070 if ( recursive ) { 00071 #if (__APPLE__ || __xlC__) 00072 pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE ); 00073 #else 00074 pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE_NP ); 00075 #endif 00076 } 00077 res = pthread_mutex_init(&mutex, &attr); 00078 #endif 00079 00080 #ifdef _WIN32 00081 mutex = CreateMutex( NULL, FALSE, NULL ); 00082 singlylocking = false; 00083 00084 if (mutex == NULL) 00085 res = (int)GetLastError(); 00086 #endif 00087 if (res != 0) 00088 throw wibble::exception::System(res, "creating pthread mutex"); 00089 } 00090 00091 Mutex( const Mutex & m ) 00092 { 00093 int res = 0; 00094 #ifdef POSIX 00095 pthread_mutexattr_t attr; 00096 pthread_mutexattr_init( &attr ); 00097 res = pthread_mutex_init(&mutex, &attr); 00098 #endif 00099 00100 #ifdef _WIN32 00101 mutex = CreateMutex(NULL, FALSE, NULL); 00102 singlylocking = false; 00103 00104 if(mutex == NULL) 00105 res = (int)GetLastError(); 00106 #endif 00107 if (res != 0) 00108 throw wibble::exception::System(res, "creating pthread mutex"); 00109 } 00110 00111 ~Mutex() 00112 { 00113 int res = 0; 00114 #ifdef POSIX 00115 res = pthread_mutex_destroy(&mutex); 00116 #endif 00117 00118 #ifdef _WIN32 00119 if(!CloseHandle(mutex)) 00120 res = (int)GetLastError(); 00121 #endif 00122 if (res != 0) 00123 throw wibble::exception::System(res, "destroying pthread mutex"); 00124 } 00125 00126 bool trylock() 00127 { 00128 int res = 0; 00129 #ifdef POSIX 00130 res = pthread_mutex_trylock(&mutex); 00131 if ( res == EBUSY ) 00132 return false; 00133 if ( res == 0 ) 00134 return true; 00135 #endif 00136 00137 #ifdef _WIN32 00138 DWORD dwWaitResult = !singlylocking ? WaitForSingleObject(mutex, 0) : WAIT_TIMEOUT; 00139 if(dwWaitResult == WAIT_OBJECT_0) 00140 return true; 00141 if(dwWaitResult == WAIT_TIMEOUT) 00142 return false; 00143 res = (int)GetLastError(); 00144 #endif 00145 throw wibble::exception::System(res, "(try)locking pthread mutex"); 00146 } 00147 00150 void lock() 00151 { 00152 int res = 0; 00153 #ifdef POSIX 00154 res = pthread_mutex_lock(&mutex); 00155 #endif 00156 00157 #ifdef _WIN32 00158 while(singlylocking) 00159 Sleep(1); 00160 if(WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0) 00161 res = (int)GetLastError(); 00162 #endif 00163 if (res != 0) 00164 throw wibble::exception::System(res, "locking pthread mutex"); 00165 } 00166 00169 void unlock() 00170 { 00171 int res = 0; 00172 #ifdef POSIX 00173 res = pthread_mutex_unlock(&mutex); 00174 #endif 00175 00176 #ifdef _WIN32 00177 if(!ReleaseMutex(mutex)) 00178 res = (int)GetLastError(); 00179 #endif 00180 if (res != 0) 00181 throw wibble::exception::System(res, "unlocking pthread mutex"); 00182 } 00183 00185 void reinit() 00186 { 00187 #ifdef POSIX 00188 if (int res = pthread_mutex_init(&mutex, 0)) 00189 throw wibble::exception::System(res, "reinitialising pthread mutex"); 00190 #endif 00191 } 00192 00193 friend class Condition; 00194 }; 00195 00199 template< typename Mutex > 00200 class MutexLockT 00201 { 00202 private: 00203 // Disallow copy 00204 MutexLockT(const MutexLockT&); 00205 MutexLockT& operator=(const MutexLockT&); 00206 00207 public: 00208 Mutex& mutex; 00209 bool locked; 00210 bool yield; 00211 00212 MutexLockT(Mutex& m) : mutex(m), locked( false ), yield( false ) { 00213 mutex.lock(); 00214 locked = true; 00215 } 00216 00217 ~MutexLockT() { 00218 if ( locked ) { 00219 mutex.unlock(); 00220 checkYield(); 00221 } 00222 } 00223 00224 void drop() { 00225 mutex.unlock(); 00226 locked = false; 00227 checkYield(); 00228 } 00229 void reclaim() { mutex.lock(); locked = true; } 00230 void setYield( bool y ) { 00231 yield = y; 00232 } 00233 00234 void checkYield() { 00235 00236 if ( yield ) 00237 #ifdef POSIX 00238 sched_yield(); 00239 #elif _WIN32 00240 Sleep(0); 00241 #else 00242 ; 00243 #endif 00244 } 00245 00246 friend class Condition; 00247 }; 00248 00249 typedef MutexLockT< Mutex > MutexLock; 00250 00251 /* 00252 * pthread condition wrapper. 00253 * 00254 * It works in association with a MutexLock. 00255 * 00256 * WARNING: the class allows copying and assignment; see Mutex: similar caveats 00257 * apply. Do not copy or assign a Condition that may be in use. 00258 */ 00259 class Condition 00260 { 00261 protected: 00262 #ifdef POSIX 00263 pthread_cond_t cond; 00264 #endif 00265 00266 #ifdef _WIN32 00267 int waiters_count_; // number of waiting threads 00268 CRITICAL_SECTION waiters_count_lock_; 00269 HANDLE sema_; // semaphore used to queue up threads waiting for the condition 00270 HANDLE waiters_done_; 00271 // An auto-reset event used by the broadcast/signal thread to wait 00272 // for all the waiting thread(s) to wake up and be released from the 00273 // semaphore. 00274 00275 bool was_broadcast_; 00276 // Keeps track of whether we were broadcasting or signaling. This 00277 // allows us to optimize the code if we're just signaling. 00278 #endif 00279 00280 public: 00281 Condition() 00282 { 00283 int res = 0; 00284 #ifdef POSIX 00285 res = pthread_cond_init(&cond, 0); 00286 #endif 00287 00288 #ifdef _WIN32 00289 waiters_count_ = 0; 00290 was_broadcast_ = false; 00291 sema_ = CreateSemaphore(NULL, 0, 0x7fffffff, NULL); 00292 InitializeCriticalSection(&waiters_count_lock_); 00293 waiters_done_ = CreateEvent(NULL, FALSE, FALSE, NULL); 00294 00295 if(sema_ == NULL || waiters_done_ == NULL) 00296 res = (int)GetLastError(); 00297 #endif 00298 if (res != 0) 00299 throw wibble::exception::System(res, "creating pthread condition"); 00300 } 00301 00302 Condition( const Condition & con ) 00303 { 00304 int res = 0; 00305 #ifdef POSIX 00306 res = pthread_cond_init(&cond, 0); 00307 #endif 00308 00309 #ifdef _WIN32 00310 waiters_count_ = 0; 00311 was_broadcast_ = false; 00312 sema_ = CreateSemaphore(NULL, 0, 0x7fffffff, NULL); 00313 InitializeCriticalSection(&waiters_count_lock_); 00314 waiters_done_ = CreateEvent(NULL, FALSE, FALSE, NULL); 00315 00316 if(sema_ == NULL || waiters_done_ == NULL) 00317 res = (int)GetLastError(); 00318 #endif 00319 if (res != 0) 00320 throw wibble::exception::System(res, "creating pthread condition"); 00321 } 00322 00323 ~Condition() 00324 { 00325 int res = 0; 00326 #ifdef POSIX 00327 res = pthread_cond_destroy(&cond); 00328 #endif 00329 00330 #ifdef _WIN32 00331 DeleteCriticalSection(&waiters_count_lock_); 00332 if(!CloseHandle(sema_) || !CloseHandle(waiters_done_)) 00333 res = (int)GetLastError(); 00334 #endif 00335 if (res != 0) 00336 throw wibble::exception::System(res, "destroying pthread condition"); 00337 } 00338 00340 void signal() 00341 { 00342 int res = 0; 00343 #ifdef POSIX 00344 res = pthread_cond_signal(&cond); 00345 #endif 00346 00347 #ifdef _WIN32 00348 EnterCriticalSection(&waiters_count_lock_); 00349 bool have_waiters = waiters_count_ > 0; 00350 LeaveCriticalSection(&waiters_count_lock_); 00351 00352 // if there aren't any waiters, then this is a no-op 00353 if(have_waiters && !ReleaseSemaphore(sema_, 1, 0)) 00354 res = (int)GetLastError(); 00355 #endif 00356 if (res != 0) 00357 throw wibble::exception::System(res, "signaling on a pthread condition"); 00358 } 00359 00361 void broadcast() 00362 { 00363 int res = 0; 00364 #ifdef POSIX 00365 res = pthread_cond_broadcast(&cond); 00366 #endif 00367 00368 #ifdef _WIN32 00369 for(bool once = true; once; once = false) 00370 { 00371 EnterCriticalSection(&waiters_count_lock_); 00372 bool have_waiters = false; 00373 00374 if(waiters_count_ > 0) { 00375 was_broadcast_ = true; 00376 have_waiters = true; 00377 } 00378 00379 if(have_waiters) { 00380 if(!ReleaseSemaphore(sema_, waiters_count_, 0)) { 00381 res = (int)GetLastError(); 00382 break; 00383 } 00384 LeaveCriticalSection(&waiters_count_lock_); 00385 if(WaitForSingleObject(waiters_done_, INFINITE) != WAIT_OBJECT_0) { 00386 res = (int)GetLastError(); 00387 break; 00388 } 00389 was_broadcast_ = false; 00390 } 00391 else 00392 LeaveCriticalSection(&waiters_count_lock_); 00393 } 00394 #endif 00395 if (res != 0) 00396 throw wibble::exception::System(res, "broadcasting on a pthread condition"); 00397 } 00398 00403 void wait(MutexLock& l) 00404 { 00405 int res = 0; 00406 #ifdef POSIX 00407 res = pthread_cond_wait(&cond, &l.mutex.mutex); 00408 #endif 00409 00410 #ifdef _WIN32 00411 for(bool once = true; once; once = false) 00412 { 00413 EnterCriticalSection (&waiters_count_lock_); 00414 waiters_count_++; 00415 LeaveCriticalSection (&waiters_count_lock_); 00416 00417 if(SignalObjectAndWait(l.mutex.mutex, sema_, INFINITE, FALSE) != WAIT_OBJECT_0) { 00418 res = (int)GetLastError(); 00419 break; 00420 } 00421 00422 EnterCriticalSection (&waiters_count_lock_); 00423 waiters_count_--; 00424 bool last_waiter = was_broadcast_ && waiters_count_ == 0; 00425 LeaveCriticalSection (&waiters_count_lock_); 00426 00427 if (last_waiter) { 00428 if(SignalObjectAndWait (waiters_done_, l.mutex.mutex, INFINITE, FALSE) != WAIT_OBJECT_0) 00429 { 00430 res = (int)GetLastError(); 00431 break; 00432 } 00433 } 00434 else { 00435 if(WaitForSingleObject (l.mutex.mutex, INFINITE) != WAIT_OBJECT_0) 00436 { 00437 res = (int)GetLastError(); 00438 break; 00439 } 00440 } 00441 } 00442 #endif 00443 if (res != 0) 00444 throw wibble::exception::System(res, "waiting on a pthread condition"); 00445 } 00446 00447 void wait(Mutex& l) 00448 { 00449 int res = 0; 00450 #ifdef POSIX 00451 res = pthread_cond_wait(&cond, &l.mutex); 00452 #endif 00453 00454 #ifdef _WIN32 00455 for(bool once = true; once; once = false) 00456 { 00457 if(WaitForSingleObject(l.mutex, 0) == WAIT_OBJECT_0) { 00458 l.singlylocking = true; 00459 while(ReleaseMutex(l.mutex)) ; 00460 if ((res = ((int)GetLastError() != 288))) //288 -> MUTEX_NOT_OWNED 00461 break; 00462 } 00463 if(WaitForSingleObject(l.mutex, INFINITE) != WAIT_OBJECT_0) { 00464 res = (int)GetLastError(); 00465 break; 00466 } 00467 l.singlylocking = false; 00468 00469 EnterCriticalSection (&waiters_count_lock_); 00470 waiters_count_++; 00471 LeaveCriticalSection (&waiters_count_lock_); 00472 00473 if(SignalObjectAndWait(l.mutex, sema_, INFINITE, FALSE) != WAIT_OBJECT_0) { 00474 res = (int)GetLastError(); 00475 break; 00476 } 00477 00478 EnterCriticalSection (&waiters_count_lock_); 00479 waiters_count_--; 00480 bool last_waiter = was_broadcast_ && waiters_count_ == 0; 00481 LeaveCriticalSection (&waiters_count_lock_); 00482 00483 if(last_waiter) { 00484 if(SignalObjectAndWait (waiters_done_, l.mutex, INFINITE, FALSE) != WAIT_OBJECT_0) { 00485 res = (int)GetLastError(); 00486 break; 00487 } 00488 } 00489 else { 00490 if(WaitForSingleObject(l.mutex, INFINITE) != WAIT_OBJECT_0) { 00491 res = (int)GetLastError(); 00492 break; 00493 } 00494 } 00495 } 00496 #endif 00497 if (res != 0) 00498 throw wibble::exception::System(res, "waiting on a pthread condition"); 00499 } 00500 00511 bool wait(MutexLock& l, const struct timespec& abstime); 00512 }; 00513 00514 } 00515 } 00516 00517 // vim:set ts=4 sw=4: 00518 #endif