Fixed split-list inc_item_count()
[libcds.git] / cds / intrusive / split_list.h
index 8225b147bda0f08a2b451c25b8f2ed48976fc0ff..0be790d30c339525ee8fe3d00b6ef6a2810b5af9 100644 (file)
@@ -1,8 +1,9 @@
 //$$CDS-header$$
 
-#ifndef __CDS_INTRUSIVE_SPLIT_LIST_H
-#define __CDS_INTRUSIVE_SPLIT_LIST_H
+#ifndef CDSLIB_INTRUSIVE_SPLIT_LIST_H
+#define CDSLIB_INTRUSIVE_SPLIT_LIST_H
 
+#include <limits>
 #include <cds/intrusive/details/split_list_base.h>
 
 namespace cds { namespace intrusive {
@@ -195,6 +196,10 @@ namespace cds { namespace intrusive {
         typedef GC     gc;     ///< Garbage collector
         typedef Traits traits; ///< Set traits
 
+        //@cond
+        typedef cds::intrusive::split_list::implementation_tag implementation_tag;
+        //@endcond
+
     protected:
         //@cond
         typedef split_list::details::rebind_list_traits<OrderedList, traits> wrapped_ordered_list;
@@ -298,7 +303,7 @@ namespace cds { namespace intrusive {
             }
 
             template <typename Q, typename Compare>
-            bool extract_at( dummy_node_type * pHead, typename gc::Guard& guard, split_list::details::search_value_type<Q> const& val, Compare cmp )
+            bool extract_at( dummy_node_type * pHead, typename guarded_ptr::native_guard& guard, split_list::details::search_value_type<Q> const& val, Compare cmp )
             {
                 assert( pHead != nullptr );
                 bucket_head_type h(pHead);
@@ -322,7 +327,7 @@ namespace cds { namespace intrusive {
             }
 
             template <typename Q, typename Compare>
-            bool get_at( dummy_node_type * pHead, typename gc::Guard& guard, split_list::details::search_value_type<Q> const& val, Compare cmp )
+            bool get_at( dummy_node_type * pHead, typename guarded_ptr::native_guard& guard, split_list::details::search_value_type<Q> const& val, Compare cmp )
             {
                 assert( pHead != nullptr );
                 bucket_head_type h(pHead);
@@ -345,6 +350,7 @@ namespace cds { namespace intrusive {
         ordered_list_wrapper    m_List;             ///< Ordered list containing split-list items
         bucket_table            m_Buckets;          ///< bucket table
         atomics::atomic<size_t> m_nBucketCountLog2; ///< log2( current bucket count )
+        atomics::atomic<size_t> m_nMaxItemCount;    ///< number of items container can hold, before we have to resize
         item_counter            m_ItemCounter;      ///< Item counter
         hash                    m_HashFunctor;      ///< Hash functor
         stat                    m_Stat;             ///< Internal statistics
@@ -373,7 +379,7 @@ namespace cds { namespace intrusive {
 
         size_t bucket_no( size_t nHash ) const
         {
-            return nHash & ( (1 << m_nBucketCountLog2.load(atomics::memory_order_relaxed)) - 1 );
+            return nHash & ( (1 << m_nBucketCountLog2.load(memory_model::memory_order_relaxed)) - 1 );
         }
 
         static size_t parent_bucket( size_t nBucket )
@@ -411,7 +417,6 @@ namespace cds { namespace intrusive {
             // In this point, we must wait while nBucket is empty.
             // The compiler can decide that waiting loop can be "optimized" (stripped)
             // To prevent this situation, we use waiting on volatile bucket_head_ptr pointer.
-            //
             m_Stat.onBucketInitContenton();
             back_off bkoff;
             while ( true ) {
@@ -454,13 +459,31 @@ namespace cds { namespace intrusive {
             m_Buckets.bucket( 0, pNode );
         }
 
-        void    inc_item_count()
+        static size_t max_item_count( size_t nBucketCount, size_t nLoadFactor )
         {
-            size_t sz = m_nBucketCountLog2.load(atomics::memory_order_relaxed);
-            if ( ( ++m_ItemCounter >> sz ) > m_Buckets.load_factor() && ((size_t)(1 << sz )) < m_Buckets.capacity() )
-            {
-                m_nBucketCountLog2.compare_exchange_strong( sz, sz + 1, atomics::memory_order_seq_cst, atomics::memory_order_relaxed );
+            return nBucketCount * nLoadFactor;
+        }
+
+        void inc_item_count()
+        {
+            size_t nMaxCount = m_nMaxItemCount.load(memory_model::memory_order_relaxed);
+            if ( ++m_ItemCounter <= nMaxCount )
+                return;
+
+            size_t sz = m_nBucketCountLog2.load(memory_model::memory_order_relaxed);
+            const size_t nBucketCount = static_cast<size_t>(1) << sz;
+            if ( nBucketCount < m_Buckets.capacity() ) {
+                // we may grow the bucket table
+                const size_t nLoadFactor = m_Buckets.load_factor();
+                if ( nMaxCount < max_item_count( nBucketCount, nLoadFactor ))
+                    return; // someone already have updated m_nBucketCountLog2, so stop here
+
+                m_nMaxItemCount.compare_exchange_strong( nMaxCount, max_item_count( nBucketCount << 1, nLoadFactor ), 
+                                                         memory_model::memory_order_relaxed, atomics::memory_order_relaxed );
+                m_nBucketCountLog2.compare_exchange_strong( sz, sz + 1, memory_model::memory_order_relaxed, atomics::memory_order_relaxed );
             }
+            else
+                m_nMaxItemCount.store( std::numeric_limits<size_t>::max(), memory_model::memory_order_relaxed );
         }
 
         template <typename Q, typename Compare, typename Func>
@@ -489,7 +512,7 @@ namespace cds { namespace intrusive {
         }
 
         template <typename Q, typename Compare>
-        bool get_( typename gc::Guard& guard, Q const& val, Compare cmp )
+        bool get_( typename guarded_ptr::native_guard& guard, Q const& val, Compare cmp )
         {
             size_t nHash = hash_value( val );
             split_list::details::search_value_type<Q const>  sv( val, split_list::regular_hash( nHash ));
@@ -500,13 +523,13 @@ namespace cds { namespace intrusive {
         }
 
         template <typename Q>
-        bool get_( typename gc::Guard& guard, Q const& key)
+        bool get_( typename guarded_ptr::native_guard& guard, Q const& key )
         {
             return get_( guard, key, key_comparator());
         }
 
         template <typename Q, typename Less>
-        bool get_with_( typename gc::Guard& guard, Q const& key, Less )
+        bool get_with_( typename guarded_ptr::native_guard& guard, Q const& key, Less )
         {
             return get_( guard, key, typename wrapped_ordered_list::template make_compare_from_less<Less>());
         }
@@ -546,10 +569,10 @@ namespace cds { namespace intrusive {
         }
 
         template <typename Q, typename Compare>
-        bool extract_( typename gc::Guard& guard, Q const& val, Compare cmp )
+        bool extract_( typename guarded_ptr::native_guard& guard, Q const& val, Compare cmp )
         {
             size_t nHash = hash_value( val );
-            split_list::details::search_value_type<Q const>  sv( val, split_list::regular_hash( nHash ));
+            split_list::details::search_value_type<Q const> sv( val, split_list::regular_hash( nHash ));
             dummy_node_type * pHead = get_bucket( nHash );
             assert( pHead != nullptr );
 
@@ -563,28 +586,28 @@ namespace cds { namespace intrusive {
         }
 
         template <typename Q>
-        bool extract_( typename gc::Guard& guard, Q const& key)
+        bool extract_( typename guarded_ptr::native_guard& guard, Q const& key )
         {
             return extract_( guard, key, key_comparator());
         }
 
         template <typename Q, typename Less>
-        bool extract_with_( typename gc::Guard& guard, Q const& key, Less )
+        bool extract_with_( typename guarded_ptr::native_guard& guard, Q const& key, Less )
         {
             return extract_( guard, key, typename wrapped_ordered_list::template make_compare_from_less<Less>() );
         }
-
         //@endcond
 
     public:
         /// Initialize split-ordered list of default capacity
         /**
             The default capacity is defined in bucket table constructor.
-            See \p split_list::expandable_bucket_table, \p split_list::static_ducket_table
+            See \p split_list::expandable_bucket_table, \p split_list::static_bucket_table
             which selects by \p split_list::dynamic_bucket_table option.
         */
         SplitListSet()
             : m_nBucketCountLog2(1)
+            , m_nMaxItemCount( max_item_count(2, m_Buckets.load_factor()) )
         {
             init();
         }
@@ -596,6 +619,7 @@ namespace cds { namespace intrusive {
             )
             : m_Buckets( nItemCount, nLoadFactor )
             , m_nBucketCountLog2(1)
+            , m_nMaxItemCount( max_item_count(2, m_Buckets.load_factor()) )
         {
             init();
         }
@@ -766,6 +790,7 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Less>
         bool erase_with( const Q& key, Less pred )
         {
+            CDS_UNUSED( pred );
             return erase_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>() );
         }
 
@@ -782,7 +807,6 @@ namespace cds { namespace intrusive {
                 void operator()( value_type const& item );
             };
             \endcode
-            The functor can be passed by reference with <tt>boost:ref</tt>
 
             If the item with key equal to \p key is not found the function return \p false.
 
@@ -804,19 +828,20 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Less, typename Func>
         bool erase_with( Q const& key, Less pred, Func f )
         {
+            CDS_UNUSED( pred );
             return erase_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>(), f );
         }
 
         /// Extracts the item with specified \p key
         /** \anchor cds_intrusive_SplitListSet_hp_extract
             The function searches an item with key equal to \p key,
-            unlinks it from the set, and returns it in \p dest parameter.
-            If the item with key equal to \p key is not found the function returns \p false.
+            unlinks it from the set, and returns it as \p guarded_ptr.
+            If \p key is not found the function returns an empty guarded pointer.
 
             Note the compare functor should accept a parameter of type \p Q that may be not the same as \p value_type.
 
-            The \ref disposer specified in \p OrderedList class' template parameter is called automatically
-            by garbage collector \p GC when returned \ref guarded_ptr object will be destroyed or released.
+            The \p disposer specified in \p OrderedList class' template parameter is called automatically
+            by garbage collector \p GC when returned \p guarded_ptr object will be destroyed or released.
             @note Each \p guarded_ptr object uses the GC's guard that can be limited resource.
 
             Usage:
@@ -825,24 +850,26 @@ namespace cds { namespace intrusive {
             splitlist_set theSet;
             // ...
             {
-                splitlist_set::guarded_ptr gp;
-                theSet.extract( gp, 5 );
-                // Deal with gp
-                // ...
-
+                splitlist_set::guarded_ptr gp( theSet.extract( 5 ));
+                if ( gp) {
+                    // Deal with gp
+                    // ...
+                }
                 // Destructor of gp releases internal HP guard
             }
             \endcode
         */
         template <typename Q>
-        bool extract( guarded_ptr& dest, Q const& key )
+        guarded_ptr extract( Q const& key )
         {
-            return extract_( dest.guard(), key );
+            guarded_ptr gp;
+            extract_( gp.guard(), key );
+            return gp;
         }
 
         /// Extracts the item using compare functor \p pred
         /**
-            The function is an analog of \ref cds_intrusive_SplitListSet_hp_extract "extract(guarded_ptr&, Q const&)"
+            The function is an analog of \ref cds_intrusive_SplitListSet_hp_extract "extract(Q const&)"
             but \p pred predicate is used for key comparing.
 
             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
@@ -850,9 +877,11 @@ namespace cds { namespace intrusive {
             \p pred must imply the same element order as the comparator used for building the set.
         */
         template <typename Q, typename Less>
-        bool extract_with( guarded_ptr& dest, Q const& key, Less pred )
+        guarded_ptr extract_with( Q const& key, Less pred )
         {
-            return extract_with_( dest.guard(), key, pred );
+            guarded_ptr gp;
+            extract_with_( gp.guard(), key, pred );
+            return gp;
         }
 
         /// Finds the key \p key
@@ -866,8 +895,6 @@ namespace cds { namespace intrusive {
             \endcode
             where \p item is the item found, \p key is the <tt>find</tt> function argument.
 
-            You can pass \p f argument by value or by reference using \p std::ref.
-
             The functor can change non-key fields of \p item. Note that the functor is only guarantee
             that \p item cannot be disposed during functor is executing.
             The functor does not serialize simultaneous access to the set \p item. If such access is
@@ -883,6 +910,13 @@ namespace cds { namespace intrusive {
         {
             return find_( key, key_comparator(), f );
         }
+        //@cond
+        template <typename Q, typename Func>
+        bool find( Q const& key, Func f )
+        {
+            return find_( key, key_comparator(), f );
+        }
+        //@endcond
 
         /// Finds the key \p key with \p pred predicate for comparing
         /**
@@ -894,8 +928,17 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Less, typename Func>
         bool find_with( Q& key, Less pred, Func f )
         {
+            CDS_UNUSED( pred );
             return find_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>(), f );
         }
+        //@cond
+        template <typename Q, typename Less, typename Func>
+        bool find_with( Q const& key, Less pred, Func f )
+        {
+            CDS_UNUSED( pred );
+            return find_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>(), f );
+        }
+        //@endcond
 
         /// Finds the key \p key
         /** \anchor cds_intrusive_SplitListSet_hp_find_val
@@ -922,18 +965,18 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Less>
         bool find_with( Q const& key, Less pred )
         {
+            CDS_UNUSED( pred );
             return find_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>() );
         }
 
         /// Finds the key \p key and return the item found
         /** \anchor cds_intrusive_SplitListSet_hp_get
             The function searches the item with key equal to \p key
-            and assigns the item found to guarded pointer \p ptr.
-            The function returns \p true if \p key is found, and \p false otherwise.
-            If \p key is not found the \p ptr parameter is not changed.
+            and returns the item found as \p guarded_ptr.
+            If \p key is not found the function returns an empty guarded pointer.
 
-            The \ref disposer specified in \p OrderedList class' template parameter is called
-            by garbage collector \p GC automatically when returned \ref guarded_ptr object
+            The \p disposer specified in \p OrderedList class' template parameter is called
+            by garbage collector \p GC automatically when returned \p guarded_ptr object
             will be destroyed or released.
             @note Each \p guarded_ptr object uses one GC's guard which can be limited resource.
 
@@ -943,8 +986,8 @@ namespace cds { namespace intrusive {
             splitlist_set theSet;
             // ...
             {
-                splitlist_set::guarded_ptr gp;
-                if ( theSet.get( gp, 5 )) {
+                splitlist_set::guarded_ptr gp = theSet.get( 5 );
+                if ( gp ) {
                     // Deal with gp
                     //...
                 }
@@ -956,14 +999,16 @@ namespace cds { namespace intrusive {
             should accept a parameter of type \p Q that can be not the same as \p value_type.
         */
         template <typename Q>
-        bool get( guarded_ptr& ptr, Q const& key )
+        guarded_ptr get( Q const& key )
         {
-            return get_( ptr.guard(), key );
+            guarded_ptr gp;
+            get_( gp.guard(), key );
+            return gp;
         }
 
         /// Finds the key \p key and return the item found
         /**
-            The function is an analog of \ref cds_intrusive_SplitListSet_hp_get "get( guarded_ptr& ptr, Q const&)"
+            The function is an analog of \ref cds_intrusive_SplitListSet_hp_get "get( Q const&)"
             but \p pred is used for comparing the keys.
 
             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
@@ -971,9 +1016,11 @@ namespace cds { namespace intrusive {
             \p pred must imply the same element order as the comparator used for building the set.
         */
         template <typename Q, typename Less>
-        bool get_with( guarded_ptr& ptr, Q const& key, Less pred )
+        guarded_ptr get_with( Q const& key, Less pred )
         {
-            return get_with_( ptr.guard(), key, pred );
+            guarded_ptr gp;
+            get_with_( gp.guard(), key, pred );
+            return gp;
         }
 
         /// Returns item count in the set
@@ -1106,4 +1153,4 @@ namespace cds { namespace intrusive {
 
 }}  // namespace cds::intrusive
 
-#endif // #ifndef __CDS_INTRUSIVE_SPLIT_LIST_H
+#endif // #ifndef CDSLIB_INTRUSIVE_SPLIT_LIST_H