wibble  0.1.28
mutex.h
Go to the documentation of this file.
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