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

Generated on Tue Feb 22 13:32:26 2011 for Fawkes API by  doxygen 1.4.7