• Main Page
  • Namespaces
  • Classes
  • Files
  • File List
  • File Members

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 
00048 class Mutex
00049 {
00050 private:
00051     // Disallow copy
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     // Disallow copy
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  * pthread condition wrapper.
00254  *
00255  * It works in association with a MutexLock.
00256  */
00257 class Condition
00258 {
00259 private:
00260     // Disallow copy
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_; // number of waiting threads
00270   CRITICAL_SECTION waiters_count_lock_;
00271   HANDLE sema_; // semaphore used to queue up threads waiting for the condition
00272   HANDLE waiters_done_;
00273   // An auto-reset event used by the broadcast/signal thread to wait
00274   // for all the waiting thread(s) to wake up and be released from the
00275   // semaphore. 
00276 
00277   bool was_broadcast_;
00278   // Keeps track of whether we were broadcasting or signaling.  This
00279   // allows us to optimize the code if we're just signaling.
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       // if there aren't any waiters, then this is a no-op
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) //288 -> MUTEX_NOT_OWNED
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 // vim:set ts=4 sw=4:
00520 #endif

Generated on Tue May 10 2011 16:51:50 for wibble by  doxygen 1.7.1