Changed SkipListSet/Map<RCU> for new get() semantics (with raw_ptr)
authorkhizmax <libcds.dev@gmail.com>
Sun, 31 May 2015 19:07:47 +0000 (22:07 +0300)
committerkhizmax <libcds.dev@gmail.com>
Sun, 31 May 2015 19:07:47 +0000 (22:07 +0300)
12 files changed:
cds/container/skip_list_map_rcu.h
cds/container/skip_list_set_rcu.h
cds/intrusive/details/raw_ptr_disposer.h [new file with mode: 0644]
cds/intrusive/michael_list_rcu.h
cds/intrusive/skip_list_rcu.h
cds/urcu/raw_ptr.h
change.log
projects/Win/vc12/cds.vcxproj
projects/Win/vc12/cds.vcxproj.filters
tests/test-hdr/map/hdr_skiplist_map_rcu.h
tests/test-hdr/set/hdr_intrusive_skiplist_set_rcu.h
tests/test-hdr/set/hdr_skiplist_set_rcu.h

index c5abe34aa234f448381206b3b2faa389d57bf93d..8b78ba8133851f695251d6d4af0782b2e06770da 100644 (file)
@@ -144,17 +144,37 @@ namespace cds { namespace container {
         /// pointer to extracted node
         using exempt_ptr = cds::urcu::exempt_ptr< gc, node_type, value_type, typename maker::intrusive_type_traits::disposer >;
 
+    private:
+        //@cond
+        struct raw_ptr_converter
+        {
+            value_type * operator()( node_type * p ) const
+            {
+               return p ? &p->m_Value : nullptr;
+            }
+
+            value_type& operator()( node_type& n ) const
+            {
+                return n.m_Value;
+            }
+
+            value_type const& operator()( node_type const& n ) const
+            {
+                return n.m_Value;
+            }
+        };
+        //@endcond
+
+    public:
+        /// Result of \p get(), \p get_with() functions - pointer to the node found
+        typedef cds::urcu::raw_ptr_adaptor< value_type, typename base_class::raw_ptr, raw_ptr_converter > raw_ptr;
+
     protected:
         //@cond
         unsigned int random_level()
         {
             return base_class::random_level();
         }
-
-        value_type * to_value_ptr( node_type * pNode ) const CDS_NOEXCEPT
-        {
-            return pNode ? &pNode->m_Value : nullptr;
-        }
         //@endcond
 
     public:
@@ -548,8 +568,8 @@ namespace cds { namespace container {
 
         /// Finds the key \p key and return the item found
         /** \anchor cds_nonintrusive_SkipListMap_rcu_get
-            The function searches the item with key equal to \p key and returns the pointer to item found.
-            If \p key is not found it returns \p nullptr.
+            The function searches the item with key equal to \p key and returns a \p raw_ptr object pointing to an item found.
+            If \p key is not found it returns empty \p raw_ptr.
 
             Note the compare functor in \p Traits class' template argument
             should accept a parameter of type \p K that can be not the same as \p key_type.
@@ -560,26 +580,25 @@ namespace cds { namespace container {
             typedef cds::container::SkipListMap< cds::urcu::gc< cds::urcu::general_buffered<> >, int, foo, my_traits > skip_list;
             skip_list theList;
             // ...
+            typename skip_list::raw_ptr pVal;
             {
                 // Lock RCU
                 skip_list::rcu_lock lock;
 
-                skip_list::value_type * pVal = theList.get( 5 );
-                // Deal with pVal
-                //...
-
-                // Unlock RCU by rcu_lock destructor
-                // pVal can be freed at any time after RCU unlocking
+                pVal = theList.get( 5 );
+                if ( pVal ) {
+                    // Deal with pVal
+                    //...
+                }
             }
+            // You can manually release pVal after RCU-locked section
+            pVal.release();
             \endcode
-
-            After RCU unlocking the \p %force_dispose member function can be called manually,
-            see \ref force_dispose for explanation.
         */
         template <typename K>
-        value_type * get( K const& key )
+        raw_ptr get( K const& key )
         {
-            return to_value_ptr( base_class::get( key ));
+            return raw_ptr( base_class::get( key ));
         }
 
         /// Finds the key \p key and return the item found
@@ -592,10 +611,10 @@ namespace cds { namespace container {
             \p pred must imply the same element order as the comparator used for building the map.
         */
         template <typename K, typename Less>
-        value_type * get_with( K const& key, Less pred )
+        raw_ptr get_with( K const& key, Less pred )
         {
             CDS_UNUSED( pred );
-            return to_value_ptr( base_class::get_with( key, cds::details::predicate_wrapper< node_type, Less, typename maker::key_accessor >() ));
+            return raw_ptr( base_class::get_with( key, cds::details::predicate_wrapper< node_type, Less, typename maker::key_accessor >() ));
         }
 
         /// Clears the map (not atomic)
@@ -624,14 +643,6 @@ namespace cds { namespace container {
         {
             return base_class::statistics();
         }
-
-        /// Clears internal list of ready-to-delete items passing them to RCU reclamation cycle
-        /** @copydetails cds_intrusive_SkipListSet_rcu_force_dispose
-        */
-        void force_dispose()
-        {
-            return base_class::force_dispose();
-        }
     };
 }} // namespace cds::container
 
index e57a803673fde31a7f46c76b329fc88393b3db6a..bb53d747bd7a66ba07ae7002853ec0e5b8301a2e 100644 (file)
@@ -191,17 +191,37 @@ namespace cds { namespace container {
         /// pointer to extracted node
         using exempt_ptr = cds::urcu::exempt_ptr< gc, node_type, value_type, typename maker::intrusive_traits::disposer >;
 
+    private:
+        //@cond
+        struct raw_ptr_converter
+        {
+            value_type * operator()( node_type * p ) const
+            {
+               return p ? &p->m_Value : nullptr;
+            }
+
+            value_type& operator()( node_type& n ) const
+            {
+                return n.m_Value;
+            }
+
+            value_type const& operator()( node_type const& n ) const
+            {
+                return n.m_Value;
+            }
+        };
+        //@endcond
+
+    public:
+        /// Result of \p get(), \p get_with() functions - pointer to the node found
+        typedef cds::urcu::raw_ptr_adaptor< value_type, typename base_class::raw_ptr, raw_ptr_converter > raw_ptr;
+
     protected:
         //@cond
         unsigned int random_level()
         {
             return base_class::random_level();
         }
-
-        value_type * to_value_ptr( node_type * pNode ) const CDS_NOEXCEPT
-        {
-            return pNode ? &pNode->m_Value : nullptr;
-        }
         //@endcond
 
     public:
@@ -610,8 +630,8 @@ namespace cds { namespace container {
 
         /// Finds \p key and return the item found
         /** \anchor cds_nonintrusive_SkipListSet_rcu_get
-            The function searches the item with key equal to \p key and returns the pointer to item found.
-            If \p key is not found it returns \p nullptr.
+            The function searches the item with key equal to \p key and returns a \p raw_ptr object pointed to item found.
+            If \p key is not found it returns empty \p raw_ptr.
 
             Note the compare functor in \p Traits class' template argument
             should accept a parameter of type \p Q that can be not the same as \p value_type.
@@ -622,26 +642,25 @@ namespace cds { namespace container {
             typedef cds::container::SkipListSet< cds::urcu::gc< cds::urcu::general_buffered<> >, foo, my_traits > skip_list;
             skip_list theList;
             // ...
+            typename skip_list::raw_ptr pVal;
             {
                 // Lock RCU
                 skip_list::rcu_lock lock;
 
-                foo * pVal = theList.get( 5 );
-                // Deal with pVal
-                //...
-
-                // Unlock RCU by rcu_lock destructor
-                // pVal can be freed at any time after RCU unlocking
+                pVal = theList.get( 5 );
+                if ( pVal ) {
+                    // Deal with pVal
+                    //...
+                }
             }
+            // You can manually release pVal after RCU-locked section
+            pVal.release();
             \endcode
-
-            After RCU unlocking the \p %force_dispose member function can be called manually,
-            see \ref force_dispose for explanation.
         */
         template <typename Q>
-        value_type * get( Q const& key )
+        raw_ptr get( Q const& key )
         {
-            return to_value_ptr( base_class::get( key ));
+            return raw_ptr( base_class::get( key ));
         }
 
         /// Finds the key \p val and return the item found
@@ -654,10 +673,10 @@ namespace cds { namespace container {
             \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& val, Less pred )
+        raw_ptr get_with( Q const& val, Less pred )
         {
             CDS_UNUSED( pred );
-            return to_value_ptr( base_class::get_with( val, cds::details::predicate_wrapper< node_type, Less, typename maker::value_accessor >() ));
+            return raw_ptr( base_class::get_with( val, cds::details::predicate_wrapper< node_type, Less, typename maker::value_accessor >() ));
         }
 
         /// Clears the set (non-atomic).
@@ -701,15 +720,6 @@ namespace cds { namespace container {
         {
             return base_class::statistics();
         }
-
-        /// Clears internal list of ready-to-delete items passing them to RCU reclamation cycle
-        /**
-            See \ref cds_intrusive_SkipListSet_rcu_force_dispose "intrusive SkipListSet" for explanation
-        */
-        void force_dispose()
-        {
-            return base_class::force_dispose();
-        }
     };
 
 }} // namespace cds::container
diff --git a/cds/intrusive/details/raw_ptr_disposer.h b/cds/intrusive/details/raw_ptr_disposer.h
new file mode 100644 (file)
index 0000000..c76fbc4
--- /dev/null
@@ -0,0 +1,65 @@
+//$$CDS-header$$
+
+#ifndef CDSLIB_INTRUSIVE_DETAILS_RAW_PTR_DISPOSER_H
+#define CDSLIB_INTRUSIVE_DETAILS_RAW_PTR_DISPOSER_H
+
+#include <cds/details/defs.h>
+
+//@cond
+namespace cds { namespace intrusive { namespace details {
+
+    template <typename RCU, typename NodeType, typename Disposer>
+    struct raw_ptr_disposer
+    {
+        typedef RCU gc;
+        typedef NodeType node_type;
+        typedef Disposer disposer;
+
+        node_type *     pReclaimedChain;
+
+        raw_ptr_disposer()
+            : pReclaimedChain( nullptr )
+        {}
+
+        template <typename Position>
+        explicit raw_ptr_disposer( Position& pos )
+            : pReclaimedChain( pos.pDelChain )
+        {
+            pos.pDelChain = nullptr;
+        }
+
+        raw_ptr_disposer( raw_ptr_disposer&& d )
+            : pReclaimedChain( d.pReclaimedChain )
+        {
+            d.pReclaimedChain = nullptr;
+        }
+
+        raw_ptr_disposer( raw_ptr_disposer const& ) = delete;
+
+        ~raw_ptr_disposer()
+        {
+            apply();
+        }
+
+        raw_ptr_disposer& operator=(raw_ptr_disposer&& d)
+        {
+            assert( pReclaimedChain == nullptr );
+            pReclaimedChain = d.pReclaimedChain;
+            d.pReclaimedChain = nullptr;
+            retur *this;
+        }
+
+        void apply()
+        {
+            if ( pReclaimedChain ) {
+                assert( !gc::is_locked());
+                disposer()( pReclaimedChain );
+                pReclaimedChain = nullptr;
+            }
+        }
+    };
+
+}}} // namespace cds::intrusive::details
+//@endcond
+
+#endif // #ifndef CDSLIB_INTRUSIVE_DETAILS_RAW_PTR_DISPOSER_H
index 5843367df8c785eba16c7d53b8303d0a035e90cc..f8b59f518d7027af49ae90c9208eadd046ddac71 100644 (file)
@@ -9,6 +9,7 @@
 #include <cds/details/make_const_type.h>
 #include <cds/urcu/exempt_ptr.h>
 #include <cds/urcu/raw_ptr.h>
+#include <cds/intrusive/details/raw_ptr_disposer.h>
 
 namespace cds { namespace intrusive {
 
@@ -195,42 +196,13 @@ namespace cds { namespace intrusive {
 
     private:
         //@cond
-        struct raw_ptr_disposer
-        {
-            node_type *     pReclaimedChain;
-
-            raw_ptr_disposer()
-                : pReclaimedChain( nullptr )
-            {}
-
-            raw_ptr_disposer( position& pos )
-                : pReclaimedChain( pos.pDelChain )
+        struct chain_disposer {
+            void operator()( node_type * pChain ) const
             {
-                pos.pDelChain = nullptr;
-            }
-
-            raw_ptr_disposer( raw_ptr_disposer&& d )
-                : pReclaimedChain( d.pReclaimedChain )
-            {
-                d.pReclaimedChain = nullptr;
-            }
-
-            raw_ptr_disposer( raw_ptr_disposer const& ) = delete;
-
-            ~raw_ptr_disposer()
-            {
-                apply();
-            }
-
-            void apply()
-            {
-                if ( pReclaimedChain ) {
-                    assert( !gc::is_locked());
-                    dispose_chain( pReclaimedChain );
-                    pReclaimedChain = nullptr;
-                }
+                dispose_chain( pChain );
             }
         };
+        typedef cds::intrusive::details::raw_ptr_disposer< gc, node_type, chain_disposer> raw_ptr_disposer;
         //@endcond
 
     public:
index 9655545ab3b67d2491cccbb601013414af5998ff..43692993ec25f2ae9944f4c4f6a4de7ae605005a 100644 (file)
@@ -10,7 +10,8 @@
 #include <cds/urcu/details/check_deadlock.h>
 #include <cds/details/binary_functor_wrapper.h>
 #include <cds/urcu/exempt_ptr.h>
-
+#include <cds/urcu/raw_ptr.h>
+#include <cds/intrusive/details/raw_ptr_disposer.h>
 
 namespace cds { namespace intrusive {
 
@@ -551,6 +552,39 @@ namespace cds { namespace intrusive {
 
         typedef std::unique_ptr< node_type, typename node_builder::node_disposer >    scoped_node_ptr;
 
+        static void dispose_node( value_type * pVal )
+        {
+            assert( pVal );
+
+            typename node_builder::node_disposer()( node_traits::to_node_ptr(pVal) );
+            disposer()( pVal );
+        }
+
+        struct node_disposer
+        {
+            void operator()( value_type * pVal )
+            {
+                dispose_node( pVal );
+            }
+        };
+
+        static void dispose_chain( node_type * pChain )
+        {
+            if ( pChain ) {
+                assert( !gc::is_locked() );
+
+                auto f = [&pChain]() -> cds::urcu::retired_ptr {
+                    node_type * p = pChain;
+                    if ( p ) {
+                        pChain = p->m_pDelChain;
+                        return cds::urcu::make_retired_ptr<node_disposer>( node_traits::to_value_ptr( p ));
+                    }
+                    return cds::urcu::make_retired_ptr<node_disposer>( static_cast<value_type *>(nullptr));
+                };
+                gc::batch_retire(std::ref(f));
+            }
+        }
+
         struct position {
             node_type *   pPrev[ c_nMaxHeight ];
             node_type *   pSucc[ c_nMaxHeight ];
@@ -562,12 +596,20 @@ namespace cds { namespace intrusive {
             position()
                 : pDelChain( nullptr )
             {}
-#       ifdef _DEBUG
+
             ~position()
             {
-                assert( pDelChain == nullptr );
+                dispose_chain( pDelChain );
+            }
+
+            void dispose( node_type * p )
+            {
+                assert( p != nullptr );
+                assert( p->m_pDelChain == nullptr );
+
+                p->m_pDelChain = pDelChain;
+                pDelChain = p;
             }
-#       endif
         };
 
         typedef cds::urcu::details::check_deadlock_policy< gc, rcu_check_deadlock>   check_deadlock_policy;
@@ -596,26 +638,25 @@ namespace cds { namespace intrusive {
         {
             return node_builder::make_tower( v, m_RandomLevelGen );
         }
+        //@endcond
 
-        static void dispose_node( value_type * pVal )
-        {
-            assert( pVal );
-
-            typename node_builder::node_disposer()( node_traits::to_node_ptr(pVal) );
-            disposer()( pVal );
-        }
+    public:
+        using exempt_ptr = cds::urcu::exempt_ptr< gc, value_type, value_type, node_disposer, void >; ///< pointer to extracted node
 
-        struct node_disposer
-        {
-            void operator()( value_type * pVal )
+    private:
+        //@cond
+        struct chain_disposer {
+            void operator()( node_type * pChain ) const
             {
-                dispose_node( pVal );
+                dispose_chain( pChain );
             }
         };
+        typedef cds::intrusive::details::raw_ptr_disposer< gc, node_type, chain_disposer> raw_ptr_disposer;
         //@endcond
 
     public:
-        using exempt_ptr = cds::urcu::exempt_ptr< gc, value_type, value_type, node_disposer, void >; ///< pointer to extracted node
+        /// Result of \p get(), \p get_with() functions - pointer to the node found
+        typedef cds::urcu::raw_ptr< gc, value_type, raw_ptr_disposer > raw_ptr;
 
     protected:
         //@cond
@@ -672,7 +713,7 @@ namespace cds { namespace intrusive {
                                 if ( !is_extracted( pSucc )) {
                                     // We cannot free the node at this moment since RCU is locked
                                     // Link deleted nodes to a chain to free later
-                                    link_for_remove( pos, pCur.ptr() );
+                                    pos.dispose( pCur.ptr() );
                                     m_Stat.onEraseWhileFind();
                                 }
                                 else {
@@ -746,7 +787,7 @@ namespace cds { namespace intrusive {
                                 if ( !is_extracted( pSucc )) {
                                     // We cannot free the node at this moment since RCU is locked
                                     // Link deleted nodes to a chain to free later
-                                    link_for_remove( pos, pCur.ptr() );
+                                    pos.dispose( pCur.ptr() );
                                     m_Stat.onEraseWhileFind();
                                 }
                                 else {
@@ -773,7 +814,7 @@ namespace cds { namespace intrusive {
             marked_node_ptr pSucc;
             marked_node_ptr pCur;
 
-retry:
+        retry:
             pPred = m_Head.head();
 
             for ( int nLevel = static_cast<int>(c_nMaxHeight - 1); nLevel >= 0; --nLevel ) {
@@ -810,7 +851,7 @@ retry:
                                 if ( !is_extracted( pSucc )) {
                                     // We cannot free the node at this moment since RCU is locked
                                     // Link deleted nodes to a chain to free later
-                                    link_for_remove( pos, pCur.ptr() );
+                                    pos.dispose( pCur.ptr() );
                                     m_Stat.onEraseWhileFind();
                                 }
                                 else {
@@ -883,14 +924,6 @@ retry:
             return true;
         }
 
-        static void link_for_remove( position& pos, node_type * pDel )
-        {
-            assert( pDel->m_pDelChain == nullptr );
-
-            pDel->m_pDelChain = pos.pDelChain;
-            pos.pDelChain = pDel;
-        }
-
         template <typename Func>
         bool try_remove_at( node_type * pDel, position& pos, Func f, bool bExtract )
         {
@@ -948,7 +981,7 @@ retry:
                     if ( !bExtract ) {
                         // We cannot free the node at this moment since RCU is locked
                         // Link deleted nodes to a chain to free later
-                        link_for_remove( pos, pDel );
+                        pos.dispose( pDel );
                         m_Stat.onFastErase();
                     }
                     else
@@ -1034,32 +1067,37 @@ retry:
         bool do_find_with( Q& val, Compare cmp, Func f )
         {
             position pos;
+            return do_find_with( val, cmp, f, pos );
+        }
+
+        template <typename Q, typename Compare, typename Func>
+        bool do_find_with( Q& val, Compare cmp, Func f, position& pos )
+        {
             bool bRet;
 
-            rcu_lock l;
+            {
+                rcu_lock l;
 
-            switch ( find_fastpath( val, cmp, f )) {
-            case find_fastpath_found:
-                m_Stat.onFindFastSuccess();
-                return true;
-            case find_fastpath_not_found:
-                m_Stat.onFindFastFailed();
-                return false;
-            default:
-                break;
-            }
+                switch ( find_fastpath( val, cmp, f )) {
+                case find_fastpath_found:
+                    m_Stat.onFindFastSuccess();
+                    return true;
+                case find_fastpath_not_found:
+                    m_Stat.onFindFastFailed();
+                    return false;
+                default:
+                    break;
+                }
 
-            if ( find_slowpath( val, cmp, f, pos )) {
-                m_Stat.onFindSlowSuccess();
-                bRet = true;
-            }
-            else {
-                m_Stat.onFindSlowFailed();
-                bRet = false;
+                if ( find_slowpath( val, cmp, f, pos )) {
+                    m_Stat.onFindSlowSuccess();
+                    bRet = true;
+                }
+                else {
+                    m_Stat.onFindSlowFailed();
+                    bRet = false;
+                }
             }
-
-            defer_chain( pos );
-
             return bRet;
         }
 
@@ -1096,17 +1134,15 @@ retry:
                 }
             }
 
-            dispose_chain( pos );
             return bRet;
         }
 
         template <typename Q, typename Compare>
-        value_type * do_extract_key( Q const& key, Compare cmp )
+        value_type * do_extract_key( Q const& key, Compare cmp, position& pos )
         {
             // RCU should be locked!!!
             assert( gc::is_locked() );
 
-            position pos;
             node_type * pDel;
 
             if ( !find_position( key, pos, cmp, false ) ) {
@@ -1130,7 +1166,6 @@ retry:
                 }
             }
 
-            defer_chain( pos );
             return pDel ? node_traits::to_value_ptr( pDel ) : nullptr;
         }
 
@@ -1139,12 +1174,12 @@ retry:
         {
             check_deadlock_policy::check();
             value_type * pDel = nullptr;
+            position pos;
             {
                 rcu_lock l;
-                pDel = do_extract_key( key, key_comparator() );
+                pDel = do_extract_key( key, key_comparator(), pos );
             }
 
-            dispose_deferred();
             return pDel;
         }
 
@@ -1154,13 +1189,12 @@ retry:
             CDS_UNUSED(pred);
             check_deadlock_policy::check();
             value_type * pDel = nullptr;
-
+            position pos;
             {
                 rcu_lock l;
-                pDel = do_extract_key( key, cds::opt::details::make_comparator_from_less<Less>() );
+                pDel = do_extract_key( key, cds::opt::details::make_comparator_from_less<Less>(), pos );
             }
 
-            dispose_deferred();
             return pDel;
         }
 
@@ -1192,11 +1226,8 @@ retry:
                         pDel = nullptr;
                     }
                 }
-
-                defer_chain( pos );
             }
 
-            dispose_deferred();
             return pDel ? node_traits::to_value_ptr( pDel ) : nullptr;
         }
 
@@ -1228,11 +1259,8 @@ retry:
                         pDel = nullptr;
                     }
                 }
-
-                defer_chain( pos );
             }
 
-            dispose_deferred();
             return pDel ? node_traits::to_value_ptr( pDel ) : nullptr;
         }
 
@@ -1242,83 +1270,6 @@ retry:
             if ( nCur < nHeight )
                 m_nHeight.compare_exchange_strong( nCur, nHeight, memory_model::memory_order_release, atomics::memory_order_relaxed );
         }
-
-        class deferred_list_iterator
-        {
-            node_type * pCur;
-        public:
-            explicit deferred_list_iterator( node_type * p )
-                : pCur(p)
-            {}
-            deferred_list_iterator()
-                : pCur( nullptr )
-            {}
-
-            cds::urcu::retired_ptr operator *() const
-            {
-                return cds::urcu::retired_ptr( node_traits::to_value_ptr(pCur), dispose_node );
-            }
-
-            void operator ++()
-            {
-                pCur = pCur->m_pDelChain;
-            }
-
-            bool operator ==( deferred_list_iterator const& i ) const
-            {
-                return pCur == i.pCur;
-            }
-            bool operator !=( deferred_list_iterator const& i ) const
-            {
-                return !operator ==( i );
-            }
-        };
-
-        void dispose_chain( node_type * pHead )
-        {
-            // RCU should NOT be locked
-            check_deadlock_policy::check();
-
-            gc::batch_retire( deferred_list_iterator( pHead ), deferred_list_iterator() );
-        }
-
-        void dispose_chain( position& pos )
-        {
-            // RCU should NOT be locked
-            check_deadlock_policy::check();
-
-            // Delete local chain
-            if ( pos.pDelChain ) {
-                dispose_chain( pos.pDelChain );
-                pos.pDelChain = nullptr;
-            }
-
-            // Delete deferred chain
-            dispose_deferred();
-        }
-
-        void dispose_deferred()
-        {
-            dispose_chain( m_pDeferredDelChain.exchange( nullptr, memory_model::memory_order_acq_rel ) );
-        }
-
-        void defer_chain( position& pos )
-        {
-            if ( pos.pDelChain ) {
-                node_type * pHead = pos.pDelChain;
-                node_type * pTail = pHead;
-                while ( pTail->m_pDelChain )
-                    pTail = pTail->m_pDelChain;
-
-                node_type * pDeferList = m_pDeferredDelChain.load( memory_model::memory_order_relaxed );
-                do {
-                    pTail->m_pDelChain = pDeferList;
-                } while ( !m_pDeferredDelChain.compare_exchange_weak( pDeferList, pHead, memory_model::memory_order_acq_rel, atomics::memory_order_relaxed ));
-
-                pos.pDelChain = nullptr;
-            }
-        }
-
         //@endcond
 
     public:
@@ -1469,8 +1420,6 @@ retry:
                 }
             }
 
-            dispose_chain( pos );
-
             return bRet;
         }
 
@@ -1553,8 +1502,6 @@ retry:
                 }
             }
 
-            dispose_chain( pos );
-
             return bRet;
         }
 
@@ -1583,7 +1530,7 @@ retry:
             bool bRet;
 
             {
-                rcu_lock rcuLock;
+                rcu_lock l;
 
                 if ( !find_position( val, pos, key_comparator(), false ) ) {
                     m_Stat.onUnlinkFailed();
@@ -1608,8 +1555,6 @@ retry:
                 }
             }
 
-            dispose_chain( pos );
-
             return bRet;
         }
 
@@ -1884,8 +1829,8 @@ retry:
 
         /// Finds \p key and return the item found
         /** \anchor cds_intrusive_SkipListSet_rcu_get
-            The function searches the item with key equal to \p key and returns the pointer to item found.
-            If \p key is not found it returns \p nullptr.
+            The function searches the item with key equal to \p key and returns a \p raw_ptr object pointed to item found.
+            If \p key is not found it returns empty \p raw_ptr.
 
             Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
 
@@ -1895,31 +1840,31 @@ retry:
             typedef cds::intrusive::SkipListSet< cds::urcu::gc< cds::urcu::general_buffered<> >, foo, my_traits > skip_list;
             skip_list theList;
             // ...
+            typename skip_list::raw_ptr pVal;
             {
                 // Lock RCU
                 skip_list::rcu_lock lock;
 
-                foo * pVal = theList.get( 5 );
+                pVal = theList.get( 5 );
                 if ( pVal ) {
                     // Deal with pVal
                     //...
                 }
             }
-            // Unlock RCU by rcu_lock destructor
-            // pVal can be retired by disposer at any time after RCU has been unlocked
+            // You can manually release pVal after RCU-locked section
+            pVal.release();
             \endcode
-
-            After RCU unlocking the \p %force_dispose member function can be called manually,
-            see \ref force_dispose for explanation.
         */
         template <typename Q>
-        value_type * get( Q const& key )
+        raw_ptr get( Q const& key )
         {
             assert( gc::is_locked());
 
+            position pos;
             value_type * pFound;
-            return do_find_with( key, key_comparator(), [&pFound](value_type& found, Q const& ) { pFound = &found; } )
-                ? pFound : nullptr;
+            if ( do_find_with( key, key_comparator(), [&pFound](value_type& found, Q const& ) { pFound = &found; }, pos ))
+                return raw_ptr( pFound, raw_ptr_disposer( pos ));
+            return raw_ptr( raw_ptr_disposer( pos ));
         }
 
         /// Finds \p key and return the item found
@@ -1932,15 +1877,19 @@ retry:
             \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 );
             assert( gc::is_locked());
 
             value_type * pFound;
-            return do_find_with( key, cds::opt::details::make_comparator_from_less<Less>(),
-                [&pFound](value_type& found, Q const& ) { pFound = &found; } )
-                ? pFound : nullptr;
+            position pos;
+            if ( do_find_with( key, cds::opt::details::make_comparator_from_less<Less>(),
+                [&pFound](value_type& found, Q const& ) { pFound = &found; }, pos ))
+            {
+                return raw_ptr( pFound, raw_ptr_disposer( pos ));
+            }
+            return raw_ptr( raw_ptr_disposer( pos ));
         }
 
         /// Returns item count in the set
@@ -1991,27 +1940,6 @@ retry:
         {
             return m_Stat;
         }
-
-        /// Clears internal list of ready-to-remove items passing it to RCU reclamation cycle
-        /** @anchor cds_intrusive_SkipListSet_rcu_force_dispose
-            Skip list has complex multi-step algorithm for removing an item. In fact, when you
-            remove the item it is just marked as removed that is enough for the success of your operation.
-            Actual removing can take place in the future, in another call or even in another thread.
-            Inside RCU lock the removed item cannot be passed to RCU reclamation cycle
-            since it can lead to deadlock. To solve this problem, the current skip list implementation
-            has internal list of items which is ready to remove but is not yet passed to RCU reclamation.
-            Usually, this list will be passed to RCU reclamation in the next suitable call of skip list member function.
-            In some cases we want to pass it to RCU reclamation immediately after RCU unlocking.
-            This function provides such opportunity: it checks whether the RCU is not locked and if it is true
-            the function passes the internal ready-to-remove list to RCU reclamation cycle.
-
-            The RCU \p synchronize can be called.
-        */
-        void force_dispose()
-        {
-            if ( !gc::is_locked() )
-                dispose_deferred();
-        }
     };
 
 }} // namespace cds::intrusive
index 58c166463891becb8193fbbfaf9d0235c1abe0d6..81ef76f0d9a82fc6555555d43c0230d24e72e63b 100644 (file)
@@ -19,7 +19,7 @@ namespace cds { namespace urcu {
         outside RCU lock.
 
         The object of \p %raw_ptr solves that problem: it contains the pointer to the node found
-        and a chain of nodes that be reclaimed during traversing. The \p %raw_ptr object destructor
+        and a chain of nodes that were reclaimed during traversing. The \p %raw_ptr object destructor
         frees the chain (but not the node found) passing it to RCU \p batch_retire().
 
         The object of \p %raw_ptr class must be destructed only outside RCU-lock of current thread.
@@ -109,12 +109,12 @@ namespace cds { namespace urcu {
         /**
             This operator may be called only inside RCU-lock.
             The \p this should be empty.
-
-            In general, move assignment is intented for internal use.
         */
         raw_ptr& operator=( raw_ptr&& p ) CDS_NOEXCEPT
         {
             assert( empty() );
+            if ( !rcu::is_locked() )
+                release();
 
             m_ptr = p.m_ptr;
             m_Enum = std::move( p.m_Enum );
@@ -159,8 +159,8 @@ namespace cds { namespace urcu {
 
         /// Releases the \p %raw_ptr object
         /**
-            This function may be called only outside RCU section.
-            After \p %release() the object can be reused.
+            This function may be called only outside RCU locked region.
+            After \p %release() the object becomes empty and can be reused.
         */
         void release()
         {
index 7960a2d2658bf5d15266cb381780e805414fd2c3..a427e181f5def203cfa9141256abc51a9ea2532a 100644 (file)
@@ -8,6 +8,9 @@
       see doc.
       Thus, semantics of extract()/get() of all RCU-based set and maps based on 
       MichaelList (MichaelSet/Map, SplitListSet/Map) has been changed too.
+    - Changed: SplitListSet/Map functions get() and get_with() return special wrapper
+      object of type raw_ptr, see doc.
+    - Removed: SplitListSet/Map force_dispose() function.
     - cds::lock namespace is renamed to cds::sync. All classes defined in cds::lock namespace 
       are moved to cds::sync with new names (for example, cds::lock::SpinLock is renamed to
       cds::sync::spin_lock). cds::lock namespace and its contents is deprecated, it is kept 
index b4290f9f13c0296deb8564efb803fcf5f2cb2043..514be40c8f9c2ca1dfe9ecebf44be049cd8e5811 100644 (file)
     <ClInclude Include="..\..\..\cds\intrusive\details\michael_list_base.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\details\michael_set_base.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\details\node_traits.h" />\r
+    <ClInclude Include="..\..\..\cds\intrusive\details\raw_ptr_disposer.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\details\single_link_struct.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\details\skip_list_base.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\details\split_list_base.h" />\r
index 55201468360c1b85598ff35dc24b9ab962800e7e..b2487acc653cc9a3618a94662d4ca318cf2e85cc 100644 (file)
     <ClInclude Include="..\..\..\cds\urcu\raw_ptr.h">\r
       <Filter>Header Files\cds\urcu</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\..\..\cds\intrusive\details\raw_ptr_disposer.h">\r
+      <Filter>Header Files\cds\intrusive\details</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
index 70a0dd655b88748ec66e6d8810ee254dbc21a7ab..b5b28e76e2d746e72866290f1d4d21831ea05d48 100644 (file)
@@ -162,17 +162,19 @@ namespace map {
 
                 typedef typename Map::value_type value_type;
                 typename Map::exempt_ptr ep;
+                typename Map::raw_ptr rp;
 
                 // extract/get
                 for ( int i = 0; i < nLimit; ++i ) {
                     int nKey = arrItem[i];
                     {
                         rcu_lock l;
-                        value_type * pVal = m.get( nKey );
-                        CPPUNIT_ASSERT( pVal != nullptr );
-                        CPPUNIT_CHECK( pVal->first == nKey );
-                        CPPUNIT_CHECK( pVal->second.m_val == nKey * 2 );
+                        rp = m.get( nKey );
+                        CPPUNIT_ASSERT( rp );
+                        CPPUNIT_CHECK( rp->first == nKey );
+                        CPPUNIT_CHECK( rp->second.m_val == nKey * 2 );
                     }
+                    rp.release();
 
                     ep = m.extract( nKey );
                     CPPUNIT_ASSERT( ep );
@@ -183,7 +185,7 @@ namespace map {
 
                     {
                         rcu_lock l;
-                        CPPUNIT_CHECK( m.get( nKey ) ==  nullptr );
+                        CPPUNIT_CHECK( !m.get( nKey ));
                     }
                     ep = m.extract( nKey );
                     CPPUNIT_CHECK( !ep );
@@ -198,11 +200,12 @@ namespace map {
                     int nKey = arrItem[i];
                     {
                         rcu_lock l;
-                        value_type * pVal = m.get_with( wrapped_item(nKey), wrapped_less() );
-                        CPPUNIT_ASSERT( pVal != nullptr );
-                        CPPUNIT_CHECK( pVal->first == nKey );
-                        CPPUNIT_CHECK( pVal->second.m_val == nKey * 2 );
+                        rp = m.get_with( wrapped_item(nKey), wrapped_less() );
+                        CPPUNIT_ASSERT( rp );
+                        CPPUNIT_CHECK( rp->first == nKey );
+                        CPPUNIT_CHECK( rp->second.m_val == nKey * 2 );
                     }
+                    rp.release();
 
                     ep = m.extract_with( wrapped_item( nKey ), wrapped_less() );
                     CPPUNIT_ASSERT( ep );
@@ -213,7 +216,7 @@ namespace map {
 
                     {
                         rcu_lock l;
-                        CPPUNIT_CHECK( m.get_with( wrapped_item(nKey), wrapped_less() ) ==  nullptr );
+                        CPPUNIT_CHECK( !m.get_with( wrapped_item(nKey), wrapped_less() ));
                     }
                     ep = m.extract_with( wrapped_item( nKey ), wrapped_less() );
                     CPPUNIT_CHECK( !ep );
index d4898a04594dc9de7e5e9966cd6cfb31767fb9bf..be46d3fdda83f617d13d2d2eedcbc7fb9f99aa25 100644 (file)
@@ -257,19 +257,21 @@ namespace set {
             // extract/get test
             {
                 typename Set::exempt_ptr ep;
+                typename Set::raw_ptr rp;
                 // extract
                 {
                     fill_skiplist( s, v );
-                    value_type * pVal;
+
                     for ( int i = c_nArrSize - 1; i >= 0; i -= 1 ) {
                         {
                             rcu_lock l;
-                            pVal = s.get( i );
-                            CPPUNIT_ASSERT( pVal != nullptr );
-                            CPPUNIT_CHECK( pVal->nKey == i );
-                            CPPUNIT_CHECK( pVal->nVal == i * 2 );
-                            pVal->nVal *= 2;
+                            rp = s.get( i );
+                            CPPUNIT_ASSERT( rp );
+                            CPPUNIT_CHECK( rp->nKey == i );
+                            CPPUNIT_CHECK( rp->nVal == i * 2 );
+                            rp->nVal *= 2;
                         }
+                        rp.release();
 
                         ep = s.extract( i );
                         CPPUNIT_ASSERT( ep );
@@ -280,7 +282,7 @@ namespace set {
 
                         {
                             rcu_lock l;
-                            CPPUNIT_CHECK( s.get( i ) == nullptr );
+                            CPPUNIT_CHECK( !s.get( i ));
                         }
                         ep = s.extract( i );
                         CPPUNIT_CHECK( !ep );
@@ -296,12 +298,13 @@ namespace set {
                     for ( int i = c_nArrSize - 1; i >= 0; i -= 1 ) {
                         {
                             rcu_lock l;
-                            value_type * pVal = s.get_with( other_key(i), other_key_less<typename Set::value_type>() );
-                            CPPUNIT_ASSERT( pVal != nullptr );
-                            CPPUNIT_CHECK( pVal->nKey == i );
-                            CPPUNIT_CHECK( pVal->nVal == i * 2 );
-                            pVal->nVal *= 2;
+                            rp = s.get_with( other_key(i), other_key_less<typename Set::value_type>() );
+                            CPPUNIT_ASSERT( rp );
+                            CPPUNIT_CHECK( rp->nKey == i );
+                            CPPUNIT_CHECK( rp->nVal == i * 2 );
+                            rp->nVal *= 2;
                         }
+                        rp.release();
 
                         ep = s.extract_with( other_key( i ), other_key_less<typename Set::value_type>() );
                         CPPUNIT_ASSERT( ep );
@@ -312,7 +315,7 @@ namespace set {
 
                         {
                             rcu_lock l;
-                            CPPUNIT_CHECK( s.get_with( other_key( i ), other_key_less<typename Set::value_type>() ) == nullptr );
+                            CPPUNIT_CHECK( !s.get_with( other_key( i ), other_key_less<typename Set::value_type>() ));
                         }
                         ep = s.extract_with( other_key( i ), other_key_less<typename Set::value_type>() );
                         CPPUNIT_CHECK( !ep );
index c655ed18589cf4070ae5483ec8024b9f7b68e374..f84b322b92e35dbdf9f882fb2c182adc1b4c44eb 100644 (file)
@@ -157,19 +157,20 @@ namespace set {
             // extract/get tests
             {
                 typedef typename base_class::less<typename Set::value_type> less_predicate;
-                typename Set::value_type * pVal;
                 typename Set::exempt_ptr ep;
+                typename Set::raw_ptr rp;
 
                 // extract/get
                 for ( int i = 0; i < nLimit; ++i ) {
                     int nKey = arrRandom[i];
                     {
                         rcu_lock l;
-                        pVal = s.get( nKey );
-                        CPPUNIT_ASSERT( pVal != nullptr );
-                        CPPUNIT_CHECK( pVal->nKey == nKey );
-                        CPPUNIT_CHECK( pVal->nVal == nKey * 2 );
+                        rp = s.get( nKey );
+                        CPPUNIT_ASSERT( rp );
+                        CPPUNIT_CHECK( rp->nKey == nKey );
+                        CPPUNIT_CHECK( rp->nVal == nKey * 2 );
                     }
+                    rp.release();
 
                     ep = s.extract( nKey );
                     CPPUNIT_ASSERT( ep );
@@ -180,7 +181,7 @@ namespace set {
 
                     {
                         rcu_lock l;
-                        CPPUNIT_CHECK( s.get( nKey ) == nullptr );
+                        CPPUNIT_CHECK( !s.get( nKey ));
                     }
                     ep = s.extract( nKey );
                     CPPUNIT_CHECK( !ep );
@@ -195,11 +196,12 @@ namespace set {
                     int nKey = arrRandom[i];
                     {
                         rcu_lock l;
-                        pVal = s.get_with( wrapped_item(nKey), wrapped_less() );
-                        CPPUNIT_ASSERT( pVal != nullptr );
-                        CPPUNIT_CHECK( pVal->nKey == nKey );
-                        CPPUNIT_CHECK( pVal->nVal == nKey );
+                        rp = s.get_with( wrapped_item(nKey), wrapped_less() );
+                        CPPUNIT_ASSERT( rp );
+                        CPPUNIT_CHECK( rp->nKey == nKey );
+                        CPPUNIT_CHECK( rp->nVal == nKey );
                     }
+                    rp.release();
 
                     ep = s.extract_with( wrapped_item( nKey ), wrapped_less() );
                     CPPUNIT_ASSERT( ep );
@@ -210,7 +212,7 @@ namespace set {
 
                     {
                         rcu_lock l;
-                        CPPUNIT_CHECK( s.get_with( wrapped_item( nKey ), wrapped_less() ) == nullptr );
+                        CPPUNIT_CHECK( !s.get_with( wrapped_item( nKey ), wrapped_less() ));
                     }
                     ep = s.extract_with( wrapped_item( nKey ), wrapped_less() );
                     CPPUNIT_CHECK( !ep );