00001 /******************************************************************************* 00002 * Copyright (C) 2004 Vintela, Inc. All rights reserved. 00003 * Copyright (C) 2005 Novell, Inc. All rights reserved. 00004 * 00005 * Redistribution and use in source and binary forms, with or without 00006 * modification, are permitted provided that the following conditions are met: 00007 * 00008 * - Redistributions of source code must retain the above copyright notice, 00009 * this list of conditions and the following disclaimer. 00010 * 00011 * - Redistributions in binary form must reproduce the above copyright notice, 00012 * this list of conditions and the following disclaimer in the documentation 00013 * and/or other materials provided with the distribution. 00014 * 00015 * - Neither the name of Vintela, Inc., Novell, Inc., nor the names of its 00016 * contributors may be used to endorse or promote products derived from this 00017 * software without specific prior written permission. 00018 * 00019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 00020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00021 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00022 * ARE DISCLAIMED. IN NO EVENT SHALL Vintela, Inc., Novell, Inc., OR THE 00023 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00024 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 00025 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 00026 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 00027 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 00028 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 00029 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00030 *******************************************************************************/ 00031 00032 00037 #include "blocxx/BLOCXX_config.h" 00038 #include "blocxx/ThreadBarrier.hpp" 00039 #include "blocxx/Assertion.hpp" 00040 #include "blocxx/Format.hpp" 00041 #include "blocxx/ExceptionIds.hpp" 00042 #include "blocxx/IntrusiveCountableBase.hpp" 00043 00044 #if defined(BLOCXX_USE_PTHREAD) && defined(BLOCXX_HAVE_PTHREAD_BARRIER) && !defined(BLOCXX_VALGRIND_SUPPORT) 00045 #include <pthread.h> 00046 #else 00047 // This is for the generic less-efficient version 00048 #include "blocxx/Condition.hpp" 00049 #include "blocxx/NonRecursiveMutex.hpp" 00050 #include "blocxx/NonRecursiveMutexLock.hpp" 00051 #endif 00052 00054 namespace BLOCXX_NAMESPACE 00055 { 00056 00057 BLOCXX_DEFINE_EXCEPTION_WITH_ID(ThreadBarrier); 00058 00059 #if defined(BLOCXX_USE_PTHREAD) && defined(BLOCXX_HAVE_PTHREAD_BARRIER) && !defined(BLOCXX_VALGRIND_SUPPORT) // valgrind doesn't support pthread_barrier_*() 00060 class ThreadBarrierImpl : public IntrusiveCountableBase 00061 { 00062 public: 00063 ThreadBarrierImpl(UInt32 threshold) 00064 { 00065 BLOCXX_ASSERT(threshold != 0); 00066 int res = pthread_barrier_init(&barrier, NULL, threshold); 00067 if (res != 0) 00068 { 00069 BLOCXX_THROW(ThreadBarrierException, Format("pthread_barrier_init failed: %1(%2)", res, strerror(res)).c_str()); 00070 } 00071 } 00072 ~ThreadBarrierImpl() 00073 { 00074 int res = pthread_barrier_destroy(&barrier); 00075 if (res != 0) 00076 { 00077 // can't throw... just log it or something... 00078 } 00079 } 00080 00081 void wait() 00082 { 00083 int res = pthread_barrier_wait(&barrier); 00084 if (res != 0 && res != PTHREAD_BARRIER_SERIAL_THREAD) 00085 { 00086 BLOCXX_THROW(ThreadBarrierException, Format("pthread_barrier_wait failed: %1(%2)", res, strerror(res)).c_str()); 00087 } 00088 } 00089 private: 00090 pthread_barrier_t barrier; 00091 }; 00092 00093 #else 00094 00095 // This is the generic less-efficient version 00096 00097 class ThreadBarrierImpl : public IntrusiveCountableBase 00098 { 00099 public: 00110 struct SubBarrier 00111 { 00112 SubBarrier() : m_waitingCount(0) {} 00114 UInt32 m_waitingCount; 00116 Condition m_cond; 00117 }; 00118 ThreadBarrierImpl(UInt32 threshold) 00119 : m_threshold(threshold) 00120 , m_curSubBarrier(0) 00121 { 00122 } 00123 void wait() 00124 { 00125 NonRecursiveMutexLock l(m_mutex); 00126 // Select the current SubBarrier 00127 SubBarrier& curBarrier = m_curSubBarrier?m_subBarrier0:m_subBarrier1; 00128 ++curBarrier.m_waitingCount; 00129 if (curBarrier.m_waitingCount == m_threshold) 00130 { 00131 // reset the sub barrier so it can be reused 00132 curBarrier.m_waitingCount = 0; 00133 // swap current barriers 00134 m_curSubBarrier = 1 - m_curSubBarrier; 00135 // now wake up all the threads that were stopped 00136 curBarrier.m_cond.notifyAll(); 00137 } 00138 else 00139 { 00140 // because of spurious wake-ups we need to put this in a loop. 00141 // we need to wait until the count is 0, which will only happen 00142 // once m_threshold threads have called wait() 00143 while (curBarrier.m_waitingCount != 0) 00144 { 00145 curBarrier.m_cond.wait(l); 00146 } 00147 } 00148 } 00149 private: 00151 UInt32 m_threshold; 00154 int m_curSubBarrier; 00155 NonRecursiveMutex m_mutex; 00156 SubBarrier m_subBarrier0; 00157 SubBarrier m_subBarrier1; 00158 }; 00159 00160 #endif 00161 00163 ThreadBarrier::ThreadBarrier(UInt32 threshold) 00164 : m_impl(new ThreadBarrierImpl(threshold)) 00165 { 00166 BLOCXX_ASSERT(threshold != 0); 00167 } 00169 void ThreadBarrier::wait() 00170 { 00171 m_impl->wait(); 00172 } 00174 ThreadBarrier::~ThreadBarrier() 00175 { 00176 } 00178 ThreadBarrier::ThreadBarrier(const ThreadBarrier& x) 00179 : m_impl(x.m_impl) 00180 { 00181 } 00183 ThreadBarrier& ThreadBarrier::operator=(const ThreadBarrier& x) 00184 { 00185 m_impl = x.m_impl; 00186 return *this; 00187 } 00188 00189 } // end namespace BLOCXX_NAMESPACE 00190