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