1 /* 2 * Copyright Andrey Semashev 2007 - 2014. 3 * Distributed under the Boost Software License, Version 1.0. 4 * (See accompanying file LICENSE_1_0.txt or copy at 5 * http://www.boost.org/LICENSE_1_0.txt) 6 */ 7 /*! 8 * \file spin_mutex.hpp 9 * \author Andrey Semashev 10 * \date 01.08.2010 11 * 12 * \brief This header is the Boost.Log library implementation, see the library documentation 13 * at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html. 14 */ 15 16 #ifndef BOOST_LOG_DETAIL_SPIN_MUTEX_HPP_INCLUDED_ 17 #define BOOST_LOG_DETAIL_SPIN_MUTEX_HPP_INCLUDED_ 18 19 #include <boost/log/detail/config.hpp> 20 21 #ifdef BOOST_HAS_PRAGMA_ONCE 22 #pragma once 23 #endif 24 25 #ifndef BOOST_LOG_NO_THREADS 26 27 #include <boost/throw_exception.hpp> 28 #include <boost/thread/exceptions.hpp> 29 30 #if defined(BOOST_THREAD_POSIX) // This one can be defined by users, so it should go first 31 #define BOOST_LOG_SPIN_MUTEX_USE_PTHREAD 32 #elif defined(BOOST_WINDOWS) 33 #define BOOST_LOG_SPIN_MUTEX_USE_WINAPI 34 #elif defined(BOOST_HAS_PTHREADS) 35 #define BOOST_LOG_SPIN_MUTEX_USE_PTHREAD 36 #endif 37 38 #if defined(BOOST_LOG_SPIN_MUTEX_USE_WINAPI) 39 40 #include <boost/detail/interlocked.hpp> 41 42 #if defined(BOOST_USE_WINDOWS_H) 43 44 #ifndef _WIN32_WINNT 45 #define _WIN32_WINNT 0x0500 46 #endif 47 48 #include <windows.h> 49 50 #else // defined(BOOST_USE_WINDOWS_H) 51 52 namespace boost { 53 54 BOOST_LOG_OPEN_NAMESPACE 55 56 namespace aux { 57 58 extern "C" { 59 60 __declspec(dllimport) int __stdcall SwitchToThread(); 61 62 } // extern "C" 63 64 } // namespace aux 65 66 BOOST_LOG_CLOSE_NAMESPACE // namespace log 67 68 } // namespace boost 69 70 #endif // BOOST_USE_WINDOWS_H 71 72 #if defined(__INTEL_COMPILER) || defined(_MSC_VER) 73 # if defined(_M_IX86) 74 # define BOOST_LOG_PAUSE_OP __asm { pause } 75 # elif defined(_M_AMD64) 76 extern "C" void _mm_pause(void); 77 #pragma intrinsic(_mm_pause) 78 # define BOOST_LOG_PAUSE_OP _mm_pause() 79 # endif 80 # if defined(__INTEL_COMPILER) 81 # define BOOST_LOG_COMPILER_BARRIER __memory_barrier() 82 # else 83 extern "C" void _ReadWriteBarrier(void); 84 #pragma intrinsic(_ReadWriteBarrier) 85 # define BOOST_LOG_COMPILER_BARRIER _ReadWriteBarrier() 86 # endif 87 #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) 88 # define BOOST_LOG_PAUSE_OP __asm__ __volatile__("pause;") 89 # define BOOST_LOG_COMPILER_BARRIER __asm__ __volatile__("" : : : "memory") 90 #endif 91 92 #include <boost/log/detail/header.hpp> 93 94 namespace boost { 95 96 BOOST_LOG_OPEN_NAMESPACE 97 98 namespace aux { 99 100 //! A simple spinning mutex 101 class spin_mutex 102 { 103 private: 104 enum state 105 { 106 initial_pause = 2, 107 max_pause = 16 108 }; 109 110 long m_State; 111 112 public: spin_mutex()113 spin_mutex() : m_State(0) {} 114 try_lock()115 bool try_lock() 116 { 117 return (BOOST_INTERLOCKED_COMPARE_EXCHANGE(&m_State, 1L, 0L) == 0L); 118 } 119 lock()120 void lock() 121 { 122 #if defined(BOOST_LOG_PAUSE_OP) 123 unsigned int pause_count = initial_pause; 124 #endif 125 while (!try_lock()) 126 { 127 #if defined(BOOST_LOG_PAUSE_OP) 128 if (pause_count < max_pause) 129 { 130 for (unsigned int i = 0; i < pause_count; ++i) 131 { 132 BOOST_LOG_PAUSE_OP; 133 } 134 pause_count += pause_count; 135 } 136 else 137 { 138 // Restart spinning after waking up this thread 139 pause_count = initial_pause; 140 SwitchToThread(); 141 } 142 #else 143 SwitchToThread(); 144 #endif 145 } 146 } 147 unlock()148 void unlock() 149 { 150 #if (defined(_M_IX86) || defined(_M_AMD64)) && defined(BOOST_LOG_COMPILER_BARRIER) 151 BOOST_LOG_COMPILER_BARRIER; 152 m_State = 0L; 153 BOOST_LOG_COMPILER_BARRIER; 154 #else 155 BOOST_INTERLOCKED_EXCHANGE(&m_State, 0L); 156 #endif 157 } 158 159 // Non-copyable 160 BOOST_DELETED_FUNCTION(spin_mutex(spin_mutex const&)) 161 BOOST_DELETED_FUNCTION(spin_mutex& operator= (spin_mutex const&)) 162 }; 163 164 #undef BOOST_LOG_PAUSE_OP 165 #undef BOOST_LOG_COMPILER_BARRIER 166 167 } // namespace aux 168 169 BOOST_LOG_CLOSE_NAMESPACE // namespace log 170 171 } // namespace boost 172 173 #include <boost/log/detail/footer.hpp> 174 175 #elif defined(BOOST_LOG_SPIN_MUTEX_USE_PTHREAD) 176 177 #include <pthread.h> 178 #include <boost/assert.hpp> 179 #include <boost/log/detail/header.hpp> 180 181 namespace boost { 182 183 BOOST_LOG_OPEN_NAMESPACE 184 185 namespace aux { 186 187 #if defined(_POSIX_SPIN_LOCKS) && _POSIX_SPIN_LOCKS > 0 188 189 //! A simple spinning mutex 190 class spin_mutex 191 { 192 private: 193 pthread_spinlock_t m_State; 194 195 public: spin_mutex()196 spin_mutex() 197 { 198 const int err = pthread_spin_init(&m_State, PTHREAD_PROCESS_PRIVATE); 199 if (err != 0) 200 throw_exception< thread_resource_error >(err, "failed to initialize a spin mutex", "spin_mutex::spin_mutex()", __FILE__, __LINE__); 201 } 202 ~spin_mutex()203 ~spin_mutex() 204 { 205 BOOST_VERIFY(pthread_spin_destroy(&m_State) == 0); 206 } 207 try_lock()208 bool try_lock() 209 { 210 const int err = pthread_spin_trylock(&m_State); 211 if (err == 0) 212 return true; 213 if (err != EBUSY) 214 throw_exception< lock_error >(err, "failed to lock a spin mutex", "spin_mutex::try_lock()", __FILE__, __LINE__); 215 return false; 216 } 217 lock()218 void lock() 219 { 220 const int err = pthread_spin_lock(&m_State); 221 if (err != 0) 222 throw_exception< lock_error >(err, "failed to lock a spin mutex", "spin_mutex::lock()", __FILE__, __LINE__); 223 } 224 unlock()225 void unlock() 226 { 227 BOOST_VERIFY(pthread_spin_unlock(&m_State) == 0); 228 } 229 230 // Non-copyable 231 BOOST_DELETED_FUNCTION(spin_mutex(spin_mutex const&)) 232 BOOST_DELETED_FUNCTION(spin_mutex& operator= (spin_mutex const&)) 233 234 private: 235 template< typename ExceptionT > throw_exception(int err,const char * descr,const char * func,const char * file,int line)236 static BOOST_NOINLINE BOOST_LOG_NORETURN void throw_exception(int err, const char* descr, const char* func, const char* file, int line) 237 { 238 #if !defined(BOOST_EXCEPTION_DISABLE) 239 boost::exception_detail::throw_exception_(ExceptionT(err, descr), func, file, line); 240 #else 241 boost::throw_exception(ExceptionT(err, descr)); 242 #endif 243 } 244 }; 245 246 #else // defined(_POSIX_SPIN_LOCKS) 247 248 //! Backup implementation in case if pthreads don't support spin locks 249 class spin_mutex 250 { 251 private: 252 pthread_mutex_t m_State; 253 254 public: 255 spin_mutex() 256 { 257 const int err = pthread_mutex_init(&m_State, NULL); 258 if (err != 0) 259 throw_exception< thread_resource_error >(err, "failed to initialize a spin mutex", "spin_mutex::spin_mutex()", __FILE__, __LINE__); 260 } 261 262 ~spin_mutex() 263 { 264 BOOST_VERIFY(pthread_mutex_destroy(&m_State) == 0); 265 } 266 267 bool try_lock() 268 { 269 const int err = pthread_mutex_trylock(&m_State); 270 if (err == 0) 271 return true; 272 if (err != EBUSY) 273 throw_exception< lock_error >(err, "failed to lock a spin mutex", "spin_mutex::try_lock()", __FILE__, __LINE__); 274 return false; 275 } 276 277 void lock() 278 { 279 const int err = pthread_mutex_lock(&m_State); 280 if (err != 0) 281 throw_exception< lock_error >(err, "failed to lock a spin mutex", "spin_mutex::lock()", __FILE__, __LINE__); 282 } 283 284 void unlock() 285 { 286 BOOST_VERIFY(pthread_mutex_unlock(&m_State) == 0); 287 } 288 289 // Non-copyable 290 BOOST_DELETED_FUNCTION(spin_mutex(spin_mutex const&)) 291 BOOST_DELETED_FUNCTION(spin_mutex& operator= (spin_mutex const&)) 292 293 private: 294 template< typename ExceptionT > 295 static BOOST_NOINLINE BOOST_LOG_NORETURN void throw_exception(int err, const char* descr, const char* func, const char* file, int line) 296 { 297 #if !defined(BOOST_EXCEPTION_DISABLE) 298 boost::exception_detail::throw_exception_(ExceptionT(err, descr), func, file, line); 299 #else 300 boost::throw_exception(ExceptionT(err, descr)); 301 #endif 302 } 303 }; 304 305 #endif // defined(_POSIX_SPIN_LOCKS) 306 307 } // namespace aux 308 309 BOOST_LOG_CLOSE_NAMESPACE // namespace log 310 311 } // namespace boost 312 313 #include <boost/log/detail/footer.hpp> 314 315 #endif 316 317 #endif // BOOST_LOG_NO_THREADS 318 319 #endif // BOOST_LOG_DETAIL_SPIN_MUTEX_HPP_INCLUDED_ 320