lock_array.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. // Copyright (c) 2006-2018 Maxim Khizhinsky
  2. //
  3. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  4. // file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef CDSLIB_SYNC_LOCK_ARRAY_H
  6. #define CDSLIB_SYNC_LOCK_ARRAY_H
  7. #include <mutex> //unique_lock
  8. #include <cds/details/allocator.h>
  9. #include <cds/algo/int_algo.h>
  10. namespace cds { namespace sync {
  11. /// Trivial lock \ref lock_array selection policy
  12. struct trivial_select_policy
  13. {
  14. /// Returns \p nWhat
  15. size_t operator()( size_t nWhat, size_t nCapacity ) const
  16. {
  17. assert( nWhat < nCapacity );
  18. CDS_UNUSED( nCapacity );
  19. return nWhat;
  20. }
  21. /// Checks if \p nCapacity is acceptable by policy. For trivial policy, any \p nCapacity is accepted.
  22. static bool is_capacity_accepted( size_t nCapacity )
  23. {
  24. CDS_UNUSED( nCapacity );
  25. return true;
  26. }
  27. };
  28. /// The lock \ref lock_array cell selection policy "division by modulo"
  29. struct mod_select_policy
  30. {
  31. /// Returns <tt> nWhat % nCapacity </tt>
  32. size_t operator()( size_t nWhat, size_t nCapacity ) const
  33. {
  34. return nWhat % nCapacity;
  35. }
  36. /// Checks if \p nCapacity is acceptable by policy. For modulo policy, any positive \p nCapacity is accepted.
  37. static bool is_capacity_accepted( size_t nCapacity )
  38. {
  39. return nCapacity > 0;
  40. }
  41. };
  42. /// The lock \ref lock_array cell selection policy "division by modulo of power of 2"
  43. /**
  44. This policy may be used if the size of lock array is equal to power of two.
  45. */
  46. struct pow2_select_policy
  47. {
  48. //@cond
  49. const size_t m_nMask;
  50. //@endcond
  51. /// Ctor. \p nCapacity must be power of two.
  52. pow2_select_policy( size_t nCapacity )
  53. : m_nMask( nCapacity - 1 )
  54. {
  55. assert( is_capacity_accepted( nCapacity ));
  56. }
  57. /// Copy constructor
  58. pow2_select_policy( pow2_select_policy const& src )
  59. : m_nMask( src.m_nMask )
  60. {}
  61. /// Move constructor
  62. pow2_select_policy( pow2_select_policy&& src )
  63. : m_nMask( src.m_nMask )
  64. {}
  65. /// Returns <tt>nWhat & (nPow2 - 1)</tt>
  66. size_t operator()( size_t nWhat, size_t ) const
  67. {
  68. return nWhat & m_nMask;
  69. }
  70. /// Checks if \p nCapacity is acceptable by policy. \p nCapacity must be power of two
  71. static bool is_capacity_accepted( size_t nCapacity )
  72. {
  73. return cds::beans::is_power2( nCapacity );
  74. }
  75. };
  76. /// Array of locks
  77. /**
  78. The lock array is useful for building fine-grained lock-based data structure
  79. based on striping technique. Instead of locking access to data struct (a hash map, for example)
  80. at whole, the striping locks only part of the map (a bucket). So, access to different buckets
  81. can be simultaneous.
  82. Template arguments:
  83. - \p Lock - lock type, for example, \p std::mutex, \p cds::sync::spin_lock
  84. - \p SelectPolicy - array cell selection policy, the default is \ref mod_select_policy
  85. Available policies: \ref trivial_select_policy, \ref pow2_select_policy, \ref mod_select_policy.
  86. - \p Alloc - memory allocator for array
  87. To determine array's cell the selection policy \p SelectPolicy functor is used. Two arguments
  88. are passed to the policy:
  89. \code size_t operator()( size_t nHint, size_t nCapacity ) const \endcode
  90. - \p nHint - a hint to calculate cell index in the lock array. Usually, it is a hash value.
  91. - \p nCapacity - the size of the lock array
  92. The functor should return the index in the lock array.
  93. Note that the type of \p nHint parameter can be any.
  94. */
  95. template <typename Lock
  96. , typename SelectPolicy = mod_select_policy
  97. , class Alloc = CDS_DEFAULT_ALLOCATOR
  98. >
  99. class lock_array
  100. {
  101. //@cond
  102. typedef ::cds::details::Allocator< Lock, Alloc > cxx_allocator;
  103. //@endcond
  104. public:
  105. typedef Lock lock_type ; ///< lock type
  106. typedef SelectPolicy select_cell_policy ; ///< Cell selection policy functor
  107. static size_t const c_nUnspecifiedCell = (size_t) -1 ; ///< failed \ref try_lock call result
  108. protected:
  109. lock_type * m_arrLocks ; ///< lock array
  110. size_t const m_nCapacity ; ///< array capacity
  111. select_cell_policy m_SelectCellPolicy ; ///< Cell selection policy
  112. protected:
  113. //@cond
  114. static lock_type * create_lock_array( size_t nCapacity )
  115. {
  116. return cxx_allocator().NewArray( nCapacity );
  117. }
  118. static void delete_lock_array( lock_type * pArr, size_t nCapacity )
  119. {
  120. if ( pArr )
  121. cxx_allocator().Delete( pArr, nCapacity );
  122. }
  123. // Only for internal use!!!
  124. lock_array()
  125. : m_arrLocks( nullptr )
  126. , m_nCapacity(0)
  127. {}
  128. lock_array( select_cell_policy const& policy )
  129. : m_arrLocks( nullptr )
  130. , m_nCapacity(0)
  131. , m_SelectCellPolicy( policy )
  132. {}
  133. //@endcond
  134. public:
  135. /// Constructs array of locks
  136. /**
  137. Allocates the array and initializes all locks as unlocked.
  138. */
  139. lock_array(
  140. size_t nCapacity ///< [in] Array size
  141. )
  142. : m_arrLocks( nullptr )
  143. , m_nCapacity( nCapacity )
  144. {
  145. m_arrLocks = create_lock_array( nCapacity );
  146. }
  147. /// Constructs array of lock and copy cell selection policy
  148. /**
  149. Allocates the array and initializes all locks as unlocked.
  150. */
  151. lock_array(
  152. size_t nCapacity, ///< [in] Array size
  153. select_cell_policy const& policy ///< Cell selection policy (copy-constructible)
  154. )
  155. : m_arrLocks( nullptr )
  156. , m_nCapacity( nCapacity )
  157. , m_SelectCellPolicy( policy )
  158. {
  159. m_arrLocks = create_lock_array( m_nCapacity );
  160. }
  161. /// Constructs array of lock and move cell selection policy
  162. /**
  163. Allocates the array and initializes all locks as unlocked.
  164. */
  165. lock_array(
  166. size_t nCapacity, ///< [in] Array size
  167. select_cell_policy&& policy ///< Cell selection policy (move-constructible)
  168. )
  169. : m_arrLocks( nullptr )
  170. , m_nCapacity( nCapacity )
  171. , m_SelectCellPolicy( std::forward<select_cell_policy>( policy ))
  172. {
  173. m_arrLocks = create_lock_array( m_nCapacity );
  174. }
  175. /// Destructs array of locks and frees used memory
  176. ~lock_array()
  177. {
  178. delete_lock_array( m_arrLocks, m_nCapacity );
  179. }
  180. /// Locks a lock at cell \p hint
  181. /**
  182. To define real array's cell which should be locked, \ref select_cell_policy is used.
  183. The target cell is a result of <tt>select_cell_policy( hint, size())</tt>.
  184. Returns the index of locked lock.
  185. */
  186. template <typename Q>
  187. size_t lock( Q const& hint )
  188. {
  189. size_t nCell = m_SelectCellPolicy( hint, size());
  190. assert( nCell < size());
  191. m_arrLocks[nCell].lock();
  192. return nCell;
  193. }
  194. /// Try lock a lock at cell \p hint
  195. /**
  196. To define real array's cell which should be locked, \ref select_cell_policy is used.
  197. The target cell is a result of <tt>select_cell_policy( hint, size())</tt>.
  198. Returns the index of locked lock if success, \ref c_nUnspecifiedCell constant otherwise.
  199. */
  200. template <typename Q>
  201. size_t try_lock( Q const& hint )
  202. {
  203. size_t nCell = m_SelectCellPolicy( hint, size());
  204. assert( nCell < size());
  205. if ( m_arrLocks[nCell].try_lock())
  206. return nCell;
  207. return c_nUnspecifiedCell;
  208. }
  209. /// Unlock the lock specified by index \p nCell
  210. void unlock( size_t nCell )
  211. {
  212. assert( nCell < size());
  213. m_arrLocks[nCell].unlock();
  214. }
  215. /// Lock all
  216. void lock_all()
  217. {
  218. lock_type * pLock = m_arrLocks;
  219. for ( lock_type * pEnd = m_arrLocks + size(); pLock != pEnd; ++pLock )
  220. pLock->lock();
  221. }
  222. /// Unlock all
  223. void unlock_all()
  224. {
  225. lock_type * pLock = m_arrLocks;
  226. for ( lock_type * pEnd = m_arrLocks + size(); pLock != pEnd; ++pLock )
  227. pLock->unlock();
  228. }
  229. /// Get lock at cell \p nCell.
  230. /**
  231. Precondition: <tt>nCell < size()</tt>
  232. */
  233. lock_type& at( size_t nCell ) const
  234. {
  235. assert( nCell < size());
  236. return m_arrLocks[ nCell ];
  237. }
  238. /// Size of lock array.
  239. size_t size() const
  240. {
  241. return m_nCapacity;
  242. }
  243. };
  244. }} // namespace cds::sync
  245. //@cond
  246. namespace std {
  247. /// Specialization \p std::unique_lock for \p sync::lock_array
  248. template <typename Lock, typename SelectPolicy, class Alloc>
  249. class unique_lock< cds::sync::lock_array< Lock, SelectPolicy, Alloc > >
  250. {
  251. public:
  252. typedef cds::sync::lock_array< Lock, SelectPolicy, Alloc > lock_array_type; ///< Lock array type
  253. private:
  254. lock_array_type& m_arrLocks;
  255. size_t m_nLockGuarded;
  256. static const size_t c_nLockAll = ~size_t( 0 );
  257. public:
  258. /// Onws the lock array \p arrLocks and locks a cell determined by \p hint parameter
  259. template <typename Q>
  260. unique_lock( lock_array_type& arrLocks, Q const& hint )
  261. : m_arrLocks( arrLocks )
  262. , m_nLockGuarded( arrLocks.lock( hint ))
  263. {}
  264. /// Locks all from \p arrLocks array
  265. unique_lock( lock_array_type& arrLocks )
  266. : m_arrLocks( arrLocks )
  267. , m_nLockGuarded( c_nLockAll )
  268. {
  269. arrLocks.lock_all();
  270. }
  271. unique_lock() = delete;
  272. unique_lock( unique_lock const& ) = delete;
  273. ~unique_lock()
  274. {
  275. if ( m_nLockGuarded == c_nLockAll )
  276. m_arrLocks.unlock_all();
  277. else
  278. m_arrLocks.unlock( m_nLockGuarded );
  279. }
  280. };
  281. } // namespace std
  282. //@endcond
  283. #endif // #ifndef CDSLIB_SYNC_LOCK_ARRAY_H