Added possibility to remove node's value via RCU disposing cycle
authorkhizmax <khizmax@gmail.com>
Tue, 10 Feb 2015 17:03:24 +0000 (20:03 +0300)
committerkhizmax <khizmax@gmail.com>
Tue, 10 Feb 2015 17:03:24 +0000 (20:03 +0300)
cds/container/details/bronson_avltree_base.h
cds/container/impl/bronson_avltree_map_rcu.h

index 4d64ce6598a6aa76ff0613e385524865ac1954c2..ca9540ec3a2f3c97133c98a56b0d3373995b4cda 100644 (file)
@@ -128,11 +128,13 @@ namespace cds { namespace container {
         };
         //@endcond
 
-        // BronsonAVLTree internal node
+        /// BronsonAVLTree internal node
         template <typename Key, typename T, typename SyncMonitor >
         struct node<Key, T*, SyncMonitor>: public link_node< node<Key, T*, SyncMonitor>, T, SyncMonitor >
         {
+            //@cond
             typedef link_node< node<Key, T*, SyncMonitor>, T, SyncMonitor > base_class;
+            //@endcond
 
             typedef Key key_type;       ///< key type
             typedef T   mapped_type;    ///< value type
@@ -159,6 +161,29 @@ namespace cds { namespace container {
             //@endcond
         };
 
+        /// Base value type for BronsonAVLTreeMap
+        /**
+            If value type for \p BronsonAVLTreeMap is based on this struct,
+            the map will support some additional methods like \p BronsonAVLTreeMap::get().
+            Moreover, the disposer behaviour is different for ordinal values and
+            values based on \p %bronson_avltree::value:
+            - for ordinal value, its disposer is called immediately after removing
+              the node from the tree. It this case it is not possible to support
+              map's methods that return raw pointer to the tree's value.
+            - if the value type is based on \p %bronson_avltree::value, 
+              i.e. \p std::is_base_of( bronson_avltree::value, value_type )::value is \p true,
+              the disposer is called via full RCU cycle. It means that under 
+              RCU lock the methods returning raw pointer can be implemented.
+        */
+        struct value
+        {
+            value *     m_pNextRetired;
+            
+            value()
+                : m_pNextRetired(nullptr)
+            {}
+        };
+
         /// BronsonAVLTreeMap internal statistics
         template <typename Counter = cds::atomicity::event_counter>
         struct stat {
index 5db8ec472c0e06425825352bb5e81e75375ee4e3..d13dd9bc6f731ef9b70e2caf51004fb3d0ea771f 100644 (file)
@@ -4,6 +4,7 @@
 #define CDSLIB_CONTAINER_IMPL_BRONSON_AVLTREE_MAP_RCU_H
 
 #include <memory> // unique_ptr
+#include <type_traits> // is_base_of
 #include <cds/container/details/bronson_avltree_base.h>
 #include <cds/urcu/details/check_deadlock.h>
 
@@ -78,6 +79,8 @@ namespace cds { namespace container {
 
         typedef cds::details::Allocator< node_type, node_allocator_type > cxx_allocator;
         typedef cds::urcu::details::check_deadlock_policy< gc, rcu_check_deadlock >   check_deadlock_policy;
+        
+        static CDS_CONSTEXPR bool const c_bRetiringValue = std::is_base_of< bronson_avltree::value, typename std::remove_pointer<mapped_type>::type>::value;
 
         enum class find_result
         {
@@ -146,10 +149,66 @@ namespace cds { namespace container {
             return pNode->m_pParent.load( order );
         }
 
-        // RCU safe disposer for node
+        
+        template <bool Enabled>
+        class rcu_value_disposer;
+        
+        template <>
+        class rcu_value_disposer<true>
+        {
+            bronson_avltree::value *    m_pValueRetiredList; ///< head of retired value list
+        public:
+            rcu_value_disposer()
+                : m_pValueRetiredList(nullptr)
+            {}
+            
+            ~rcu_value_disposer()
+            {
+                clean();
+            }
+            
+            void dispose( mapped_type pVal )
+            {
+                assert( pVal );
+                static_cast< bronson_avltree::value *>(pVal)->m_pNextRetired = m_pValueRetiredList;
+                m_pValueRetiredList = static_cast< bronson_avltree::value *>(pVal);
+            }
+            
+        private:
+            struct internal_disposer
+            {
+                void operator()( bronson_avltree::value * p ) const
+                {
+                    free_value( static_cast<mapped_type>( p ));
+                }
+            };
+            
+            void clean()
+            {
+                // TODO: use RCU::batch_retire
+                for ( auto p = m_pValueRetiredList; p; ) {
+                    auto pNext = p->m_pNextRetired;
+                    gc::template retire_ptr<internal_disposer>( p );
+                    p = pNext;
+                }
+            }
+        };
+        
+        template <>
+        class rcu_value_disposer<false>
+        {
+        public:
+            void dispose( mapped_type pVal )
+            {
+                free_value( pVal );
+            }
+        };
+        
+        // RCU safe disposer 
         class rcu_disposer
         {
-            node_type *     m_pRetiredList; ///< head of retired list
+            node_type *     m_pRetiredList; ///< head of retired node list
+            rcu_value_disposer< c_bRetiringValue > m_RetiredValueList;
 
         public:
             rcu_disposer()
@@ -167,7 +226,12 @@ namespace cds { namespace container {
                 pNode->m_pNextRemoved = m_pRetiredList;
                 m_pRetiredList = pNode;
             }
-
+            
+            void dispose_value( mapped_type pVal )
+            {
+                m_RetiredValueList.dispose( pVal );
+            }
+            
         private:
             struct internal_disposer
             {
@@ -180,7 +244,10 @@ namespace cds { namespace container {
             void clean()
             {
                 assert( !gc::is_locked() );
+                
+                // TODO: use RCU::batch_retire
 
+                // Dispose nodes
                 for ( node_type * p = m_pRetiredList; p; ) {
                     node_type * pNext = static_cast<node_type *>( p->m_pNextRemoved );
                     // Value already disposed
@@ -559,6 +626,7 @@ namespace cds { namespace container {
         */
         void unsafe_clear()
         {
+            clear(); // temp solution
             //TODO
         }