Renamed "ensure" to "update" in statistics
[libcds.git] / cds / intrusive / split_list_rcu.h
index 7c8618f7e0c3b86f5fb59d68c7913d313c3e31c7..e27c64d8a7dd5864ef6c59e3352f8e17a8a292e1 100644 (file)
@@ -1,4 +1,32 @@
-//$$CDS-header$$
+/*
+    This file is a part of libcds - Concurrent Data Structures library
+
+    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
+
+    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:
+
+    * Redistributions of source code must retain the above copyright notice, this
+      list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    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.     
+*/
 
 #ifndef CDSLIB_INTRUSIVE_SPLIT_LIST_RCU_H
 #define CDSLIB_INTRUSIVE_SPLIT_LIST_RCU_H
@@ -32,7 +60,7 @@ namespace cds { namespace intrusive {
         - \p Traits - set traits, default isd \p split_list::traits.
             Instead of defining \p Traits struct you may use option-based syntax with \p split_list::make_traits metafunction.
 
-        @note About reqired features of hash functor see \ref cds_SplitList_hash_functor "SplitList general description".
+        @note About required features of hash functor see \ref cds_SplitList_hash_functor "SplitList general description".
 
         \par How to use
         Before including <tt><cds/intrusive/split_list_rcu.h></tt> you should include appropriate RCU header file,
@@ -70,10 +98,6 @@ namespace cds { namespace intrusive {
         /// Hash functor for \ref value_type and all its derivatives that you use
         typedef typename cds::opt::v::hash_selector< typename traits::hash >::type   hash;
 
-        //@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;
@@ -90,6 +114,7 @@ namespace cds { namespace intrusive {
         typedef typename ordered_list::disposer       disposer;       ///< Node disposer functor
         typedef typename ordered_list::rcu_lock       rcu_lock;       ///< RCU scoped lock
         typedef typename ordered_list::exempt_ptr     exempt_ptr;     ///< pointer to extracted node
+        typedef typename ordered_list::raw_ptr        raw_ptr;        ///< pointer to the node for \p get() function
         /// Group of \p extract_xxx functions require external locking if underlying ordered list requires that
         static CDS_CONSTEXPR const bool c_bExtractLockExternal = ordered_list::c_bExtractLockExternal;
 
@@ -128,7 +153,7 @@ namespace cds { namespace intrusive {
         class ordered_list_wrapper: public ordered_list
         {
             typedef ordered_list base_class;
-            typedef typename base_class::auxiliary_head       bucket_head_type;
+            typedef typename base_class::auxiliary_head bucket_head_type;
 
         public:
             bool insert_at( dummy_node_type * pHead, value_type& val )
@@ -147,11 +172,11 @@ namespace cds { namespace intrusive {
             }
 
             template <typename Func>
-            std::pair<bool, bool> ensure_at( dummy_node_type * pHead, value_type& val, Func func )
+            std::pair<bool, bool> update_at( dummy_node_type * pHead, value_type& val, Func func, bool bAllowInsert )
             {
                 assert( pHead != nullptr );
                 bucket_head_type h(pHead);
-                return base_class::ensure_at( h, val, func );
+                return base_class::update_at( h, val, func, bAllowInsert );
             }
 
             bool unlink_at( dummy_node_type * pHead, value_type& val )
@@ -186,7 +211,7 @@ namespace cds { namespace intrusive {
             }
 
             template <typename Q, typename Compare, typename Func>
-            bool find_at( dummy_node_type * pHead, split_list::details::search_value_type<Q>& val, Compare cmp, Func f ) const
+            bool find_at( dummy_node_type * pHead, split_list::details::search_value_type<Q>& val, Compare cmp, Func f )
             {
                 assert( pHead != nullptr );
                 bucket_head_type h(pHead);
@@ -194,7 +219,7 @@ namespace cds { namespace intrusive {
             }
 
             template <typename Q, typename Compare>
-            bool find_at( dummy_node_type * pHead, split_list::details::search_value_type<Q> const & val, Compare cmp ) const
+            bool find_at( dummy_node_type * pHead, split_list::details::search_value_type<Q> const & val, Compare cmp )
             {
                 assert( pHead != nullptr );
                 bucket_head_type h(pHead);
@@ -202,7 +227,7 @@ namespace cds { namespace intrusive {
             }
 
             template <typename Q, typename Compare>
-            value_type * get_at( dummy_node_type * pHead, split_list::details::search_value_type<Q>& val, Compare cmp ) const
+            raw_ptr get_at( dummy_node_type * pHead, split_list::details::search_value_type<Q>& val, Compare cmp )
             {
                 assert( pHead != nullptr );
                 bucket_head_type h(pHead);
@@ -246,7 +271,7 @@ namespace cds { namespace intrusive {
         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 stattistics accumulator
+        stat                    m_Stat;             ///< Internal statistics accumulator
 
     protected:
         //@cond
@@ -372,7 +397,7 @@ namespace cds { namespace intrusive {
                 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 ), 
+                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 );
             }
@@ -404,15 +429,15 @@ namespace cds { namespace intrusive {
         }
 
         template <typename Q, typename Compare>
-        value_type * get_( Q const& val, Compare cmp )
+        raw_ptr get_( 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 ));
             dummy_node_type * pHead = get_bucket( nHash );
             assert( pHead != nullptr );
 
-            value_type * p = m_List.get_at( pHead, sv, cmp );
-            m_Stat.onFind( p != nullptr );
+            raw_ptr p = m_List.get_at( pHead, sv, cmp );
+            m_Stat.onFind( !!p );
             return p;
         }
 
@@ -519,10 +544,7 @@ namespace cds { namespace intrusive {
             dummy_node_type * pHead = get_bucket( nHash );
             assert( pHead != nullptr );
 
-            // TSan false positive: hash is read-only, will be ordered when we insert a node
-            CDS_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
             node_traits::to_node_ptr( val )->m_nHash = split_list::regular_hash( nHash );
-            CDS_TSAN_ANNOTATE_IGNORE_WRITES_END;
 
             if ( m_List.insert_at( pHead, val )) {
                 inc_item_count();
@@ -561,10 +583,7 @@ namespace cds { namespace intrusive {
             dummy_node_type * pHead = get_bucket( nHash );
             assert( pHead != nullptr );
 
-            // TSan false positive: hash is read-only, will be ordered when we insert a node
-            CDS_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
             node_traits::to_node_ptr( val )->m_nHash = split_list::regular_hash( nHash );
-            CDS_TSAN_ANNOTATE_IGNORE_WRITES_END;
 
             if ( m_List.insert_at( pHead, val, f )) {
                 inc_item_count();
@@ -575,11 +594,12 @@ namespace cds { namespace intrusive {
             return false;
         }
 
-        /// Ensures that the \p val exists in the set
+        /// Updates the node
         /**
             The operation performs inserting or changing data with lock-free manner.
 
-            If the item \p val is not found in the set, then \p val is inserted into the set.
+            If the item \p val is not found in the set, then \p val is inserted
+            iff \p bAllowInsert is \p true.
             Otherwise, the functor \p func is called with item found.
             The functor signature is:
             \code
@@ -588,41 +608,48 @@ namespace cds { namespace intrusive {
             with arguments:
             - \p bNew - \p true if the item has been inserted, \p false otherwise
             - \p item - item of the set
-            - \p val - argument \p val passed into the \p ensure function
+            - \p val - argument \p val passed into the \p update() function
             If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
-            refers to the same thing.
+            refers to the same stuff.
 
-            The function makes RCU lock internally.
+            The functor may change non-key fields of the \p item.
+
+            The function applies RCU lock internally.
 
             Returns std::pair<bool, bool> where \p first is \p true if operation is successfull,
             \p second is \p true if new item has been added or \p false if the item with \p key
-            already is in the set.
+            already is in the list.
 
             @warning For \ref cds_intrusive_MichaelList_rcu "MichaelList" as the bucket see \ref cds_intrusive_item_creating "insert item troubleshooting".
             \ref cds_intrusive_LazyList_rcu "LazyList" provides exclusive access to inserted item and does not require any node-level
             synchronization.
-            */
+        */
         template <typename Func>
-        std::pair<bool, bool> ensure( value_type& val, Func func )
+        std::pair<bool, bool> update( value_type& val, Func func, bool bAllowInsert = true )
         {
             size_t nHash = hash_value( val );
             dummy_node_type * pHead = get_bucket( nHash );
             assert( pHead != nullptr );
 
-            // TSan false positive: hash is read-only, will be ordered when we insert a node
-            CDS_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
             node_traits::to_node_ptr( val )->m_nHash = split_list::regular_hash( nHash );
-            CDS_TSAN_ANNOTATE_IGNORE_WRITES_END;
 
-            std::pair<bool, bool> bRet = m_List.ensure_at( pHead, val, func );
+            std::pair<bool, bool> bRet = m_List.update_at( pHead, val, func, bAllowInsert );
             if ( bRet.first && bRet.second ) {
                 inc_item_count();
-                m_Stat.onEnsureNew();
+                m_Stat.onUpdateNew();
             }
             else
-                m_Stat.onEnsureExist();
+                m_Stat.onUpdateExist();
             return bRet;
         }
+        //@cond
+        template <typename Func>
+        CDS_DEPRECATED("ensure() is deprecated, use update()")
+        std::pair<bool, bool> ensure( value_type& val, Func func )
+        {
+            return update( val, func, true );
+        }
+        //@endcond
 
         /// Unlinks the item \p val from the set
         /**
@@ -734,11 +761,10 @@ namespace cds { namespace intrusive {
             unlinks it, and returns \ref cds::urcu::exempt_ptr "exempt_ptr" pointer to the item found.
             If the item with the key equal to \p key is not found the function returns an empty \p exempt_ptr.
 
-            @note The function does NOT call RCU read-side lock or synchronization,
-            and does NOT dispose the item found. It just excludes the item from the set
-            and returns a pointer to item found.
-            You should lock RCU before calling of the function, and you should synchronize RCU
-            outside the RCU lock before reusing returned pointer.
+            Depends on \p bucket_type you should or should not lock RCU before calling of this function:
+            - for the set based on \ref cds_intrusive_MichaelList_rcu "MichaelList" RCU should not be locked
+            - for the set based on \ref cds_intrusive_LazyList_rcu "LazyList" RCU should be locked
+            See ordered list implementation for details.
 
             \code
             typedef cds::urcu::gc< general_buffered<> > rcu;
@@ -749,17 +775,15 @@ namespace cds { namespace intrusive {
             // ...
 
             rcu_splitlist_set::exempt_ptr p;
-            {
-                // first, we should lock RCU
-                rcu_splitlist_set::rcu_lock lock;
-
-                // Now, you can apply extract function
-                // Note that you must not delete the item found inside the RCU lock
-                p = theList.extract( 10 );
-                if ( p ) {
-                    // do something with p
-                    ...
-                }
+
+            // For MichaelList we should not lock RCU
+
+            // Now, you can apply extract function
+            // Note that you must not delete the item found inside the RCU lock
+            p = theList.extract( 10 );
+            if ( p ) {
+                // do something with p
+                ...
             }
 
             // We may safely release p here
@@ -847,30 +871,49 @@ namespace cds { namespace intrusive {
         }
         //@endcond
 
-        /// Finds the key \p key
-        /** \anchor cds_intrusive_SplitListSet_rcu_find_val
+        /// Checks whether the set contains \p key
+        /**
             The function searches the item with key equal to \p key
-            and returns \p true if \p key found or \p false otherwise.
+            and returns \p true if it is found, and \p false otherwise.
+
+            Note the hash functor specified for class \p Traits template parameter
+            should accept a parameter of type \p Q that can be not the same as \p value_type.
+            Otherwise, you may use \p contains( Q const&, Less pred ) functions with explicit predicate for key comparing.
         */
         template <typename Q>
-        bool find( Q const& key )
+        bool contains( Q const& key )
         {
             return find_value( key, key_comparator() );
         }
+        //@cond
+        template <typename Q>
+        CDS_DEPRECATED("deprecated, use contains()")
+        bool find( Q const& key )
+        {
+            return contains( key );
+        }
+        //@endcond
 
-        /// Finds the key \p key with \p pred predicate for comparing
+        /// Checks whether the set contains \p key using \p pred predicate for searching
         /**
-            The function is an analog of \ref cds_intrusive_SplitListSet_rcu_find_val "find(Q const&)"
-            but \p cmp is used for key compare.
-            \p Less has the interface like \p std::less.
-            \p pred must imply the same element order as the comparator used for building the set.
+            The function is an analog of <tt>contains( key )</tt> but \p pred is used for key comparing.
+            \p Less functor has the interface like \p std::less.
+            \p Less must imply the same element order as the comparator used for building the list.
         */
         template <typename Q, typename Less>
-        bool find_with( Q const& key, Less pred )
+        bool contains( Q const& key, Less pred )
         {
             CDS_UNUSED( pred );
             return find_value( key, typename wrapped_ordered_list::template make_compare_from_less<Less>() );
         }
+        //@cond
+        template <typename Q, typename Less>
+        CDS_DEPRECATED("deprecated, use contains()")
+        bool find_with( Q const& key, Less pred )
+        {
+            return contains( key, pred );
+        }
+        //@endcond
 
         /// Finds the key \p key and return the item found
         /** \anchor cds_intrusive_SplitListSet_rcu_get
@@ -882,24 +925,26 @@ namespace cds { namespace intrusive {
             RCU should be locked before call of this function.
             Returned item is valid only while RCU is locked:
             \code
-            cds::intrusive::SplitListSet< your_template_parameters > theSet;
+            typedef cds::intrusive::SplitListSet< your_template_parameters > set_class;
+            set_class theSet;
             // ...
+            typename set_class::raw_ptr rp;
             {
                 // Lock RCU
                 hash_set::rcu_lock lock;
 
-                foo * pVal = theSet.get( 5 );
-                if ( pVal ) {
-                    // Deal with pVal
+                rp = theSet.get( 5 );
+                if ( rp ) {
+                    // Deal with rp
                     //...
                 }
                 // Unlock RCU by rcu_lock destructor
-                // pVal can be retired by disposer at any time after RCU has been unlocked
+                // rp can be retired by disposer at any time after RCU has been unlocked
             }
             \endcode
         */
         template <typename Q>
-        value_type * get( Q const& key )
+        raw_ptr get( Q const& key )
         {
             return get_( key, key_comparator() );
         }
@@ -914,7 +959,7 @@ 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>
-        value_type * get_with( Q const& key, Less pred )
+        raw_ptr get_with( Q const& key, Less pred )
         {
             CDS_UNUSED( pred );
             return get_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>());
@@ -978,20 +1023,21 @@ namespace cds { namespace intrusive {
             {}
         };
         //@endcond
+
     public:
+    ///@name Forward iterators (thread-safe under RCU lock)
+    //@{
         /// Forward iterator
         /**
             The forward iterator for a split-list has some features:
             - it has no post-increment operator
             - it depends on iterator of underlying \p OrderedList
-            - The iterator cannot be moved across thread boundary since it may contain GC's guard that is thread-private GC data.
-            - Iterator ensures thread-safety even if you delete the item that iterator points to. However, in case of concurrent
-              deleting operations it is no guarantee that you iterate all item in the split-list.
 
-            Therefore, the use of iterators in concurrent environment is not good idea. Use the iterator on the concurrent container
-            for debug purpose only.
+            You may safely use iterators in multi-threaded environment only under RCU lock.
+            Otherwise, a crash is possible if another thread deletes the element the iterator points to.
         */
         typedef iterator_type<false>    iterator;
+
         /// Const forward iterator
         /**
             For iterator's features and requirements see \ref iterator
@@ -1040,7 +1086,7 @@ namespace cds { namespace intrusive {
         {
             return const_iterator( m_List.cend(), m_List.cend() );
         }
-
+    //@}
     };
 
 }}  // namespace cds::intrusive