SkipList:
authorkhizmax <libcds.dev@gmail.com>
Sat, 25 Jul 2015 09:45:32 +0000 (12:45 +0300)
committerkhizmax <libcds.dev@gmail.com>
Sat, 25 Jul 2015 09:45:32 +0000 (12:45 +0300)
- fixed memory order
- TSan checking
- replaced ensure() function with update()
- small optimizations

12 files changed:
cds/container/impl/skip_list_map.h
cds/container/impl/skip_list_set.h
cds/container/skip_list_map_nogc.h
cds/container/skip_list_map_rcu.h
cds/container/skip_list_set_nogc.h
cds/container/skip_list_set_rcu.h
cds/intrusive/details/skip_list_base.h
cds/intrusive/impl/skip_list.h
cds/intrusive/skip_list_nogc.h
cds/intrusive/skip_list_rcu.h
tests/test-hdr/set/hdr_intrusive_skiplist_set.h
tests/unit/print_skip_list_stat.h

index 29987199b56bae2274965d38ffb1edf3c3a76ee7..80e1ccc51ea5bd7f116e815a1f6731c904276a1f 100644 (file)
@@ -295,49 +295,53 @@ namespace cds { namespace container {
             return false;
         }
 
             return false;
         }
 
-        /// Ensures that the \p key exists in the map
+        /// Updates data by \p key
         /**
             The operation performs inserting or changing data with lock-free manner.
 
             If the \p key not found in the map, then the new item created from \p key
         /**
             The operation performs inserting or changing data with lock-free manner.
 
             If the \p key not found in the map, then the new item created from \p key
-            is inserted into the map (note that in this case the \ref key_type should be
-            constructible from type \p K).
-            Otherwise, the functor \p func is called with item found.
-            The functor \p Func may be a function with signature:
-            \code
-                void func( bool bNew, value_type& item );
-            \endcode
-            or a functor:
+            will be inserted into the map iff \p bInsert is \p true
+            (note that in this case the \ref key_type should be constructible from type \p K).
+            Otherwise, if \p key is found, the functor \p func is called with item found.
+            The functor \p Func signature:
             \code
                 struct my_functor {
                     void operator()( bool bNew, value_type& item );
                 };
             \endcode
             \code
                 struct my_functor {
                     void operator()( bool bNew, value_type& item );
                 };
             \endcode
-
-            with arguments:
+            where:
             - \p bNew - \p true if the item has been inserted, \p false otherwise
             - \p bNew - \p true if the item has been inserted, \p false otherwise
-            - \p item - item of the list
+            - \p item - item of the map
 
             The functor may change any fields of the \p item.second that is \ref value_type.
 
 
             The functor may change any fields of the \p item.second that is \ref value_type.
 
-            Returns <tt> std::pair<bool, bool> </tt> where \p first is true if operation is successfull,
-            \p second is true if new item has been added or \p false if the item with \p key
-            already is in the list.
+            Returns <tt> std::pair<bool, bool> </tt> 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 \p key already exists.
 
             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
         */
         template <typename K, typename Func>
 
             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
         */
         template <typename K, typename Func>
-        std::pair<bool, bool> ensure( K const& key, Func func )
+        std::pair<bool, bool> update( K const& key, Func func, bool bInsert = true )
         {
             scoped_node_ptr pNode( node_allocator().New( random_level(), key ));
         {
             scoped_node_ptr pNode( node_allocator().New( random_level(), key ));
-            std::pair<bool, bool> res = base_class::ensure( *pNode,
-                [&func](bool bNew, node_type& item, node_type const& ){ func( bNew, item.m_Value ); }
+            std::pair<bool, bool> res = base_class::update( *pNode,
+                [&func](bool bNew, node_type& item, node_type const& ){ func( bNew, item.m_Value );},
+                bInsert
             );
             if ( res.first && res.second )
                 pNode.release();
             return res;
         }
 
             );
             if ( res.first && res.second )
                 pNode.release();
             return res;
         }
 
+        //@cond
+        // Deprecated, use update()
+        template <typename K, typename Func>
+        std::pair<bool, bool> ensure( K const& key, Func func )
+        {
+            return update( key, func, true );
+        }
+        //@endcond
+
         /// Delete \p key from the map
         /** \anchor cds_nonintrusive_SkipListMap_erase_val
 
         /// Delete \p key from the map
         /** \anchor cds_nonintrusive_SkipListMap_erase_val
 
index 0935780b0ddcffe81ca780c623ffa4f5023f6835..a310d780fa3072ac7bd29219fd274e0d11e8a64a 100644 (file)
@@ -250,48 +250,56 @@ namespace cds { namespace container {
             return false;
         }
 
             return false;
         }
 
-        /// Ensures that the item exists in the set
+        /// Updates the item
         /**
             The operation performs inserting or changing data with lock-free manner.
 
             If the \p val key not found in the set, then the new item created from \p val
         /**
             The operation performs inserting or changing data with lock-free manner.
 
             If the \p val key not found in the set, then the new item created from \p val
-            is inserted into the set. Otherwise, the functor \p func is called with the item found.
-            The functor \p Func should be a function with signature:
-            \code
-                void func( bool bNew, value_type& item, const Q& val );
-            \endcode
-            or a functor:
+            will be inserted into the set iff \p bInsert is \p true. 
+            Otherwise, if \p val is found, the functor \p func will be called with the item found.
+
+            The functor \p Func signature:
             \code
                 struct my_functor {
                     void operator()( bool bNew, value_type& item, const Q& val );
                 };
             \endcode
             \code
                 struct my_functor {
                     void operator()( bool bNew, value_type& item, const Q& val );
                 };
             \endcode
-
-            with arguments:
+            where:
             - \p bNew - \p true if the item has been inserted, \p false otherwise
             - \p item - item of the set
             - \p bNew - \p true if the item has been inserted, \p false otherwise
             - \p item - item of the set
-            - \p val - argument \p key passed into the \p %ensure() function
+            - \p val - argument \p key passed into the \p %update() function
 
             The functor may change non-key fields of the \p item; however, \p func must guarantee
             that during changing no any other modifications could be made on this item by concurrent threads.
 
 
             The functor may change non-key fields of the \p item; however, \p func must guarantee
             that during changing no any other modifications could be made on this item by concurrent threads.
 
-            Returns <tt> std::pair<bool, bool> </tt> where \p first is true if operation is successfull,
-            \p second is true if new item has been added or \p false if the item with \p key
-            already is in the set.
+            Returns <tt> std::pair<bool, bool> </tt> where \p first is \p true if operation is successfull,
+            i.e. the item has been inserted or updated,
+            \p second is \p true if new item has been added or \p false if the item with key equal to \p val
+            already exists.
 
             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
         */
         template <typename Q, typename Func>
 
             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
         */
         template <typename Q, typename Func>
-        std::pair<bool, bool> ensure( const Q& val, Func func )
+        std::pair<bool, bool> update( const Q& val, Func func, bool bInsert = true )
         {
             scoped_node_ptr sp( node_allocator().New( random_level(), val ));
         {
             scoped_node_ptr sp( node_allocator().New( random_level(), val ));
-            std::pair<bool, bool> bRes = base_class::ensure( *sp,
-                [&func, &val](bool bNew, node_type& node, node_type&){ func( bNew, node.m_Value, val ); });
+            std::pair<bool, bool> bRes = base_class::update( *sp,
+                [&func, &val](bool bNew, node_type& node, node_type&){ func( bNew, node.m_Value, val ); }, 
+                bInsert );
             if ( bRes.first && bRes.second )
                 sp.release();
             return bRes;
         }
 
             if ( bRes.first && bRes.second )
                 sp.release();
             return bRes;
         }
 
+        //@cond
+        // Deprecated, use update()
+        template <typename Q, typename Func>
+        std::pair<bool, bool> ensure( const Q& val, Func func )
+        {
+            return update( val, func, true );
+        }
+        //@endcond
+
         /// Inserts data of type \p value_type created in-place from <tt>std::forward<Args>(args)...</tt>
         /**
             Returns \p true if inserting successful, \p false otherwise.
         /// Inserts data of type \p value_type created in-place from <tt>std::forward<Args>(args)...</tt>
         /**
             Returns \p true if inserting successful, \p false otherwise.
index 57e1c5ad734ed1ac53456e3a6a668b1ee12673ca..932fbea8980de15c7a8b93bc86a17487c8283298 100644 (file)
@@ -235,21 +235,30 @@ namespace cds { namespace container {
             return base_class::emplace( std::forward<K>(key), std::move(mapped_type(std::forward<Args>(args)...)));
         }
 
             return base_class::emplace( std::forward<K>(key), std::move(mapped_type(std::forward<Args>(args)...)));
         }
 
-        /// Ensures that the key \p key exists in the map
+        /// UPdates data by \p key
         /**
         /**
-            The operation inserts new item if the key \p key is not found in the map.
-            Otherwise, the function returns an iterator that points to item found.
+            The operation inserts new item if \p key is not found in the map and \p bInsert is \p true.
+            Otherwise, if \p key is found, the function returns an iterator that points to item found.
 
             Returns <tt> std::pair<iterator, bool>  </tt> where \p first is an iterator pointing to
 
             Returns <tt> std::pair<iterator, bool>  </tt> where \p first is an iterator pointing to
-            item found or inserted, \p second is true if new item has been added or \p false if the item
-            already is in the list.
+            item found or inserted or \p end() if \p key is not found and insertion is not allowed (\p bInsert is \p false),  
+            \p second is \p true if new item has been added or \p false if the item already exists.
         */
         template <typename K>
         */
         template <typename K>
-        std::pair<iterator, bool> ensure( K const& key )
+        std::pair<iterator, bool> update( K const& key, bool bInsert = true )
         {
             //TODO: pass arguments by reference (make_pair makes copy)
         {
             //TODO: pass arguments by reference (make_pair makes copy)
-            return base_class::ensure( std::make_pair( key, mapped_type() ));
+            return base_class::update( std::make_pair( key, mapped_type() ), bInsert );
+        }
+
+        //@cond
+        // Deprecated, use update()
+        template <typename K>
+        std::pair<iterator, bool> ensure( K const& key )
+        {
+            return update( key, true );
         }
         }
+        //@endcond
 
         /// Finds the key \p key
         /** \anchor cds_nonintrusive_SkipListMap_nogc_find_val
 
         /// Finds the key \p key
         /** \anchor cds_nonintrusive_SkipListMap_nogc_find_val
index 8b78ba8133851f695251d6d4af0782b2e06770da..70185d7fe8a296ee1d78f598b5481a972b9b6e83 100644 (file)
@@ -326,46 +326,55 @@ namespace cds { namespace container {
             return false;
         }
 
             return false;
         }
 
-        /// Ensures that the \p key exists in the map
+        /// Updates data by \p key
         /**
             The operation performs inserting or changing data with lock-free manner.
 
             If the \p key not found in the map, then the new item created from \p key
         /**
             The operation performs inserting or changing data with lock-free manner.
 
             If the \p key not found in the map, then the new item created from \p key
-            is inserted into the map (note that in this case the \ref key_type should be
-            constructible from type \p K).
-            Otherwise, the functor \p func is called with item found.
+            is inserted into the map iff \p bInsert is \p true.
+            Otherwise, if \p key found, the functor \p func is called with item found.
             The functor \p Func interface is:
             \code
                 struct my_functor {
                     void operator()( bool bNew, value_type& item );
                 };
             \endcode
             The functor \p Func interface is:
             \code
                 struct my_functor {
                     void operator()( bool bNew, value_type& item );
                 };
             \endcode
-            with arguments:
+            where:
             - \p bNew - \p true if the item has been inserted, \p false otherwise
             - \p bNew - \p true if the item has been inserted, \p false otherwise
-            - \p item - item of the list
+            - \p item - item of the map
 
             The functor may change any fields of \p item.second.
 
             RCU \p synchronize() method can be called. RCU should not be locked.
 
 
             The functor may change any fields of \p item.second.
 
             RCU \p synchronize() method can be called. RCU should not be locked.
 
-            Returns <tt> std::pair<bool, bool> </tt> where \p first is true if operation is successfull,
-            \p second is true if new item has been added or \p false if the item with \p key
-            already is in the list.
+            Returns <tt> std::pair<bool, bool> </tt> 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 exists.
 
             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
         */
         template <typename K, typename Func>
 
             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
         */
         template <typename K, typename Func>
-        std::pair<bool, bool> ensure( K const& key, Func func )
+        std::pair<bool, bool> update( K const& key, Func func, bool bInsert = true )
         {
             scoped_node_ptr pNode( node_allocator().New( random_level(), key ));
         {
             scoped_node_ptr pNode( node_allocator().New( random_level(), key ));
-            std::pair<bool, bool> res = base_class::ensure( *pNode,
-                [&func](bool bNew, node_type& item, node_type const& ){ func( bNew, item.m_Value ); }
+            std::pair<bool, bool> res = base_class::update( *pNode,
+                [&func](bool bNew, node_type& item, node_type const& ){ func( bNew, item.m_Value );},
+                bInsert
             );
             if ( res.first && res.second )
                 pNode.release();
             return res;
         }
 
             );
             if ( res.first && res.second )
                 pNode.release();
             return res;
         }
 
+        //@cond
+        // Deprecated, use update()
+        template <typename K, typename Func>
+        std::pair<bool, bool> ensure( K const& key, Func func )
+        {
+            return update( key, func, true );
+        }
+        //@endcond
+
         /// Delete \p key from the map
         /**\anchor cds_nonintrusive_SkipListMap_rcu_erase_val
 
         /// Delete \p key from the map
         /**\anchor cds_nonintrusive_SkipListMap_rcu_erase_val
 
index 24f8171b31a330443bff61fb0daf3899f97ff5bf..0d6f88505620a0dd9725bc750edd1a93e489d232 100644 (file)
@@ -267,27 +267,39 @@ namespace cds { namespace container {
             return end();
         }
 
             return end();
         }
 
-        /// Ensures that the item \p val exists in the set
+        /// Updates the item
         /**
         /**
-            The operation inserts new item if the key \p val is not found in the set.
-            Otherwise, the function returns an iterator that points to item found.
+            The operation inserts new item if \p val is not found in the set and \p bInsert is \p true.
+            Otherwise, if that key exists, the function returns an iterator that points to item found.
 
 
-            Returns <tt> std::pair<iterator, bool>  </tt> where \p first is an iterator pointing to
-            item found or inserted, \p second is true if new item has been added or \p false if the item
+            Returns <tt> std::pair<iterator, bool> </tt> where \p first is an iterator pointing to
+            item found or inserted or \p end() if \p val is not found and \p bInsert is \p false,
+            \p second is \p true if new item has been added or \p false if the item
             already is in the set.
         */
         template <typename Q>
             already is in the set.
         */
         template <typename Q>
-        std::pair<iterator, bool> ensure( const Q& val )
+        std::pair<iterator, bool> update( const Q& val, bool bInsert = true )
         {
             scoped_node_ptr sp( node_allocator().New( base_class::random_level(), val ));
             node_type * pNode;
         {
             scoped_node_ptr sp( node_allocator().New( base_class::random_level(), val ));
             node_type * pNode;
-            std::pair<bool, bool> bRes = base_class::ensure( *sp, [&pNode](bool, node_type& item, node_type&) { pNode = &item; } );
+            std::pair<bool, bool> bRes = base_class::update( *sp, [&pNode](bool, node_type& item, node_type&) { pNode = &item; }, bInsert );
             if ( bRes.first && bRes.second )
                 sp.release();
             if ( bRes.first && bRes.second )
                 sp.release();
+            else if ( !bRes.first )
+                return std::make_pair( end(), false );
             assert( pNode );
             return std::make_pair( node_to_iterator( pNode ), bRes.second );
         }
 
             assert( pNode );
             return std::make_pair( node_to_iterator( pNode ), bRes.second );
         }
 
+        //@cond
+        // Deprecated, use update()
+        template <typename Q>
+        std::pair<iterator, bool> ensure( const Q& val )
+        {
+            return update( val, true );
+        }
+        //@endcond
+
         /// Searches \p key
         /** \anchor cds_nonintrusive_SkipListSet_nogc_find_val
 
         /// Searches \p key
         /** \anchor cds_nonintrusive_SkipListSet_nogc_find_val
 
index bb53d747bd7a66ba07ae7002853ec0e5b8301a2e..32453bfa5eccb604c7402eb7524efde2e7bceb9f 100644 (file)
@@ -330,27 +330,23 @@ namespace cds { namespace container {
             return false;
         }
 
             return false;
         }
 
-        /// Ensures that the item exists in the set
+        /// Updates the item
         /**
             The operation performs inserting or changing data with lock-free manner.
 
         /**
             The operation performs inserting or changing data with lock-free manner.
 
-            If the \p val key not found in the set, then the new item created from \p val
-            is inserted into the set. Otherwise, the functor \p func is called with the item found.
-            The functor \p Func should be a function with signature:
-            \code
-                void func( bool bNew, value_type& item, const Q& val );
-            \endcode
-            or a functor:
+            If \p val not found in the set, then the new item created from \p val
+            is inserted into the set iff \p bInsert is \p true.
+            Otherwise, the functor \p func is called with the item found.
+            The functor \p Func signature:
             \code
                 struct my_functor {
                     void operator()( bool bNew, value_type& item, const Q& val );
                 };
             \endcode
             \code
                 struct my_functor {
                     void operator()( bool bNew, value_type& item, const Q& val );
                 };
             \endcode
-
-            with arguments:
+            where:
             - \p bNew - \p true if the item has been inserted, \p false otherwise
             - \p bNew - \p true if the item has been inserted, \p false otherwise
-            - \p item - item of the set
-            - \p val - argument \p key passed into the \p ensure function
+            - \p item - an item of the set
+            - \p val - argument \p val passed into the \p %update() function
 
             The functor may change non-key fields of the \p item; however, \p func must guarantee
             that during changing no any other modifications could be made on this item by concurrent threads.
 
             The functor may change non-key fields of the \p item; however, \p func must guarantee
             that during changing no any other modifications could be made on this item by concurrent threads.
@@ -359,19 +355,28 @@ namespace cds { namespace container {
 
             Returns <tt> std::pair<bool, bool> </tt> where \p first is true if operation is successfull,
             \p second is true if new item has been added or \p false if the item with \p key
 
             Returns <tt> std::pair<bool, bool> </tt> where \p first is true if operation is successfull,
             \p second is true if new item has been added or \p false if the item with \p key
-            already is in the set.
+            already exists.
         */
         template <typename Q, typename Func>
         */
         template <typename Q, typename Func>
-        std::pair<bool, bool> ensure( const Q& val, Func func )
+        std::pair<bool, bool> update( const Q& val, Func func, bool bInsert = true )
         {
             scoped_node_ptr sp( node_allocator().New( random_level(), val ));
         {
             scoped_node_ptr sp( node_allocator().New( random_level(), val ));
-            std::pair<bool, bool> bRes = base_class::ensure( *sp,
-                [&func, &val](bool bNew, node_type& node, node_type&){ func( bNew, node.m_Value, val ); });
+            std::pair<bool, bool> bRes = base_class::update( *sp,
+                [&func, &val](bool bNew, node_type& node, node_type&){ func( bNew, node.m_Value, val );}, bInsert );
             if ( bRes.first && bRes.second )
                 sp.release();
             return bRes;
         }
 
             if ( bRes.first && bRes.second )
                 sp.release();
             return bRes;
         }
 
+        //@cond
+        // Deprecated, use update()
+        template <typename Q, typename Func>
+        std::pair<bool, bool> ensure( const Q& val, Func func )
+        {
+            return update( val, func, true );
+        }
+        //@endcond
+
         /// Inserts data of type \ref value_type constructed with <tt>std::forward<Args>(args)...</tt>
         /**
             Returns \p true if inserting successful, \p false otherwise.
         /// Inserts data of type \ref value_type constructed with <tt>std::forward<Args>(args)...</tt>
         /**
             Returns \p true if inserting successful, \p false otherwise.
index be70fc1bd356dc46c69e3cd4ba456ad1e2ca37e0..e24ae7a676d5027ba6bbd684fac4042caeb3ec58 100644 (file)
@@ -86,7 +86,15 @@ namespace cds { namespace intrusive {
                 assert( nLevel < height() );
                 assert( nLevel == 0 || (nLevel > 0 && m_arrNext != nullptr) );
 
                 assert( nLevel < height() );
                 assert( nLevel == 0 || (nLevel > 0 && m_arrNext != nullptr) );
 
+#           ifdef CDS_THREAD_SANITIZER_ENABLED
+                // TSan false positive: m_arrNext is read-only array
+                CDS_TSAN_ANNOTATE_IGNORE_READS_BEGIN;
+                atomic_marked_ptr& r = nLevel ? m_arrNext[ nLevel - 1] : m_pNext;
+                CDS_TSAN_ANNOTATE_IGNORE_READS_END;
+                return r;
+#           else
                 return nLevel ? m_arrNext[ nLevel - 1] : m_pNext;
                 return nLevel ? m_arrNext[ nLevel - 1] : m_pNext;
+#           endif
             }
 
             /// Access to element of next pointer array (const version)
             }
 
             /// Access to element of next pointer array (const version)
@@ -95,7 +103,15 @@ namespace cds { namespace intrusive {
                 assert( nLevel < height() );
                 assert( nLevel == 0 || nLevel > 0 && m_arrNext != nullptr );
 
                 assert( nLevel < height() );
                 assert( nLevel == 0 || nLevel > 0 && m_arrNext != nullptr );
 
+#           ifdef CDS_THREAD_SANITIZER_ENABLED
+                // TSan false positive: m_arrNext is read-only array
+                CDS_TSAN_ANNOTATE_IGNORE_READS_BEGIN;
+                atomic_marked_ptr const& r = nLevel ? m_arrNext[ nLevel - 1] : m_pNext;
+                CDS_TSAN_ANNOTATE_IGNORE_READS_END;
+                return r;
+#           else
                 return nLevel ? m_arrNext[ nLevel - 1] : m_pNext;
                 return nLevel ? m_arrNext[ nLevel - 1] : m_pNext;
+#           endif
             }
 
             /// Access to element of next pointer array (same as \ref next function)
             }
 
             /// Access to element of next pointer array (same as \ref next function)
@@ -350,12 +366,13 @@ namespace cds { namespace intrusive {
             event_counter   m_nInsertSuccess        ; ///< Count of success insertion
             event_counter   m_nInsertFailed         ; ///< Count of failed insertion
             event_counter   m_nInsertRetries        ; ///< Count of unsuccessful retries of insertion
             event_counter   m_nInsertSuccess        ; ///< Count of success insertion
             event_counter   m_nInsertFailed         ; ///< Count of failed insertion
             event_counter   m_nInsertRetries        ; ///< Count of unsuccessful retries of insertion
-            event_counter   m_nEnsureExist          ; ///< Count of \p ensure call for existed node
-            event_counter   m_nEnsureNew            ; ///< Count of \p ensure call for new node
+            event_counter   m_nUpdateExist          ; ///< Count of \p update() call for existed node
+            event_counter   m_nUpdateNew            ; ///< Count of \p update() call for new node
             event_counter   m_nUnlinkSuccess        ; ///< Count of successful call of \p unlink
             event_counter   m_nUnlinkFailed         ; ///< Count of failed call of \p unlink
             event_counter   m_nEraseSuccess         ; ///< Count of successful call of \p erase
             event_counter   m_nEraseFailed          ; ///< Count of failed call of \p erase
             event_counter   m_nUnlinkSuccess        ; ///< Count of successful call of \p unlink
             event_counter   m_nUnlinkFailed         ; ///< Count of failed call of \p unlink
             event_counter   m_nEraseSuccess         ; ///< Count of successful call of \p erase
             event_counter   m_nEraseFailed          ; ///< Count of failed call of \p erase
+            event_counter   m_nEraseRetry           ; ///< Count of retries while erasing node
             event_counter   m_nFindFastSuccess      ; ///< Count of successful call of \p find and all derivatives (via fast-path)
             event_counter   m_nFindFastFailed       ; ///< Count of failed call of \p find and all derivatives (via fast-path)
             event_counter   m_nFindSlowSuccess      ; ///< Count of successful call of \p find and all derivatives (via slow-path)
             event_counter   m_nFindFastSuccess      ; ///< Count of successful call of \p find and all derivatives (via fast-path)
             event_counter   m_nFindFastFailed       ; ///< Count of failed call of \p find and all derivatives (via fast-path)
             event_counter   m_nFindSlowSuccess      ; ///< Count of successful call of \p find and all derivatives (via slow-path)
@@ -394,12 +411,13 @@ namespace cds { namespace intrusive {
             void onInsertSuccess()          { ++m_nInsertSuccess    ; }
             void onInsertFailed()           { ++m_nInsertFailed     ; }
             void onInsertRetry()            { ++m_nInsertRetries    ; }
             void onInsertSuccess()          { ++m_nInsertSuccess    ; }
             void onInsertFailed()           { ++m_nInsertFailed     ; }
             void onInsertRetry()            { ++m_nInsertRetries    ; }
-            void onEnsureExist()            { ++m_nEnsureExist      ; }
-            void onEnsureNew()              { ++m_nEnsureNew        ; }
+            void onUpdateExist()            { ++m_nUpdateExist      ; }
+            void onUpdateNew()              { ++m_nUpdateNew        ; }
             void onUnlinkSuccess()          { ++m_nUnlinkSuccess    ; }
             void onUnlinkFailed()           { ++m_nUnlinkFailed     ; }
             void onEraseSuccess()           { ++m_nEraseSuccess     ; }
             void onEraseFailed()            { ++m_nEraseFailed      ; }
             void onUnlinkSuccess()          { ++m_nUnlinkSuccess    ; }
             void onUnlinkFailed()           { ++m_nUnlinkFailed     ; }
             void onEraseSuccess()           { ++m_nEraseSuccess     ; }
             void onEraseFailed()            { ++m_nEraseFailed      ; }
+            void onEraseRetry()             { ++m_nEraseRetry; }
             void onFindFastSuccess()        { ++m_nFindFastSuccess  ; }
             void onFindFastFailed()         { ++m_nFindFastFailed   ; }
             void onFindSlowSuccess()        { ++m_nFindSlowSuccess  ; }
             void onFindFastSuccess()        { ++m_nFindFastSuccess  ; }
             void onFindFastFailed()         { ++m_nFindFastFailed   ; }
             void onFindSlowSuccess()        { ++m_nFindSlowSuccess  ; }
@@ -434,12 +452,13 @@ namespace cds { namespace intrusive {
             void onInsertSuccess()          const {}
             void onInsertFailed()           const {}
             void onInsertRetry()            const {}
             void onInsertSuccess()          const {}
             void onInsertFailed()           const {}
             void onInsertRetry()            const {}
-            void onEnsureExist()            const {}
-            void onEnsureNew()              const {}
+            void onUpdateExist()            const {}
+            void onUpdateNew()              const {}
             void onUnlinkSuccess()          const {}
             void onUnlinkFailed()           const {}
             void onEraseSuccess()           const {}
             void onEraseFailed()            const {}
             void onUnlinkSuccess()          const {}
             void onUnlinkFailed()           const {}
             void onEraseSuccess()           const {}
             void onEraseFailed()            const {}
+            void onEraseRetry()             const {}
             void onFindFastSuccess()        const {}
             void onFindFastFailed()         const {}
             void onFindSlowSuccess()        const {}
             void onFindFastSuccess()        const {}
             void onFindFastFailed()         const {}
             void onFindSlowSuccess()        const {}
index 9abad6f5dec3dfdd3542a9d64e69b4b4fa93153e..3574937aec07991f6478fba18792e6ee5915a35b 100644 (file)
@@ -391,7 +391,7 @@ namespace cds { namespace intrusive {
             node_type *   pSucc[ c_nMaxHeight ];
 
             typename gc::template GuardArray< c_nMaxHeight * 2 > guards;   ///< Guards array for pPrev/pSucc
             node_type *   pSucc[ c_nMaxHeight ];
 
             typename gc::template GuardArray< c_nMaxHeight * 2 > guards;   ///< Guards array for pPrev/pSucc
-            node_type *   pCur;   // guarded by guards; needed only for \p ensure()
+            node_type *   pCur;   // guarded by guards; needed only for \p update()
         };
         //@endcond
 
         };
         //@endcond
 
@@ -460,16 +460,16 @@ namespace cds { namespace intrusive {
                     }
 
                     // pSucc contains deletion mark for pCur
                     }
 
                     // pSucc contains deletion mark for pCur
-                    pSucc = pCur->next( nLevel ).load( memory_model::memory_order_relaxed );
+                    pSucc = pCur->next( nLevel ).load( memory_model::memory_order_acquire );
 
 
-                    if ( pPred->next( nLevel ).load( memory_model::memory_order_relaxed ).all() != pCur.ptr() )
+                    if ( pPred->next( nLevel ).load( memory_model::memory_order_acquire ).all() != pCur.ptr() )
                         goto retry;
 
                     if ( pSucc.bits() ) {
                         // pCur is marked, i.e. logically deleted.
                         marked_node_ptr p( pCur.ptr() );
                         if ( pPred->next( nLevel ).compare_exchange_strong( p, marked_node_ptr( pSucc.ptr() ),
                         goto retry;
 
                     if ( pSucc.bits() ) {
                         // pCur is marked, i.e. logically deleted.
                         marked_node_ptr p( pCur.ptr() );
                         if ( pPred->next( nLevel ).compare_exchange_strong( p, marked_node_ptr( pSucc.ptr() ),
-                            memory_model::memory_order_release, atomics::memory_order_relaxed ))
+                            memory_model::memory_order_acquire, atomics::memory_order_relaxed ))
                         {
                             if ( nLevel == 0 ) {
                                 gc::retire( node_traits::to_value_ptr( pCur.ptr() ), dispose_node );
                         {
                             if ( nLevel == 0 ) {
                                 gc::retire( node_traits::to_value_ptr( pCur.ptr() ), dispose_node );
@@ -528,19 +528,21 @@ namespace cds { namespace intrusive {
                 if ( pCur.ptr() ) {
 
                     // pSucc contains deletion mark for pCur
                 if ( pCur.ptr() ) {
 
                     // pSucc contains deletion mark for pCur
-                    pSucc = pCur->next( nLevel ).load( memory_model::memory_order_relaxed );
+                    pSucc = pCur->next( nLevel ).load( memory_model::memory_order_acquire );
 
 
-                    if ( pPred->next( nLevel ).load( memory_model::memory_order_relaxed ).all() != pCur.ptr() )
+                    if ( pPred->next( nLevel ).load( memory_model::memory_order_acquire ).all() != pCur.ptr() )
                         goto retry;
 
                     if ( pSucc.bits() ) {
                         // pCur is marked, i.e. logically deleted.
                         marked_node_ptr p( pCur.ptr() );
                         if ( pPred->next( nLevel ).compare_exchange_strong( p, marked_node_ptr( pSucc.ptr() ),
                         goto retry;
 
                     if ( pSucc.bits() ) {
                         // pCur is marked, i.e. logically deleted.
                         marked_node_ptr p( pCur.ptr() );
                         if ( pPred->next( nLevel ).compare_exchange_strong( p, marked_node_ptr( pSucc.ptr() ),
-                            memory_model::memory_order_release, atomics::memory_order_relaxed ))
+                            memory_model::memory_order_acquire, atomics::memory_order_relaxed ))
                         {
                         {
-                            if ( nLevel == 0 )
+                            if ( nLevel == 0 ) {
                                 gc::retire( node_traits::to_value_ptr( pCur.ptr() ), dispose_node );
                                 gc::retire( node_traits::to_value_ptr( pCur.ptr() ), dispose_node );
+                                m_Stat.onEraseWhileFind();
+                            }
                         }
                         goto retry;
                     }
                         }
                         goto retry;
                     }
@@ -582,19 +584,21 @@ namespace cds { namespace intrusive {
                     }
 
                     // pSucc contains deletion mark for pCur
                     }
 
                     // pSucc contains deletion mark for pCur
-                    pSucc = pCur->next( nLevel ).load( memory_model::memory_order_relaxed );
+                    pSucc = pCur->next( nLevel ).load( memory_model::memory_order_acquire );
 
 
-                    if ( pPred->next( nLevel ).load( memory_model::memory_order_relaxed ).all() != pCur.ptr() )
+                    if ( pPred->next( nLevel ).load( memory_model::memory_order_acquire ).all() != pCur.ptr() )
                         goto retry;
 
                     if ( pSucc.bits() ) {
                         // pCur is marked, i.e. logically deleted.
                         marked_node_ptr p( pCur.ptr() );
                         if ( pPred->next( nLevel ).compare_exchange_strong( p, marked_node_ptr( pSucc.ptr() ),
                         goto retry;
 
                     if ( pSucc.bits() ) {
                         // pCur is marked, i.e. logically deleted.
                         marked_node_ptr p( pCur.ptr() );
                         if ( pPred->next( nLevel ).compare_exchange_strong( p, marked_node_ptr( pSucc.ptr() ),
-                            memory_model::memory_order_release, atomics::memory_order_relaxed ))
+                            memory_model::memory_order_acquire, atomics::memory_order_relaxed ))
                         {
                         {
-                            if ( nLevel == 0 )
+                            if ( nLevel == 0 ) {
                                 gc::retire( node_traits::to_value_ptr( pCur.ptr() ), dispose_node );
                                 gc::retire( node_traits::to_value_ptr( pCur.ptr() ), dispose_node );
+                                m_Stat.onEraseWhileFind();
+                            }
                         }
                         goto retry;
                     }
                         }
                         goto retry;
                     }
@@ -624,15 +628,17 @@ namespace cds { namespace intrusive {
             for ( unsigned int nLevel = 1; nLevel < nHeight; ++nLevel )
                 pNode->next(nLevel).store( marked_node_ptr(), memory_model::memory_order_relaxed );
 
             for ( unsigned int nLevel = 1; nLevel < nHeight; ++nLevel )
                 pNode->next(nLevel).store( marked_node_ptr(), memory_model::memory_order_relaxed );
 
+            // Insert at level 0
             {
                 marked_node_ptr p( pos.pSucc[0] );
                 pNode->next( 0 ).store( p, memory_model::memory_order_release );
             {
                 marked_node_ptr p( pos.pSucc[0] );
                 pNode->next( 0 ).store( p, memory_model::memory_order_release );
-                if ( !pos.pPrev[0]->next(0).compare_exchange_strong( p, marked_node_ptr(pNode), memory_model::memory_order_release, atomics::memory_order_relaxed ) ) {
+                if ( !pos.pPrev[0]->next(0).compare_exchange_strong( p, marked_node_ptr(pNode), memory_model::memory_order_release, atomics::memory_order_relaxed ))
                     return false;
                     return false;
-                }
+
                 f( val );
             }
 
                 f( val );
             }
 
+            // Insert at level 1..max
             for ( unsigned int nLevel = 1; nLevel < nHeight; ++nLevel ) {
                 marked_node_ptr p;
                 while ( true ) {
             for ( unsigned int nLevel = 1; nLevel < nHeight; ++nLevel ) {
                 marked_node_ptr p;
                 while ( true ) {
@@ -645,7 +651,7 @@ namespace cds { namespace intrusive {
                         return true;
                     }
                     p = q;
                         return true;
                     }
                     p = q;
-                    if ( pos.pPrev[nLevel]->next(nLevel).compare_exchange_strong( q, marked_node_ptr( pNode ), memory_model::memory_order_release, atomics::memory_order_relaxed ) )
+                    if ( pos.pPrev[nLevel]->next(nLevel).compare_exchange_strong( q, marked_node_ptr( pNode ), memory_model::memory_order_release, atomics::memory_order_relaxed ))
                         break;
 
                     // Renew insert position
                         break;
 
                     // Renew insert position
@@ -666,14 +672,13 @@ namespace cds { namespace intrusive {
             assert( pDel != nullptr );
 
             marked_node_ptr pSucc;
             assert( pDel != nullptr );
 
             marked_node_ptr pSucc;
-            typename gc::Guard gSucc;
 
             // logical deletion (marking)
             for ( unsigned int nLevel = pDel->height() - 1; nLevel > 0; --nLevel ) {
                 while ( true ) {
 
             // logical deletion (marking)
             for ( unsigned int nLevel = pDel->height() - 1; nLevel > 0; --nLevel ) {
                 while ( true ) {
-                    pSucc = gSucc.protect( pDel->next(nLevel), gc_protect );
+                    pSucc = pDel->next(nLevel);
                     if ( pSucc.bits() || pDel->next(nLevel).compare_exchange_weak( pSucc, pSucc | 1,
                     if ( pSucc.bits() || pDel->next(nLevel).compare_exchange_weak( pSucc, pSucc | 1,
-                         memory_model::memory_order_acquire, atomics::memory_order_relaxed ))
+                         memory_model::memory_order_release, atomics::memory_order_relaxed ))
                     {
                         break;
                     }
                     {
                         break;
                     }
@@ -681,10 +686,8 @@ namespace cds { namespace intrusive {
             }
 
             while ( true ) {
             }
 
             while ( true ) {
-                pSucc = gSucc.protect( pDel->next(0), gc_protect );
-                marked_node_ptr p( pSucc.ptr() );
-                if ( pDel->next(0).compare_exchange_strong( p, marked_node_ptr(p.ptr(), 1),
-                     memory_model::memory_order_acquire, atomics::memory_order_relaxed ))
+                marked_node_ptr p( pDel->next(0).load(memory_model::memory_order_relaxed).ptr() );
+                if ( pDel->next(0).compare_exchange_strong( p, p | 1, memory_model::memory_order_release, atomics::memory_order_relaxed ))
                 {
                     f( *node_traits::to_value_ptr( pDel ));
 
                 {
                     f( *node_traits::to_value_ptr( pDel ));
 
@@ -692,9 +695,9 @@ namespace cds { namespace intrusive {
                     // try fast erase
                     p = pDel;
                     for ( int nLevel = static_cast<int>( pDel->height() - 1 ); nLevel >= 0; --nLevel ) {
                     // try fast erase
                     p = pDel;
                     for ( int nLevel = static_cast<int>( pDel->height() - 1 ); nLevel >= 0; --nLevel ) {
-                        pSucc = gSucc.protect( pDel->next(nLevel), gc_protect );
+                        pSucc = pDel->next(nLevel).load(memory_model::memory_order_relaxed);
                         if ( !pos.pPrev[nLevel]->next(nLevel).compare_exchange_strong( p, marked_node_ptr(pSucc.ptr()),
                         if ( !pos.pPrev[nLevel]->next(nLevel).compare_exchange_strong( p, marked_node_ptr(pSucc.ptr()),
-                            memory_model::memory_order_release, atomics::memory_order_relaxed) )
+                            memory_model::memory_order_acquire, atomics::memory_order_relaxed) )
                         {
                             // Make slow erase
                             find_position( *node_traits::to_value_ptr( pDel ), pos, key_comparator(), false );
                         {
                             // Make slow erase
                             find_position( *node_traits::to_value_ptr( pDel ), pos, key_comparator(), false );
@@ -710,9 +713,11 @@ namespace cds { namespace intrusive {
                 }
                 else {
                     if ( p.bits() ) {
                 }
                 else {
                     if ( p.bits() ) {
+                        // Another thread is deleting pDel right now
                         return false;
                     }
                 }
                         return false;
                     }
                 }
+                m_Stat.onEraseRetry();
             }
         }
 
             }
         }
 
@@ -1045,8 +1050,7 @@ namespace cds { namespace intrusive {
             position pos;
             while ( true )
             {
             position pos;
             while ( true )
             {
-                bool bFound = find_position( val, pos, key_comparator(), true );
-                if ( bFound ) {
+                if ( find_position( val, pos, key_comparator(), true )) {
                     // scoped_node_ptr deletes the node tower if we create it
                     if ( !bTowerMade )
                         scp.release();
                     // scoped_node_ptr deletes the node tower if we create it
                     if ( !bTowerMade )
                         scp.release();
@@ -1076,31 +1080,33 @@ namespace cds { namespace intrusive {
             }
         }
 
             }
         }
 
-        /// Ensures that the \p val exists in the set
+        /// Updates the node
         /**
             The operation performs inserting or changing data with lock-free manner.
 
         /**
             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 into the set
+            iff \p bInsert is \p true.
             Otherwise, the functor \p func is called with item found.
             Otherwise, the functor \p func is called with item found.
-            The functor signature is:
+            The functor \p func signature is:
             \code
                 void func( bool bNew, value_type& item, value_type& val );
             \endcode
             with arguments:
             - \p bNew - \p true if the item has been inserted, \p false otherwise
             - \p item - item of the set
             \code
                 void func( bool bNew, value_type& item, value_type& val );
             \endcode
             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
             refer to the same thing.
 
             Returns std::pair<bool, bool> where \p first is \p true if operation is successfull,
             If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
             refer to the same thing.
 
             Returns std::pair<bool, bool> where \p first is \p true if operation is successfull,
+            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
             \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 exists.
 
             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
         */
         template <typename Func>
 
             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
         */
         template <typename Func>
-        std::pair<bool, bool> ensure( value_type& val, Func func )
+        std::pair<bool, bool> update( value_type& val, Func func, bool bInsert = true )
         {
             typename gc::Guard gNew;
             gNew.assign( &val );
         {
             typename gc::Guard gNew;
             gNew.assign( &val );
@@ -1121,10 +1127,15 @@ namespace cds { namespace intrusive {
                         scp.release();
 
                     func( false, *node_traits::to_value_ptr(pos.pCur), val );
                         scp.release();
 
                     func( false, *node_traits::to_value_ptr(pos.pCur), val );
-                    m_Stat.onEnsureExist();
+                    m_Stat.onUpdateExist();
                     return std::make_pair( true, false );
                 }
 
                     return std::make_pair( true, false );
                 }
 
+                if ( !bInsert ) {
+                    scp.release();
+                    return std::make_pair( false, false );
+                }
+
                 if ( !bTowerOk ) {
                     build_node( pNode );
                     nHeight = pNode->height();
                 if ( !bTowerOk ) {
                     build_node( pNode );
                     nHeight = pNode->height();
@@ -1141,11 +1152,20 @@ namespace cds { namespace intrusive {
                 ++m_ItemCounter;
                 scp.release();
                 m_Stat.onAddNode( nHeight );
                 ++m_ItemCounter;
                 scp.release();
                 m_Stat.onAddNode( nHeight );
-                m_Stat.onEnsureNew();
+                m_Stat.onUpdateNew();
                 return std::make_pair( true, true );
             }
         }
 
                 return std::make_pair( true, true );
             }
         }
 
+        //@cond
+        // Deprecated, use update() instead
+        template <typename Func>
+        std::pair<bool, bool> ensure( value_type& val, Func func )
+        {
+            return update( val, func, true );
+        }
+        //@endcond
+
         /// Unlinks the item \p val from the set
         /**
             The function searches the item \p val in the set and unlink it from the set
         /// Unlinks the item \p val from the set
         /**
             The function searches the item \p val in the set and unlink it from the set
index 6a8f4c3f702b9a21a7f4d8bb004e64c117c1ae1e..1d4503f4430ad539d8e854c30c4c3065fa684365 100644 (file)
@@ -679,11 +679,12 @@ namespace cds { namespace intrusive {
             }
         }
 
             }
         }
 
-        /// Ensures that the \p val exists in the set
+        /// Updates the node
         /**
             The operation performs inserting or changing data with lock-free manner.
 
         /**
             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 into the set
+            iff \p bInsert is \p true.
             Otherwise, the functor \p func is called with item found.
             The functor signature is:
             \code
             Otherwise, the functor \p func is called with item found.
             The functor signature is:
             \code
@@ -692,7 +693,7 @@ 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
             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
             refer to the same thing.
 
             If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
             refer to the same thing.
 
@@ -706,7 +707,7 @@ namespace cds { namespace intrusive {
             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
         */
         template <typename Func>
             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
         */
         template <typename Func>
-        std::pair<bool, bool> ensure( value_type& val, Func func )
+        std::pair<bool, bool> update( value_type& val, Func func, bool bInsert = true )
         {
             node_type * pNode = node_traits::to_node_ptr( val );
             scoped_node_ptr scp( pNode );
         {
             node_type * pNode = node_traits::to_node_ptr( val );
             scoped_node_ptr scp( pNode );
@@ -724,10 +725,15 @@ namespace cds { namespace intrusive {
                         scp.release();
 
                     func( false, *node_traits::to_value_ptr(pos.pCur), val );
                         scp.release();
 
                     func( false, *node_traits::to_value_ptr(pos.pCur), val );
-                    m_Stat.onEnsureExist();
+                    m_Stat.onUpdateExist();
                     return std::make_pair( true, false );
                 }
 
                     return std::make_pair( true, false );
                 }
 
+                if ( !bInsert ) {
+                    scp.release();
+                    return std::make_pair( false, false );
+                }
+
                 if ( !bTowerOk ) {
                     build_node( pNode );
                     nHeight = pNode->height();
                 if ( !bTowerOk ) {
                     build_node( pNode );
                     nHeight = pNode->height();
@@ -744,11 +750,20 @@ namespace cds { namespace intrusive {
                 ++m_ItemCounter;
                 scp.release();
                 m_Stat.onAddNode( nHeight );
                 ++m_ItemCounter;
                 scp.release();
                 m_Stat.onAddNode( nHeight );
-                m_Stat.onEnsureNew();
+                m_Stat.onUpdateNew();
                 return std::make_pair( true, true );
             }
         }
 
                 return std::make_pair( true, true );
             }
         }
 
+        //@cond
+        // Deprecated, use update() instead
+        template <typename Func>
+        std::pair<bool, bool> ensure( value_type& val, Func func )
+        {
+            return update( val, func, true );
+        }
+        //@endcond
+
         /// Finds \p key
         /** \anchor cds_intrusive_SkipListSet_nogc_find_func
             The function searches the item with key equal to \p key and calls the functor \p f for item found.
         /// Finds \p key
         /** \anchor cds_intrusive_SkipListSet_nogc_find_func
             The function searches the item with key equal to \p key and calls the functor \p f for item found.
index 58ac7eda18d3aa7fd4d9c413cb42b8d5558fd5d4..95d9f59212f0e5d51d28241befc28156c77caf1e 100644 (file)
@@ -101,7 +101,15 @@ namespace cds { namespace intrusive {
                 assert( nLevel < height() );
                 assert( nLevel == 0 || (nLevel > 0 && m_arrNext != nullptr) );
 
                 assert( nLevel < height() );
                 assert( nLevel == 0 || (nLevel > 0 && m_arrNext != nullptr) );
 
-                return nLevel ? m_arrNext[nLevel - 1] : m_pNext;
+#           ifdef CDS_THREAD_SANITIZER_ENABLED
+                // TSan false positive: m_arrNext is read-only array
+                CDS_TSAN_ANNOTATE_IGNORE_READS_BEGIN;
+                atomic_marked_ptr& r = nLevel ? m_arrNext[ nLevel - 1] : m_pNext;
+                CDS_TSAN_ANNOTATE_IGNORE_READS_END;
+                return r;
+#           else
+                return nLevel ? m_arrNext[ nLevel - 1] : m_pNext;
+#           endif
             }
 
             /// Access to element of next pointer array (const version)
             }
 
             /// Access to element of next pointer array (const version)
@@ -110,7 +118,15 @@ namespace cds { namespace intrusive {
                 assert( nLevel < height() );
                 assert( nLevel == 0 || nLevel > 0 && m_arrNext != nullptr );
 
                 assert( nLevel < height() );
                 assert( nLevel == 0 || nLevel > 0 && m_arrNext != nullptr );
 
-                return nLevel ? m_arrNext[nLevel - 1] : m_pNext;
+#           ifdef CDS_THREAD_SANITIZER_ENABLED
+                // TSan false positive: m_arrNext is read-only array
+                CDS_TSAN_ANNOTATE_IGNORE_READS_BEGIN;
+                atomic_marked_ptr& r = nLevel ? m_arrNext[ nLevel - 1] : m_pNext;
+                CDS_TSAN_ANNOTATE_IGNORE_READS_END;
+                return r;
+#           else
+                return nLevel ? m_arrNext[ nLevel - 1] : m_pNext;
+#           endif
             }
 
             /// Access to element of next pointer array (same as \ref next function)
             }
 
             /// Access to element of next pointer array (same as \ref next function)
@@ -682,7 +698,7 @@ namespace cds { namespace intrusive {
             for ( int nLevel = static_cast<int>(c_nMaxHeight - 1); nLevel >= 0; --nLevel ) {
 
                 while ( true ) {
             for ( int nLevel = static_cast<int>(c_nMaxHeight - 1); nLevel >= 0; --nLevel ) {
 
                 while ( true ) {
-                    pCur = pPred->next( nLevel ).load( memory_model::memory_order_relaxed );
+                    pCur = pPred->next( nLevel ).load( memory_model::memory_order_acquire );
                     if ( pCur.bits() ) {
                         // pCur.bits() means that pPred is logically deleted
                         goto retry;
                     if ( pCur.bits() ) {
                         // pCur.bits() means that pPred is logically deleted
                         goto retry;
@@ -694,9 +710,9 @@ namespace cds { namespace intrusive {
                     }
 
                     // pSucc contains deletion mark for pCur
                     }
 
                     // pSucc contains deletion mark for pCur
-                    pSucc = pCur->next( nLevel ).load( memory_model::memory_order_relaxed );
+                    pSucc = pCur->next( nLevel ).load( memory_model::memory_order_acquire );
 
 
-                    if ( pPred->next( nLevel ).load( memory_model::memory_order_relaxed ).all() != pCur.ptr() )
+                    if ( pPred->next( nLevel ).load( memory_model::memory_order_acquire ).all() != pCur.ptr() )
                         goto retry;
 
                     if ( pSucc.bits() ) {
                         goto retry;
 
                     if ( pSucc.bits() ) {
@@ -760,7 +776,7 @@ namespace cds { namespace intrusive {
 
             for ( int nLevel = static_cast<int>(c_nMaxHeight - 1); nLevel >= 0; --nLevel ) {
 
 
             for ( int nLevel = static_cast<int>(c_nMaxHeight - 1); nLevel >= 0; --nLevel ) {
 
-                pCur = pPred->next( nLevel ).load( memory_model::memory_order_relaxed );
+                pCur = pPred->next( nLevel ).load( memory_model::memory_order_acquire );
                 // pCur.bits() means that pPred is logically deleted
                 // head cannot be deleted
                 assert( pCur.bits() == 0 );
                 // pCur.bits() means that pPred is logically deleted
                 // head cannot be deleted
                 assert( pCur.bits() == 0 );
@@ -768,9 +784,9 @@ namespace cds { namespace intrusive {
                 if ( pCur.ptr() ) {
 
                     // pSucc contains deletion mark for pCur
                 if ( pCur.ptr() ) {
 
                     // pSucc contains deletion mark for pCur
-                    pSucc = pCur->next( nLevel ).load( memory_model::memory_order_relaxed );
+                    pSucc = pCur->next( nLevel ).load( memory_model::memory_order_acquire );
 
 
-                    if ( pPred->next( nLevel ).load( memory_model::memory_order_relaxed ).all() != pCur.ptr() )
+                    if ( pPred->next( nLevel ).load( memory_model::memory_order_acquire ).all() != pCur.ptr() )
                         goto retry;
 
                     if ( pSucc.bits() ) {
                         goto retry;
 
                     if ( pSucc.bits() ) {
@@ -820,7 +836,7 @@ namespace cds { namespace intrusive {
             for ( int nLevel = static_cast<int>(c_nMaxHeight - 1); nLevel >= 0; --nLevel ) {
 
                 while ( true ) {
             for ( int nLevel = static_cast<int>(c_nMaxHeight - 1); nLevel >= 0; --nLevel ) {
 
                 while ( true ) {
-                    pCur = pPred->next( nLevel ).load( memory_model::memory_order_relaxed );
+                    pCur = pPred->next( nLevel ).load( memory_model::memory_order_acquire );
                     if ( pCur.bits() ) {
                         // pCur.bits() means that pPred is logically deleted
                         goto retry;
                     if ( pCur.bits() ) {
                         // pCur.bits() means that pPred is logically deleted
                         goto retry;
@@ -832,9 +848,9 @@ namespace cds { namespace intrusive {
                     }
 
                     // pSucc contains deletion mark for pCur
                     }
 
                     // pSucc contains deletion mark for pCur
-                    pSucc = pCur->next( nLevel ).load( memory_model::memory_order_relaxed );
+                    pSucc = pCur->next( nLevel ).load( memory_model::memory_order_acquire );
 
 
-                    if ( pPred->next( nLevel ).load( memory_model::memory_order_relaxed ).all() != pCur.ptr() )
+                    if ( pPred->next( nLevel ).load( memory_model::memory_order_acquire ).all() != pCur.ptr() )
                         goto retry;
 
                     if ( pSucc.bits() ) {
                         goto retry;
 
                     if ( pSucc.bits() ) {
@@ -986,9 +1002,9 @@ namespace cds { namespace intrusive {
                     }
                     else
                         m_Stat.onFastExtract();
                     }
                     else
                         m_Stat.onFastExtract();
-
                     return true;
                 }
                     return true;
                 }
+                m_Stat.onEraseRetry();
             }
         }
 
             }
         }
 
@@ -1423,11 +1439,12 @@ namespace cds { namespace intrusive {
             return bRet;
         }
 
             return bRet;
         }
 
-        /// Ensures that the \p val exists in the set
+        /// Updates the node
         /**
             The operation performs inserting or changing data with lock-free manner.
 
         /**
             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 into the set
+            iff \p bInsert is \p true.
             Otherwise, the functor \p func is called with item found.
             The functor signature is:
             \code
             Otherwise, the functor \p func is called with item found.
             The functor signature is:
             \code
@@ -1436,7 +1453,7 @@ 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
             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
             refer to the same thing.
 
             If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
             refer to the same thing.
 
@@ -1446,13 +1463,14 @@ namespace cds { namespace intrusive {
             RCU \p synchronize method can be called. RCU should not be locked.
 
             Returns std::pair<bool, bool> where \p first is \p true if operation is successfull,
             RCU \p synchronize method can be called. RCU should not be locked.
 
             Returns std::pair<bool, bool> where \p first is \p true if operation is successfull,
+            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
             \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 exists.
 
             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
         */
         template <typename Func>
 
             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
         */
         template <typename Func>
-        std::pair<bool, bool> ensure( value_type& val, Func func )
+        std::pair<bool, bool> update( value_type& val, Func func, bool bInsert = true )
         {
             check_deadlock_policy::check();
 
         {
             check_deadlock_policy::check();
 
@@ -1476,7 +1494,13 @@ namespace cds { namespace intrusive {
                             scp.release();
 
                         func( false, *node_traits::to_value_ptr(pos.pCur), val );
                             scp.release();
 
                         func( false, *node_traits::to_value_ptr(pos.pCur), val );
-                        m_Stat.onEnsureExist();
+                        m_Stat.onUpdateExist();
+                        break;
+                    }
+
+                    if ( !bInsert ) {
+                        scp.release();
+                        bRet.first = false;
                         break;
                     }
 
                         break;
                     }
 
@@ -1496,7 +1520,7 @@ namespace cds { namespace intrusive {
                     ++m_ItemCounter;
                     scp.release();
                     m_Stat.onAddNode( nHeight );
                     ++m_ItemCounter;
                     scp.release();
                     m_Stat.onAddNode( nHeight );
-                    m_Stat.onEnsureNew();
+                    m_Stat.onUpdateNew();
                     bRet.second = true;
                     break;
                 }
                     bRet.second = true;
                     break;
                 }
@@ -1505,6 +1529,15 @@ namespace cds { namespace intrusive {
             return bRet;
         }
 
             return bRet;
         }
 
+        //@cond
+        // Deprecated, use update().
+        template <typename Func>
+        std::pair<bool, bool> ensure( value_type& val, Func func )
+        {
+            return update( val, func, true );
+        }
+        //@endcond
+
         /// Unlinks the item \p val from the set
         /**
             The function searches the item \p val in the set and unlink it from the set
         /// Unlinks the item \p val from the set
         /**
             The function searches the item \p val in the set and unlink it from the set
index 6d882bde8bbb8d7538abf94855dc86b9a57a273e..04b0ddce33fc6b58d08748f457a80446084387f3 100644 (file)
@@ -397,21 +397,28 @@ namespace set {
             //CPPUNIT_MSG( PrintStat()(s, "Insert test") );
 
             ensure_functor f;
             //CPPUNIT_MSG( PrintStat()(s, "Insert test") );
 
             ensure_functor f;
-            std::pair<bool, bool> ret = s.ensure( v1, f );
+            std::pair<bool, bool> ret = s.update( v1, f, true );
             CPPUNIT_ASSERT( ret.first );
             CPPUNIT_ASSERT( ret.second );
             CPPUNIT_ASSERT( v1.nEnsureNewCount == 1 );
             CPPUNIT_ASSERT( v1.nEnsureCount == 0 );
             CPPUNIT_ASSERT( check_size( s, 1 ));
 
             CPPUNIT_ASSERT( ret.first );
             CPPUNIT_ASSERT( ret.second );
             CPPUNIT_ASSERT( v1.nEnsureNewCount == 1 );
             CPPUNIT_ASSERT( v1.nEnsureCount == 0 );
             CPPUNIT_ASSERT( check_size( s, 1 ));
 
-            ret = s.ensure( v2, f );
+            ret = s.update( v2, f, false );
+            CPPUNIT_ASSERT( !ret.first );
+            CPPUNIT_ASSERT( !ret.second );
+            CPPUNIT_ASSERT( v2.nEnsureNewCount == 0 );
+            CPPUNIT_ASSERT( v2.nEnsureCount == 0 );
+            CPPUNIT_ASSERT( check_size( s, 1 ));
+
+            ret = s.update( v2, f );
             CPPUNIT_ASSERT( ret.first );
             CPPUNIT_ASSERT( ret.second );
             CPPUNIT_ASSERT( v2.nEnsureNewCount == 1 );
             CPPUNIT_ASSERT( v2.nEnsureCount == 0 );
             CPPUNIT_ASSERT( check_size( s, 2 ));
 
             CPPUNIT_ASSERT( ret.first );
             CPPUNIT_ASSERT( ret.second );
             CPPUNIT_ASSERT( v2.nEnsureNewCount == 1 );
             CPPUNIT_ASSERT( v2.nEnsureCount == 0 );
             CPPUNIT_ASSERT( check_size( s, 2 ));
 
-            ret = s.ensure( v3, f );
+            ret = s.update( v3, f, true );
             CPPUNIT_ASSERT( ret.first );
             CPPUNIT_ASSERT( ret.second );
             CPPUNIT_ASSERT( v3.nEnsureNewCount == 1 );
             CPPUNIT_ASSERT( ret.first );
             CPPUNIT_ASSERT( ret.second );
             CPPUNIT_ASSERT( v3.nEnsureNewCount == 1 );
@@ -422,21 +429,21 @@ namespace set {
             CPPUNIT_ASSERT( s.find_with( v2, base_class::less<value_type>() ) == &v2 );
             CPPUNIT_ASSERT( s.find( v3 ) == &v3 );
 
             CPPUNIT_ASSERT( s.find_with( v2, base_class::less<value_type>() ) == &v2 );
             CPPUNIT_ASSERT( s.find( v3 ) == &v3 );
 
-            ret = s.ensure( v1, f );
+            ret = s.update( v1, f, true );
             CPPUNIT_ASSERT( ret.first );
             CPPUNIT_ASSERT( !ret.second );
             CPPUNIT_ASSERT( v1.nEnsureNewCount == 1 );
             CPPUNIT_ASSERT( v1.nEnsureCount == 1 );
             CPPUNIT_ASSERT( check_size( s, 3 ));
 
             CPPUNIT_ASSERT( ret.first );
             CPPUNIT_ASSERT( !ret.second );
             CPPUNIT_ASSERT( v1.nEnsureNewCount == 1 );
             CPPUNIT_ASSERT( v1.nEnsureCount == 1 );
             CPPUNIT_ASSERT( check_size( s, 3 ));
 
-            ret = s.ensure( v2, f );
+            ret = s.update( v2, f, false );
             CPPUNIT_ASSERT( ret.first );
             CPPUNIT_ASSERT( !ret.second );
             CPPUNIT_ASSERT( v2.nEnsureNewCount == 1 );
             CPPUNIT_ASSERT( v2.nEnsureCount == 1 );
             CPPUNIT_ASSERT( check_size( s, 3 ));
 
             CPPUNIT_ASSERT( ret.first );
             CPPUNIT_ASSERT( !ret.second );
             CPPUNIT_ASSERT( v2.nEnsureNewCount == 1 );
             CPPUNIT_ASSERT( v2.nEnsureCount == 1 );
             CPPUNIT_ASSERT( check_size( s, 3 ));
 
-            ret = s.ensure( v3, f );
+            ret = s.update( v3, f );
             CPPUNIT_ASSERT( ret.first );
             CPPUNIT_ASSERT( !ret.second );
             CPPUNIT_ASSERT( v3.nEnsureNewCount == 1 );
             CPPUNIT_ASSERT( ret.first );
             CPPUNIT_ASSERT( !ret.second );
             CPPUNIT_ASSERT( v3.nEnsureNewCount == 1 );
index 7633a7538ec4ed699b5576533c530d7721219250..615cffc3f8fd849ff8272c7a7be8b28ec9bfd8b8 100644 (file)
@@ -22,8 +22,8 @@ namespace std {
             << "\t\t          m_nInsertSuccess: " << s.m_nInsertSuccess.get()           << "\n"
             << "\t\t           m_nInsertFailed: " << s.m_nInsertFailed.get()            << "\n"
             << "\t\t          m_nInsertRetries: " << s.m_nInsertRetries.get()           << "\n"
             << "\t\t          m_nInsertSuccess: " << s.m_nInsertSuccess.get()           << "\n"
             << "\t\t           m_nInsertFailed: " << s.m_nInsertFailed.get()            << "\n"
             << "\t\t          m_nInsertRetries: " << s.m_nInsertRetries.get()           << "\n"
-            << "\t\t            m_nEnsureExist: " << s.m_nEnsureExist.get()             << "\n"
-            << "\t\t              m_nEnsureNew: " << s.m_nEnsureNew.get()               << "\n"
+            << "\t\t            m_nUpdateExist: " << s.m_nUpdateExist.get()             << "\n"
+            << "\t\t              m_nUpdateNew: " << s.m_nUpdateNew.get()               << "\n"
             << "\t\t          m_nUnlinkSuccess: " << s.m_nUnlinkSuccess.get()           << "\n"
             << "\t\t           m_nUnlinkFailed: " << s.m_nUnlinkFailed.get()            << "\n"
             << "\t\t         m_nExtractSuccess: " << s.m_nExtractSuccess.get()          << "\n"
             << "\t\t          m_nUnlinkSuccess: " << s.m_nUnlinkSuccess.get()           << "\n"
             << "\t\t           m_nUnlinkFailed: " << s.m_nUnlinkFailed.get()            << "\n"
             << "\t\t         m_nExtractSuccess: " << s.m_nExtractSuccess.get()          << "\n"
@@ -37,6 +37,7 @@ namespace std {
             << "\t\t      m_nExtractMaxRetries: " << s.m_nExtractMaxRetries.get()       << "\n"
             << "\t\t           m_nEraseSuccess: " << s.m_nEraseSuccess.get()            << "\n"
             << "\t\t            m_nEraseFailed: " << s.m_nEraseFailed.get()             << "\n"
             << "\t\t      m_nExtractMaxRetries: " << s.m_nExtractMaxRetries.get()       << "\n"
             << "\t\t           m_nEraseSuccess: " << s.m_nEraseSuccess.get()            << "\n"
             << "\t\t            m_nEraseFailed: " << s.m_nEraseFailed.get()             << "\n"
+            << "\t\t             m_nEraseRetry: " << s.m_nEraseRetry.get()              << "\n"
             << "\t\t        m_nFindFastSuccess: " << s.m_nFindFastSuccess.get()         << "\n"
             << "\t\t         m_nFindFastFailed: " << s.m_nFindFastFailed.get()          << "\n"
             << "\t\t        m_nFindSlowSuccess: " << s.m_nFindSlowSuccess.get()         << "\n"
             << "\t\t        m_nFindFastSuccess: " << s.m_nFindFastSuccess.get()         << "\n"
             << "\t\t         m_nFindFastFailed: " << s.m_nFindFastFailed.get()          << "\n"
             << "\t\t        m_nFindSlowSuccess: " << s.m_nFindSlowSuccess.get()         << "\n"