3 #ifndef __CDS_LOCK_ARRAY_H
4 #define __CDS_LOCK_ARRAY_H
6 #include <mutex> //unique_lock
7 #include <cds/details/allocator.h>
8 #include <cds/algo/int_algo.h>
10 namespace cds { namespace lock {
12 /// Trivial lock \ref array selection policy
13 struct trivial_select_policy
16 size_t operator()( size_t nWhat, size_t nCapacity ) const
18 assert( nWhat < nCapacity );
22 /// Checks if \p nCapacity is acceptable by policy. For trivial policy, any \p nCapacity is accepted.
23 static bool is_capacity_accepted( size_t nCapacity )
29 /// The lock \ref array cell selection policy "division by modulo"
30 struct mod_select_policy
32 /// Returns <tt> nWhat % nCapacity </tt>
33 size_t operator()( size_t nWhat, size_t nCapacity ) const
35 return nWhat % nCapacity;
38 /// Checks if \p nCapacity is acceptable by policy. For modulo policy, any positive \p nCapacity is accepted.
39 static bool is_capacity_accepted( size_t nCapacity )
45 /// The lock \ref array cell selection policy "division by modulo of power of 2"
47 This policy may be used if the size of lock array is equal to power of two.
49 struct pow2_select_policy
55 /// Ctor. \p nCapacity must be power of two.
56 pow2_select_policy( size_t nCapacity )
57 : m_nMask( nCapacity - 1 )
59 assert( is_capacity_accepted( nCapacity ));
63 pow2_select_policy( pow2_select_policy const& src )
64 : m_nMask( src.m_nMask )
68 pow2_select_policy( pow2_select_policy&& src )
69 : m_nMask( src.m_nMask )
72 /// Returns <tt>nWhat & (nPow2 - 1)</tt>
73 size_t operator()( size_t nWhat, size_t ) const
75 return nWhat & m_nMask;
78 /// Checks if \p nCapacity is acceptable by policy. \p nCapacity must be power of two
79 static bool is_capacity_accepted( size_t nCapacity )
81 return cds::beans::is_power2( nCapacity );
87 The lock array is useful for building fine-grained lock-based data structure
88 based on striping technique. Instead of locking access to data struct (a hash map, for example)
89 at whole, the striping locks only part of the map (a bucket). So, access to different buckets
93 - \p Lock - lock type, for example, \p std::mutex, \p cds::lock::Spinlock
94 - \p SelectPolicy - array cell selection policy, the default is \ref mod_select_policy
95 Available policies: \ref trivial_select_policy, \ref pow2_select_policy, \ref mod_select_policy.
96 - \p Alloc - memory allocator for array
98 To determine array's cell the selection policy \p SelectPolicy functor is used. Two arguments
99 are passed to the policy:
100 \code size_t operator()( size_t nHint, size_t nCapacity ) const \endcode
101 - \p nHint - a hint to calculate cell index in the lock array. Usually, it is a hash value.
102 - \p nCapacity - the size of the lock array
103 The functor should return the index in the lock array.
105 Note that the type of \p nHint parameter can be any.
107 template <typename Lock
108 , typename SelectPolicy = mod_select_policy
109 , class Alloc = CDS_DEFAULT_ALLOCATOR
114 typedef ::cds::details::Allocator< Lock, Alloc > cxx_allocator;
117 typedef Lock lock_type ; ///< lock type
118 typedef SelectPolicy select_cell_policy ; ///< Cell selection policy functor
119 static size_t const c_nUnspecifiedCell = (size_t) -1 ; ///< failed \ref try_lock call result
122 lock_type * m_arrLocks ; ///< lock array
123 size_t const m_nCapacity ; ///< array capacity
125 select_cell_policy m_SelectCellPolicy ; ///< Cell selection policy
129 static lock_type * create_lock_array( size_t nCapacity )
131 return cxx_allocator().NewArray( nCapacity );
133 static void delete_lock_array( lock_type * pArr, size_t nCapacity )
136 cxx_allocator().Delete( pArr, nCapacity );
139 // Only for internal use!!!
141 : m_arrLocks( nullptr )
144 array( select_cell_policy const& policy )
145 : m_arrLocks( nullptr )
147 , m_SelectCellPolicy( policy )
152 /// Constructs array of locks
154 Allocates the array and initializes all locks as unlocked.
157 size_t nCapacity ///< [in] Array size
159 : m_arrLocks( nullptr )
160 , m_nCapacity( nCapacity )
162 m_arrLocks = create_lock_array( nCapacity );
165 /// Constructs array of lock and copy cell selection policy
167 Allocates the array and initializes all locks as unlocked.
170 size_t nCapacity, ///< [in] Array size
171 select_cell_policy const& policy ///< Cell selection policy (copy-constructible)
173 : m_arrLocks( nullptr )
174 , m_nCapacity( nCapacity )
175 , m_SelectCellPolicy( policy )
177 m_arrLocks = create_lock_array( m_nCapacity );
180 /// Constructs array of lock and move cell selection policy
182 Allocates the array and initializes all locks as unlocked.
185 size_t nCapacity, ///< [in] Array size
186 select_cell_policy&& policy ///< Cell selection policy (move-constructible)
188 : m_arrLocks( nullptr )
189 , m_nCapacity( nCapacity )
190 , m_SelectCellPolicy( std::forward<select_cell_policy>( policy ))
192 m_arrLocks = create_lock_array( m_nCapacity );
195 /// Destructs array of locks and frees used memory
198 delete_lock_array( m_arrLocks, m_nCapacity );
201 /// Locks a lock at cell \p hint
203 To define real array's cell which should be locked, \ref select_cell_policy is used.
204 The target cell is a result of <tt>select_cell_policy( hint, size() )</tt>.
206 Returns the index of locked lock.
208 template <typename Q>
209 size_t lock( Q const& hint )
211 size_t nCell = m_SelectCellPolicy( hint, size() );
212 assert( nCell < size() );
213 m_arrLocks[nCell].lock();
217 /// Try lock a lock at cell \p hint
219 To define real array's cell which should be locked, \ref select_cell_policy is used.
220 The target cell is a result of <tt>select_cell_policy( hint, size() )</tt>.
222 Returns the index of locked lock if success, \ref c_nUnspecifiedCell constant otherwise.
224 template <typename Q>
225 size_t try_lock( Q const& hint )
227 size_t nCell = m_SelectCellPolicy( hint, size() );
228 assert( nCell < size() );
229 if ( m_arrLocks[nCell].try_lock() )
231 return c_nUnspecifiedCell;
234 /// Unlock the lock specified by index \p nCell
235 void unlock( size_t nCell )
237 assert( nCell < size() );
238 m_arrLocks[nCell].unlock();
244 lock_type * pLock = m_arrLocks;
245 for ( lock_type * pEnd = m_arrLocks + size(); pLock != pEnd; ++pLock )
252 lock_type * pLock = m_arrLocks;
253 for ( lock_type * pEnd = m_arrLocks + size(); pLock != pEnd; ++pLock )
257 /// Get lock at cell \p nCell.
259 Precondition: <tt>nCell < size()</tt>
261 lock_type& at( size_t nCell ) const
263 assert( nCell < size() );
264 return m_arrLocks[ nCell ];
267 /// Size of lock array.
274 }} // namespace cds::lock
279 /// Specialization \ref scoped_lock for lock::array
280 template <typename Lock, typename SelectPolicy, class Alloc>
281 class unique_lock< cds::lock::array< Lock, SelectPolicy, Alloc > >
284 typedef cds::lock::array< Lock, SelectPolicy, Alloc > lock_array_type; ///< Lock array type
287 lock_array_type& m_arrLocks;
288 size_t m_nLockGuarded;
290 static const size_t c_nLockAll = ~size_t( 0 );
293 /// Onws the lock array \p arrLocks and locks a cell determined by \p hint parameter
294 template <typename Q>
295 unique_lock( lock_array_type& arrLocks, Q const& hint )
296 : m_arrLocks( arrLocks )
297 , m_nLockGuarded( arrLocks.lock( hint ) )
300 /// Locks all from \p arrLocks array
301 unique_lock( lock_array_type& arrLocks )
302 : m_arrLocks( arrLocks )
303 , m_nLockGuarded( c_nLockAll )
308 unique_lock() = delete;
309 unique_lock( unique_lock const& ) = delete;
313 if ( m_nLockGuarded == c_nLockAll )
314 m_arrLocks.unlock_all();
316 m_arrLocks.unlock( m_nLockGuarded );
322 #endif // #ifndef __CDS_LOCK_ARRAY_H