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
00048 class Mutex
00049 {
00050 private:
00051
00052 Mutex& operator=(const Mutex&);
00053
00054 protected:
00055 #ifdef POSIX
00056 pthread_mutex_t mutex;
00057 #endif
00058
00059 #ifdef _WIN32
00060 HANDLE mutex;
00061 bool singlylocking;
00062 #endif
00063
00064 public:
00065 Mutex(bool recursive = false)
00066 {
00067 int res = 0;
00068 #ifdef POSIX
00069 pthread_mutexattr_t attr;
00070 pthread_mutexattr_init( &attr );
00071 if ( recursive ) {
00072 #ifdef __APPLE__
00073 pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
00074 #else
00075 pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE_NP );
00076 #endif
00077 }
00078 res = pthread_mutex_init(&mutex, &attr);
00079 #endif
00080
00081 #ifdef _WIN32
00082 mutex = CreateMutex( NULL, FALSE, NULL );
00083 singlylocking = false;
00084
00085 if (mutex == NULL)
00086 res = (int)GetLastError();
00087 #endif
00088 if (res != 0)
00089 throw wibble::exception::System(res, "creating pthread mutex");
00090 }
00091
00092 Mutex( const Mutex & m )
00093 {
00094 int res = 0;
00095 #ifdef POSIX
00096 pthread_mutexattr_t attr;
00097 pthread_mutexattr_init( &attr );
00098 res = pthread_mutex_init(&mutex, &attr);
00099 #endif
00100
00101 #ifdef _WIN32
00102 mutex = CreateMutex(NULL, FALSE, NULL);
00103 singlylocking = false;
00104
00105 if(mutex == NULL)
00106 res = (int)GetLastError();
00107 #endif
00108 if (res != 0)
00109 throw wibble::exception::System(res, "creating pthread mutex");
00110 }
00111
00112 ~Mutex()
00113 {
00114 int res = 0;
00115 #ifdef POSIX
00116 res = pthread_mutex_destroy(&mutex);
00117 #endif
00118
00119 #ifdef _WIN32
00120 if(!CloseHandle(mutex))
00121 res = (int)GetLastError();
00122 #endif
00123 if (res != 0)
00124 throw wibble::exception::System(res, "destroying pthread mutex");
00125 }
00126
00127 bool trylock()
00128 {
00129 int res = 0;
00130 #ifdef POSIX
00131 res = pthread_mutex_trylock(&mutex);
00132 if ( res == EBUSY )
00133 return false;
00134 if ( res == 0 )
00135 return true;
00136 #endif
00137
00138 #ifdef _WIN32
00139 DWORD dwWaitResult = !singlylocking ? WaitForSingleObject(mutex, 0) : WAIT_TIMEOUT;
00140 if(dwWaitResult == WAIT_OBJECT_0)
00141 return true;
00142 if(dwWaitResult == WAIT_TIMEOUT)
00143 return false;
00144 res = (int)GetLastError();
00145 #endif
00146 throw wibble::exception::System(res, "(try)locking pthread mutex");
00147 }
00148
00151 void lock()
00152 {
00153 int res = 0;
00154 #ifdef POSIX
00155 res = pthread_mutex_lock(&mutex);
00156 #endif
00157
00158 #ifdef _WIN32
00159 while(singlylocking)
00160 Sleep(1);
00161 if(WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0)
00162 res = (int)GetLastError();
00163 #endif
00164 if (res != 0)
00165 throw wibble::exception::System(res, "locking pthread mutex");
00166 }
00167
00170 void unlock()
00171 {
00172 int res = 0;
00173 #ifdef POSIX
00174 res = pthread_mutex_unlock(&mutex);
00175 #endif
00176
00177 #ifdef _WIN32
00178 if(!ReleaseMutex(mutex))
00179 res = (int)GetLastError();
00180 #endif
00181 if (res != 0)
00182 throw wibble::exception::System(res, "unlocking pthread mutex");
00183 }
00184
00186 void reinit()
00187 {
00188 #ifdef POSIX
00189 if (int res = pthread_mutex_init(&mutex, 0))
00190 throw wibble::exception::System(res, "reinitialising pthread mutex");
00191 #endif
00192 }
00193
00194 friend class Condition;
00195 };
00196
00200 template< typename Mutex >
00201 class MutexLockT
00202 {
00203 private:
00204
00205 MutexLockT(const MutexLockT&);
00206 MutexLockT& operator=(const MutexLockT&);
00207
00208 public:
00209 Mutex& mutex;
00210 bool locked;
00211 bool yield;
00212
00213 MutexLockT(Mutex& m) : mutex(m), locked( false ), yield( false ) {
00214 mutex.lock();
00215 locked = true;
00216 }
00217
00218 ~MutexLockT() {
00219 if ( locked ) {
00220 mutex.unlock();
00221 checkYield();
00222 }
00223 }
00224
00225 void drop() {
00226 mutex.unlock();
00227 locked = false;
00228 checkYield();
00229 }
00230 void reclaim() { mutex.lock(); locked = true; }
00231 void setYield( bool y ) {
00232 yield = y;
00233 }
00234
00235 void checkYield() {
00236
00237 if ( yield )
00238 #ifdef POSIX
00239 sched_yield();
00240 #endif
00241
00242 #ifdef _WIN32
00243 Sleep(0);
00244 #endif
00245 }
00246
00247 friend class Condition;
00248 };
00249
00250 typedef MutexLockT< Mutex > MutexLock;
00251
00252
00253
00254
00255
00256
00257 class Condition
00258 {
00259 private:
00260
00261 Condition& operator=(const Condition&);
00262
00263 protected:
00264 #ifdef POSIX
00265 pthread_cond_t cond;
00266 #endif
00267
00268 #ifdef _WIN32
00269 int waiters_count_;
00270 CRITICAL_SECTION waiters_count_lock_;
00271 HANDLE sema_;
00272 HANDLE waiters_done_;
00273
00274
00275
00276
00277 bool was_broadcast_;
00278
00279
00280 #endif
00281
00282 public:
00283 Condition()
00284 {
00285 int res = 0;
00286 #ifdef POSIX
00287 res = pthread_cond_init(&cond, 0);
00288 #endif
00289
00290 #ifdef _WIN32
00291 waiters_count_ = 0;
00292 was_broadcast_ = false;
00293 sema_ = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
00294 InitializeCriticalSection(&waiters_count_lock_);
00295 waiters_done_ = CreateEvent(NULL, FALSE, FALSE, NULL);
00296
00297 if(sema_ == NULL || waiters_done_ == NULL)
00298 res = (int)GetLastError();
00299 #endif
00300 if (res != 0)
00301 throw wibble::exception::System(res, "creating pthread condition");
00302 }
00303
00304 Condition( const Condition & con )
00305 {
00306 int res = 0;
00307 #ifdef POSIX
00308 res = pthread_cond_init(&cond, 0);
00309 #endif
00310
00311 #ifdef _WIN32
00312 waiters_count_ = 0;
00313 was_broadcast_ = false;
00314 sema_ = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
00315 InitializeCriticalSection(&waiters_count_lock_);
00316 waiters_done_ = CreateEvent(NULL, FALSE, FALSE, NULL);
00317
00318 if(sema_ == NULL || waiters_done_ == NULL)
00319 res = (int)GetLastError();
00320 #endif
00321 if (res != 0)
00322 throw wibble::exception::System(res, "creating pthread condition");
00323 }
00324
00325 ~Condition()
00326 {
00327 int res = 0;
00328 #ifdef POSIX
00329 res = pthread_cond_destroy(&cond);
00330 #endif
00331
00332 #ifdef _WIN32
00333 DeleteCriticalSection(&waiters_count_lock_);
00334 if(!CloseHandle(sema_) || !CloseHandle(waiters_done_))
00335 res = (int)GetLastError();
00336 #endif
00337 if (res != 0)
00338 throw wibble::exception::System(res, "destroying pthread condition");
00339 }
00340
00342 void signal()
00343 {
00344 int res = 0;
00345 #ifdef POSIX
00346 res = pthread_cond_signal(&cond);
00347 #endif
00348
00349 #ifdef _WIN32
00350 EnterCriticalSection(&waiters_count_lock_);
00351 bool have_waiters = waiters_count_ > 0;
00352 LeaveCriticalSection(&waiters_count_lock_);
00353
00354
00355 if(have_waiters && !ReleaseSemaphore(sema_, 1, 0))
00356 res = (int)GetLastError();
00357 #endif
00358 if (res != 0)
00359 throw wibble::exception::System(res, "signaling on a pthread condition");
00360 }
00361
00363 void broadcast()
00364 {
00365 int res = 0;
00366 #ifdef POSIX
00367 res = pthread_cond_broadcast(&cond);
00368 #endif
00369
00370 #ifdef _WIN32
00371 for(bool once = true; once; once = false)
00372 {
00373 EnterCriticalSection(&waiters_count_lock_);
00374 bool have_waiters = false;
00375
00376 if(waiters_count_ > 0) {
00377 was_broadcast_ = true;
00378 have_waiters = true;
00379 }
00380
00381 if(have_waiters) {
00382 if(!ReleaseSemaphore(sema_, waiters_count_, 0)) {
00383 res = (int)GetLastError();
00384 break;
00385 }
00386 LeaveCriticalSection(&waiters_count_lock_);
00387 if(WaitForSingleObject(waiters_done_, INFINITE) != WAIT_OBJECT_0) {
00388 res = (int)GetLastError();
00389 break;
00390 }
00391 was_broadcast_ = false;
00392 }
00393 else
00394 LeaveCriticalSection(&waiters_count_lock_);
00395 }
00396 #endif
00397 if (res != 0)
00398 throw wibble::exception::System(res, "broadcasting on a pthread condition");
00399 }
00400
00405 void wait(MutexLock& l)
00406 {
00407 int res = 0;
00408 #ifdef POSIX
00409 res = pthread_cond_wait(&cond, &l.mutex.mutex);
00410 #endif
00411
00412 #ifdef _WIN32
00413 for(bool once = true; once; once = false)
00414 {
00415 EnterCriticalSection (&waiters_count_lock_);
00416 waiters_count_++;
00417 LeaveCriticalSection (&waiters_count_lock_);
00418
00419 if(SignalObjectAndWait(l.mutex.mutex, sema_, INFINITE, FALSE) != WAIT_OBJECT_0) {
00420 res = (int)GetLastError();
00421 break;
00422 }
00423
00424 EnterCriticalSection (&waiters_count_lock_);
00425 waiters_count_--;
00426 bool last_waiter = was_broadcast_ && waiters_count_ == 0;
00427 LeaveCriticalSection (&waiters_count_lock_);
00428
00429 if (last_waiter) {
00430 if(SignalObjectAndWait (waiters_done_, l.mutex.mutex, INFINITE, FALSE) != WAIT_OBJECT_0)
00431 {
00432 res = (int)GetLastError();
00433 break;
00434 }
00435 }
00436 else {
00437 if(WaitForSingleObject (l.mutex.mutex, INFINITE) != WAIT_OBJECT_0)
00438 {
00439 res = (int)GetLastError();
00440 break;
00441 }
00442 }
00443 }
00444 #endif
00445 if (res != 0)
00446 throw wibble::exception::System(res, "waiting on a pthread condition");
00447 }
00448
00449 void wait(Mutex& l)
00450 {
00451 int res = 0;
00452 #ifdef POSIX
00453 res = pthread_cond_wait(&cond, &l.mutex);
00454 #endif
00455
00456 #ifdef _WIN32
00457 for(bool once = true; once; once = false)
00458 {
00459 if(WaitForSingleObject(l.mutex, 0) == WAIT_OBJECT_0) {
00460 l.singlylocking = true;
00461 while(ReleaseMutex(l.mutex)) ;
00462 if(res = (int)GetLastError() != 288)
00463 break;
00464 }
00465 if(WaitForSingleObject(l.mutex, INFINITE) != WAIT_OBJECT_0) {
00466 res = (int)GetLastError();
00467 break;
00468 }
00469 l.singlylocking = false;
00470
00471 EnterCriticalSection (&waiters_count_lock_);
00472 waiters_count_++;
00473 LeaveCriticalSection (&waiters_count_lock_);
00474
00475 if(SignalObjectAndWait(l.mutex, sema_, INFINITE, FALSE) != WAIT_OBJECT_0) {
00476 res = (int)GetLastError();
00477 break;
00478 }
00479
00480 EnterCriticalSection (&waiters_count_lock_);
00481 waiters_count_--;
00482 bool last_waiter = was_broadcast_ && waiters_count_ == 0;
00483 LeaveCriticalSection (&waiters_count_lock_);
00484
00485 if(last_waiter) {
00486 if(SignalObjectAndWait (waiters_done_, l.mutex, INFINITE, FALSE) != WAIT_OBJECT_0) {
00487 res = (int)GetLastError();
00488 break;
00489 }
00490 }
00491 else {
00492 if(WaitForSingleObject(l.mutex, INFINITE) != WAIT_OBJECT_0) {
00493 res = (int)GetLastError();
00494 break;
00495 }
00496 }
00497 }
00498 #endif
00499 if (res != 0)
00500 throw wibble::exception::System(res, "waiting on a pthread condition");
00501 }
00502
00513 bool wait(MutexLock& l, const struct timespec& abstime);
00514 };
00515
00516 }
00517 }
00518
00519
00520 #endif