wibble  1.1
mutex.h
Go to the documentation of this file.
1 #ifndef WIBBLE_SYS_MUTEX_H
2 #define WIBBLE_SYS_MUTEX_H
3 
4 /*
5  * Encapsulated pthread mutex and condition
6  *
7  * Copyright (C) 2003--2006 Enrico Zini <enrico@debian.org>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22  */
23 
24 #include <wibble/sys/macros.h>
25 #include <wibble/exception.h>
26 #ifdef POSIX
27 #include <pthread.h>
28 #endif
29 
30 #ifdef _WIN32
31 #include <windows.h>
32 #include <queue>
33 #include <time.h>
34 #endif
35 
36 #include <errno.h>
37 
38 namespace wibble {
39 namespace sys {
40 
47 class Mutex
48 {
49 protected:
50 #ifdef POSIX
51  pthread_mutex_t mutex;
52 #endif
53 
54 #ifdef _WIN32
55  HANDLE mutex;
56  bool singlylocking;
57 #endif
58 
59 public:
60  Mutex(bool recursive = false)
61  {
62  int res = 0;
63 #ifdef POSIX
64  pthread_mutexattr_t attr;
65  pthread_mutexattr_init( &attr );
66  if ( recursive ) {
67 #if (__APPLE__ || __xlC__)
68  pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
69 #else
70  pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE_NP );
71 #endif
72  } else {
73 #ifndef NDEBUG
74  pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK_NP );
75 #endif
76  }
77  res = pthread_mutex_init(&mutex, &attr);
78 #endif
79 
80 #ifdef _WIN32
81  mutex = CreateMutex( NULL, FALSE, NULL );
82  singlylocking = false;
83 
84  if (mutex == NULL)
85  res = (int)GetLastError();
86 #endif
87  if (res != 0)
88  throw wibble::exception::System(res, "creating pthread mutex");
89  }
90 
91  Mutex( const Mutex & )
92  {
93  int res = 0;
94 #ifdef POSIX
95  pthread_mutexattr_t attr;
96  pthread_mutexattr_init( &attr );
97  res = pthread_mutex_init(&mutex, &attr);
98 #endif
99 
100 #ifdef _WIN32
101  mutex = CreateMutex(NULL, FALSE, NULL);
102  singlylocking = false;
103 
104  if(mutex == NULL)
105  res = (int)GetLastError();
106 #endif
107  if (res != 0)
108  throw wibble::exception::System(res, "creating pthread mutex");
109  }
110 
112  {
113  int res = 0;
114 #ifdef POSIX
115  res = pthread_mutex_destroy(&mutex);
116 #endif
117 
118 #ifdef _WIN32
119  if(!CloseHandle(mutex))
120  res = (int)GetLastError();
121 #endif
122  if (res != 0)
123  throw wibble::exception::System(res, "destroying pthread mutex");
124  }
125 
126  bool trylock()
127  {
128  int res = 0;
129 #ifdef POSIX
130  res = pthread_mutex_trylock(&mutex);
131  if ( res == EBUSY )
132  return false;
133  if ( res == 0 )
134  return true;
135 #endif
136 
137 #ifdef _WIN32
138  DWORD dwWaitResult = !singlylocking ? WaitForSingleObject(mutex, 0) : WAIT_TIMEOUT;
139  if(dwWaitResult == WAIT_OBJECT_0)
140  return true;
141  if(dwWaitResult == WAIT_TIMEOUT)
142  return false;
143  res = (int)GetLastError();
144 #endif
145  throw wibble::exception::System(res, "(try)locking pthread mutex");
146  }
147 
150  void lock()
151  {
152  int res = 0;
153 #ifdef POSIX
154  res = pthread_mutex_lock(&mutex);
155 #endif
156 
157 #ifdef _WIN32
158  while(singlylocking)
159  Sleep(1);
160  if(WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0)
161  res = (int)GetLastError();
162 #endif
163  if (res != 0)
164  throw wibble::exception::System(res, "locking pthread mutex");
165  }
166 
169  void unlock()
170  {
171  int res = 0;
172 #ifdef POSIX
173  res = pthread_mutex_unlock(&mutex);
174 #endif
175 
176 #ifdef _WIN32
177  if(!ReleaseMutex(mutex))
178  res = (int)GetLastError();
179 #endif
180  if (res != 0)
181  throw wibble::exception::System(res, "unlocking pthread mutex");
182  }
183 
185  void reinit()
186  {
187 #ifdef POSIX
188  if (int res = pthread_mutex_init(&mutex, 0))
189  throw wibble::exception::System(res, "reinitialising pthread mutex");
190 #endif
191  }
192 
193  friend class Condition;
194 };
195 
199 template< typename Mutex >
201 {
202 private:
203  // Disallow copy
204  MutexLockT(const MutexLockT&);
205  MutexLockT& operator=(const MutexLockT&);
206 
207 public:
209  bool locked;
210  bool yield;
211 
212  MutexLockT(Mutex& m) : mutex(m), locked( false ), yield( false ) {
213  mutex.lock();
214  locked = true;
215  }
216 
218  if ( locked ) {
219  mutex.unlock();
220  checkYield();
221  }
222  }
223 
224  void drop() {
225  mutex.unlock();
226  locked = false;
227  checkYield();
228  }
229  void reclaim() { mutex.lock(); locked = true; }
230  void setYield( bool y ) {
231  yield = y;
232  }
233 
234  void checkYield() {
235 
236  if ( yield )
237 #ifdef POSIX
238  sched_yield();
239 #elif _WIN32
240  Sleep(0);
241 #else
242  ;
243 #endif
244  }
245 
246  friend class Condition;
247 };
248 
250 
251 /*
252  * pthread condition wrapper.
253  *
254  * It works in association with a MutexLock.
255  *
256  * WARNING: the class allows copying and assignment; see Mutex: similar caveats
257  * apply. Do not copy or assign a Condition that may be in use.
258  */
260 {
261 protected:
262 #ifdef POSIX
263  pthread_cond_t cond;
264 #endif
265 
266 #ifdef _WIN32
267  int waiters_count_; // number of waiting threads
268  CRITICAL_SECTION waiters_count_lock_;
269  HANDLE sema_; // semaphore used to queue up threads waiting for the condition
270  HANDLE waiters_done_;
271  // An auto-reset event used by the broadcast/signal thread to wait
272  // for all the waiting thread(s) to wake up and be released from the
273  // semaphore.
274 
275  bool was_broadcast_;
276  // Keeps track of whether we were broadcasting or signaling. This
277  // allows us to optimize the code if we're just signaling.
278 #endif
279 
280 public:
282  {
283  int res = 0;
284 #ifdef POSIX
285  res = pthread_cond_init(&cond, 0);
286 #endif
287 
288 #ifdef _WIN32
289  waiters_count_ = 0;
290  was_broadcast_ = false;
291  sema_ = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
292  InitializeCriticalSection(&waiters_count_lock_);
293  waiters_done_ = CreateEvent(NULL, FALSE, FALSE, NULL);
294 
295  if(sema_ == NULL || waiters_done_ == NULL)
296  res = (int)GetLastError();
297 #endif
298  if (res != 0)
299  throw wibble::exception::System(res, "creating pthread condition");
300  }
301 
302  Condition( const Condition & )
303  {
304  int res = 0;
305 #ifdef POSIX
306  res = pthread_cond_init(&cond, 0);
307 #endif
308 
309 #ifdef _WIN32
310  waiters_count_ = 0;
311  was_broadcast_ = false;
312  sema_ = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
313  InitializeCriticalSection(&waiters_count_lock_);
314  waiters_done_ = CreateEvent(NULL, FALSE, FALSE, NULL);
315 
316  if(sema_ == NULL || waiters_done_ == NULL)
317  res = (int)GetLastError();
318 #endif
319  if (res != 0)
320  throw wibble::exception::System(res, "creating pthread condition");
321  }
322 
324  {
325  int res = 0;
326 #ifdef POSIX
327  res = pthread_cond_destroy(&cond);
328 #endif
329 
330 #ifdef _WIN32
331  DeleteCriticalSection(&waiters_count_lock_);
332  if(!CloseHandle(sema_) || !CloseHandle(waiters_done_))
333  res = (int)GetLastError();
334 #endif
335  if (res != 0)
336  throw wibble::exception::System(res, "destroying pthread condition");
337  }
338 
340  void signal()
341  {
342  int res = 0;
343 #ifdef POSIX
344  res = pthread_cond_signal(&cond);
345 #endif
346 
347 #ifdef _WIN32
348  EnterCriticalSection(&waiters_count_lock_);
349  bool have_waiters = waiters_count_ > 0;
350  LeaveCriticalSection(&waiters_count_lock_);
351 
352  // if there aren't any waiters, then this is a no-op
353  if(have_waiters && !ReleaseSemaphore(sema_, 1, 0))
354  res = (int)GetLastError();
355 #endif
356  if (res != 0)
357  throw wibble::exception::System(res, "signaling on a pthread condition");
358  }
359 
361  void broadcast()
362  {
363  int res = 0;
364 #ifdef POSIX
365  res = pthread_cond_broadcast(&cond);
366 #endif
367 
368 #ifdef _WIN32
369  for(bool once = true; once; once = false)
370  {
371  EnterCriticalSection(&waiters_count_lock_);
372  bool have_waiters = false;
373 
374  if(waiters_count_ > 0) {
375  was_broadcast_ = true;
376  have_waiters = true;
377  }
378 
379  if(have_waiters) {
380  if(!ReleaseSemaphore(sema_, waiters_count_, 0)) {
381  res = (int)GetLastError();
382  break;
383  }
384  LeaveCriticalSection(&waiters_count_lock_);
385  if(WaitForSingleObject(waiters_done_, INFINITE) != WAIT_OBJECT_0) {
386  res = (int)GetLastError();
387  break;
388  }
389  was_broadcast_ = false;
390  }
391  else
392  LeaveCriticalSection(&waiters_count_lock_);
393  }
394 #endif
395  if (res != 0)
396  throw wibble::exception::System(res, "broadcasting on a pthread condition");
397  }
398 
403  void wait(MutexLock& l)
404  {
405  int res = 0;
406 #ifdef POSIX
407  res = pthread_cond_wait(&cond, &l.mutex.mutex);
408 #endif
409 
410 #ifdef _WIN32
411  for(bool once = true; once; once = false)
412  {
413  EnterCriticalSection (&waiters_count_lock_);
414  waiters_count_++;
415  LeaveCriticalSection (&waiters_count_lock_);
416 
417  if(SignalObjectAndWait(l.mutex.mutex, sema_, INFINITE, FALSE) != WAIT_OBJECT_0) {
418  res = (int)GetLastError();
419  break;
420  }
421 
422  EnterCriticalSection (&waiters_count_lock_);
423  waiters_count_--;
424  bool last_waiter = was_broadcast_ && waiters_count_ == 0;
425  LeaveCriticalSection (&waiters_count_lock_);
426 
427  if (last_waiter) {
428  if(SignalObjectAndWait (waiters_done_, l.mutex.mutex, INFINITE, FALSE) != WAIT_OBJECT_0)
429  {
430  res = (int)GetLastError();
431  break;
432  }
433  }
434  else {
435  if(WaitForSingleObject (l.mutex.mutex, INFINITE) != WAIT_OBJECT_0)
436  {
437  res = (int)GetLastError();
438  break;
439  }
440  }
441  }
442 #endif
443  if (res != 0)
444  throw wibble::exception::System(res, "waiting on a pthread condition");
445  }
446 
447  void wait(Mutex& l)
448  {
449  int res = 0;
450 #ifdef POSIX
451  res = pthread_cond_wait(&cond, &l.mutex);
452 #endif
453 
454 #ifdef _WIN32
455  for(bool once = true; once; once = false)
456  {
457  if(WaitForSingleObject(l.mutex, 0) == WAIT_OBJECT_0) {
458  l.singlylocking = true;
459  while(ReleaseMutex(l.mutex)) ;
460  if ((res = ((int)GetLastError() != 288))) //288 -> MUTEX_NOT_OWNED
461  break;
462  }
463  if(WaitForSingleObject(l.mutex, INFINITE) != WAIT_OBJECT_0) {
464  res = (int)GetLastError();
465  break;
466  }
467  l.singlylocking = false;
468 
469  EnterCriticalSection (&waiters_count_lock_);
470  waiters_count_++;
471  LeaveCriticalSection (&waiters_count_lock_);
472 
473  if(SignalObjectAndWait(l.mutex, sema_, INFINITE, FALSE) != WAIT_OBJECT_0) {
474  res = (int)GetLastError();
475  break;
476  }
477 
478  EnterCriticalSection (&waiters_count_lock_);
479  waiters_count_--;
480  bool last_waiter = was_broadcast_ && waiters_count_ == 0;
481  LeaveCriticalSection (&waiters_count_lock_);
482 
483  if(last_waiter) {
484  if(SignalObjectAndWait (waiters_done_, l.mutex, INFINITE, FALSE) != WAIT_OBJECT_0) {
485  res = (int)GetLastError();
486  break;
487  }
488  }
489  else {
490  if(WaitForSingleObject(l.mutex, INFINITE) != WAIT_OBJECT_0) {
491  res = (int)GetLastError();
492  break;
493  }
494  }
495  }
496 #endif
497  if (res != 0)
498  throw wibble::exception::System(res, "waiting on a pthread condition");
499  }
500 
501 #ifdef POSIX
502 
512  bool wait(MutexLock& l, const struct timespec& abstime);
513 #endif
514 };
515 
516 }
517 }
518 
519 // vim:set ts=4 sw=4:
520 #endif
bool trylock()
Definition: mutex.h:126
void lock()
Lock the mutex Normally it&#39;s better to use MutexLock.
Definition: mutex.h:150
void reinit()
Reinitialize the mutex.
Definition: mutex.h:185
void drop()
Definition: mutex.h:224
void broadcast()
Wake up all processes waiting on the condition.
Definition: mutex.h:361
void wait(MutexLock &l)
Wait on the condition, locking with l.
Definition: mutex.h:403
Acquire a mutex lock, RAII-style.
Definition: mutex.h:200
~MutexLockT()
Definition: mutex.h:217
bool locked
Definition: mutex.h:209
MutexLockT< Mutex > MutexLock
Definition: mutex.h:249
pthread mutex wrapper; WARNING: the class allows copying and assignment, but this is not always safe...
Definition: mutex.h:47
void checkYield()
Definition: mutex.h:234
Mutex & mutex
Definition: mutex.h:208
~Condition()
Definition: mutex.h:323
Condition()
Definition: mutex.h:281
void setYield(bool y)
Definition: mutex.h:230
void wait(Mutex &l)
Definition: mutex.h:447
Mutex(const Mutex &)
Definition: mutex.h:91
void reclaim()
Definition: mutex.h:229
Definition: mutex.h:259
void unlock()
Unlock the mutex Normally it&#39;s better to use MutexLock.
Definition: mutex.h:169
MutexLockT(Mutex &m)
Definition: mutex.h:212
Definition: amorph.h:17
bool yield
Definition: mutex.h:210
~Mutex()
Definition: mutex.h:111
Mutex(bool recursive=false)
Definition: mutex.h:60
void signal()
Wake up one process waiting on the condition.
Definition: mutex.h:340
Base class for system exceptions.
Definition: exception.h:396
Condition(const Condition &)
Definition: mutex.h:302