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