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/exception.h>
25 #include <core/threading/mutex.h>
26 #include <core/threading/mutex_data.h>
27 #include <core/threading/wait_condition.h>
28 
29 #include <cerrno>
30 #include <pthread.h>
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 /** @class WaitCondition <core/threading/wait_condition.h>
53  * Wait until a given condition holds.
54  * Consider two values x and y and you want to wait until they are equal.
55  * For instance there may be a thread counting up after he has finished one
56  * particular job before he goes to handle the next one. After 10 threads you
57  * want to send out the produced entities in one batch run. So the sending
58  * thread has to wait for the producing thread until 10 packages have been
59  * produced. Simplified this could be implemented as
60  *
61  * @code
62  * virtual void run()
63  * {
64  * forever {
65  * mutex->lock();
66  * while (count != 10) {
67  * wait_condition->wait();
68  * }
69  * }
70  * }
71  * @endcode
72  *
73  * The other thread will wake up this waiting thread after each produced
74  * package (the thread does not have to know after how many packages they are
75  * sent out). The code could look like this:
76  *
77  * @code
78  * virtual void run()
79  * {
80  * forever {
81  * produce_package();
82  * wait_condition->wake_one();
83  * }
84  * }
85  * @endcode
86  *
87  * The WaitCondition can operate in two principal modes, either with an internal
88  * or with an external Mutex. If no mutex is passed to the constructor an
89  * internal mutex is created and used. If a mutex is passed this instance is used,
90  * but ownership is not claimed and you have to delete it manually. Additionally,
91  * for external mutexes they are <i>never</i> locked by the wait condition. For
92  * external mutexes you get all the freedom, but also have the duty to ensure
93  * proper locking from the outside! This applies to wait and wake methods.
94  *
95  * @ingroup Threading
96  * @ingroup FCL
97  * @see Mutex
98  * @see qa_waitcond_serialize.cpp
99  * @see qa_waitcond.cpp
100  *
101  * @author Tim Niemueller
102  *
103  */
104 
105 /** Constructor.
106  * @param mutex the mutex used for this wait condition. If none is given, an
107  * internal mutex will be created and used.
108  */
109 WaitCondition::WaitCondition(Mutex *mutex)
110 {
111  cond_data_ = new WaitConditionData();
112  pthread_cond_init(&(cond_data_->cond), NULL);
113  if (mutex) {
114  mutex_ = mutex;
115  own_mutex_ = false;
116  } else {
117  mutex_ = new Mutex();
118  own_mutex_ = true;
119  }
120 }
121 
122 /** Destructor. */
124 {
125  pthread_cond_destroy(&(cond_data_->cond));
126  delete cond_data_;
127  if (own_mutex_) {
128  delete mutex_;
129  }
130 }
131 
132 /** Wait for the condition forever.
133  * This waits forever until a wakup signal is received by another thread calling
134  * wake_all() or wake_one(). If an external mutex is used it must be locked or
135  * before calling wait() or the result is undefined. After the method returns
136  * the mutex is locked again.
137  */
138 void
140 {
141  int err;
142  if (own_mutex_) {
143  mutex_->lock();
144  pthread_cleanup_push(cleanup_mutex, mutex_);
145  err = pthread_cond_wait(&(cond_data_->cond), &(mutex_->mutex_data->mutex));
146  mutex_->unlock();
147  pthread_cleanup_pop(0);
148  } else {
149  err = pthread_cond_wait(&(cond_data_->cond), &(mutex_->mutex_data->mutex));
150  }
151  if (err != 0) {
152  throw Exception(err, "Waiting for wait condition failed");
153  }
154 }
155 
156 /** Wait with absolute timeout.
157  * This waits for the given mutex until either a wakup signal is received or
158  * the timeout has passed. The timeout has to be given in absolute system time,
159  * a simulated clock source cannot be used.
160  * @param sec Seconds of absolute time since the epoch (value compatible to
161  * timeval tv_sec part is sufficient).
162  * @param nanosec Nanoseconds part of the absolute timeout. Added to the seconds
163  * part.
164  * @return true, if the thread was woken up by another thread calling
165  * wake_one() or wake_all(), false otherwise if the timeout has been reached
166  * @exception Exception thrown if another error occurs for the POSIX wait condition
167  */
168 bool
169 WaitCondition::abstimed_wait(long int sec, long int nanosec)
170 {
171  int err = 0;
172  struct timespec ts = {sec, nanosec};
173 
174  if (own_mutex_) {
175  mutex_->lock();
176  pthread_cleanup_push(cleanup_mutex, mutex_);
177  err = pthread_cond_timedwait(&(cond_data_->cond), &(mutex_->mutex_data->mutex), &ts);
178  mutex_->unlock();
179  pthread_cleanup_pop(0);
180  } else {
181  err = pthread_cond_timedwait(&(cond_data_->cond), &(mutex_->mutex_data->mutex), &ts);
182  }
183 
184  if (err == ETIMEDOUT) {
185  return false;
186  } else if (err != 0) {
187  // some other error happened, a "real" error
188  throw Exception(err, "Waiting for wait condition failed");
189  } else {
190  return true;
191  }
192 }
193 
194 /** Wait with relative timeout.
195  * This waits for the given mutex until either a wakup signal is received or
196  * the timeout has passed. The timeout has to be given in relative system time.
197  * It is added to the current time and is then used similar to abstime_wait().
198  * A timeout of (0,0) will cause this method to wait forever, similar to wait().
199  * @param sec Number of seconds to wait
200  * @param nanosec Number of nanoseconds to wait, added to seconds value
201  * @return true, if the thread was woken up by another thread calling
202  * wake_one() or wake_all(), false otherwise if the timeout has been reached
203  * @exception Exception thrown if another error occurs for the POSIX wait condition
204  */
205 bool
206 WaitCondition::reltimed_wait(unsigned int sec, unsigned int nanosec)
207 {
208  if (!(sec || nanosec)) {
209  wait();
210  return true;
211  } else {
212  struct timespec now;
213 #if defined(__MACH__) && defined(__APPLE__)
214  struct timeval nowt;
215  if (gettimeofday(&nowt, NULL) != 0) {
216  throw Exception(errno, "WaitCondition::reltimed_wait: Failed to get current time");
217  }
218  now.tv_sec = nowt.tv_sec;
219  now.tv_nsec = nowt.tv_usec * 1000;
220 #else
221  if (clock_gettime(CLOCK_REALTIME, &now) != 0) {
222  throw Exception(errno, "WaitCondition::reltimed_wait: Failed to get current time");
223  }
224 #endif
225 
226  long int s = now.tv_sec + sec;
227  long int ns = now.tv_nsec + nanosec;
228  if (ns >= 1000000000) {
229  s += 1;
230  ns -= 1000000000;
231  }
232 
233  struct timespec ts = {s, ns};
234  long err = 0;
235 
236  if (own_mutex_) {
237  mutex_->lock();
238  pthread_cleanup_push(cleanup_mutex, mutex_);
239  err = pthread_cond_timedwait(&(cond_data_->cond), &(mutex_->mutex_data->mutex), &ts);
240  mutex_->unlock();
241  pthread_cleanup_pop(0);
242  } else {
243  err = pthread_cond_timedwait(&(cond_data_->cond), &(mutex_->mutex_data->mutex), &ts);
244  }
245 
246  if (err == ETIMEDOUT) {
247  return false;
248  } else if (err != 0) {
249  // some other error happened, a "real" error
250  throw Exception(err, "Waiting for wait condition failed");
251  } else {
252  return true;
253  }
254  }
255 }
256 
257 /** Wake another thread waiting for this condition.
258  * This wakes up any thread waiting for the condition, not a particular one.
259  * No guarantee is given about the order of the woken up threads.
260  * Note: If the internal mutex is used for this wait/wakeup cycle, the lock
261  * to this mutex will be acquired during the wakeup, to ensure that all waiting
262  * threads are woken up, even if a call to wait() and wake_one() overlapped.
263  * If however, an external Mutex is used, you must ensure by yourself that it
264  * is properly locked during the wakeup to ensure this.
265  */
266 void
268 {
269  if (own_mutex_) { // it's our internal mutex, lock!
270  mutex_->lock();
271  pthread_cond_signal(&(cond_data_->cond));
272  mutex_->unlock();
273  } else { // it's an external mutex, the user should care
274  pthread_cond_signal(&(cond_data_->cond));
275  }
276 }
277 
278 /** Wake up all waiting threads.
279  * This wakes up all threads waiting for this condition.
280  * Note: If the internal mutex is used for this wait/wakeup cycle, the lock
281  * to this mutex will be acquired during the wakeup, to ensure that all waiting
282  * threads are woken up, even if a call to wait() and wake_one() overlapped.
283  * If however, an external Mutex is used, you must ensure by yourself that it
284  * is properly locked during the wakeup to ensure this.
285  */
286 void
288 {
289  if (own_mutex_) { // it's our internal mutex, lock!
290  mutex_->lock();
291  pthread_cond_broadcast(&(cond_data_->cond));
292  mutex_->unlock();
293  } else { // it's an external mutex, the user should care
294  pthread_cond_broadcast(&(cond_data_->cond));
295  }
296 }
297 
298 } // end namespace fawkes
fawkes::Mutex::lock
void lock()
Lock this mutex.
Definition: mutex.cpp:93
fawkes::Mutex
Definition: mutex.h:38
fawkes::Mutex::unlock
void unlock()
Unlock the mutex.
Definition: mutex.cpp:137
fawkes::WaitCondition::wait
void wait()
Wait for the condition forever.
Definition: wait_condition.cpp:145
fawkes::WaitCondition::abstimed_wait
bool abstimed_wait(long int sec, long int nanosec)
Wait with absolute timeout.
Definition: wait_condition.cpp:175
fawkes::WaitCondition::~WaitCondition
~WaitCondition()
Destructor.
Definition: wait_condition.cpp:129
fawkes
fawkes::WaitCondition::reltimed_wait
bool reltimed_wait(unsigned int sec, unsigned int nanosec)
Wait with relative timeout.
Definition: wait_condition.cpp:212
fawkes::WaitCondition::wake_all
void wake_all()
Wake up all waiting threads.
Definition: wait_condition.cpp:293
fawkes::WaitCondition::wake_one
void wake_one()
Wake another thread waiting for this condition.
Definition: wait_condition.cpp:273
fawkes::WaitCondition::WaitCondition
WaitCondition(Mutex *mutex=0)
Constructor.
Definition: wait_condition.cpp:115
fawkes::Exception
Definition: exception.h:41