1 /* 2 * Copyright Andrey Semashev 2007 - 2015. 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 adaptive_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_ADAPTIVE_MUTEX_HPP_INCLUDED_ 17 #define BOOST_LOG_DETAIL_ADAPTIVE_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 #include <boost/assert/source_location.hpp> 30 31 #if defined(BOOST_THREAD_POSIX) // This one can be defined by users, so it should go first 32 #define BOOST_LOG_ADAPTIVE_MUTEX_USE_PTHREAD 33 #elif defined(BOOST_WINDOWS) 34 #define BOOST_LOG_ADAPTIVE_MUTEX_USE_WINAPI 35 #elif defined(BOOST_HAS_PTHREADS) 36 #define BOOST_LOG_ADAPTIVE_MUTEX_USE_PTHREAD 37 #endif 38 39 #if defined(BOOST_LOG_ADAPTIVE_MUTEX_USE_WINAPI) 40 41 #include <boost/log/detail/pause.hpp> 42 #include <boost/winapi/thread.hpp> 43 #include <boost/detail/interlocked.hpp> 44 45 #if defined(__INTEL_COMPILER) || defined(_MSC_VER) 46 # if defined(__INTEL_COMPILER) 47 # define BOOST_LOG_COMPILER_BARRIER __memory_barrier() 48 # elif defined(__clang__) // clang-win also defines _MSC_VER 49 # define BOOST_LOG_COMPILER_BARRIER __atomic_signal_fence(__ATOMIC_SEQ_CST) 50 # else 51 extern "C" void _ReadWriteBarrier(void); 52 # if defined(BOOST_MSVC) 53 # pragma intrinsic(_ReadWriteBarrier) 54 # endif 55 # define BOOST_LOG_COMPILER_BARRIER _ReadWriteBarrier() 56 # endif 57 #elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) 58 # define BOOST_LOG_COMPILER_BARRIER __asm__ __volatile__("" : : : "memory") 59 #endif 60 61 #include <boost/log/detail/header.hpp> 62 63 namespace boost { 64 65 BOOST_LOG_OPEN_NAMESPACE 66 67 namespace aux { 68 69 //! A mutex that performs spinning or thread yielding in case of contention 70 class adaptive_mutex 71 { 72 private: 73 enum state 74 { 75 initial_pause = 2, 76 max_pause = 16 77 }; 78 79 long m_State; 80 81 public: adaptive_mutex()82 adaptive_mutex() : m_State(0) {} 83 try_lock()84 bool try_lock() 85 { 86 return (BOOST_INTERLOCKED_COMPARE_EXCHANGE(&m_State, 1L, 0L) == 0L); 87 } 88 lock()89 void lock() 90 { 91 #if defined(BOOST_LOG_AUX_PAUSE) 92 unsigned int pause_count = initial_pause; 93 #endif 94 while (!try_lock()) 95 { 96 #if defined(BOOST_LOG_AUX_PAUSE) 97 if (pause_count < max_pause) 98 { 99 for (unsigned int i = 0; i < pause_count; ++i) 100 { 101 BOOST_LOG_AUX_PAUSE; 102 } 103 pause_count += pause_count; 104 } 105 else 106 { 107 // Restart spinning after waking up this thread 108 pause_count = initial_pause; 109 boost::winapi::SwitchToThread(); 110 } 111 #else 112 boost::winapi::SwitchToThread(); 113 #endif 114 } 115 } 116 unlock()117 void unlock() 118 { 119 #if (defined(_M_IX86) || defined(_M_AMD64)) && defined(BOOST_LOG_COMPILER_BARRIER) 120 BOOST_LOG_COMPILER_BARRIER; 121 m_State = 0L; 122 BOOST_LOG_COMPILER_BARRIER; 123 #else 124 BOOST_INTERLOCKED_EXCHANGE(&m_State, 0L); 125 #endif 126 } 127 128 // Non-copyable 129 BOOST_DELETED_FUNCTION(adaptive_mutex(adaptive_mutex const&)) 130 BOOST_DELETED_FUNCTION(adaptive_mutex& operator= (adaptive_mutex const&)) 131 }; 132 133 #undef BOOST_LOG_AUX_PAUSE 134 #undef BOOST_LOG_COMPILER_BARRIER 135 136 } // namespace aux 137 138 BOOST_LOG_CLOSE_NAMESPACE // namespace log 139 140 } // namespace boost 141 142 #include <boost/log/detail/footer.hpp> 143 144 #elif defined(BOOST_LOG_ADAPTIVE_MUTEX_USE_PTHREAD) 145 146 #include <pthread.h> 147 #include <boost/assert.hpp> 148 #include <boost/log/detail/header.hpp> 149 150 #if defined(PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP) 151 #define BOOST_LOG_ADAPTIVE_MUTEX_USE_PTHREAD_MUTEX_ADAPTIVE_NP 152 #endif 153 154 namespace boost { 155 156 BOOST_LOG_OPEN_NAMESPACE 157 158 namespace aux { 159 160 //! A mutex that performs spinning or thread yielding in case of contention 161 class adaptive_mutex 162 { 163 private: 164 pthread_mutex_t m_State; 165 166 public: adaptive_mutex()167 adaptive_mutex() 168 { 169 #if defined(BOOST_LOG_ADAPTIVE_MUTEX_USE_PTHREAD_MUTEX_ADAPTIVE_NP) 170 pthread_mutexattr_t attrs; 171 pthread_mutexattr_init(&attrs); 172 pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_ADAPTIVE_NP); 173 174 const int err = pthread_mutex_init(&m_State, &attrs); 175 pthread_mutexattr_destroy(&attrs); 176 #else 177 const int err = pthread_mutex_init(&m_State, NULL); 178 #endif 179 if (BOOST_UNLIKELY(err != 0)) 180 throw_exception< thread_resource_error >(err, "Failed to initialize an adaptive mutex", "adaptive_mutex::adaptive_mutex()", __FILE__, __LINE__); 181 } 182 ~adaptive_mutex()183 ~adaptive_mutex() 184 { 185 BOOST_VERIFY(pthread_mutex_destroy(&m_State) == 0); 186 } 187 try_lock()188 bool try_lock() 189 { 190 const int err = pthread_mutex_trylock(&m_State); 191 if (err == 0) 192 return true; 193 if (BOOST_UNLIKELY(err != EBUSY)) 194 throw_exception< lock_error >(err, "Failed to lock an adaptive mutex", "adaptive_mutex::try_lock()", __FILE__, __LINE__); 195 return false; 196 } 197 lock()198 void lock() 199 { 200 const int err = pthread_mutex_lock(&m_State); 201 if (BOOST_UNLIKELY(err != 0)) 202 throw_exception< lock_error >(err, "Failed to lock an adaptive mutex", "adaptive_mutex::lock()", __FILE__, __LINE__); 203 } 204 unlock()205 void unlock() 206 { 207 BOOST_VERIFY(pthread_mutex_unlock(&m_State) == 0); 208 } 209 210 // Non-copyable 211 BOOST_DELETED_FUNCTION(adaptive_mutex(adaptive_mutex const&)) 212 BOOST_DELETED_FUNCTION(adaptive_mutex& operator= (adaptive_mutex const&)) 213 214 private: 215 template< typename ExceptionT > throw_exception(int err,const char * descr,const char * func,const char * file,int line)216 static BOOST_NOINLINE BOOST_LOG_NORETURN void throw_exception(int err, const char* descr, const char* func, const char* file, int line) 217 { 218 boost::throw_exception(ExceptionT(err, descr), boost::source_location(file, line, func)); 219 } 220 }; 221 222 } // namespace aux 223 224 BOOST_LOG_CLOSE_NAMESPACE // namespace log 225 226 } // namespace boost 227 228 #include <boost/log/detail/footer.hpp> 229 230 #endif 231 232 #endif // BOOST_LOG_NO_THREADS 233 234 #endif // BOOST_LOG_DETAIL_ADAPTIVE_MUTEX_HPP_INCLUDED_ 235