You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

427 lines
14 KiB

  1. /* /////////////////////////////////////////////////////////////////////////
  2. * File: unixstl/synch/spin_mutex.hpp (originally MWSpinMx.h, ::SynesisWin)
  3. *
  4. * Purpose: Intra-process mutex, based on spin waits.
  5. *
  6. * Created: 27th August 1997
  7. * Updated: 10th August 2009
  8. *
  9. * Thanks: To Rupert Kittinger, for pointing out that the prior
  10. * implementation that always yielded was not really "spinning".
  11. *
  12. * Brad Cox, for helping out in testing and fixing the
  13. * implementation for MAC OSX (Intel).
  14. *
  15. * Home: http://stlsoft.org/
  16. *
  17. * Copyright (c) 1997-2009, Matthew Wilson and Synesis Software
  18. * All rights reserved.
  19. *
  20. * Redistribution and use in source and binary forms, with or without
  21. * modification, are permitted provided that the following conditions are met:
  22. *
  23. * - Redistributions of source code must retain the above copyright notice, this
  24. * list of conditions and the following disclaimer.
  25. * - Redistributions in binary form must reproduce the above copyright notice,
  26. * this list of conditions and the following disclaimer in the documentation
  27. * and/or other materials provided with the distribution.
  28. * - Neither the name(s) of Matthew Wilson and Synesis Software nor the names of
  29. * any contributors may be used to endorse or promote products derived from
  30. * this software without specific prior written permission.
  31. *
  32. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  33. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  34. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  35. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  36. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  37. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  38. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  39. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  40. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  42. * POSSIBILITY OF SUCH DAMAGE.
  43. *
  44. * ////////////////////////////////////////////////////////////////////// */
  45. /** \file unixstl/synch/spin_mutex.hpp
  46. *
  47. * \brief [C++ only] Definition of the unixstl::spin_mutex class
  48. * (\ref group__library__synch "Synchronisation" Library).
  49. */
  50. #ifndef UNIXSTL_INCL_UNIXSTL_SYNCH_HPP_SPIN_MUTEX
  51. #define UNIXSTL_INCL_UNIXSTL_SYNCH_HPP_SPIN_MUTEX
  52. #ifndef STLSOFT_DOCUMENTATION_SKIP_SECTION
  53. # define UNIXSTL_VER_UNIXSTL_SYNCH_HPP_SPIN_MUTEX_MAJOR 5
  54. # define UNIXSTL_VER_UNIXSTL_SYNCH_HPP_SPIN_MUTEX_MINOR 0
  55. # define UNIXSTL_VER_UNIXSTL_SYNCH_HPP_SPIN_MUTEX_REVISION 3
  56. # define UNIXSTL_VER_UNIXSTL_SYNCH_HPP_SPIN_MUTEX_EDIT 60
  57. #endif /* !STLSOFT_DOCUMENTATION_SKIP_SECTION */
  58. /* /////////////////////////////////////////////////////////////////////////
  59. * Auto-generation and compatibility
  60. */
  61. /*
  62. [Incompatibilies-start]
  63. !UNIXSTL_OS_IS_MACOSX:
  64. [Incompatibilies-end]
  65. */
  66. /* /////////////////////////////////////////////////////////////////////////
  67. * Includes
  68. */
  69. #ifndef UNIXSTL_INCL_UNIXSTL_H_UNIXSTL
  70. # include <unixstl/unixstl.h>
  71. #endif /* !UNIXSTL_INCL_UNIXSTL_H_UNIXSTL */
  72. #ifndef UNIXSTL_INCL_UNIXSTL_SYNCH_UTIL_H_FEATURES
  73. # include <unixstl/synch/util/features.h>
  74. #endif /* !UNIXSTL_INCL_UNIXSTL_SYNCH_UTIL_H_FEATURES */
  75. #ifndef UNIXSTL_HAS_ATOMIC_INTEGER_OPERATIONS
  76. # error unixstl/synch/spin_mutex.hpp requires support for atomic integer operations. Consult unixstl/synch/util/features.h for details
  77. #endif /* !UNIXSTL_HAS_ATOMIC_INTEGER_OPERATIONS */
  78. #ifndef UNIXSTL_INCL_UNIXSTL_SYNCH_H_ATOMIC_FUNCTIONS
  79. # include <unixstl/synch/atomic_functions.h>
  80. #endif /* !UNIXSTL_INCL_UNIXSTL_SYNCH_H_ATOMIC_FUNCTIONS */
  81. #ifndef STLSOFT_INCL_STLSOFT_SYNCH_HPP_CONCEPTS
  82. # include <stlsoft/synch/concepts.hpp>
  83. #endif /* !STLSOFT_INCL_STLSOFT_SYNCH_HPP_CONCEPTS */
  84. #ifndef STLSOFT_INCL_STLSOFT_SYNCH_HPP_SPIN_POLICIES
  85. # include <stlsoft/synch/spin_policies.hpp>
  86. #endif /* !STLSOFT_INCL_STLSOFT_SYNCH_HPP_SPIN_POLICIES */
  87. #ifndef STLSOFT_INCL_H_SCHED
  88. # define STLSOFT_INCL_H_SCHED
  89. # include <sched.h>
  90. #endif /* !STLSOFT_INCL_H_SCHED */
  91. #ifdef STLSOFT_UNITTEST
  92. # include <stlsoft/synch/lock_scope.hpp>
  93. #endif /* STLSOFT_UNITTEST */
  94. #if defined(_DEBUG)
  95. # define STLSOFT_SPINMUTEX_COUNT_LOCKS
  96. #endif /* _DEBUG */
  97. /* /////////////////////////////////////////////////////////////////////////
  98. * Namespace
  99. */
  100. #ifndef _UNIXSTL_NO_NAMESPACE
  101. # if defined(_STLSOFT_NO_NAMESPACE) || \
  102. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  103. /* There is no stlsoft namespace, so must define ::unixstl */
  104. namespace unixstl
  105. {
  106. # else
  107. /* Define stlsoft::unixstl_project */
  108. namespace stlsoft
  109. {
  110. namespace unixstl_project
  111. {
  112. # endif /* _STLSOFT_NO_NAMESPACE */
  113. #endif /* !_UNIXSTL_NO_NAMESPACE */
  114. /* /////////////////////////////////////////////////////////////////////////
  115. * Classes
  116. */
  117. // class spin_mutex
  118. /** \brief This class provides an implementation of the mutex model based on
  119. * a spinning mechanism.
  120. *
  121. * \ingroup group__library__synch
  122. *
  123. * \note A spin mutex is not recursive. If you re-enter it your thread will
  124. * be in irrecoverable deadlock.
  125. */
  126. template <ss_typename_param_k SP>
  127. class spin_mutex_base
  128. : public stlsoft_ns_qual(critical_section)< STLSOFT_CRITICAL_SECTION_ISNOT_RECURSIVE
  129. , STLSOFT_CRITICAL_SECTION_ISNOT_TRYABLE
  130. >
  131. {
  132. /// \name Member Types
  133. /// @{
  134. private:
  135. /// \brief The spin-policy class
  136. typedef SP spin_policy_class;
  137. public:
  138. /// \brief This class
  139. typedef spin_mutex_base<SP> class_type;
  140. /// \brief The atomic integer type
  141. typedef unixstl_ns_qual(atomic_int_t) atomic_int_type;
  142. /// \brief The count type
  143. typedef us_sint32_t count_type;
  144. /// \brief The bool type
  145. typedef us_bool_t bool_type;
  146. /// @}
  147. /// \name Construction
  148. /// @{
  149. public:
  150. #ifdef __SYNSOFT_DBS_COMPILER_SUPPORTS_PRAGMA_MESSAGE
  151. # pragma message(_sscomp_fileline_message("Create stlsoft/synch/spin_mutex_base.hpp, and factor out"))
  152. #endif /* __SYNSOFT_DBS_COMPILER_SUPPORTS_PRAGMA_MESSAGE */
  153. /// \brief Creates an instance of the mutex
  154. ///
  155. /// \param p Pointer to an external counter variable. May be NULL, in
  156. /// which case an internal member is used for the counter variable.
  157. ///
  158. /// \note
  159. ss_explicit_k spin_mutex_base(atomic_int_type *p = NULL) stlsoft_throw_0()
  160. : m_spinCount((NULL != p) ? p : &m_internalCount)
  161. , m_internalCount(0)
  162. #ifdef STLSOFT_SPINMUTEX_COUNT_LOCKS
  163. , m_cLocks(0)
  164. #endif // STLSOFT_SPINMUTEX_COUNT_LOCKS
  165. , m_spunCount(0)
  166. , m_bYieldOnSpin(spin_policy_class::value)
  167. {}
  168. /// \brief Creates an instance of the mutex
  169. ///
  170. /// \param p Pointer to an external counter variable. May be NULL, in
  171. /// which case an internal member is used for the counter variable.
  172. /// \param bYieldOnSpin
  173. spin_mutex_base(atomic_int_type *p, bool_type bYieldOnSpin) stlsoft_throw_0()
  174. : m_spinCount((NULL != p) ? p : &m_internalCount)
  175. , m_internalCount(0)
  176. #ifdef STLSOFT_SPINMUTEX_COUNT_LOCKS
  177. , m_cLocks(0)
  178. #endif // STLSOFT_SPINMUTEX_COUNT_LOCKS
  179. , m_spunCount(0)
  180. , m_bYieldOnSpin(bYieldOnSpin)
  181. {}
  182. /// Destroys an instance of the mutex
  183. ~spin_mutex_base() stlsoft_throw_0()
  184. {
  185. #ifdef STLSOFT_SPINMUTEX_COUNT_LOCKS
  186. UNIXSTL_ASSERT(0 == m_cLocks);
  187. #endif // STLSOFT_SPINMUTEX_COUNT_LOCKS
  188. }
  189. /// @}
  190. /// \name Operations
  191. /// @{
  192. public:
  193. /// \brief Acquires a lock on the mutex, pending the thread until the lock is aquired
  194. void lock() stlsoft_throw_0()
  195. {
  196. #ifdef UNIXSTL_SPINMUTEX_CHECK_INIT
  197. // If the dynamic initialisation phase has been skipped, the
  198. // members will all be assigned to 0, which is correct for
  199. // all except m_spinCount, which must be assigned to
  200. // &m_internalCount
  201. if(NULL == m_spinCount)
  202. {
  203. m_spinCount = &m_internalCount;
  204. }
  205. #endif /* UNIXSTL_SPINMUTEX_CHECK_INIT */
  206. UNIXSTL_MESSAGE_ASSERT("A global instance of spin_mutex has skipped dynamic initialisation. You must #define UNIXSTL_SPINMUTEX_CHECK_INIT if your compilation causes dynamic initialisation to be skipped.", NULL != m_spinCount);
  207. #if defined(UNIXSTL_OS_IS_LINUX) && \
  208. !defined(UNIXSTL_ARCH_IS_INTEL)
  209. for(m_spunCount = 1; 0 != ::atomic_inc_and_test(m_spinCount); ++m_spunCount)
  210. #elif defined(UNIXSTL_OS_IS_MACOSX)
  211. for(m_spunCount = 1; !::OSAtomicCompareAndSwap32Barrier(0, 1, m_spinCount); ++m_spunCount)
  212. #elif defined(UNIXSTL_HAS_ATOMIC_WRITE)
  213. for(m_spunCount = 1; 0 != atomic_write(m_spinCount, 1); ++m_spunCount)
  214. #else /* ? arch */
  215. # error Your platform does not support atomic_write(), or has provides it in a manner unknown to the authors of UNIXSTL. If you know of the correct implementation, please send a patch.
  216. #endif /* arch */
  217. {
  218. if(m_bYieldOnSpin)
  219. {
  220. ::sched_yield();
  221. }
  222. }
  223. #ifdef STLSOFT_SPINMUTEX_COUNT_LOCKS
  224. UNIXSTL_ASSERT(0 != ++m_cLocks);
  225. #endif // STLSOFT_SPINMUTEX_COUNT_LOCKS
  226. }
  227. /// \brief Releases an aquired lock on the mutex
  228. void unlock() stlsoft_throw_0()
  229. {
  230. #ifdef STLSOFT_SPINMUTEX_COUNT_LOCKS
  231. UNIXSTL_ASSERT(m_cLocks-- != 0);
  232. #endif // STLSOFT_SPINMUTEX_COUNT_LOCKS
  233. m_spunCount = 0;
  234. #if defined(UNIXSTL_OS_IS_LINUX) && \
  235. !defined(UNIXSTL_ARCH_IS_INTEL)
  236. ::atomic_dec(m_spinCount);
  237. #elif defined(UNIXSTL_OS_IS_MACOSX)
  238. ::OSAtomicDecrement32Barrier(m_spinCount);
  239. #else /* ? arch */
  240. atomic_write(m_spinCount, 0);
  241. #endif /* arch */
  242. }
  243. /// @}
  244. /// \name Attributes
  245. /// @{
  246. public:
  247. /// \brief An indicator as to the level of contention on the mutex.
  248. ///
  249. /// \note The value returned is only meaningful after lock() has been
  250. /// called and before a corresponding unlock() has been called.
  251. ///
  252. /// \note The value returned is only reliable when an external counter
  253. /// variable is being used, and when each spin_mutex instance is
  254. /// thread-specific. In all other cases, the spun count is subject to
  255. /// race conditions (that do <b>not</b> affect the good functioning of
  256. /// the spin_mutex) and value returned may be, at best, used only as a
  257. /// guide as to contention.
  258. count_type spun_count() const
  259. {
  260. return m_spunCount;
  261. }
  262. /// @}
  263. /// \name Members
  264. /// @{
  265. private:
  266. atomic_int_type *m_spinCount;
  267. atomic_int_type m_internalCount;
  268. #ifdef STLSOFT_SPINMUTEX_COUNT_LOCKS
  269. count_type m_cLocks; // Used as check on matched Lock/Unlock calls
  270. #endif // STLSOFT_SPINMUTEX_COUNT_LOCKS
  271. count_type m_spunCount;
  272. const bool_type m_bYieldOnSpin;
  273. /// @}
  274. /// \name Not to be implemented
  275. /// @{
  276. private:
  277. spin_mutex_base(class_type const& rhs);
  278. class_type& operator =(class_type const& rhs);
  279. /// @}
  280. };
  281. typedef spin_mutex_base<stlsoft_ns_qual(spin_yield)> spin_mutex_yield;
  282. typedef spin_mutex_base<stlsoft_ns_qual(spin_no_yield)> spin_mutex_no_yield;
  283. #ifdef STLSOFT_OLD_SPIN_MUTEX_BEHAVIOUR
  284. typedef spin_mutex_no_yield spin_mutex;
  285. #else /* ? STLSOFT_OLD_SPIN_MUTEX_BEHAVIOUR */
  286. typedef spin_mutex_yield spin_mutex;
  287. #endif /* STLSOFT_OLD_SPIN_MUTEX_BEHAVIOUR */
  288. /* /////////////////////////////////////////////////////////////////////////
  289. * Control shims
  290. */
  291. #ifndef _UNIXSTL_NO_NAMESPACE
  292. # if defined(_STLSOFT_NO_NAMESPACE) || \
  293. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  294. } // namespace unixstl
  295. # else
  296. } // namespace unixstl_project
  297. # endif /* _STLSOFT_NO_NAMESPACE */
  298. #endif /* !_UNIXSTL_NO_NAMESPACE */
  299. /** \brief This \ref group__concept__shims "control shim" aquires a lock on the given mutex
  300. *
  301. * \ingroup group__concept__shim__synchronisation_control
  302. *
  303. * \param mx The mutex on which to aquire the lock.
  304. */
  305. template <ss_typename_param_k SP>
  306. inline void lock_instance(unixstl_ns_qual(spin_mutex_base)<SP> &mx)
  307. {
  308. mx.lock();
  309. }
  310. /** \brief This \ref group__concept__shims "control shim" releases a lock on the given mutex
  311. *
  312. * \ingroup group__concept__shim__synchronisation_control
  313. *
  314. * \param mx The mutex on which to release the lock
  315. */
  316. template <ss_typename_param_k SP>
  317. inline void unlock_instance(unixstl_ns_qual(spin_mutex_base)<SP> &mx)
  318. {
  319. mx.unlock();
  320. }
  321. #ifndef _UNIXSTL_NO_NAMESPACE
  322. # if defined(_STLSOFT_NO_NAMESPACE) || \
  323. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  324. namespace unixstl
  325. {
  326. # else
  327. namespace unixstl_project
  328. {
  329. # if defined(STLSOFT_COMPILER_IS_BORLAND)
  330. using ::stlsoft::lock_instance;
  331. using ::stlsoft::unlock_instance;
  332. # endif /* compiler */
  333. # endif /* _STLSOFT_NO_NAMESPACE */
  334. #endif /* !_UNIXSTL_NO_NAMESPACE */
  335. /* /////////////////////////////////////////////////////////////////////////
  336. * lock_traits
  337. */
  338. // class lock_traits
  339. /** \brief Traits for the spin_mutex class
  340. *
  341. * \ingroup group__library__synch
  342. */
  343. struct spin_mutex_lock_traits
  344. {
  345. public:
  346. /// The lockable type
  347. typedef spin_mutex lock_type;
  348. typedef spin_mutex_lock_traits class_type;
  349. // Operations
  350. public:
  351. /// Lock the given spin_mutex instance
  352. static void lock(spin_mutex &c)
  353. {
  354. lock_instance(c);
  355. }
  356. /// Unlock the given spin_mutex instance
  357. static void unlock(spin_mutex &c)
  358. {
  359. unlock_instance(c);
  360. }
  361. };
  362. ////////////////////////////////////////////////////////////////////////////
  363. // Unit-testing
  364. #ifdef STLSOFT_UNITTEST
  365. # include "./unittest/spin_mutex_unittest_.h"
  366. #endif /* STLSOFT_UNITTEST */
  367. /* ////////////////////////////////////////////////////////////////////// */
  368. #ifndef _UNIXSTL_NO_NAMESPACE
  369. # if defined(_STLSOFT_NO_NAMESPACE) || \
  370. defined(STLSOFT_DOCUMENTATION_SKIP_SECTION)
  371. } // namespace unixstl
  372. # else
  373. } // namespace unixstl_project
  374. } // namespace stlsoft
  375. # endif /* _STLSOFT_NO_NAMESPACE */
  376. #endif /* !_UNIXSTL_NO_NAMESPACE */
  377. /* ////////////////////////////////////////////////////////////////////// */
  378. #endif /* !UNIXSTL_INCL_UNIXSTL_SYNCH_HPP_SPIN_MUTEX */
  379. /* ///////////////////////////// end of file //////////////////////////// */