Fixed -Wshadow warning
[libcds.git] / cds / intrusive / cuckoo_set.h
index 09b3cb408aa729f6c4c1289b06933c0d85a57c69..9508c24ae861125e95c812d87663d10fcbccd293 100644 (file)
@@ -1,11 +1,11 @@
 /*
     This file is a part of libcds - Concurrent Data Structures library
 
-    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
+    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017
 
     Source code repo: http://github.com/khizmax/libcds/
     Download: http://sourceforge.net/projects/libcds/files/
-    
+
     Redistribution and use in source and binary forms, with or without
     modification, are permitted provided that the following conditions are met:
 
@@ -25,7 +25,7 @@
     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.     
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 #ifndef CDSLIB_INTRUSIVE_CUCKOO_SET_H
@@ -413,10 +413,10 @@ namespace cds { namespace intrusive {
             {
             public:
                 // placeholder ctor
-                lock_array(): lock_array_type( typename lock_array_type::select_cell_policy(2) ) {}
+                lock_array(): lock_array_type( typename lock_array_type::select_cell_policy(2)) {}
 
                 // real ctor
-                lock_array( size_t nCapacity ): lock_array_type( nCapacity, typename lock_array_type::select_cell_policy(nCapacity) ) {}
+                lock_array( size_t nCapacity ): lock_array_type( nCapacity, typename lock_array_type::select_cell_policy(nCapacity)) {}
             };
 
             class scoped_lock: public std::unique_lock< lock_array_type >
@@ -469,7 +469,7 @@ namespace cds { namespace intrusive {
                     if ( m_bLocked ) {
                         m_guard[0] = &(policy.m_Locks[0].at(nCell));
                         for ( unsigned int i = 1; i < c_nArity; ++i ) {
-                            m_guard[i] = &( policy.m_Locks[i].at( policy.m_Locks[i].lock( arrHash[i] )) );
+                            m_guard[i] = &( policy.m_Locks[i].at( policy.m_Locks[i].lock( arrHash[i] )));
                         }
                     }
                     else {
@@ -615,9 +615,9 @@ namespace cds { namespace intrusive {
 
         /// Refinable concurrent access policy
         /**
-            This is one of available opt::mutex_policy option type for CuckooSet
+            This is one of available \p opt::mutex_policy option type for \p CuckooSet
 
-            Refining is like a striping technique (see cuckoo::striping)
+            Refining is like a striping technique (see \p cuckoo::striping)
             but it allows growing the size of lock array when resizing the hash table.
             So, the sizes of hash table and lock array are equal.
 
@@ -626,7 +626,7 @@ namespace cds { namespace intrusive {
                 The default is \p std::recursive_mutex. The mutex type should be default-constructible.
             - \p Arity - unsigned int constant that specifies an arity. The arity is the count of hash functors, i.e., the
                 count of lock arrays. Default value is 2.
-            - \p BackOff - back-off strategy. Default is cds::backoff::yield
+            - \p BackOff - back-off strategy. Default is \p cds::backoff::Default
             - \p Alloc - allocator type used for lock array memory allocation. Default is \p CDS_DEFAULT_ALLOCATOR.
             - \p Stat - internal statistics type. Note that this template argument is automatically selected by \ref CuckooSet
                 class according to its \p opt::stat option.
@@ -634,7 +634,7 @@ namespace cds { namespace intrusive {
         template <
             class RecursiveLock = std::recursive_mutex,
             unsigned int Arity = 2,
-            typename BackOff = cds::backoff::yield,
+            typename BackOff = cds::backoff::Default,
             class Alloc = CDS_DEFAULT_ALLOCATOR,
             class Stat = empty_refinable_stat
         >
@@ -687,9 +687,9 @@ namespace cds { namespace intrusive {
 
             atomics::atomic< owner_t >   m_Owner     ;   ///< owner mark (thread id + boolean flag)
             atomics::atomic<size_t>      m_nCapacity ;   ///< lock array capacity
-            lock_array_ptr                  m_arrLocks[ c_nArity ]  ; ///< Lock array. The capacity of array is specified in constructor.
-            spinlock_type                   m_access    ;   ///< access to m_arrLocks
-            statistics_type                 m_Stat      ;   ///< internal statistics
+            lock_array_ptr               m_arrLocks[ c_nArity ]  ; ///< Lock array. The capacity of array is specified in constructor.
+            spinlock_type                m_access    ;   ///< access to m_arrLocks
+            statistics_type              m_Stat      ;   ///< internal statistics
             //@endcond
 
         protected:
@@ -697,19 +697,25 @@ namespace cds { namespace intrusive {
             struct lock_array_disposer {
                 void operator()( lock_array_type * pArr )
                 {
+                    // Seems, there is a false positive in std::shared_ptr deallocation in uninstrumented libc++
+                    // see, for example, https://groups.google.com/forum/#!topic/thread-sanitizer/eHu4dE_z7Cc
+                    // https://reviews.llvm.org/D21609
+                    CDS_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
                     lock_array_allocator().Delete( pArr );
+                    CDS_TSAN_ANNOTATE_IGNORE_WRITES_END;
                 }
             };
 
             lock_array_ptr create_lock_array( size_t nCapacity )
             {
-                return lock_array_ptr( lock_array_allocator().New( nCapacity ), lock_array_disposer() );
+                return lock_array_ptr( lock_array_allocator().New( nCapacity ), lock_array_disposer());
             }
 
             void acquire( size_t const * arrHash, lock_array_ptr * pLockArr, lock_type ** parrLock )
             {
                 owner_t me = (owner_t) cds::OS::get_current_thread_id();
                 owner_t who;
+                size_t cur_capacity;
 
                 back_off bkoff;
                 while ( true ) {
@@ -718,21 +724,19 @@ namespace cds { namespace intrusive {
                         scoped_spinlock sl(m_access);
                         for ( unsigned int i = 0; i < c_nArity; ++i )
                             pLockArr[i] = m_arrLocks[i];
+                        cur_capacity = m_nCapacity.load( atomics::memory_order_acquire );
                     }
 
                     // wait while resizing
                     while ( true ) {
                         who = m_Owner.load( atomics::memory_order_acquire );
-                        if ( !( who & 1 ) || (who >> 1) == (me & c_nOwnerMask) )
+                        if ( !( who & 1 ) || (who >> 1) == (me & c_nOwnerMask))
                             break;
                         bkoff();
                         m_Stat.onCellWaitResizing();
                     }
 
-                    if ( pLockArr[0] != m_arrLocks[0] ) {
-                        m_Stat.onCellArrayChanged();
-                    }
-                    else {
+                    if ( cur_capacity == m_nCapacity.load( atomics::memory_order_acquire )) {
 
                         size_t const nMask = pLockArr[0]->size() - 1;
                         assert( cds::beans::is_power2( nMask + 1 ));
@@ -743,22 +747,23 @@ namespace cds { namespace intrusive {
                         }
 
                         who = m_Owner.load( atomics::memory_order_acquire );
-                        if ( ( !(who & 1) || (who >> 1) == (me & c_nOwnerMask) ) && m_arrLocks[0] == pLockArr[0] ) {
+                        if ( ( !(who & 1) || (who >> 1) == (me & c_nOwnerMask)) && cur_capacity == m_nCapacity.load( atomics::memory_order_acquire )) {
                             m_Stat.onCellLock();
                             return;
                         }
 
-                        for ( unsigned int i = 0; i < c_nArity; ++i ) {
+                        for ( unsigned int i = 0; i < c_nArity; ++i )
                             parrLock[i]->unlock();
-                        }
 
                         m_Stat.onCellLockFailed();
                     }
+                    else
+                        m_Stat.onCellArrayChanged();
 
                     // clears pLockArr can lead to calling dtor for each item of pLockArr[i] that may be a heavy-weighted operation
                     // (each pLockArr[i] is a shared pointer to array of a ton of mutexes)
                     // It is better to do this before the next loop iteration where we will use spin-locked assignment to pLockArr
-                    // Destructing a lot of mutexes under spin-lock is a bad solution
+                    // However, destructing a lot of mutexes under spin-lock is a bad solution
                     for ( unsigned int i = 0; i < c_nArity; ++i )
                         pLockArr[i].reset();
                 }
@@ -778,7 +783,7 @@ namespace cds { namespace intrusive {
                 parrLock[0] = &(m_arrLocks[0]->at(nCell));
 
                 for ( unsigned int i = 1; i < c_nArity; ++i ) {
-                    parrLock[i] = &( m_arrLocks[i]->at( m_arrLocks[i]->lock( arrHash[i] & nMask)) );
+                    parrLock[i] = &( m_arrLocks[i]->at( m_arrLocks[i]->lock( arrHash[i] & nMask)));
                 }
 
                 m_Stat.onSecondCellLock();
@@ -812,26 +817,27 @@ namespace cds { namespace intrusive {
             void acquire_resize( lock_array_ptr * pOldLocks )
             {
                 owner_t me = (owner_t) cds::OS::get_current_thread_id();
+                size_t cur_capacity;
 
                 while ( true ) {
                     {
                         scoped_spinlock sl(m_access);
                         for ( unsigned int i = 0; i < c_nArity; ++i )
                             pOldLocks[i] = m_arrLocks[i];
+                        cur_capacity = m_nCapacity.load( atomics::memory_order_acquire );
                     }
 
                     // global lock
                     owner_t ownNull = 0;
                     if ( m_Owner.compare_exchange_strong( ownNull, (me << 1) | 1, atomics::memory_order_acq_rel, atomics::memory_order_relaxed )) {
-                        if ( pOldLocks[0] != m_arrLocks[0] ) {
-                            m_Owner.store( 0, atomics::memory_order_release );
-                            m_Stat.onResizeLockArrayChanged();
-                        }
-                        else {
+                        if ( cur_capacity == m_nCapacity.load( atomics::memory_order_acquire )) {
                             pOldLocks[0]->lock_all();
                             m_Stat.onResizeLock();
                             return;
                         }
+
+                        m_Owner.store( 0, atomics::memory_order_release );
+                        m_Stat.onResizeLockArrayChanged();
                     }
                     else
                         m_Stat.onResizeLockIter();
@@ -839,7 +845,7 @@ namespace cds { namespace intrusive {
                     // clears pOldLocks can lead to calling dtor for each item of pOldLocks[i] that may be a heavy-weighted operation
                     // (each pOldLocks[i] is a shared pointer to array of a ton of mutexes)
                     // It is better to do this before the next loop iteration where we will use spin-locked assignment to pOldLocks
-                    // Destructing a lot of mutexes under spin-lock is a bad solution
+                    // However, destructing a lot of mutexes under spin-lock is a bad solution
                     for ( unsigned int i = 0; i < c_nArity; ++i )
                         pOldLocks[i].reset();
                 }
@@ -947,10 +953,10 @@ namespace cds { namespace intrusive {
 
                 {
                     scoped_spinlock sl(m_access);
+                    m_nCapacity.store( nCapacity, atomics::memory_order_release );
                     for ( unsigned int i = 0; i < c_nArity; ++i )
                         m_arrLocks[i] = pNew[i];
                 }
-                m_nCapacity.store( nCapacity, atomics::memory_order_release );
 
                 m_Stat.onResize();
             }
@@ -985,23 +991,23 @@ namespace cds { namespace intrusive {
             counter_type    m_nRelocateCallCount    ; ///< Count of \p relocate() function call
             counter_type    m_nRelocateRoundCount   ; ///< Count of attempts to relocate items
             counter_type    m_nFalseRelocateCount   ; ///< Count of unneeded attempts of \p relocate call
-            counter_type    m_nSuccessRelocateCount ; ///< Count of successfull item relocating
+            counter_type    m_nSuccessRelocateCount ; ///< Count of successful item relocating
             counter_type    m_nRelocateAboveThresholdCount; ///< Count of item relocating above probeset threshold
             counter_type    m_nFailedRelocateCount  ;   ///< Count of failed relocation attemp (when all probeset is full)
 
             counter_type    m_nResizeCallCount      ;   ///< Count of \p resize() function call
             counter_type    m_nFalseResizeCount     ;   ///< Count of false \p resize() function call (when other thread has been resized the set)
-            counter_type    m_nResizeSuccessNodeMove;   ///< Count of successfull node moving when resizing
+            counter_type    m_nResizeSuccessNodeMove;   ///< Count of successful node moving when resizing
             counter_type    m_nResizeRelocateCall   ;   ///< Count of \p relocate() function call from \p resize function
 
-            counter_type    m_nInsertSuccess        ;   ///< Count of successfull \p insert() function call
+            counter_type    m_nInsertSuccess        ;   ///< Count of successful \p insert() function call
             counter_type    m_nInsertFailed         ;   ///< Count of failed \p insert() function call
             counter_type    m_nInsertResizeCount    ;   ///< Count of \p resize() function call from \p insert()
             counter_type    m_nInsertRelocateCount  ;   ///< Count of \p relocate() function call from \p insert()
             counter_type    m_nInsertRelocateFault  ;   ///< Count of failed \p relocate() function call from \p insert()
 
             counter_type    m_nUpdateExistCount     ;   ///< Count of call \p update() function for existing node
-            counter_type    m_nUpdateSuccessCount   ;   ///< Count of successfull \p insert() function call for new node
+            counter_type    m_nUpdateSuccessCount   ;   ///< Count of successful \p insert() function call for new node
             counter_type    m_nUpdateResizeCount    ;   ///< Count of \p resize() function call from \p update()
             counter_type    m_nUpdateRelocateCount  ;   ///< Count of \p relocate() function call from \p update()
             counter_type    m_nUpdateRelocateFault  ;   ///< Count of failed \p relocate() function call from \p update()
@@ -1335,7 +1341,7 @@ namespace cds { namespace intrusive {
                 {
                     node_type * pPrev = itPrev.pNode;
                     node_type * pWhat = itWhat.pNode;
-                    assert( (!pPrev && pWhat == pHead) || (pPrev && pPrev->m_pNext == pWhat) );
+                    assert( (!pPrev && pWhat == pHead) || (pPrev && pPrev->m_pNext == pWhat));
 
                     if ( pPrev )
                         pPrev->m_pNext = pWhat->m_pNext;
@@ -1613,8 +1619,8 @@ namespace cds { namespace intrusive {
         <b>About Cuckoo hashing</b>
 
             [From <i>"The Art of Multiprocessor Programming"</i>]
-            Cuckoo hashing is a hashing algorithm in which a newly added item displaces any earlier item
-            occupying the same slot. For brevity, a table is a k-entry array of items. For a hash set f size
+            <a href="https://en.wikipedia.org/wiki/Cuckoo_hashing">Cuckoo hashing</a> is a hashing algorithm in which a newly added item displaces any earlier item
+            occupying the same slot. For brevity, a table is a k-entry array of items. For a hash set of size
             N = 2k we use a two-entry array of tables, and two independent hash functions,
             <tt> h0, h1: KeyRange -> 0,...,k-1</tt>
             mapping the set of possible keys to entries in he array. To test whether a value \p x is in the set,
@@ -1651,9 +1657,9 @@ namespace cds { namespace intrusive {
             the average search complexity is <tt>O(PROBE_SET/2)</tt>.
             However, the overhead of sorting can eliminate a gain of ordered search.
 
-            The probe set is ordered if \p opt::compare or \p opt::less is specified in \p Traits template
+            The probe set is ordered if \p compare or \p less is specified in \p Traits template
             parameter. Otherwise, the probe set is unordered and \p Traits should provide
-            \p opt::equal_to option.
+            \p equal_to predicate.
 
             The \p cds::intrusive::cuckoo namespace contains \p %CuckooSet-related declarations.
 
@@ -1879,7 +1885,7 @@ namespace cds { namespace intrusive {
             Otherwise, it is unordered and \p Traits should provide \p equal_to functor.
         */
         static bool const c_isSorted = !( std::is_same< typename traits::compare, opt::none >::value
-                && std::is_same< typename traits::less, opt::none >::value ) ;
+                && std::is_same< typename traits::less, opt::none >::value );
         static size_t const c_nArity = hash::size ; ///< the arity of cuckoo hashing: the number of hash functors provided; minimum 2.
 
         /// Key equality functor; used only for unordered probe-set
@@ -1937,9 +1943,9 @@ namespace cds { namespace intrusive {
     protected:
         bucket_entry *      m_BucketTable[ c_nArity ] ; ///< Bucket tables
 
-        size_t              m_nBucketMask           ;   ///< Hash bitmask; bucket table size minus 1.
-        unsigned int const  m_nProbesetSize         ;   ///< Probe set size
-        unsigned int const  m_nProbesetThreshold    ;   ///< Probe set threshold
+        atomics::atomic<size_t> m_nBucketMask           ;   ///< Hash bitmask; bucket table size minus 1.
+        unsigned int const      m_nProbesetSize         ;   ///< Probe set size
+        unsigned int const      m_nProbesetThreshold    ;   ///< Probe set threshold
 
         hash            m_Hash              ;   ///< Hash functor tuple
         mutex_policy    m_MutexPolicy       ;   ///< concurrent access policy
@@ -1978,7 +1984,7 @@ namespace cds { namespace intrusive {
         bucket_entry& bucket( unsigned int nTable, size_t nHash )
         {
             assert( nTable < c_nArity );
-            return m_BucketTable[nTable][nHash & m_nBucketMask];
+            return m_BucketTable[nTable][nHash & m_nBucketMask.load( atomics::memory_order_relaxed ) ];
         }
 
         static void store_hash( node_type * pNode, size_t * pHashes )
@@ -1993,9 +1999,9 @@ namespace cds { namespace intrusive {
 
         void allocate_bucket_tables( size_t nSize )
         {
-            assert( cds::beans::is_power2( nSize ) );
+            assert( cds::beans::is_power2( nSize ));
 
-            m_nBucketMask = nSize - 1;
+            m_nBucketMask.store( nSize - 1, atomics::memory_order_release );
             bucket_table_allocator alloc;
             for ( unsigned int i = 0; i < c_nArity; ++i )
                 m_BucketTable[i] = alloc.NewArray( nSize );
@@ -2011,7 +2017,7 @@ namespace cds { namespace intrusive {
         }
         void free_bucket_tables()
         {
-            free_bucket_tables( m_BucketTable, m_nBucketMask + 1 );
+            free_bucket_tables( m_BucketTable, m_nBucketMask.load( atomics::memory_order_relaxed ) + 1 );
         }
 
         static CDS_CONSTEXPR unsigned int const c_nUndefTable = (unsigned int) -1;
@@ -2041,7 +2047,7 @@ namespace cds { namespace intrusive {
                 unsigned int nTable = contains( arrPos, arrHash, val, pred );
                 if ( nTable != c_nUndefTable ) {
                     node_type& node = *arrPos[nTable].itFound;
-                    f( *node_traits::to_value_ptr(node) );
+                    f( *node_traits::to_value_ptr(node));
                     bucket( nTable, arrHash[nTable]).remove( arrPos[nTable].itPrev, arrPos[nTable].itFound );
                     --m_ItemCounter;
                     m_Stat.onEraseSuccess();
@@ -2094,14 +2100,14 @@ namespace cds { namespace intrusive {
                         return true;
                     }
 
-                    pVal = node_traits::to_value_ptr( *refBucket.begin() );
+                    pVal = node_traits::to_value_ptr( *refBucket.begin());
                     copy_hash( arrHash, *pVal );
 
                     scoped_cell_trylock guard2( m_MutexPolicy, arrHash );
-                    if ( !guard2.locked() )
+                    if ( !guard2.locked())
                         continue ;  // try one more time
 
-                    refBucket.remove( typename bucket_entry::iterator(), refBucket.begin() );
+                    refBucket.remove( typename bucket_entry::iterator(), refBucket.begin());
 
                     unsigned int i = (nTable + 1) % c_nArity;
 
@@ -2110,7 +2116,7 @@ namespace cds { namespace intrusive {
                         bucket_entry& bkt = bucket( i, arrHash[i] );
                         if ( bkt.size() < m_nProbesetThreshold ) {
                             position pos;
-                            contains_action::find( bkt, pos, i, arrHash[i], *pVal, key_predicate() ) ; // must return false!
+                            contains_action::find( bkt, pos, i, arrHash[i], *pVal, key_predicate()) ; // must return false!
                             bkt.insert_after( pos.itPrev, node_traits::to_node_ptr( pVal ));
                             m_Stat.onSuccessRelocateRound();
                             return true;
@@ -2124,7 +2130,7 @@ namespace cds { namespace intrusive {
                         bucket_entry& bkt = bucket( i, arrHash[i] );
                         if ( bkt.size() < m_nProbesetSize ) {
                             position pos;
-                            contains_action::find( bkt, pos, i, arrHash[i], *pVal, key_predicate() ) ; // must return false!
+                            contains_action::find( bkt, pos, i, arrHash[i], *pVal, key_predicate()) ; // must return false!
                             bkt.insert_after( pos.itPrev, node_traits::to_node_ptr( pVal ));
                             nTable = i;
                             memcpy( arrGoalHash, arrHash, sizeof(arrHash));
@@ -2149,12 +2155,12 @@ namespace cds { namespace intrusive {
         {
             m_Stat.onResizeCall();
 
-            size_t nOldCapacity = bucket_count();
+            size_t nOldCapacity = bucket_count( atomics::memory_order_acquire );
             bucket_entry *      pOldTable[ c_nArity ];
             {
                 scoped_resize_lock guard( m_MutexPolicy );
 
-                if ( nOldCapacity != bucket_count() ) {
+                if ( nOldCapacity != bucket_count()) {
                     m_Stat.onFalseResizeCall();
                     return;
                 }
@@ -2165,7 +2171,6 @@ namespace cds { namespace intrusive {
                 memcpy( pOldTable, m_BucketTable, sizeof(pOldTable));
                 allocate_bucket_tables( nCapacity );
 
-                typedef typename bucket_entry::iterator bucket_iterator;
                 hash_array arrHash;
                 position arrPos[ c_nArity ];
 
@@ -2179,7 +2184,7 @@ namespace cds { namespace intrusive {
 
                             value_type& val = *node_traits::to_value_ptr( *it );
                             copy_hash( arrHash, val );
-                            contains( arrPos, arrHash, val, key_predicate() ) ; // must return c_nUndefTable
+                            contains( arrPos, arrHash, val, key_predicate()) ; // must return c_nUndefTable
 
                             for ( unsigned int i = 0; i < c_nArity; ++i ) {
                                 bucket_entry& refBucket = bucket( i, arrHash[i] );
@@ -2195,7 +2200,7 @@ namespace cds { namespace intrusive {
                                 if ( refBucket.size() < m_nProbesetSize ) {
                                     refBucket.insert_after( arrPos[i].itPrev, &*it );
                                     assert( refBucket.size() > 1 );
-                                    copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()) );
+                                    copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()));
                                     m_Stat.onResizeRelocateCall();
                                     relocate( i, arrHash );
                                     break;
@@ -2231,7 +2236,7 @@ namespace cds { namespace intrusive {
             Probe set threshold = probe set size - 1
         */
         CuckooSet()
-            : m_nProbesetSize( calc_probeset_size(0) )
+            : m_nProbesetSize( calc_probeset_size(0))
             , m_nProbesetThreshold( m_nProbesetSize - 1 )
             , m_MutexPolicy( c_nDefaultInitialSize )
         {
@@ -2251,7 +2256,7 @@ namespace cds { namespace intrusive {
             , unsigned int nProbesetSize        ///< probe set size
             , unsigned int nProbesetThreshold = 0   ///< probe set threshold, <tt>nProbesetThreshold < nProbesetSize</tt>. If 0, <tt>nProbesetThreshold = nProbesetSize - 1</tt>
         )
-            : m_nProbesetSize( calc_probeset_size(nProbesetSize) )
+            : m_nProbesetSize( calc_probeset_size(nProbesetSize))
             , m_nProbesetThreshold( nProbesetThreshold ? nProbesetThreshold : m_nProbesetSize - 1 )
             , m_MutexPolicy( cds::beans::ceil2(nInitialSize ? nInitialSize : c_nDefaultInitialSize ))
         {
@@ -2268,7 +2273,7 @@ namespace cds { namespace intrusive {
         CuckooSet(
             hash_tuple_type const& h    ///< hash functor tuple of type <tt>std::tuple<H1, H2, ... Hn></tt> where <tt> n == \ref c_nArity </tt>
         )
-            : m_nProbesetSize( calc_probeset_size(0) )
+            : m_nProbesetSize( calc_probeset_size(0))
             , m_nProbesetThreshold( m_nProbesetSize -1 )
             , m_Hash( h )
             , m_MutexPolicy( c_nDefaultInitialSize )
@@ -2290,7 +2295,7 @@ namespace cds { namespace intrusive {
             , unsigned int nProbesetThreshold   ///< probe set threshold, <tt>nProbesetThreshold < nProbesetSize</tt>. If 0, <tt>nProbesetThreshold = nProbesetSize - 1</tt>
             , hash_tuple_type const& h    ///< hash functor tuple of type <tt>std::tuple<H1, H2, ... Hn></tt> where <tt> n == \ref c_nArity </tt>
         )
-            : m_nProbesetSize( calc_probeset_size(nProbesetSize) )
+            : m_nProbesetSize( calc_probeset_size(nProbesetSize))
             , m_nProbesetThreshold( nProbesetThreshold ? nProbesetThreshold : m_nProbesetSize - 1)
             , m_Hash( h )
             , m_MutexPolicy( cds::beans::ceil2(nInitialSize ? nInitialSize : c_nDefaultInitialSize ))
@@ -2308,9 +2313,9 @@ namespace cds { namespace intrusive {
         CuckooSet(
             hash_tuple_type&& h     ///< hash functor tuple of type <tt>std::tuple<H1, H2, ... Hn></tt> where <tt> n == \ref c_nArity </tt>
         )
-            : m_nProbesetSize( calc_probeset_size(0) )
+            : m_nProbesetSize( calc_probeset_size(0))
             , m_nProbesetThreshold( m_nProbesetSize / 2 )
-            , m_Hash( std::forward<hash_tuple_type>(h) )
+            , m_Hash( std::forward<hash_tuple_type>(h))
             , m_MutexPolicy( c_nDefaultInitialSize )
         {
             check_common_constraints();
@@ -2330,9 +2335,9 @@ namespace cds { namespace intrusive {
             , unsigned int nProbesetThreshold   ///< probe set threshold, <tt>nProbesetThreshold < nProbesetSize</tt>. If 0, <tt>nProbesetThreshold = nProbesetSize - 1</tt>
             , hash_tuple_type&& h    ///< hash functor tuple of type <tt>std::tuple<H1, H2, ... Hn></tt> where <tt> n == \ref c_nArity </tt>
         )
-            : m_nProbesetSize( calc_probeset_size(nProbesetSize) )
+            : m_nProbesetSize( calc_probeset_size(nProbesetSize))
             , m_nProbesetThreshold( nProbesetThreshold ? nProbesetThreshold : m_nProbesetSize - 1)
-            , m_Hash( std::forward<hash_tuple_type>(h) )
+            , m_Hash( std::forward<hash_tuple_type>(h))
             , m_MutexPolicy( cds::beans::ceil2(nInitialSize ? nInitialSize : c_nDefaultInitialSize ))
         {
             check_common_constraints();
@@ -2389,7 +2394,7 @@ namespace cds { namespace intrusive {
                 {
                     scoped_cell_lock guard( m_MutexPolicy, arrHash );
 
-                    if ( contains( arrPos, arrHash, val, key_predicate() ) != c_nUndefTable ) {
+                    if ( contains( arrPos, arrHash, val, key_predicate()) != c_nUndefTable ) {
                         m_Stat.onInsertFailed();
                         return false;
                     }
@@ -2413,7 +2418,7 @@ namespace cds { namespace intrusive {
                             ++m_ItemCounter;
                             nGoalTable = i;
                             assert( refBucket.size() > 1 );
-                            copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()) );
+                            copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()));
                             goto do_relocate;
                         }
                     }
@@ -2455,7 +2460,7 @@ namespace cds { namespace intrusive {
 
             The functor may change non-key fields of the \p item.
 
-            Returns std::pair<bool, bool> where \p first is \p true if operation is successfull,
+            Returns std::pair<bool, bool> where \p first is \p true if operation is successful,
             i.e. the node has been inserted or updated,
             \p second is \p true if new item has been added or \p false if the item with \p key
             already exists.
@@ -2475,7 +2480,7 @@ namespace cds { namespace intrusive {
                 {
                     scoped_cell_lock guard( m_MutexPolicy, arrHash );
 
-                    unsigned int nTable = contains( arrPos, arrHash, val, key_predicate() );
+                    unsigned int nTable = contains( arrPos, arrHash, val, key_predicate());
                     if ( nTable != c_nUndefTable ) {
                         func( false, *node_traits::to_value_ptr( *arrPos[nTable].itFound ), val );
                         m_Stat.onUpdateExist();
@@ -2507,7 +2512,7 @@ namespace cds { namespace intrusive {
                             ++m_ItemCounter;
                             nGoalTable = i;
                             assert( refBucket.size() > 1 );
-                            copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()) );
+                            copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()));
                             goto do_relocate;
                         }
                     }
@@ -2555,7 +2560,7 @@ namespace cds { namespace intrusive {
             {
                 scoped_cell_lock guard( m_MutexPolicy, arrHash );
 
-                unsigned int nTable = contains( arrPos, arrHash, val, key_predicate() );
+                unsigned int nTable = contains( arrPos, arrHash, val, key_predicate());
                 if ( nTable != c_nUndefTable && node_traits::to_value_ptr(*arrPos[nTable].itFound) == &val ) {
                     bucket( nTable, arrHash[nTable]).remove( arrPos[nTable].itPrev, arrPos[nTable].itFound );
                     --m_ItemCounter;
@@ -2736,11 +2741,11 @@ namespace cds { namespace intrusive {
         /// Clears the set
         /**
             The function unlinks all items from the set.
-            For any item <tt> @ref disposer</tt> is called
+            For any item \p Traits::disposer is called
         */
         void clear()
         {
-            clear_and_dispose( disposer() );
+            clear_and_dispose( disposer());
         }
 
         /// Clears the set and calls \p disposer for each item
@@ -2753,7 +2758,7 @@ namespace cds { namespace intrusive {
             };
             \endcode
 
-            The <tt> @ref disposer</tt> specified in \p Traits is not called.
+            The \p Traits::disposer is not called.
         */
         template <typename Disposer>
         void clear_and_dispose( Disposer oDisposer )
@@ -2763,7 +2768,7 @@ namespace cds { namespace intrusive {
 
             for ( unsigned int i = 0; i < c_nArity; ++i ) {
                 bucket_entry * pEntry = m_BucketTable[i];
-                bucket_entry * pEnd = pEntry + m_nBucketMask + 1;
+                bucket_entry * pEnd = pEntry + m_nBucketMask.load( atomics::memory_order_relaxed ) + 1;
                 for ( ; pEntry != pEnd ; ++pEntry ) {
                     pEntry->clear( [&oDisposer]( node_type * pNode ){ oDisposer( node_traits::to_value_ptr( pNode )) ; } );
                 }
@@ -2792,8 +2797,14 @@ namespace cds { namespace intrusive {
         */
         size_t bucket_count() const
         {
-            return m_nBucketMask + 1;
+            return m_nBucketMask.load( atomics::memory_order_relaxed ) + 1;
         }
+        //@cond
+        size_t bucket_count( atomics::memory_order load_mo ) const
+        {
+            return m_nBucketMask.load( load_mo ) + 1;
+        }
+        //@endcond
 
         /// Returns lock array size
         size_t lock_count() const