Fawkes API  Fawkes Development Version
wait_condition.cpp
1 
2 /***************************************************************************
3  * wait_condition.cpp - condition variable implementation
4  *
5  * Created: Thu Sep 14 21:43:30 2006
6  * Copyright 2006-2009 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include <core/threading/wait_condition.h>
25 #include <core/threading/mutex.h>
26 #include <core/threading/mutex_data.h>
27 #include <core/exception.h>
28 
29 #include <pthread.h>
30 #include <cerrno>
31 #if defined(__MACH__) && defined(__APPLE__)
32 # include <sys/time.h>
33 #endif
34 
35 namespace fawkes {
36 
37 /// @cond INTERNALS
38 class WaitConditionData
39 {
40  public:
41  pthread_cond_t cond;
42 };
43 
44 void
45 cleanup_mutex(void *arg)
46 {
47  Mutex *mutex = (Mutex *) arg;
48  mutex->unlock();
49 }
50 /// @endcond
51 
52 
53 /** @class WaitCondition <core/threading/wait_condition.h>
54  * Wait until a given condition holds.
55  * Consider two values x and y and you want to wait until they are equal.
56  * For instance there may be a thread counting up after he has finished one
57  * particular job before he goes to handle the next one. After 10 threads you
58  * want to send out the produced entities in one batch run. So the sending
59  * thread has to wait for the producing thread until 10 packages have been
60  * produced. Simplified this could be implemented as
61  *
62  * @code
63  * virtual void run()
64  * {
65  * forever {
66  * mutex->lock();
67  * while (count != 10) {
68  * wait_condition->wait();
69  * }
70  * }
71  * }
72  * @endcode
73  *
74  * The other thread will wake up this waiting thread after each produced
75  * package (the thread does not have to know after how many packages they are
76  * sent out). The code could look like this:
77  *
78  * @code
79  * virtual void run()
80  * {
81  * forever {
82  * produce_package();
83  * wait_condition->wake_one();
84  * }
85  * }
86  * @endcode
87  *
88  * The WaitCondition can operate in two principal modes, either with an internal
89  * or with an external Mutex. If no mutex is passed to the constructor an
90  * internal mutex is created and used. If a mutex is passed this instance is used,
91  * but ownership is not claimed and you have to delete it manually. Additionally,
92  * for external mutexes they are <i>never</i> locked by the wait condition. For
93  * external mutexes you get all the freedom, but also have the duty to ensure
94  * proper locking from the outside! This applies to wait and wake methods.
95  *
96  * @ingroup Threading
97  * @ingroup FCL
98  * @see Mutex
99  * @see qa_waitcond_serialize.cpp
100  * @see qa_waitcond.cpp
101  *
102  * @author Tim Niemueller
103  *
104  */
105 
106 
107 /** Constructor.
108  * @param mutex the mutex used for this wait condition. If none is given, an
109  * internal mutex will be created and used.
110  */
112 {
113  __cond_data = new WaitConditionData();
114  pthread_cond_init( &(__cond_data->cond), NULL);
115  if (mutex) {
116  __mutex = mutex;
117  __own_mutex = false;
118  } else {
119  __mutex = new Mutex();
120  __own_mutex = true;
121  }
122 }
123 
124 
125 /** Destructor. */
127 {
128  pthread_cond_destroy( &(__cond_data->cond) );
129  delete __cond_data;
130  if (__own_mutex) {
131  delete __mutex;
132  }
133 }
134 
135 
136 /** Wait for the condition forever.
137  * This waits forever until a wakup signal is received by another thread calling
138  * wake_all() or wake_one(). If an external mutex is used it must be locked or
139  * before calling wait() or the result is undefined. After the method returns
140  * the mutex is locked again.
141  */
142 void
144 {
145  int err;
146  if ( __own_mutex) {
147  __mutex->lock();
148  pthread_cleanup_push(cleanup_mutex, __mutex);
149  err = pthread_cond_wait( &(__cond_data->cond), &(__mutex->mutex_data->mutex) );
150  __mutex->unlock();
151  pthread_cleanup_pop(0);
152  } else {
153  err = pthread_cond_wait( &(__cond_data->cond), &(__mutex->mutex_data->mutex) );
154  }
155  if ( err != 0 ) {
156  throw Exception(err, "Waiting for wait condition failed");
157  }
158 }
159 
160 
161 /** Wait with absolute timeout.
162  * This waits for the given mutex until either a wakup signal is received or
163  * the timeout has passed. The timeout has to be given in absolute system time,
164  * a simulated clock source cannot be used.
165  * @param sec Seconds of absolute time since the epoch (value compatible to
166  * timeval tv_sec part is sufficient).
167  * @param nanosec Nanoseconds part of the absolute timeout. Added to the seconds
168  * part.
169  * @return true, if the thread was woken up by another thread calling
170  * wake_one() or wake_all(), false otherwise if the timeout has been reached
171  * @exception Exception thrown if another error occurs for the POSIX wait condition
172  */
173 bool
174 WaitCondition::abstimed_wait(long int sec, long int nanosec)
175 {
176  int err = 0;
177  struct timespec ts = { sec, nanosec };
178 
179  if ( __own_mutex) {
180  __mutex->lock();
181  pthread_cleanup_push(cleanup_mutex, __mutex);
182  err = pthread_cond_timedwait( &(__cond_data->cond), &(__mutex->mutex_data->mutex), &ts );
183  __mutex->unlock();
184  pthread_cleanup_pop(0);
185  } else {
186  err = pthread_cond_timedwait( &(__cond_data->cond), &(__mutex->mutex_data->mutex), &ts );
187  }
188 
189  if ( err == ETIMEDOUT ) {
190  return false;
191  } else if ( err != 0 ) {
192  // some other error happened, a "real" error
193  throw Exception(err, "Waiting for wait condition failed");
194  } else {
195  return true;
196  }
197 }
198 
199 
200 /** Wait with relative timeout.
201  * This waits for the given mutex until either a wakup signal is received or
202  * the timeout has passed. The timeout has to be given in relative system time.
203  * It is added to the current time and is then used similar to abstime_wait().
204  * A timeout of (0,0) will cause this method to wait forever, similar to wait().
205  * @param sec Number of seconds to wait
206  * @param nanosec Number of nanoseconds to wait, added to seconds value
207  * @return true, if the thread was woken up by another thread calling
208  * wake_one() or wake_all(), false otherwise if the timeout has been reached
209  * @exception Exception thrown if another error occurs for the POSIX wait condition
210  */
211 bool
212 WaitCondition::reltimed_wait(unsigned int sec, unsigned int nanosec)
213 {
214  if ( ! (sec || nanosec) ) {
215  wait();
216  return true;
217  } else {
218  struct timespec now;
219 #if defined(__MACH__) && defined(__APPLE__)
220  struct timeval nowt;
221  if ( gettimeofday(&nowt, NULL) != 0 ) {
222  throw Exception(errno, "WaitCondition::reltimed_wait: Failed to get current time");
223  }
224  now.tv_sec = nowt.tv_sec;
225  now.tv_nsec = nowt.tv_usec * 1000;
226 #else
227  if ( clock_gettime(CLOCK_REALTIME, &now) != 0 ) {
228  throw Exception(errno, "WaitCondition::reltimed_wait: Failed to get current time");
229  }
230 #endif
231 
232  long int s = now.tv_sec + sec;
233  long int ns = now.tv_nsec + nanosec;
234  if (ns >= 1000000000) {
235  s += 1;
236  ns -= 1000000000;
237  }
238 
239  struct timespec ts = { s, ns };
240  long err = 0;
241 
242  if ( __own_mutex) {
243  __mutex->lock();
244  pthread_cleanup_push(cleanup_mutex, __mutex);
245  err = pthread_cond_timedwait( &(__cond_data->cond), &(__mutex->mutex_data->mutex), &ts );
246  __mutex->unlock();
247  pthread_cleanup_pop(0);
248  } else {
249  err = pthread_cond_timedwait( &(__cond_data->cond), &(__mutex->mutex_data->mutex), &ts );
250  }
251 
252  if ( err == ETIMEDOUT ) {
253  return false;
254  } else if ( err != 0 ) {
255  // some other error happened, a "real" error
256  throw Exception(err, "Waiting for wait condition failed");
257  } else {
258  return true;
259  }
260  }
261 }
262 
263 
264 /** Wake another thread waiting for this condition.
265  * This wakes up any thread waiting for the condition, not a particular one.
266  * No guarantee is given about the order of the woken up threads.
267  * Note: If the internal mutex is used for this wait/wakeup cycle, the lock
268  * to this mutex will be acquired during the wakeup, to ensure that all waiting
269  * threads are woken up, even if a call to wait() and wake_one() overlapped.
270  * If however, an external Mutex is used, you must ensure by yourself that it
271  * is properly locked during the wakeup to ensure this.
272  */
273 void
275 {
276  if (__own_mutex) { // it's our internal mutex, lock!
277  __mutex->lock();
278  pthread_cond_signal( &(__cond_data->cond) );
279  __mutex->unlock();
280  } else { // it's an external mutex, the user should care
281  pthread_cond_signal( &(__cond_data->cond) );
282  }
283 }
284 
285 
286 /** Wake up all waiting threads.
287  * This wakes up all threads waiting for this condition.
288  * Note: If the internal mutex is used for this wait/wakeup cycle, the lock
289  * to this mutex will be acquired during the wakeup, to ensure that all waiting
290  * threads are woken up, even if a call to wait() and wake_one() overlapped.
291  * If however, an external Mutex is used, you must ensure by yourself that it
292  * is properly locked during the wakeup to ensure this.
293  */
294 void
296 {
297  if (__own_mutex) { // it's our internal mutex, lock!
298  __mutex->lock();
299  pthread_cond_broadcast( &(__cond_data->cond) );
300  __mutex->unlock();
301  } else { // it's an external mutex, the user should care
302  pthread_cond_broadcast( &(__cond_data->cond) );
303  }
304 }
305 
306 } // end namespace fawkes
bool reltimed_wait(unsigned int sec, unsigned int nanosec)
Wait with relative timeout.
WaitCondition(Mutex *mutex=0)
Constructor.
~WaitCondition()
Destructor.
Fawkes library namespace.
void wake_all()
Wake up all waiting threads.
void wake_one()
Wake another thread waiting for this condition.
Base class for exceptions in Fawkes.
Definition: exception.h:36
void wait()
Wait for the condition forever.
Mutex mutual exclusion lock.
Definition: mutex.h:32
bool abstimed_wait(long int sec, long int nanosec)
Wait with absolute timeout.