| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328 |
- // Copyright (c) 2006-2018 Maxim Khizhinsky
- //
- // Distributed under the Boost Software License, Version 1.0. (See accompanying
- // file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
- #ifndef CDSLIB_SYNC_LOCK_ARRAY_H
- #define CDSLIB_SYNC_LOCK_ARRAY_H
- #include <mutex> //unique_lock
- #include <cds/details/allocator.h>
- #include <cds/algo/int_algo.h>
- namespace cds { namespace sync {
- /// Trivial lock \ref lock_array selection policy
- struct trivial_select_policy
- {
- /// Returns \p nWhat
- size_t operator()( size_t nWhat, size_t nCapacity ) const
- {
- assert( nWhat < nCapacity );
- CDS_UNUSED( nCapacity );
- return nWhat;
- }
- /// Checks if \p nCapacity is acceptable by policy. For trivial policy, any \p nCapacity is accepted.
- static bool is_capacity_accepted( size_t nCapacity )
- {
- CDS_UNUSED( nCapacity );
- return true;
- }
- };
- /// The lock \ref lock_array cell selection policy "division by modulo"
- struct mod_select_policy
- {
- /// Returns <tt> nWhat % nCapacity </tt>
- size_t operator()( size_t nWhat, size_t nCapacity ) const
- {
- return nWhat % nCapacity;
- }
- /// Checks if \p nCapacity is acceptable by policy. For modulo policy, any positive \p nCapacity is accepted.
- static bool is_capacity_accepted( size_t nCapacity )
- {
- return nCapacity > 0;
- }
- };
- /// The lock \ref lock_array cell selection policy "division by modulo of power of 2"
- /**
- This policy may be used if the size of lock array is equal to power of two.
- */
- struct pow2_select_policy
- {
- //@cond
- const size_t m_nMask;
- //@endcond
- /// Ctor. \p nCapacity must be power of two.
- pow2_select_policy( size_t nCapacity )
- : m_nMask( nCapacity - 1 )
- {
- assert( is_capacity_accepted( nCapacity ));
- }
- /// Copy constructor
- pow2_select_policy( pow2_select_policy const& src )
- : m_nMask( src.m_nMask )
- {}
- /// Move constructor
- pow2_select_policy( pow2_select_policy&& src )
- : m_nMask( src.m_nMask )
- {}
- /// Returns <tt>nWhat & (nPow2 - 1)</tt>
- size_t operator()( size_t nWhat, size_t ) const
- {
- return nWhat & m_nMask;
- }
- /// Checks if \p nCapacity is acceptable by policy. \p nCapacity must be power of two
- static bool is_capacity_accepted( size_t nCapacity )
- {
- return cds::beans::is_power2( nCapacity );
- }
- };
- /// Array of locks
- /**
- The lock array is useful for building fine-grained lock-based data structure
- based on striping technique. Instead of locking access to data struct (a hash map, for example)
- at whole, the striping locks only part of the map (a bucket). So, access to different buckets
- can be simultaneous.
- Template arguments:
- - \p Lock - lock type, for example, \p std::mutex, \p cds::sync::spin_lock
- - \p SelectPolicy - array cell selection policy, the default is \ref mod_select_policy
- Available policies: \ref trivial_select_policy, \ref pow2_select_policy, \ref mod_select_policy.
- - \p Alloc - memory allocator for array
- To determine array's cell the selection policy \p SelectPolicy functor is used. Two arguments
- are passed to the policy:
- \code size_t operator()( size_t nHint, size_t nCapacity ) const \endcode
- - \p nHint - a hint to calculate cell index in the lock array. Usually, it is a hash value.
- - \p nCapacity - the size of the lock array
- The functor should return the index in the lock array.
- Note that the type of \p nHint parameter can be any.
- */
- template <typename Lock
- , typename SelectPolicy = mod_select_policy
- , class Alloc = CDS_DEFAULT_ALLOCATOR
- >
- class lock_array
- {
- //@cond
- typedef ::cds::details::Allocator< Lock, Alloc > cxx_allocator;
- //@endcond
- public:
- typedef Lock lock_type ; ///< lock type
- typedef SelectPolicy select_cell_policy ; ///< Cell selection policy functor
- static size_t const c_nUnspecifiedCell = (size_t) -1 ; ///< failed \ref try_lock call result
- protected:
- lock_type * m_arrLocks ; ///< lock array
- size_t const m_nCapacity ; ///< array capacity
- select_cell_policy m_SelectCellPolicy ; ///< Cell selection policy
- protected:
- //@cond
- static lock_type * create_lock_array( size_t nCapacity )
- {
- return cxx_allocator().NewArray( nCapacity );
- }
- static void delete_lock_array( lock_type * pArr, size_t nCapacity )
- {
- if ( pArr )
- cxx_allocator().Delete( pArr, nCapacity );
- }
- // Only for internal use!!!
- lock_array()
- : m_arrLocks( nullptr )
- , m_nCapacity(0)
- {}
- lock_array( select_cell_policy const& policy )
- : m_arrLocks( nullptr )
- , m_nCapacity(0)
- , m_SelectCellPolicy( policy )
- {}
- //@endcond
- public:
- /// Constructs array of locks
- /**
- Allocates the array and initializes all locks as unlocked.
- */
- lock_array(
- size_t nCapacity ///< [in] Array size
- )
- : m_arrLocks( nullptr )
- , m_nCapacity( nCapacity )
- {
- m_arrLocks = create_lock_array( nCapacity );
- }
- /// Constructs array of lock and copy cell selection policy
- /**
- Allocates the array and initializes all locks as unlocked.
- */
- lock_array(
- size_t nCapacity, ///< [in] Array size
- select_cell_policy const& policy ///< Cell selection policy (copy-constructible)
- )
- : m_arrLocks( nullptr )
- , m_nCapacity( nCapacity )
- , m_SelectCellPolicy( policy )
- {
- m_arrLocks = create_lock_array( m_nCapacity );
- }
- /// Constructs array of lock and move cell selection policy
- /**
- Allocates the array and initializes all locks as unlocked.
- */
- lock_array(
- size_t nCapacity, ///< [in] Array size
- select_cell_policy&& policy ///< Cell selection policy (move-constructible)
- )
- : m_arrLocks( nullptr )
- , m_nCapacity( nCapacity )
- , m_SelectCellPolicy( std::forward<select_cell_policy>( policy ))
- {
- m_arrLocks = create_lock_array( m_nCapacity );
- }
- /// Destructs array of locks and frees used memory
- ~lock_array()
- {
- delete_lock_array( m_arrLocks, m_nCapacity );
- }
- /// Locks a lock at cell \p hint
- /**
- To define real array's cell which should be locked, \ref select_cell_policy is used.
- The target cell is a result of <tt>select_cell_policy( hint, size())</tt>.
- Returns the index of locked lock.
- */
- template <typename Q>
- size_t lock( Q const& hint )
- {
- size_t nCell = m_SelectCellPolicy( hint, size());
- assert( nCell < size());
- m_arrLocks[nCell].lock();
- return nCell;
- }
- /// Try lock a lock at cell \p hint
- /**
- To define real array's cell which should be locked, \ref select_cell_policy is used.
- The target cell is a result of <tt>select_cell_policy( hint, size())</tt>.
- Returns the index of locked lock if success, \ref c_nUnspecifiedCell constant otherwise.
- */
- template <typename Q>
- size_t try_lock( Q const& hint )
- {
- size_t nCell = m_SelectCellPolicy( hint, size());
- assert( nCell < size());
- if ( m_arrLocks[nCell].try_lock())
- return nCell;
- return c_nUnspecifiedCell;
- }
- /// Unlock the lock specified by index \p nCell
- void unlock( size_t nCell )
- {
- assert( nCell < size());
- m_arrLocks[nCell].unlock();
- }
- /// Lock all
- void lock_all()
- {
- lock_type * pLock = m_arrLocks;
- for ( lock_type * pEnd = m_arrLocks + size(); pLock != pEnd; ++pLock )
- pLock->lock();
- }
- /// Unlock all
- void unlock_all()
- {
- lock_type * pLock = m_arrLocks;
- for ( lock_type * pEnd = m_arrLocks + size(); pLock != pEnd; ++pLock )
- pLock->unlock();
- }
- /// Get lock at cell \p nCell.
- /**
- Precondition: <tt>nCell < size()</tt>
- */
- lock_type& at( size_t nCell ) const
- {
- assert( nCell < size());
- return m_arrLocks[ nCell ];
- }
- /// Size of lock array.
- size_t size() const
- {
- return m_nCapacity;
- }
- };
- }} // namespace cds::sync
- //@cond
- namespace std {
- /// Specialization \p std::unique_lock for \p sync::lock_array
- template <typename Lock, typename SelectPolicy, class Alloc>
- class unique_lock< cds::sync::lock_array< Lock, SelectPolicy, Alloc > >
- {
- public:
- typedef cds::sync::lock_array< Lock, SelectPolicy, Alloc > lock_array_type; ///< Lock array type
- private:
- lock_array_type& m_arrLocks;
- size_t m_nLockGuarded;
- static const size_t c_nLockAll = ~size_t( 0 );
- public:
- /// Onws the lock array \p arrLocks and locks a cell determined by \p hint parameter
- template <typename Q>
- unique_lock( lock_array_type& arrLocks, Q const& hint )
- : m_arrLocks( arrLocks )
- , m_nLockGuarded( arrLocks.lock( hint ))
- {}
- /// Locks all from \p arrLocks array
- unique_lock( lock_array_type& arrLocks )
- : m_arrLocks( arrLocks )
- , m_nLockGuarded( c_nLockAll )
- {
- arrLocks.lock_all();
- }
- unique_lock() = delete;
- unique_lock( unique_lock const& ) = delete;
- ~unique_lock()
- {
- if ( m_nLockGuarded == c_nLockAll )
- m_arrLocks.unlock_all();
- else
- m_arrLocks.unlock( m_nLockGuarded );
- }
- };
- } // namespace std
- //@endcond
- #endif // #ifndef CDSLIB_SYNC_LOCK_ARRAY_H
|