Fawkes API
Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * wait_condition.cpp - condition variable implementation 00004 * 00005 * Created: Thu Sep 14 21:43:30 2006 00006 * Copyright 2006-2009 Tim Niemueller [www.niemueller.de] 00007 * 00008 ****************************************************************************/ 00009 00010 /* This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. A runtime exception applies to 00014 * this software (see LICENSE.GPL_WRE file mentioned below for details). 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU Library General Public License for more details. 00020 * 00021 * Read the full text in the LICENSE.GPL_WRE file in the doc directory. 00022 */ 00023 00024 #include <core/threading/wait_condition.h> 00025 #include <core/threading/mutex.h> 00026 #include <core/threading/mutex_data.h> 00027 #include <core/exception.h> 00028 00029 #include <pthread.h> 00030 #include <cerrno> 00031 #if defined(__MACH__) && defined(__APPLE__) 00032 # include <sys/time.h> 00033 #endif 00034 00035 namespace fawkes { 00036 00037 /// @cond INTERNALS 00038 class WaitConditionData 00039 { 00040 public: 00041 pthread_cond_t cond; 00042 }; 00043 /// @endcond 00044 00045 00046 /** @class WaitCondition <core/threading/wait_condition.h> 00047 * Wait until a given condition holds. 00048 * Consider two values x and y and you want to wait until they are equal. 00049 * For instance there may be a thread counting up after he has finished one 00050 * particular job before he goes to handle the next one. After 10 threads you 00051 * want to send out the produced entities in one batch run. So the sending 00052 * thread has to wait for the producing thread until 10 packages have been 00053 * produced. Simplified this could be implemented as 00054 * 00055 * @code 00056 * virtual void run() 00057 * { 00058 * forever { 00059 * mutex->lock(); 00060 * while (count != 10) { 00061 * wait_condition->wait(); 00062 * } 00063 * } 00064 * } 00065 * @endcode 00066 * 00067 * The other thread will wake up this waiting thread after each produced 00068 * package (the thread does not have to know after how many packages they are 00069 * sent out). The code could look like this: 00070 * 00071 * @code 00072 * virtual void run() 00073 * { 00074 * forever { 00075 * produce_package(); 00076 * wait_condition->wake_one(); 00077 * } 00078 * } 00079 * @endcode 00080 * 00081 * The WaitCondition can operate in two principal modes, either with an internal 00082 * or with an external Mutex. If no mutex is passed to the constructor an 00083 * internal mutex is created and used. If a mutex is passed this instance is used, 00084 * but ownership is not claimed and you have to delete it manually. Additionally, 00085 * for external mutexes they are <i>never</i> locked by the wait condition. For 00086 * external mutexes you get all the freedom, but also have the duty to ensure 00087 * proper locking from the outside! This applies to wait and wake methods. 00088 * 00089 * @ingroup Threading 00090 * @ingroup FCL 00091 * @see Mutex 00092 * @see qa_waitcond_serialize.cpp 00093 * @see qa_waitcond.cpp 00094 * 00095 * @author Tim Niemueller 00096 * 00097 */ 00098 00099 00100 /** Constructor. 00101 * @param mutex the mutex used for this wait condition. If none is given, an 00102 * internal mutex will be created and used. 00103 */ 00104 WaitCondition::WaitCondition(Mutex *mutex) 00105 { 00106 __cond_data = new WaitConditionData(); 00107 pthread_cond_init( &(__cond_data->cond), NULL); 00108 if (mutex) { 00109 __mutex = mutex; 00110 __own_mutex = false; 00111 } else { 00112 __mutex = new Mutex(); 00113 __own_mutex = true; 00114 } 00115 } 00116 00117 00118 /** Destructor. */ 00119 WaitCondition::~WaitCondition() 00120 { 00121 pthread_cond_destroy( &(__cond_data->cond) ); 00122 delete __cond_data; 00123 if (__own_mutex) { 00124 delete __mutex; 00125 } 00126 } 00127 00128 00129 /** Wait for the condition forever. 00130 * This waits forever until a wakup signal is received by another thread calling 00131 * wake_all() or wake_one(). If an external mutex is used it must be locked or 00132 * before calling wait() or the result is undefined. After the method returns 00133 * the mutex is locked again. 00134 */ 00135 void 00136 WaitCondition::wait() 00137 { 00138 int err; 00139 if ( __own_mutex) { 00140 __mutex->lock(); 00141 err = pthread_cond_wait( &(__cond_data->cond), &(__mutex->mutex_data->mutex) ); 00142 __mutex->unlock(); 00143 } else { 00144 err = pthread_cond_wait( &(__cond_data->cond), &(__mutex->mutex_data->mutex) ); 00145 } 00146 if ( err != 0 ) { 00147 throw Exception(err, "Waiting for wait condition failed"); 00148 } 00149 } 00150 00151 00152 /** Wait with absolute timeout. 00153 * This waits for the given mutex until either a wakup signal is received or 00154 * the timeout has passed. The timeout has to be given in absolute system time, 00155 * a simulated clock source cannot be used. 00156 * @param sec Seconds of absolute time since the epoch (value compatible to 00157 * timeval tv_sec part is sufficient). 00158 * @param nanosec Nanoseconds part of the absolute timeout. Added to the seconds 00159 * part. 00160 * @return true, if the thread was woken up by another thread calling 00161 * wake_one() or wake_all(), false otherwise if the timeout has been reached 00162 * @exception Exception thrown if another error occurs for the POSIX wait condition 00163 */ 00164 bool 00165 WaitCondition::abstimed_wait(long int sec, long int nanosec) 00166 { 00167 int err = 0; 00168 struct timespec ts = { sec, nanosec }; 00169 00170 if ( __own_mutex) { 00171 __mutex->lock(); 00172 err = pthread_cond_timedwait( &(__cond_data->cond), &(__mutex->mutex_data->mutex), &ts ); 00173 __mutex->unlock(); 00174 } else { 00175 err = pthread_cond_timedwait( &(__cond_data->cond), &(__mutex->mutex_data->mutex), &ts ); 00176 } 00177 00178 if ( err == ETIMEDOUT ) { 00179 return false; 00180 } else if ( err != 0 ) { 00181 // some other error happened, a "real" error 00182 throw Exception(err, "Waiting for wait condition failed"); 00183 } else { 00184 return true; 00185 } 00186 } 00187 00188 00189 /** Wait with relative timeout. 00190 * This waits for the given mutex until either a wakup signal is received or 00191 * the timeout has passed. The timeout has to be given in relative system time. 00192 * It is added to the current time and is then used similar to abstime_wait(). 00193 * A timeout of (0,0) will cause this method to wait forever, similar to wait(). 00194 * @param sec Number of seconds to wait 00195 * @param nanosec Number of nanoseconds to wait, added to seconds value 00196 * @return true, if the thread was woken up by another thread calling 00197 * wake_one() or wake_all(), false otherwise if the timeout has been reached 00198 * @exception Exception thrown if another error occurs for the POSIX wait condition 00199 */ 00200 bool 00201 WaitCondition::reltimed_wait(unsigned int sec, unsigned int nanosec) 00202 { 00203 if ( ! (sec || nanosec) ) { 00204 wait(); 00205 return true; 00206 } else { 00207 struct timespec now; 00208 #if defined(__MACH__) && defined(__APPLE__) 00209 struct timeval nowt; 00210 if ( gettimeofday(&nowt, NULL) != 0 ) { 00211 throw Exception(errno, "WaitCondition::reltimed_wait: Failed to get current time"); 00212 } 00213 now.tv_sec = nowt.tv_sec; 00214 now.tv_nsec = nowt.tv_usec * 1000; 00215 #else 00216 if ( clock_gettime(CLOCK_REALTIME, &now) != 0 ) { 00217 throw Exception(errno, "WaitCondition::reltimed_wait: Failed to get current time"); 00218 } 00219 #endif 00220 00221 long int s = now.tv_sec + sec; 00222 long int ns = now.tv_nsec + nanosec; 00223 if (ns >= 1000000000) { 00224 s += 1; 00225 ns -= 1000000000; 00226 } 00227 00228 struct timespec ts = { s, ns }; 00229 long err = 0; 00230 00231 if ( __own_mutex) { 00232 __mutex->lock(); 00233 err = pthread_cond_timedwait( &(__cond_data->cond), &(__mutex->mutex_data->mutex), &ts ); 00234 __mutex->unlock(); 00235 } else { 00236 err = pthread_cond_timedwait( &(__cond_data->cond), &(__mutex->mutex_data->mutex), &ts ); 00237 } 00238 00239 if ( err == ETIMEDOUT ) { 00240 return false; 00241 } else if ( err != 0 ) { 00242 // some other error happened, a "real" error 00243 throw Exception(err, "Waiting for wait condition failed"); 00244 } else { 00245 return true; 00246 } 00247 } 00248 } 00249 00250 00251 /** Wake another thread waiting for this condition. 00252 * This wakes up any thread waiting for the condition, not a particular one. 00253 * No guarantee is given about the order of the woken up threads. 00254 * Note: If the internal mutex is used for this wait/wakeup cycle, the lock 00255 * to this mutex will be acquired during the wakeup, to ensure that all waiting 00256 * threads are woken up, even if a call to wait() and wake_one() overlapped. 00257 * If however, an external Mutex is used, you must ensure by yourself that it 00258 * is properly locked during the wakeup to ensure this. 00259 */ 00260 void 00261 WaitCondition::wake_one() 00262 { 00263 if (__own_mutex) { // it's our internal mutex, lock! 00264 __mutex->lock(); 00265 pthread_cond_signal( &(__cond_data->cond) ); 00266 __mutex->unlock(); 00267 } else { // it's an external mutex, the user should care 00268 pthread_cond_signal( &(__cond_data->cond) ); 00269 } 00270 } 00271 00272 00273 /** Wake up all waiting threads. 00274 * This wakes up all threads waiting for this condition. 00275 * Note: If the internal mutex is used for this wait/wakeup cycle, the lock 00276 * to this mutex will be acquired during the wakeup, to ensure that all waiting 00277 * threads are woken up, even if a call to wait() and wake_one() overlapped. 00278 * If however, an external Mutex is used, you must ensure by yourself that it 00279 * is properly locked during the wakeup to ensure this. 00280 */ 00281 void 00282 WaitCondition::wake_all() 00283 { 00284 if (__own_mutex) { // it's our internal mutex, lock! 00285 __mutex->lock(); 00286 pthread_cond_broadcast( &(__cond_data->cond) ); 00287 __mutex->unlock(); 00288 } else { // it's an external mutex, the user should care 00289 pthread_cond_broadcast( &(__cond_data->cond) ); 00290 } 00291 } 00292 00293 00294 } // end namespace fawkes