barrier.cpp

00001
00002 /***************************************************************************
00003  *  barrier.cpp - Barrier
00004  *
00005  *  Created: Thu Sep 15 00:33:13 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/barrier.h>
00025 #include <core/exception.h>
00026
00027 #include <pthread.h>
00028 #include <unistd.h>
00029
00030 // cf. http://people.redhat.com/drepper/posix-option-groups.html
00031 #if defined(_POSIX_BARRIERS) && (_POSIX_BARRIERS - 200112L) >= 0
00032 #  define USE_POSIX_BARRIERS
00033 #else
00034 #  undef USE_POSIX_BARRIERS
00035 #  include <core/threading/mutex.h>
00036 #  include <core/threading/wait_condition.h>
00037 #endif
00038 
00039 namespace fawkes {
00040
00041 
00042 /// @cond INTERNALS
00043 class BarrierData
00044 {
00045  public:
00046 #ifdef USE_POSIX_BARRIERS
00047   pthread_barrier_t barrier;
00048 #else
00049   unsigned int  threads_left;
00050   Mutex         mutex;
00051   WaitCondition waitcond;
00052 #endif
00053 };
00054 /// @endcond
00055 
00056 
00057 /** @class Barrier core/threading/barrier.h
00058  * A barrier is a synchronization tool which blocks until a given number of
00059  * threads have reached the barrier.
00060  *
00061  * Consider you have three threads all doing work. In the end you have to merge
00062  * their results. For this you need a point where all threads are finished. Of
00063  * course you don't want another thread waiting and periodically checking if
00064  * the other threads are finished, that will loose time - processing time and
00065  * the time that passed between the threads being finished and the time when
00066  * the controller checked again.
00067  *
00068  * For this problem we have a barrier. You initialize the barrier with the
00069  * number of threads to wait for and then wait in all threads at a specific
00070  * point. After this one of the threads can merge the result (and the others
00071  * may wait for another "go-on barrier".
00072  *
00073  * The code in the thread would be
00074  * @code
00075  * virtual void run()
00076  * {
00077  *   forever {
00078  *     do_something();
00079  *     result_barrier->wait();
00080  *     if ( master_thread ) {
00081  *       merge_results();
00082  *     }
00083  *     goon_barrier->wait();
00084  *   }
00085  * }
00086  * @endcode
00087  *
00088  * The threads would all get a reference to the same barriers and one would
00089  * have master thread to be true.
00090  *
00091  * After the barrier has been passed (count threads waited) the barrier is
00092  * reset automatically and can be re-used with the same amount of barrier
00093  * waiters.
00094  *
00095  * The implementation of Barrier takes into account that PThread barriers are
00096  * optional in POSIX. If barriers are not available they are emulated using a
00097  * wait condition. Note however that on systems that do have both, barriers and
00098  * wait conditions it has been observed that in a typical barrier scenario the
00099  * POSIX barriers perform much better in many situations than a wait condition
00100  * (processes tend to be rescheduled immediately if a barrier is reached, while
00101  * with a wait condition they are rescheduled with lower priority and thus they
00102  * delay may increase on a loaded system). Because of this on systems without
00103  * real POSIX barriers the performance may be not as good as is expected.
00104  *
00105  * @ingroup Threading
00106  * @author Tim Niemueller
00107  */
00108
00109 
00110 /** Constructor
00111  * @param count the number of threads to wait for
00112  */
00113 Barrier::Barrier(unsigned int count)
00114 {
00115   _count = count;
00116   if ( _count == 0 ) {
00117     throw Exception("Barrier count must be at least 1");
00118   }
00119   barrier_data = new BarrierData();
00120 #ifdef USE_POSIX_BARRIERS
00121   pthread_barrier_init( &(barrier_data->barrier), NULL, _count );
00122 #else
00123   barrier_data->threads_left = _count;
00124 #endif
00125 }
00126
00127 
00128 /** Protected Constructor.
00129  * Does not initialize any internal structures other than setting them to 0.
00130  */
00131 Barrier::Barrier()
00132 {
00133   _count = 0;
00134   barrier_data = NULL;
00135 }
00136
00137 
00138 /** Destructor */
00139 Barrier::~Barrier()
00140 {
00141   if (barrier_data) {
00142 #ifdef USE_POSIX_BARRIERS
00143     pthread_barrier_destroy( &(barrier_data->barrier) );
00144 #endif
00145     delete barrier_data;
00146   }
00147 }
00148
00149 
00150 /** Wait for other threads.
00151  * This method will block until as many threads have called wait as you have
00152  * given count to the constructor.
00153  */
00154 void
00155 Barrier::wait()
00156 {
00157 #ifdef USE_POSIX_BARRIERS
00158   pthread_barrier_wait( &(barrier_data->barrier) );
00159 #else
00160   barrier_data->mutex.lock();
00161
00162   if ( --barrier_data->threads_left == 0 ) {
00163     barrier_data->threads_left = _count;
00164     barrier_data->mutex.unlock();
00165     barrier_data->waitcond.wake_all();
00166   } else {
00167     barrier_data->waitcond.wait(&(barrier_data->mutex));
00168     barrier_data->mutex.unlock();
00169   }
00170
00171 #endif
00172 }
00173
00174 
00175 /** Get number of threads this barrier will wait for.
00176  * @return number of threads this barrier will wait for.
00177  */
00178 unsigned int
00179 Barrier::count()
00180 {
00181   return _count;
00182 }
00183
00184
00185 } // end namespace fawkes