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.

396 lines
13 KiB

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