adaptive_mutex.hpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. /*
  2. * Copyright Andrey Semashev 2007 - 2025.
  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. #ifndef BOOST_LOG_DETAIL_ADAPTIVE_MUTEX_HPP_INCLUDED_
  16. #define BOOST_LOG_DETAIL_ADAPTIVE_MUTEX_HPP_INCLUDED_
  17. #include <boost/log/detail/config.hpp>
  18. #ifdef BOOST_HAS_PRAGMA_ONCE
  19. #pragma once
  20. #endif
  21. #ifndef BOOST_LOG_NO_THREADS
  22. #include <boost/atomic/capabilities.hpp>
  23. #if defined(BOOST_WINDOWS) || (BOOST_ATOMIC_INT_LOCK_FREE == 2) && (BOOST_ATOMIC_HAS_NATIVE_INT_WAIT_NOTIFY == 2)
  24. #define BOOST_LOG_AUX_ADAPTIVE_MUTEX_USE_ATOMIC
  25. #else
  26. #define BOOST_LOG_AUX_ADAPTIVE_MUTEX_USE_PTHREAD
  27. #endif
  28. #if defined(BOOST_LOG_AUX_ADAPTIVE_MUTEX_USE_ATOMIC)
  29. #include <boost/memory_order.hpp>
  30. #include <boost/atomic/atomic.hpp>
  31. #include <boost/atomic/thread_pause.hpp>
  32. #include <boost/log/detail/header.hpp>
  33. namespace boost {
  34. BOOST_LOG_OPEN_NAMESPACE
  35. namespace aux {
  36. //! A mutex that performs spinning or thread blocking in case of contention
  37. class adaptive_mutex
  38. {
  39. private:
  40. enum state : unsigned int
  41. {
  42. locked_bit = 1u,
  43. waiters_one = 1u << 1u
  44. };
  45. enum pause_constants : unsigned int
  46. {
  47. initial_pause = 1u,
  48. max_pause = 16u
  49. };
  50. //! Mutex state. Lowest bit indicates whether the mutex is locked or not, all higher bits store the number of blocked waiters. See \c state enum.
  51. boost::atomics::atomic< unsigned int > m_state;
  52. public:
  53. adaptive_mutex() noexcept : m_state(0u) {}
  54. // Non-copyable
  55. adaptive_mutex(adaptive_mutex const&) = delete;
  56. adaptive_mutex& operator= (adaptive_mutex const&) = delete;
  57. bool try_lock() noexcept
  58. {
  59. unsigned int old_state = m_state.load(boost::memory_order::relaxed);
  60. return (old_state & locked_bit) == 0u &&
  61. m_state.compare_exchange_strong(old_state, old_state | locked_bit, boost::memory_order::acquire, boost::memory_order::relaxed);
  62. }
  63. void lock() noexcept
  64. {
  65. unsigned int pause_count = initial_pause;
  66. unsigned int waiter_added = 0u;
  67. unsigned int old_state = m_state.load(boost::memory_order::relaxed);
  68. while (true)
  69. {
  70. if ((old_state & locked_bit) == 0u)
  71. {
  72. unsigned int new_state = (old_state - waiter_added) | locked_bit;
  73. if (m_state.compare_exchange_weak(old_state, new_state, boost::memory_order::acquire, boost::memory_order::relaxed))
  74. break;
  75. continue;
  76. }
  77. if (pause_count < max_pause)
  78. {
  79. if (waiter_added != 0u)
  80. {
  81. old_state = m_state.sub(waiters_one, boost::memory_order::relaxed);
  82. waiter_added = 0u;
  83. }
  84. else
  85. {
  86. for (unsigned int i = 0u; i < pause_count; ++i)
  87. {
  88. boost::atomics::thread_pause();
  89. }
  90. pause_count += pause_count;
  91. old_state = m_state.load(boost::memory_order::relaxed);
  92. }
  93. }
  94. else if (waiter_added == 0u)
  95. {
  96. old_state = m_state.add(waiters_one, boost::memory_order::relaxed);
  97. waiter_added = waiters_one;
  98. }
  99. else
  100. {
  101. // Restart spinning after waking up this thread
  102. pause_count = initial_pause;
  103. old_state = m_state.wait(old_state, boost::memory_order::relaxed);
  104. }
  105. }
  106. }
  107. void unlock() noexcept
  108. {
  109. if (m_state.and_and_test(~static_cast< unsigned int >(locked_bit), boost::memory_order::release))
  110. {
  111. // If the resulting state is non-zero then there are blocked waiters
  112. m_state.notify_one();
  113. }
  114. }
  115. };
  116. } // namespace aux
  117. BOOST_LOG_CLOSE_NAMESPACE // namespace log
  118. } // namespace boost
  119. #include <boost/log/detail/footer.hpp>
  120. #elif defined(BOOST_LOG_AUX_ADAPTIVE_MUTEX_USE_PTHREAD)
  121. #include <pthread.h>
  122. #include <cerrno>
  123. #include <system_error>
  124. #include <boost/assert.hpp>
  125. #include <boost/assert/source_location.hpp>
  126. #include <boost/log/detail/header.hpp>
  127. #if defined(PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP)
  128. #define BOOST_LOG_AUX_ADAPTIVE_MUTEX_USE_PTHREAD_MUTEX_ADAPTIVE_NP
  129. #endif
  130. namespace boost {
  131. BOOST_LOG_OPEN_NAMESPACE
  132. namespace aux {
  133. //! A mutex that performs spinning or thread blocking in case of contention
  134. class adaptive_mutex
  135. {
  136. private:
  137. pthread_mutex_t m_state;
  138. public:
  139. adaptive_mutex()
  140. {
  141. #if defined(BOOST_LOG_AUX_ADAPTIVE_MUTEX_USE_PTHREAD_MUTEX_ADAPTIVE_NP)
  142. pthread_mutexattr_t attrs;
  143. pthread_mutexattr_init(&attrs);
  144. pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_ADAPTIVE_NP);
  145. const int err = pthread_mutex_init(&m_state, &attrs);
  146. pthread_mutexattr_destroy(&attrs);
  147. #else
  148. const int err = pthread_mutex_init(&m_state, nullptr);
  149. #endif
  150. if (BOOST_UNLIKELY(err != 0))
  151. throw_system_error(err, "Failed to initialize an adaptive mutex", "adaptive_mutex::adaptive_mutex()", __FILE__, __LINE__);
  152. }
  153. // Non-copyable
  154. adaptive_mutex(adaptive_mutex const&) = delete;
  155. adaptive_mutex& operator= (adaptive_mutex const&) = delete;
  156. ~adaptive_mutex()
  157. {
  158. BOOST_VERIFY(pthread_mutex_destroy(&m_state) == 0);
  159. }
  160. bool try_lock()
  161. {
  162. const int err = pthread_mutex_trylock(&m_state);
  163. if (err == 0)
  164. return true;
  165. if (BOOST_UNLIKELY(err != EBUSY))
  166. throw_system_error(err, "Failed to lock an adaptive mutex", "adaptive_mutex::try_lock()", __FILE__, __LINE__);
  167. return false;
  168. }
  169. void lock()
  170. {
  171. const int err = pthread_mutex_lock(&m_state);
  172. if (BOOST_UNLIKELY(err != 0))
  173. throw_system_error(err, "Failed to lock an adaptive mutex", "adaptive_mutex::lock()", __FILE__, __LINE__);
  174. }
  175. void unlock() noexcept
  176. {
  177. BOOST_VERIFY(pthread_mutex_unlock(&m_state) == 0);
  178. }
  179. private:
  180. static BOOST_NOINLINE BOOST_LOG_NORETURN void throw_system_error(int err, const char* descr, const char* func, const char* file, int line)
  181. {
  182. boost::throw_exception(std::system_error(std::error_code(err, std::system_category()), descr), boost::source_location(file, line, func));
  183. }
  184. };
  185. } // namespace aux
  186. BOOST_LOG_CLOSE_NAMESPACE // namespace log
  187. } // namespace boost
  188. #include <boost/log/detail/footer.hpp>
  189. #endif
  190. #endif // BOOST_LOG_NO_THREADS
  191. #endif // BOOST_LOG_DETAIL_ADAPTIVE_MUTEX_HPP_INCLUDED_