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