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

