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
35namespace fawkes {
36
37/// @cond INTERNALS
38class WaitConditionData
39{
40public:
41 pthread_cond_t cond;
42};
43
44void
45cleanup_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 */
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 */
138void
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 */
168bool
169WaitCondition::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 */
205bool
206WaitCondition::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 */
266void
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 */
286void
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
Base class for exceptions in Fawkes.
Definition: exception.h:36
Mutex mutual exclusion lock.
Definition: mutex.h:33
void lock()
Lock this mutex.
Definition: mutex.cpp:87
void unlock()
Unlock the mutex.
Definition: mutex.cpp:131
void wait()
Wait for the condition forever.
void wake_one()
Wake another thread waiting for this condition.
WaitCondition(Mutex *mutex=0)
Constructor.
void wake_all()
Wake up all waiting threads.
bool reltimed_wait(unsigned int sec, unsigned int nanosec)
Wait with relative timeout.
bool abstimed_wait(long int sec, long int nanosec)
Wait with absolute timeout.
Fawkes library namespace.