wait_condition.cpp

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

Generated on Tue Feb 22 13:31:25 2011 for Fawkes API by  doxygen 1.4.7