00001 #ifndef WIBBLE_SYS_MUTEX_H
00002 #define WIBBLE_SYS_MUTEX_H
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
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;
00037 long tv_nsec;
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
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
00253
00254
00255
00256
00257
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_;
00268 CRITICAL_SECTION waiters_count_lock_;
00269 HANDLE sema_;
00270 HANDLE waiters_done_;
00271
00272
00273
00274
00275 bool was_broadcast_;
00276
00277
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
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)))
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
00518 #endif