blocxx
|
00001 /******************************************************************************* 00002 * Copyright (C) 2005, Vintela, Inc. All rights reserved. 00003 * Copyright (C) 2006, 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 * * Redistributions in binary form must reproduce the above copyright 00011 * notice, this list of conditions and the following disclaimer in the 00012 * documentation and/or other materials provided with the distribution. 00013 * * Neither the name of 00014 * Vintela, Inc., 00015 * nor Novell, Inc., 00016 * nor the names of its contributors or employees may be used to 00017 * endorse or promote products derived from this software without 00018 * specific prior written permission. 00019 * 00020 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00021 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00022 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00023 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 00024 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00025 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 00026 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00027 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00028 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00029 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00030 * POSSIBILITY OF SUCH DAMAGE. 00031 *******************************************************************************/ 00032 00033 00038 #ifndef BLOCXX_THREAD_ONCE_HPP_INCLUDE_GUARD_ 00039 #define BLOCXX_THREAD_ONCE_HPP_INCLUDE_GUARD_ 00040 #include "blocxx/BLOCXX_config.h" 00041 #include "blocxx/Assertion.hpp" 00042 00043 #if defined(BLOCXX_USE_PTHREAD) 00044 #include <pthread.h> 00045 #include <csignal> // for sig_atomic_t 00046 #include <cassert> 00047 #include "blocxx/MemoryBarrier.hpp" 00048 #elif defined(BLOCXX_WIN32) 00049 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 00050 #include <wtypes.h> 00051 #endif 00052 00053 namespace BLOCXX_NAMESPACE 00054 { 00055 00056 #ifdef BLOCXX_NCR 00057 #ifndef PTHREAD_MUTEX_INITIALIZER 00058 #define PTHREAD_MUTEX_INITIALIZER {NULL, 0, 0} 00059 #endif 00060 #endif 00061 00062 #if defined(BLOCXX_USE_PTHREAD) 00063 00064 struct OnceFlag 00065 { 00066 volatile ::sig_atomic_t flag; 00067 ::pthread_mutex_t mtx; 00068 }; 00069 00070 #define BLOCXX_ONCE_INIT {0, PTHREAD_MUTEX_INITIALIZER} 00071 00072 #elif defined(BLOCXX_WIN32) 00073 00074 typedef long OnceFlag; 00075 #define BLOCXX_ONCE_INIT 0 00076 00077 #else 00078 #error "Port me!" 00079 #endif 00080 00085 template <typename FuncT> 00086 void BLOCXX_COMMON_API callOnce(OnceFlag& flag, FuncT F); 00087 00088 00089 00090 #if defined(BLOCXX_USE_PTHREAD) 00091 00092 class CallOnce_pthread_MutexLock 00093 { 00094 public: 00095 CallOnce_pthread_MutexLock(::pthread_mutex_t* mtx) 00096 : m_mtx(mtx) 00097 { 00098 #ifdef BLOCXX_NCR //we get coredump without initialization 00099 if (m_mtx->field1 == NULL) 00100 { 00101 pthread_mutexattr_t attr; 00102 int ret = pthread_mutexattr_create(&attr); 00103 assert(ret == 0); 00104 ret = pthread_mutex_init(m_mtx, attr); 00105 assert(ret == 0); 00106 pthread_mutexattr_delete(&attr); 00107 } 00108 #endif 00109 00110 #ifndef NDEBUG 00111 int res = 00112 #endif // Avoid unused variable warnings 00113 pthread_mutex_lock(m_mtx); 00114 assert(res == 0); 00115 } 00116 ~CallOnce_pthread_MutexLock() 00117 { 00118 #ifndef NDEBUG 00119 int res = 00120 #endif // Avoid unused variable warnings 00121 pthread_mutex_unlock(m_mtx); 00122 assert(res == 0); 00123 00124 #ifdef BLOCXX_NCR 00125 pthread_mutex_destroy(m_mtx); 00126 #endif 00127 } 00128 private: 00129 ::pthread_mutex_t* m_mtx; 00130 }; 00131 00132 template <typename FuncT> 00133 inline void callOnce(OnceFlag& flag, FuncT f) 00134 { 00135 readWriteMemoryBarrier(); 00136 if (flag.flag == 0) 00137 { 00138 CallOnce_pthread_MutexLock lock(&flag.mtx); 00139 if (flag.flag == 0) 00140 { 00141 f(); 00142 flag.flag = 1; 00143 } 00144 } 00145 } 00146 00147 #endif 00148 00149 #if defined(BLOCXX_WIN32) 00150 00151 template <typename FuncT> 00152 inline void callOnce(OnceFlag& flag, FuncT f) 00153 { 00154 // this is the double-checked locking pattern, but with a bit more strength than normally implemented :-) 00155 if (InterlockedCompareExchange(&flag, 1, 1) == 0) 00156 { 00157 wchar_t mutexName[MAX_PATH]; 00158 _snwprintf(mutexName, MAX_PATH, L"%X-%p-587ccea9-c95a-4e81-ac51-ab0ddc6cef63", GetCurrentProcessId(), &flag); 00159 mutexName[MAX_PATH - 1] = 0; 00160 00161 HANDLE mutex = CreateMutexW(NULL, FALSE, mutexName); 00162 BLOCXX_ASSERT(mutex != NULL); 00163 00164 int res = 0; 00165 res = WaitForSingleObject(mutex, INFINITE); 00166 BLOCXX_ASSERT(res == WAIT_OBJECT_0); 00167 00168 if (InterlockedCompareExchange(&flag, 1, 1) == 0) 00169 { 00170 try 00171 { 00172 f(); 00173 } 00174 catch (...) 00175 { 00176 res = ReleaseMutex(mutex); 00177 BLOCXX_ASSERT(res); 00178 res = CloseHandle(mutex); 00179 BLOCXX_ASSERT(res); 00180 throw; 00181 } 00182 InterlockedExchange(&flag, 1); 00183 } 00184 00185 res = ReleaseMutex(mutex); 00186 BLOCXX_ASSERT(res); 00187 res = CloseHandle(mutex); 00188 BLOCXX_ASSERT(res); 00189 } 00190 } 00191 00192 #endif 00193 00194 } // end namespace BLOCXX_NAMESPACE 00195 00196 #endif 00197