Added intrusive SplitListSet based on IterableList
authorkhizmax <libcds.dev@gmail.com>
Sun, 23 Oct 2016 21:07:22 +0000 (00:07 +0300)
committerkhizmax <libcds.dev@gmail.com>
Sun, 23 Oct 2016 21:07:22 +0000 (00:07 +0300)
16 files changed:
cds/intrusive/details/split_list_base.h
cds/intrusive/impl/iterable_list.h
cds/intrusive/impl/lazy_list.h
cds/intrusive/impl/michael_list.h
cds/intrusive/michael_set.h
cds/intrusive/split_list.h
cds/intrusive/split_list_nogc.h
cds/intrusive/split_list_rcu.h
projects/Win/vc14/gtest-intrusive-set.vcxproj
projects/Win/vc14/gtest-intrusive-set.vcxproj.filters
test/unit/intrusive-set/CMakeLists.txt
test/unit/intrusive-set/intrusive_split_iterable_dhp.cpp [new file with mode: 0644]
test/unit/intrusive-set/intrusive_split_iterable_hp.cpp [new file with mode: 0644]
test/unit/intrusive-set/test_intrusive_split_iterable_set.h [new file with mode: 0644]
test/unit/intrusive-set/test_intrusive_split_iterable_set_hp.h [new file with mode: 0644]
test/unit/set/split_lazy_nogc.cpp

index a10a50f136094ead306068ad4e67f42ef2b5d7ec..a44a327df0d2457e1bcc29124b87aed488780237 100644 (file)
@@ -45,30 +45,55 @@ namespace cds { namespace intrusive {
     /** @ingroup cds_intrusive_helper
     */
     namespace split_list {
     /** @ingroup cds_intrusive_helper
     */
     namespace split_list {
+        //@cond
+        struct hash_node
+        {
+            size_t  m_nHash;   ///< Hash value for node
+
+            /// Default constructor
+            hash_node()
+                : m_nHash( 0 )
+            {
+                assert( is_dummy() );
+            }
+
+            /// Initializes dummy node with \p nHash value
+            hash_node( size_t nHash )
+                : m_nHash( nHash )
+            {
+                assert( is_dummy() );
+            }
+
+            /// Checks if the node is dummy node
+            bool is_dummy() const
+            {
+                return (m_nHash & 1) == 0;
+            }
+        };
+        //@endcond
+
         /// Split-ordered list node
         /**
             Template parameter:
             - \p OrderedListNode - node type for underlying ordered list
         */
         template <typename OrderedListNode>
         /// Split-ordered list node
         /**
             Template parameter:
             - \p OrderedListNode - node type for underlying ordered list
         */
         template <typename OrderedListNode>
-        struct node: public OrderedListNode
+        struct node: public OrderedListNode, public hash_node
         {
             //@cond
             typedef OrderedListNode base_class;
             //@endcond
 
         {
             //@cond
             typedef OrderedListNode base_class;
             //@endcond
 
-            size_t  m_nHash ;   ///< Hash value for node
-
             /// Default constructor
             node()
             /// Default constructor
             node()
-                : m_nHash(0)
+                : hash_node(0)
             {
                 assert( is_dummy() );
             }
 
             /// Initializes dummy node with \p nHash value
             node( size_t nHash )
             {
                 assert( is_dummy() );
             }
 
             /// Initializes dummy node with \p nHash value
             node( size_t nHash )
-                : m_nHash( nHash )
+                : hash_node( nHash )
             {
                 assert( is_dummy() );
             }
             {
                 assert( is_dummy() );
             }
@@ -76,10 +101,36 @@ namespace cds { namespace intrusive {
             /// Checks if the node is dummy node
             bool is_dummy() const
             {
             /// Checks if the node is dummy node
             bool is_dummy() const
             {
-                return (m_nHash & 1) == 0;
+                return hash_node::is_dummy();
             }
         };
 
             }
         };
 
+        //@cond
+        template <>
+        struct node<void>: public hash_node
+        {
+            // Default ctor
+            node()
+                : hash_node( 0 )
+            {
+                assert( is_dummy() );
+            }
+
+            /// Initializes dummy node with \p nHash value
+            node( size_t nHash )
+                : hash_node( nHash )
+            {
+                assert( is_dummy() );
+            }
+
+            /// Checks if the node is dummy node
+            bool is_dummy() const
+            {
+                return hash_node::is_dummy();
+            }
+        };
+        //@endcond
+
         /// \p SplitListSet internal statistics. May be used for debugging or profiling
         /**
             Template argument \p Counter defines type of counter, default is \p cds::atomicity::event_counter.
         /// \p SplitListSet internal statistics. May be used for debugging or profiling
         /**
             Template argument \p Counter defines type of counter, default is \p cds::atomicity::event_counter.
@@ -729,70 +780,6 @@ namespace cds { namespace intrusive {
             //@endcond
         };
 
             //@endcond
         };
 
-        /// Split-list node traits
-        /**
-            This traits is intended for converting between underlying ordered list node type
-            and split-list node type
-
-            Template parameter:
-            - \p BaseNodeTraits - node traits of base ordered list type
-        */
-        template <class BaseNodeTraits>
-        struct node_traits: private BaseNodeTraits
-        {
-            typedef BaseNodeTraits base_class;     ///< Base ordered list node type
-            typedef typename base_class::value_type value_type;     ///< Value type
-            typedef typename base_class::node_type  base_node_type; ///< Ordered list node type
-            typedef node<base_node_type>            node_type;      ///< Spit-list node type
-
-            /// Convert value reference to node pointer
-            static node_type * to_node_ptr( value_type& v )
-            {
-                return static_cast<node_type *>( base_class::to_node_ptr( v ) );
-            }
-
-            /// Convert value pointer to node pointer
-            static node_type * to_node_ptr( value_type * v )
-            {
-                return static_cast<node_type *>( base_class::to_node_ptr( v ) );
-            }
-
-            /// Convert value reference to node pointer (const version)
-            static node_type const * to_node_ptr( value_type const& v )
-            {
-                return static_cast<node_type const*>( base_class::to_node_ptr( v ) );
-            }
-
-            /// Convert value pointer to node pointer (const version)
-            static node_type const * to_node_ptr( value_type const * v )
-            {
-                return static_cast<node_type const *>( base_class::to_node_ptr( v ) );
-            }
-
-            /// Convert node reference to value pointer
-            static value_type * to_value_ptr( node_type&  n )
-            {
-                return base_class::to_value_ptr( static_cast<base_node_type &>( n ) );
-            }
-
-            /// Convert node pointer to value pointer
-            static value_type * to_value_ptr( node_type *  n )
-            {
-                return base_class::to_value_ptr( static_cast<base_node_type *>( n ) );
-            }
-
-            /// Convert node reference to value pointer (const version)
-            static const value_type * to_value_ptr( node_type const & n )
-            {
-                return base_class::to_value_ptr( static_cast<base_node_type const &>( n ) );
-            }
-
-            /// Convert node pointer to value pointer (const version)
-            static const value_type * to_value_ptr( node_type const * n )
-            {
-                return base_class::to_value_ptr( static_cast<base_node_type const *>( n ) );
-            }
-        };
 
         //@cond
         namespace details {
 
         //@cond
         namespace details {
@@ -823,8 +810,11 @@ namespace cds { namespace intrusive {
                 {}
             };
 
                 {}
             };
 
+            template <class OrderedList, class Traits, bool Iterable >
+            class ordered_list_adapter;
+
             template <class OrderedList, class Traits>
             template <class OrderedList, class Traits>
-            class rebind_list_traits
+            class ordered_list_adapter< OrderedList, Traits, false >
             {
                 typedef OrderedList native_ordered_list;
                 typedef Traits      traits;
             {
                 typedef OrderedList native_ordered_list;
                 typedef Traits      traits;
@@ -833,7 +823,7 @@ namespace cds { namespace intrusive {
                 typedef typename native_ordered_list::key_comparator    native_key_comparator;
                 typedef typename native_ordered_list::node_type         node_type;
                 typedef typename native_ordered_list::value_type        value_type;
                 typedef typename native_ordered_list::key_comparator    native_key_comparator;
                 typedef typename native_ordered_list::node_type         node_type;
                 typedef typename native_ordered_list::value_type        value_type;
-                typedef typename native_ordered_list::node_traits       node_traits;
+                typedef typename native_ordered_list::node_traits       native_node_traits;
                 typedef typename native_ordered_list::disposer          native_disposer;
 
                 typedef split_list::node<node_type>                     splitlist_node_type;
                 typedef typename native_ordered_list::disposer          native_disposer;
 
                 typedef split_list::node<node_type>                     splitlist_node_type;
@@ -841,8 +831,8 @@ namespace cds { namespace intrusive {
                 struct key_compare {
                     int operator()( value_type const& v1, value_type const& v2 ) const
                     {
                 struct key_compare {
                     int operator()( value_type const& v1, value_type const& v2 ) const
                     {
-                        splitlist_node_type const * n1 = static_cast<splitlist_node_type const *>( node_traits::to_node_ptr( v1 ));
-                        splitlist_node_type const * n2 = static_cast<splitlist_node_type const *>( node_traits::to_node_ptr( v2 ));
+                        splitlist_node_type const * n1 = static_cast<splitlist_node_type const *>(native_node_traits::to_node_ptr( v1 ));
+                        splitlist_node_type const * n2 = static_cast<splitlist_node_type const *>(native_node_traits::to_node_ptr( v2 ));
                         if ( n1->m_nHash != n2->m_nHash )
                             return n1->m_nHash < n2->m_nHash ? -1 : 1;
 
                         if ( n1->m_nHash != n2->m_nHash )
                             return n1->m_nHash < n2->m_nHash ? -1 : 1;
 
@@ -853,18 +843,18 @@ namespace cds { namespace intrusive {
 
                         assert( !n1->is_dummy() && !n2->is_dummy() );
 
 
                         assert( !n1->is_dummy() && !n2->is_dummy() );
 
-                        return native_key_comparator()( v1, v2 );
+                        return native_key_comparator()(v1, v2);
                     }
 
                     template <typename Q>
                     int operator()( value_type const& v, search_value_type<Q> const& q ) const
                     {
                     }
 
                     template <typename Q>
                     int operator()( value_type const& v, search_value_type<Q> const& q ) const
                     {
-                        splitlist_node_type const * n = static_cast<splitlist_node_type const *>( node_traits::to_node_ptr( v ));
+                        splitlist_node_type const * n = static_cast<splitlist_node_type const *>(native_node_traits::to_node_ptr( v ));
                         if ( n->m_nHash != q.nHash )
                             return n->m_nHash < q.nHash ? -1 : 1;
 
                         assert( !n->is_dummy() );
                         if ( n->m_nHash != q.nHash )
                             return n->m_nHash < q.nHash ? -1 : 1;
 
                         assert( !n->is_dummy() );
-                        return native_key_comparator()( v, q.val );
+                        return native_key_comparator()(v, q.val);
                     }
 
                     template <typename Q>
                     }
 
                     template <typename Q>
@@ -878,13 +868,72 @@ namespace cds { namespace intrusive {
                 {
                     void operator()( value_type * v )
                     {
                 {
                     void operator()( value_type * v )
                     {
-                        splitlist_node_type * p = static_cast<splitlist_node_type *>( node_traits::to_node_ptr( v ));
+                        splitlist_node_type * p = static_cast<splitlist_node_type *>(native_node_traits::to_node_ptr( v ));
                         if ( !p->is_dummy() )
                         if ( !p->is_dummy() )
-                            native_disposer()( v );
+                            native_disposer()(v);
                     }
                 };
 
             public:
                     }
                 };
 
             public:
+                typedef node_type ordered_list_node_type;
+                typedef splitlist_node_type aux_node;
+
+                struct node_traits: private native_node_traits
+                {
+                    typedef native_node_traits              base_class;     ///< Base ordered list node type
+                    typedef typename base_class::value_type value_type;     ///< Value type
+                    typedef typename base_class::node_type  base_node_type; ///< Ordered list node type
+                    typedef node<base_node_type>            node_type;      ///< Split-list node type
+
+                    /// Convert value reference to node pointer
+                    static node_type * to_node_ptr( value_type& v )
+                    {
+                        return static_cast<node_type *>(base_class::to_node_ptr( v ));
+                    }
+
+                    /// Convert value pointer to node pointer
+                    static node_type * to_node_ptr( value_type * v )
+                    {
+                        return static_cast<node_type *>(base_class::to_node_ptr( v ));
+                    }
+
+                    /// Convert value reference to node pointer (const version)
+                    static node_type const * to_node_ptr( value_type const& v )
+                    {
+                        return static_cast<node_type const*>(base_class::to_node_ptr( v ));
+                    }
+
+                    /// Convert value pointer to node pointer (const version)
+                    static node_type const * to_node_ptr( value_type const * v )
+                    {
+                        return static_cast<node_type const *>(base_class::to_node_ptr( v ));
+                    }
+
+                    /// Convert node reference to value pointer
+                    static value_type * to_value_ptr( node_type&  n )
+                    {
+                        return base_class::to_value_ptr( static_cast<base_node_type &>(n) );
+                    }
+
+                    /// Convert node pointer to value pointer
+                    static value_type * to_value_ptr( node_type *  n )
+                    {
+                        return base_class::to_value_ptr( static_cast<base_node_type *>(n) );
+                    }
+
+                    /// Convert node reference to value pointer (const version)
+                    static const value_type * to_value_ptr( node_type const & n )
+                    {
+                        return base_class::to_value_ptr( static_cast<base_node_type const &>(n) );
+                    }
+
+                    /// Convert node pointer to value pointer (const version)
+                    static const value_type * to_value_ptr( node_type const * n )
+                    {
+                        return base_class::to_value_ptr( static_cast<base_node_type const *>(n) );
+                    }
+                };
+
                 template <typename Less>
                 struct make_compare_from_less: public cds::opt::details::make_comparator_from_less<Less>
                 {
                 template <typename Less>
                 struct make_compare_from_less: public cds::opt::details::make_comparator_from_less<Less>
                 {
@@ -893,39 +942,175 @@ namespace cds { namespace intrusive {
                     template <typename Q>
                     int operator()( value_type const& v, search_value_type<Q> const& q ) const
                     {
                     template <typename Q>
                     int operator()( value_type const& v, search_value_type<Q> const& q ) const
                     {
-                        splitlist_node_type const * n = static_cast<splitlist_node_type const *>( node_traits::to_node_ptr( v ));
+                        splitlist_node_type const * n = static_cast<splitlist_node_type const *>(native_node_traits::to_node_ptr( v ));
                         if ( n->m_nHash != q.nHash )
                             return n->m_nHash < q.nHash ? -1 : 1;
 
                         assert( !n->is_dummy() );
                         if ( n->m_nHash != q.nHash )
                             return n->m_nHash < q.nHash ? -1 : 1;
 
                         assert( !n->is_dummy() );
-                        return base_class()( v, q.val );
+                        return base_class()(v, q.val);
                     }
 
                     template <typename Q>
                     int operator()( search_value_type<Q> const& q, value_type const& v ) const
                     {
                     }
 
                     template <typename Q>
                     int operator()( search_value_type<Q> const& q, value_type const& v ) const
                     {
-                        splitlist_node_type const * n = static_cast<splitlist_node_type const *>( node_traits::to_node_ptr( v ));
+                        splitlist_node_type const * n = static_cast<splitlist_node_type const *>(native_node_traits::to_node_ptr( v ));
                         if ( n->m_nHash != q.nHash )
                             return q.nHash < n->m_nHash ? -1 : 1;
 
                         assert( !n->is_dummy() );
                         if ( n->m_nHash != q.nHash )
                             return q.nHash < n->m_nHash ? -1 : 1;
 
                         assert( !n->is_dummy() );
-                        return base_class()( q.val, v );
+                        return base_class()(q.val, v);
+                    }
+
+                    template <typename Q1, typename Q2>
+                    int operator()( Q1 const& v1, Q2 const& v2 ) const
+                    {
+                        return base_class()(v1, v2);
+                    }
+                };
+
+                typedef typename native_ordered_list::template rebind_traits<
+                    opt::compare< key_compare >
+                    , opt::disposer< wrapped_disposer >
+                    , opt::boundary_node_type< splitlist_node_type >
+                >::type result;
+            };
+
+            template <class OrderedList, class Traits>
+            class ordered_list_adapter< OrderedList, Traits, true >
+            {
+                typedef OrderedList native_ordered_list;
+                typedef Traits      traits;
+
+                typedef typename native_ordered_list::gc                gc;
+                typedef typename native_ordered_list::key_comparator    native_key_comparator;
+                typedef typename native_ordered_list::value_type        value_type;
+                typedef typename native_ordered_list::disposer          native_disposer;
+
+                struct key_compare {
+                    int operator()( value_type const& v1, value_type const& v2 ) const
+                    {
+                        hash_node const& n1 = static_cast<hash_node const&>( v1 );
+                        hash_node const& n2 = static_cast<hash_node const&>( v2 );
+                        if ( n1.m_nHash != n2.m_nHash )
+                            return n1.m_nHash < n2.m_nHash ? -1 : 1;
+
+                        if ( n1.is_dummy() ) {
+                            assert( n2.is_dummy() );
+                            return 0;
+                        }
+
+                        assert( !n1.is_dummy() && !n2.is_dummy() );
+
+                        return native_key_comparator()(v1, v2);
+                    }
+
+                    template <typename Q>
+                    int operator()( value_type const& v, search_value_type<Q> const& q ) const
+                    {
+                        hash_node const& n = static_cast<hash_node const&>( v );
+                        if ( n.m_nHash != q.nHash )
+                            return n.m_nHash < q.nHash ? -1 : 1;
+
+                        assert( !n.is_dummy() );
+                        return native_key_comparator()(v, q.val);
+                    }
+
+                    template <typename Q>
+                    int operator()( search_value_type<Q> const& q, value_type const& v ) const
+                    {
+                        return -operator()( v, q );
+                    }
+                };
+
+                struct wrapped_disposer
+                {
+                    void operator()( value_type * v )
+                    {
+                        hash_node* p = static_cast<hash_node*>( v );
+                        if ( !p->is_dummy() )
+                            native_disposer()(v);
+                    }
+                };
+
+            public:
+                typedef void ordered_list_node_type;
+
+                struct aux_node: public native_ordered_list::node_type, public hash_node
+                {
+                    aux_node()
+                    {
+                        typedef typename native_ordered_list::node_type list_node_type;
+                        list_node_type::data.store( typename list_node_type::marked_data_ptr( static_cast<value_type*>( static_cast<hash_node *>( this ))), atomics::memory_order_release );
+                    }
+                };
+
+                struct node_traits
+                {
+                    static hash_node * to_node_ptr( value_type& v )
+                    {
+                        return static_cast<hash_node *>( &v );
+                    }
+
+                    static hash_node * to_node_ptr( value_type * v )
+                    {
+                        return static_cast<hash_node *>( v );
+                    }
+
+                    static hash_node const * to_node_ptr( value_type const& v )
+                    {
+                        return static_cast<hash_node const*>( &v );
+                    }
+
+                    static hash_node const * to_node_ptr( value_type const * v )
+                    {
+                        return static_cast<hash_node const *>( v );
+                    }
+                };
+
+                template <typename Less>
+                struct make_compare_from_less: public cds::opt::details::make_comparator_from_less<Less>
+                {
+                    typedef cds::opt::details::make_comparator_from_less<Less>  base_class;
+
+                    template <typename Q>
+                    int operator()( value_type const& v, search_value_type<Q> const& q ) const
+                    {
+                        hash_node const& n = static_cast<hash_node const&>( v );
+                        if ( n.m_nHash != q.nHash )
+                            return n.m_nHash < q.nHash ? -1 : 1;
+
+                        assert( !n.is_dummy() );
+                        return base_class()(v, q.val);
+                    }
+
+                    template <typename Q>
+                    int operator()( search_value_type<Q> const& q, value_type const& v ) const
+                    {
+                        hash_node const& n = static_cast<hash_node const&>( v );
+                        if ( n.m_nHash != q.nHash )
+                            return q.nHash < n.m_nHash ? -1 : 1;
+
+                        assert( !n.is_dummy() );
+                        return base_class()(q.val, v);
                     }
 
                     template <typename Q1, typename Q2>
                     int operator()( Q1 const& v1, Q2 const& v2 ) const
                     {
                     }
 
                     template <typename Q1, typename Q2>
                     int operator()( Q1 const& v1, Q2 const& v2 ) const
                     {
-                        return base_class()( v1, v2 );
+                        return base_class()(v1, v2);
                     }
                 };
 
                 typedef typename native_ordered_list::template rebind_traits<
                     opt::compare< key_compare >
                     }
                 };
 
                 typedef typename native_ordered_list::template rebind_traits<
                     opt::compare< key_compare >
-                    ,opt::disposer< wrapped_disposer >
-                    ,opt::boundary_node_type< splitlist_node_type >
+                    , opt::disposer< wrapped_disposer >
+                    , opt::boundary_node_type< aux_node >
                 >::type result;
             };
 
                 >::type result;
             };
 
+            template <class OrderedList, class Traits>
+            using rebind_list_traits = ordered_list_adapter< OrderedList, Traits, is_iterable_list<OrderedList>::value >;
+
             template <typename OrderedList, bool IsConst>
             struct select_list_iterator;
 
             template <typename OrderedList, bool IsConst>
             struct select_list_iterator;
 
index fc5fb14a4e84f8390cee565d897ff735e4f1fb4a..a3d6e1d0463efb528e1229e42fd5a270956d31a3 100644 (file)
@@ -490,7 +490,7 @@ namespace cds { namespace intrusive {
         */
         std::pair<bool, bool> upsert( value_type& val, bool bInsert = true )
         {
         */
         std::pair<bool, bool> upsert( value_type& val, bool bInsert = true )
         {
-            return update_at( &m_Head, val, []( value_type&, value_type* ) {}, bInsert );
+            return upsert_at( &m_Head, val, bInsert );
         }
 
         /// Unlinks the item \p val from the list
         }
 
         /// Unlinks the item \p val from the list
@@ -828,7 +828,7 @@ namespace cds { namespace intrusive {
 
     protected:
         //@cond
 
     protected:
         //@cond
-#if 0
+
         // split-list support
         bool insert_aux_node( node_type * pNode )
         {
         // split-list support
         bool insert_aux_node( node_type * pNode )
         {
@@ -839,13 +839,25 @@ namespace cds { namespace intrusive {
         bool insert_aux_node( node_type* pHead, node_type * pNode )
         {
             assert( pNode != nullptr );
         bool insert_aux_node( node_type* pHead, node_type * pNode )
         {
             assert( pNode != nullptr );
+            assert( pNode->data.load( memory_model::memory_order_relaxed ) != nullptr );
+
+            insert_position pos;
 
 
-            // Hack: convert node_type to value_type.
-            // In principle, auxiliary node can be non-reducible to value_type
-            // We assume that comparator can correctly distinguish aux and regular node.
-            return insert_at( pHead, *node_traits::to_value_ptr( pNode ) );
+            while ( true ) {
+                if ( inserting_search( pHead, *pNode->data.load(memory_model::memory_order_relaxed).ptr(), pos, key_comparator() ) ) {
+                    m_Stat.onInsertFailed();
+                    return false;
+                }
+
+                if ( link_aux_node( pNode, pos ) ) {
+                    ++m_ItemCounter;
+                    m_Stat.onInsertSuccess();
+                    return true;
+                }
+
+                m_Stat.onInsertRetry();
+            }
         }
         }
-#endif
 
         bool insert_at( node_type* pHead, value_type& val )
         {
 
         bool insert_at( node_type* pHead, value_type& val )
         {
@@ -936,6 +948,11 @@ namespace cds { namespace intrusive {
             }
         }
 
             }
         }
 
+        std::pair<bool, bool> upsert_at( node_type* pHead, value_type& val, bool bInsert )
+        {
+            return update_at( pHead, val, []( value_type&, value_type* ) {}, bInsert );
+        }
+
         bool unlink_at( node_type* pHead, value_type& val )
         {
             position pos;
         bool unlink_at( node_type* pHead, value_type& val )
         {
             position pos;
@@ -1129,7 +1146,7 @@ namespace cds { namespace intrusive {
         {
             pos.pHead = pHead;
             node_type*  pPrev = const_cast<node_type*>(pHead);
         {
             pos.pHead = pHead;
             node_type*  pPrev = const_cast<node_type*>(pHead);
-            value_type* pPrevVal = nullptr;
+            value_type* pPrevVal = pPrev->data.load( memory_model::memory_order_relaxed ).ptr();
 
             while ( true ) {
                 node_type * pCur = pPrev->next.load( memory_model::memory_order_relaxed );
 
             while ( true ) {
                 node_type * pCur = pPrev->next.load( memory_model::memory_order_relaxed );
@@ -1166,6 +1183,25 @@ namespace cds { namespace intrusive {
             }
         }
 
             }
         }
 
+        // split-list support
+        template <typename Predicate>
+        void destroy( Predicate pred )
+        {
+            node_type * pNode = m_Head.next.load( memory_model::memory_order_relaxed );
+            while ( pNode != pNode->next.load( memory_model::memory_order_relaxed ) ) {
+                value_type * pVal = pNode->data.load( memory_model::memory_order_relaxed ).ptr();
+                node_type * pNext = pNode->next.load( memory_model::memory_order_relaxed );
+                bool const is_regular_node = !pVal || pred( pVal );
+                if ( is_regular_node ) {
+                    if ( pVal )
+                        retire_data( pVal );
+                    delete_node( pNode );
+                }
+                pNode = pNext;
+            }
+
+            m_Head.next.store( &m_Tail, memory_model::memory_order_relaxed );
+        }
         //@endcond
 
     private:
         //@endcond
 
     private:
@@ -1279,6 +1315,51 @@ namespace cds { namespace intrusive {
             return false;
         }
 
             return false;
         }
 
+        // split-list support
+        bool link_aux_node( node_type * pNode, insert_position& pos )
+        {
+            assert( pos.pPrev != nullptr );
+            assert( pos.pCur != nullptr );
+
+            // We need pos.pCur data should be unchanged, otherwise ordering violation can be possible
+            // if current thread will be preempted and another thread will delete pos.pCur data
+            // and then set it to another.
+            // To prevent this we mark pos.pCur data as undeletable by setting LSB
+            marked_data_ptr valCur( pos.pFound );
+            if ( !pos.pCur->data.compare_exchange_strong( valCur, valCur | 1, memory_model::memory_order_acquire, atomics::memory_order_relaxed ) ) {
+                // oops, pos.pCur data has been changed or another thread is setting pos.pPrev data
+                m_Stat.onNodeMarkFailed();
+                return false;
+            }
+
+            marked_data_ptr valPrev( pos.pPrevVal );
+            if ( !pos.pPrev->data.compare_exchange_strong( valPrev, valPrev | 1, memory_model::memory_order_acquire, atomics::memory_order_relaxed ) ) {
+                pos.pCur->data.store( valCur, memory_model::memory_order_relaxed );
+                m_Stat.onNodeMarkFailed();
+                return false;
+            }
+
+            // checks if link pPrev -> pCur is broken
+            if ( pos.pPrev->next.load( memory_model::memory_order_acquire ) != pos.pCur ) {
+                // sequence pPrev - pCur is broken
+                pos.pPrev->data.store( valPrev, memory_model::memory_order_relaxed );
+                pos.pCur->data.store( valCur, memory_model::memory_order_relaxed );
+                m_Stat.onNodeSeqBreak();
+                return false;
+            }
+
+            // insert new node between pos.pPrev and pos.pCur
+            pNode->next.store( pos.pCur, memory_model::memory_order_relaxed );
+
+            bool result = pos.pPrev->next.compare_exchange_strong( pos.pCur, pNode, memory_model::memory_order_release, atomics::memory_order_relaxed );
+
+            // Clears data marks
+            pos.pPrev->data.store( valPrev, memory_model::memory_order_relaxed );
+            pos.pCur->data.store( valCur, memory_model::memory_order_relaxed );
+
+            return result;
+        }
+
         static bool unlink_data( position& pos )
         {
             assert( pos.pCur != nullptr );
         static bool unlink_data( position& pos )
         {
             assert( pos.pCur != nullptr );
index 13fa9b92bb8ebd2f6db0b0ff5d6e4e222a4c0bd9..e438c10e28e8697a85430862eb8050dcb17174b9 100644 (file)
@@ -5,7 +5,7 @@
 
     Source code repo: http://github.com/khizmax/libcds/
     Download: http://sourceforge.net/projects/libcds/files/
 
     Source code repo: http://github.com/khizmax/libcds/
     Download: http://sourceforge.net/projects/libcds/files/
-    
+
     Redistribution and use in source and binary forms, with or without
     modification, are permitted provided that the following conditions are met:
 
     Redistribution and use in source and binary forms, with or without
     modification, are permitted provided that the following conditions are met:
 
@@ -1208,6 +1208,13 @@ namespace cds { namespace intrusive {
             return guarded_ptr();
         }
 
             return guarded_ptr();
         }
 
+        // split-list support
+        template <typename Predicate>
+        void destroy( Predicate /*pred*/ )
+        {
+            clear();
+        }
+
         //@endcond
 
     protected:
         //@endcond
 
     protected:
index 759b13cf8500d329e41eb1fb029fff66fa90e565..2656a4e89247cdc97c245bda93093afecddcff31 100644 (file)
@@ -1170,6 +1170,13 @@ namespace cds { namespace intrusive {
             return guarded_ptr();
         }
 
             return guarded_ptr();
         }
 
+        // split-list support
+        template <typename Predicate>
+        void destroy( Predicate /*pred*/ )
+        {
+            clear();
+        }
+
         //@endcond
 
     protected:
         //@endcond
 
     protected:
index 532ac4de1e40907844ed442985a8789efd4d5558..acb9b5050c04249274a5113e8c640c7a9dfd0ed3 100644 (file)
@@ -318,7 +318,6 @@ namespace cds { namespace intrusive {
               Moreover, a crash is possible when you try to iterate the next element that has been deleted by concurrent thread.
               Use this iterator on the concurrent container for debugging purpose only.
             - for \p IterableList: iterator is thread-safe. You may use it freely in concurrent environment.
               Moreover, a crash is possible when you try to iterate the next element that has been deleted by concurrent thread.
               Use this iterator on the concurrent container for debugging purpose only.
             - for \p IterableList: iterator is thread-safe. You may use it freely in concurrent environment.
-              
         */
         typedef michael_set::details::iterator< internal_bucket_type, false > iterator;
 
         */
         typedef michael_set::details::iterator< internal_bucket_type, false > iterator;
 
index de983faed3c390f0f09aa610d4b514c561c6b2c9..86fc2fec5c7d057a55bd6bedfa1bd4fa2522cc7c 100644 (file)
@@ -97,8 +97,6 @@ namespace cds { namespace intrusive {
         - \p Traits - split-list traits, default is \p split_list::traits.
             Instead of defining \p Traits struct you can use option-based syntax provided by \p split_list::make_traits metafunction.
 
         - \p Traits - split-list traits, default is \p split_list::traits.
             Instead of defining \p Traits struct you can use option-based syntax provided by \p split_list::make_traits metafunction.
 
-        @warning \p IterableList is not supported as \p OrderedList template parameter.
-
         There are several specialization of the split-list class for different \p GC:
         - for \ref cds_urcu_gc "RCU type" include <tt><cds/intrusive/split_list_rcu.h></tt> - see
             \ref cds_intrusive_SplitListSet_rcu "RCU-based split-list"
         There are several specialization of the split-list class for different \p GC:
         - for \ref cds_urcu_gc "RCU type" include <tt><cds/intrusive/split_list_rcu.h></tt> - see
             \ref cds_intrusive_SplitListSet_rcu "RCU-based split-list"
@@ -135,6 +133,37 @@ namespace cds { namespace intrusive {
 
         <b>How to use</b>
 
 
         <b>How to use</b>
 
+        Split-list based on \p IterableList differs from split-list based on \p MichaelList or \p LazyList
+        because \p %IterableList stores data "as is" - it cannot use any hook.
+
+        Suppose, your split-list contains values of type \p Foo.
+        For \p %MichaelList and \p %LazyList, \p Foo declaration should be based on ordered-list node:
+        - \p %MichaelList:
+        \code
+        struct Foo: public cds::intrusive::split_list::node< cds::intrusive::michael_list::node< cds::gc::HP > >
+        {
+            // ... field declarations
+        };
+        \endcode
+        - \p %LazyList:
+        \code
+        struct Foo: public cds::intrusive::split_list::node< cds::intrusive::lazy_list::node< cds::gc::HP > >
+        {
+            // ... field declarations
+        };
+        \endcode
+
+        For \p %IterableList, \p Foo should be based on \p void:
+        \code
+        struct Foo: public cds::intrusive::split_list::node<void>
+        {
+            // ... field declarations
+        };
+        \endcode
+
+        Everything else is the same.
+        Consider split-list based on \p MichaelList.
+
         First, you should choose ordered list type to use in your split-list set:
         \code
         // For gc::HP-based MichaelList implementation
         First, you should choose ordered list type to use in your split-list set:
         \code
         // For gc::HP-based MichaelList implementation
@@ -228,25 +257,25 @@ namespace cds { namespace intrusive {
 
     protected:
         //@cond
 
     protected:
         //@cond
-        typedef split_list::details::rebind_list_traits<OrderedList, traits> wrapped_ordered_list;
+        typedef split_list::details::rebind_list_traits<OrderedList, traits> ordered_list_adapter;
         //@endcond
 
     public:
 #   ifdef CDS_DOXYGEN_INVOKED
         typedef OrderedList         ordered_list;   ///< type of ordered list used as a base for split-list
 #   else
         //@endcond
 
     public:
 #   ifdef CDS_DOXYGEN_INVOKED
         typedef OrderedList         ordered_list;   ///< type of ordered list used as a base for split-list
 #   else
-        typedef typename wrapped_ordered_list::result   ordered_list;
+        typedef typename ordered_list_adapter::result   ordered_list;
 #   endif
         typedef typename ordered_list::value_type       value_type;     ///< type of value stored in the split-list
         typedef typename ordered_list::key_comparator   key_comparator; ///< key comparison functor
         typedef typename ordered_list::disposer         disposer;       ///< Node disposer functor
 
 #   endif
         typedef typename ordered_list::value_type       value_type;     ///< type of value stored in the split-list
         typedef typename ordered_list::key_comparator   key_comparator; ///< key comparison functor
         typedef typename ordered_list::disposer         disposer;       ///< Node disposer functor
 
-        /// Hash functor for \p %value_type and all its derivatives that you use
+        /// Hash functor for \p %value_type and all its derivatives you use
         typedef typename cds::opt::v::hash_selector< typename traits::hash >::type hash;
 
         typedef typename traits::item_counter      item_counter; ///< Item counter type
         typedef typename traits::back_off          back_off;     ///< back-off strategy for spinning
         typedef typename cds::opt::v::hash_selector< typename traits::hash >::type hash;
 
         typedef typename traits::item_counter      item_counter; ///< Item counter type
         typedef typename traits::back_off          back_off;     ///< back-off strategy for spinning
-        typedef typename traits::memory_model      memory_model; ///< Memory ordering. See cds::opt::memory_model option
+        typedef typename traits::memory_model      memory_model; ///< Memory ordering. See \p cds::opt::memory_model option
         typedef typename traits::stat              stat;         ///< Internal statistics, see \p spit_list::stat
         typedef typename ordered_list::guarded_ptr guarded_ptr;  ///< Guarded pointer
 
         typedef typename traits::stat              stat;         ///< Internal statistics, see \p spit_list::stat
         typedef typename ordered_list::guarded_ptr guarded_ptr;  ///< Guarded pointer
 
@@ -255,21 +284,14 @@ namespace cds { namespace intrusive {
 
     protected:
         //@cond
 
     protected:
         //@cond
-        typedef typename ordered_list::node_type    list_node_type;  ///< Node type as declared in ordered list
-        typedef split_list::node<list_node_type>    node_type;       ///< split-list node type
-
-        /// Split-list node traits
-        /**
-            This traits is intended for converting between underlying ordered list node type \p list_node_type
-            and split-list node type \p node_type
-        */
-        typedef split_list::node_traits<typename ordered_list::node_traits>  node_traits;
+        typedef split_list::node<typename ordered_list_adapter::ordered_list_node_type> node_type; ///< split-list node type
+        typedef typename ordered_list_adapter::node_traits node_traits;
 
         /// Bucket table implementation
         typedef typename split_list::details::bucket_table_selector<
             traits::dynamic_bucket_table
             , gc
 
         /// Bucket table implementation
         typedef typename split_list::details::bucket_table_selector<
             traits::dynamic_bucket_table
             , gc
-            , node_type
+            , typename ordered_list_adapter::aux_node
             , opt::allocator< typename traits::allocator >
             , opt::memory_model< memory_model >
             , opt::free_list< typename traits::free_list >
             , opt::allocator< typename traits::allocator >
             , opt::memory_model< memory_model >
             , opt::free_list< typename traits::free_list >
@@ -284,7 +306,7 @@ namespace cds { namespace intrusive {
         class ordered_list_wrapper: public ordered_list
         {
             typedef ordered_list base_class;
         class ordered_list_wrapper: public ordered_list
         {
             typedef ordered_list base_class;
-            typedef typename base_class::auxiliary_head       bucket_head_type;
+            typedef typename base_class::auxiliary_head bucket_head_type;
 
         public:
             bool insert_at( aux_node_type* pHead, value_type& val )
 
         public:
             bool insert_at( aux_node_type* pHead, value_type& val )
@@ -310,6 +332,18 @@ namespace cds { namespace intrusive {
                 return base_class::update_at( h, val, func, bAllowInsert );
             }
 
                 return base_class::update_at( h, val, func, bAllowInsert );
             }
 
+            template <typename Q>
+            typename std::enable_if<
+                std::is_same< Q, value_type>::value && is_iterable_list< ordered_list >::value,
+                std::pair<bool, bool>
+            >::type
+            upsert_at( aux_node_type * pHead, Q& val, bool bAllowInsert )
+            {
+                assert( pHead != nullptr );
+                bucket_head_type h( pHead );
+                return base_class::upsert_at( h, val, bAllowInsert );
+            }
+
             bool unlink_at( aux_node_type * pHead, value_type& val )
             {
                 assert( pHead != nullptr );
             bool unlink_at( aux_node_type * pHead, value_type& val )
             {
                 assert( pHead != nullptr );
@@ -357,6 +391,18 @@ namespace cds { namespace intrusive {
                 return base_class::find_at( h, val, cmp );
             }
 
                 return base_class::find_at( h, val, cmp );
             }
 
+            template <typename Q, typename Compare>
+            typename std::enable_if<
+                std::is_same<Q, Q>::value && is_iterable_list< ordered_list >::value,
+                typename base_class::iterator
+            >::type
+            find_iterator_at( aux_node_type * pHead, split_list::details::search_value_type<Q> const& val, Compare cmp )
+            {
+                assert( pHead != nullptr );
+                bucket_head_type h( pHead );
+                return base_class::find_iterator_at( h, val, cmp );
+            }
+
             template <typename Q, typename Compare>
             guarded_ptr get_at( aux_node_type * pHead, split_list::details::search_value_type<Q> const& val, Compare cmp )
             {
             template <typename Q, typename Compare>
             guarded_ptr get_at( aux_node_type * pHead, split_list::details::search_value_type<Q> const& val, Compare cmp )
             {
@@ -374,9 +420,109 @@ namespace cds { namespace intrusive {
                 bucket_head_type h(pHead);
                 return base_class::insert_aux_node( h, pNode );
             }
                 bucket_head_type h(pHead);
                 return base_class::insert_aux_node( h, pNode );
             }
+
+            template <typename Predicate>
+            void destroy( Predicate pred )
+            {
+                base_class::destroy( pred );
+            }
+        };
+        //@endcond
+
+    protected:
+        //@cond
+        template <bool IsConst>
+        class iterator_type
+            :public split_list::details::iterator_type<node_traits, ordered_list, IsConst>
+        {
+            typedef split_list::details::iterator_type<node_traits, ordered_list, IsConst> iterator_base_class;
+            typedef typename iterator_base_class::list_iterator list_iterator;
+        public:
+            iterator_type()
+                : iterator_base_class()
+            {}
+
+            iterator_type( iterator_type const& src )
+                : iterator_base_class( src )
+            {}
+
+            // This ctor should be protected...
+            iterator_type( list_iterator itCur, list_iterator itEnd )
+                : iterator_base_class( itCur, itEnd )
+            {}
         };
         //@endcond
 
         };
         //@endcond
 
+    public:
+    ///@name Forward iterators
+    //@{
+        /// Forward iterator
+        /**
+            The forward iterator is based on \p OrderedList forward iterator and has some features:
+            - it has no post-increment operator
+            - it iterates items in unordered fashion
+            - iterator cannot be moved across thread boundary because it may contain GC's guard that is thread-private GC data.
+
+            Iterator thread safety depends on type of \p OrderedList:
+            - for \p MichaelList and \p LazyList: iterator guarantees safety even if you delete the item that iterator points to
+              because that item is guarded by hazard pointer.
+              However, in case of concurrent deleting operations it is no guarantee that you iterate all item in the set.
+              Moreover, a crash is possible when you try to iterate the next element that has been deleted by concurrent thread.
+              Use this iterator on the concurrent container for debugging purpose only.
+            - for \p IterableList: iterator is thread-safe. You may use it freely in concurrent environment.
+        */
+        typedef iterator_type<false>    iterator;
+
+        /// Const forward iterator
+        /**
+            For iterator's features and requirements see \ref iterator
+        */
+        typedef iterator_type<true>     const_iterator;
+
+        /// Returns a forward iterator addressing the first element in a split-list
+        /**
+            For empty list \code begin() == end() \endcode
+        */
+        iterator begin()
+        {
+            return iterator( m_List.begin(), m_List.end());
+        }
+
+        /// Returns an iterator that addresses the location succeeding the last element in a split-list
+        /**
+            Do not use the value returned by <tt>end</tt> function to access any item.
+
+            The returned value can be used only to control reaching the end of the split-list.
+            For empty list \code begin() == end() \endcode
+        */
+        iterator end()
+        {
+            return iterator( m_List.end(), m_List.end());
+        }
+
+        /// Returns a forward const iterator addressing the first element in a split-list
+        const_iterator begin() const
+        {
+            return cbegin();
+        }
+        /// Returns a forward const iterator addressing the first element in a split-list
+        const_iterator cbegin() const
+        {
+            return const_iterator( m_List.cbegin(), m_List.cend());
+        }
+
+        /// Returns an const iterator that addresses the location succeeding the last element in a split-list
+        const_iterator end() const
+        {
+            return cend();
+        }
+        /// Returns an const iterator that addresses the location succeeding the last element in a split-list
+        const_iterator cend() const
+        {
+            return const_iterator( m_List.cend(), m_List.cend());
+        }
+    //@}
+
     public:
         /// Initialize split-ordered list of default capacity
         /**
     public:
         /// Initialize split-ordered list of default capacity
         /**
@@ -408,7 +554,11 @@ namespace cds { namespace intrusive {
         {
             // list contains aux node that cannot be retired
             // all aux nodes will be destroyed by bucket table dtor
         {
             // list contains aux node that cannot be retired
             // all aux nodes will be destroyed by bucket table dtor
-            m_List.clear();
+            m_List.destroy(
+                []( node_type * pNode ) -> bool {
+                    return !pNode->is_dummy();
+                }
+            );
             gc::force_dispose();
         }
 
             gc::force_dispose();
         }
 
@@ -482,26 +632,38 @@ namespace cds { namespace intrusive {
             If the item \p val is not found in the set, then \p val is inserted
             iff \p bAllowInsert is \p true.
             Otherwise, the functor \p func is called with item found.
             If the item \p val is not found in the set, then \p val is inserted
             iff \p bAllowInsert is \p true.
             Otherwise, the functor \p func is called with item found.
-            The functor signature is:
-            \code
-                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 update() function
-            If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
-            refers to the same thing.
 
 
-            The functor may change non-key fields of the \p item.
+            The functor signature depends of the type of \p OrderedList:
+
+            <b>for \p MichaelList, \p LazyList</b>
+                \code
+                    struct functor {
+                        void operator()( 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 %update() function
+                If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
+                refers to the same thing.
+
+                The functor may change non-key fields of the \p item.
+                @warning For \ref cds_intrusive_MichaelList_hp "MichaelList" as the bucket see \ref cds_intrusive_item_creating "insert item troubleshooting".
+                \ref cds_intrusive_LazyList_hp "LazyList" provides exclusive access to inserted item and does not require any node-level
+                synchronization.
+
+            <b>for \p IterableList</b>
+                \code
+                void func( value_type& val, value_type * old );
+                \endcode
+                where
+                - \p val - argument \p val passed into the \p %update() function
+                - \p old - old value that will be retired. If new item has been inserted then \p old is \p nullptr.
 
             Returns std::pair<bool, bool> where \p first is \p true if operation is successful,
             \p second is \p true if new item has been added or \p false if the item with \p val
             already is in the list.
 
             Returns std::pair<bool, bool> where \p first is \p true if operation is successful,
             \p second is \p true if new item has been added or \p false if the item with \p val
             already is in the list.
-
-            @warning For \ref cds_intrusive_MichaelList_hp "MichaelList" as the bucket see \ref cds_intrusive_item_creating "insert item troubleshooting".
-            \ref cds_intrusive_LazyList_hp "LazyList" provides exclusive access to inserted item and does not require any node-level
-            synchronization.
         */
         template <typename Func>
         std::pair<bool, bool> update( value_type& val, Func func, bool bAllowInsert = true )
         */
         template <typename Func>
         std::pair<bool, bool> update( value_type& val, Func func, bool bAllowInsert = true )
@@ -530,6 +692,45 @@ namespace cds { namespace intrusive {
         }
         //@endcond
 
         }
         //@endcond
 
+        /// Inserts or updates the node (only for \p IterableList)
+        /**
+            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 iff \p bAllowInsert is \p true.
+            Otherwise, the current element is changed to \p val, the old element will be retired later
+            by call \p Traits::disposer.
+
+            Returns std::pair<bool, bool> where \p first is \p true if operation is successful,
+            \p second is \p true if \p val has been added or \p false if the item with that key
+            already in the set.
+        */
+#ifdef CDS_DOXYGEN_INVOKED
+        std::pair<bool, bool> upsert( value_type& val, bool bAllowInsert = true )
+#else
+        template <typename Q>
+        typename std::enable_if< 
+            std::is_same< Q, value_type>::value && is_iterable_list< ordered_list >::value,
+            std::pair<bool, bool>
+        >::type
+        upsert( Q& val, bool bAllowInsert = true )
+#endif
+        {
+            size_t nHash = hash_value( val );
+            aux_node_type * pHead = get_bucket( nHash );
+            assert( pHead != nullptr );
+
+            node_traits::to_node_ptr( val )->m_nHash = split_list::regular_hash( nHash );
+
+            std::pair<bool, bool> bRet = m_List.upsert_at( pHead, val, bAllowInsert );
+            if ( bRet.first && bRet.second ) {
+                inc_item_count();
+                m_Stat.onUpdateNew();
+            }
+            else
+                m_Stat.onUpdateExist();
+            return bRet;
+        }
+
         /// Unlinks the item \p val from the set
         /**
             The function searches the item \p val in the set and unlinks it from the set
         /// Unlinks the item \p val from the set
         /**
             The function searches the item \p val in the set and unlinks it from the set
@@ -588,7 +789,7 @@ namespace cds { namespace intrusive {
         bool erase_with( const Q& key, Less pred )
         {
             CDS_UNUSED( pred );
         bool erase_with( const Q& key, Less pred )
         {
             CDS_UNUSED( pred );
-            return erase_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>());
+            return erase_( key, typename ordered_list_adapter::template make_compare_from_less<Less>());
         }
 
         /// Deletes the item from the set
         }
 
         /// Deletes the item from the set
@@ -626,7 +827,7 @@ namespace cds { namespace intrusive {
         bool erase_with( Q const& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
         bool erase_with( Q const& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
-            return erase_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>(), f );
+            return erase_( key, typename ordered_list_adapter::template make_compare_from_less<Less>(), f );
         }
 
         /// Extracts the item with specified \p key
         }
 
         /// Extracts the item with specified \p key
@@ -711,6 +912,32 @@ namespace cds { namespace intrusive {
         }
         //@endcond
 
         }
         //@endcond
 
+        /// Finds \p key and returns iterator pointed to the item found (only for \p IterableList)
+        /**
+            If \p key is not found the function returns \p end().
+
+            @note This function is supported only for the set based on \p IterableList
+        */
+        template <typename Q>
+#ifdef CDS_DOXYGEN_INVOKED
+        iterator
+#else
+        typename std::enable_if< std::is_same<Q, Q>::value && is_iterable_list< ordered_list >::value, iterator >::type
+#endif
+        find( Q& key )
+        {
+            return find_iterator_( key, key_comparator() );
+        }
+        //@cond
+        template <typename Q>
+        typename std::enable_if< std::is_same<Q, Q>::value && is_iterable_list< ordered_list >::value, iterator >::type
+        find( Q const& key )
+        {
+            return find_iterator_( key, key_comparator() );
+        }
+        //@endcond
+
+
         /// Finds the key \p key with \p pred predicate for comparing
         /**
             The function is an analog of \ref cds_intrusive_SplitListSet_hp_find_func "find(Q&, Func)"
         /// Finds the key \p key with \p pred predicate for comparing
         /**
             The function is an analog of \ref cds_intrusive_SplitListSet_hp_find_func "find(Q&, Func)"
@@ -722,17 +949,49 @@ namespace cds { namespace intrusive {
         bool find_with( Q& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
         bool find_with( Q& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
-            return find_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>(), f );
+            return find_( key, typename ordered_list_adapter::template make_compare_from_less<Less>(), f );
         }
         //@cond
         template <typename Q, typename Less, typename Func>
         bool find_with( Q const& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
         }
         //@cond
         template <typename Q, typename Less, typename Func>
         bool find_with( Q const& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
-            return find_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>(), f );
+            return find_( key, typename ordered_list_adapter::template make_compare_from_less<Less>(), f );
+        }
+        //@endcond
+
+        /// Finds \p key using \p pred predicate and returns iterator pointed to the item found (only for \p IterableList)
+        /**
+            The function is an analog of \p find(Q&) but \p pred is used for key comparing.
+            \p Less functor has the interface like \p std::less.
+            \p pred must imply the same element order as the comparator used for building the set.
+
+            If \p key is not found the function returns \p end().
+
+            @note This function is supported only for the set based on \p IterableList
+        */
+        template <typename Q, typename Less>
+#ifdef CDS_DOXYGEN_INVOKED
+        iterator
+#else
+        typename std::enable_if< std::is_same<Q, Q>::value && is_iterable_list< ordered_list >::value, iterator >::type
+#endif
+        find_with( Q& key, Less pred )
+        {
+            CDS_UNUSED( pred );
+            return find_iterator_( key, typename ordered_list_adapter::template make_compare_from_less<Less>() );
+        }
+        //@cond
+        template <typename Q, typename Less>
+        typename std::enable_if< std::is_same<Q, Q>::value && is_iterable_list< ordered_list >::value, iterator >::type
+        find_with( Q const& key, Less pred )
+        {
+            CDS_UNUSED( pred );
+            return find_iterator_( key, typename ordered_list_adapter::template make_compare_from_less<Less>() );
         }
         //@endcond
 
         }
         //@endcond
 
+
         /// Checks whether the set contains \p key
         /**
             The function searches the item with key equal to \p key
         /// Checks whether the set contains \p key
         /**
             The function searches the item with key equal to \p key
@@ -747,14 +1006,6 @@ namespace cds { namespace intrusive {
         {
             return find_( key, key_comparator());
         }
         {
             return find_( key, key_comparator());
         }
-        //@cond
-        template <typename Q>
-        CDS_DEPRECATED("deprecated, use contains()")
-        bool find( Q const& key )
-        {
-            return contains( key );
-        }
-        //@endcond
 
         /// Checks whether the set contains \p key using \p pred predicate for searching
         /**
 
         /// Checks whether the set contains \p key using \p pred predicate for searching
         /**
@@ -766,16 +1017,8 @@ namespace cds { namespace intrusive {
         bool contains( Q const& key, Less pred )
         {
             CDS_UNUSED( pred );
         bool contains( Q const& key, Less pred )
         {
             CDS_UNUSED( pred );
-            return find_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>());
+            return find_( key, typename ordered_list_adapter::template make_compare_from_less<Less>());
         }
         }
-        //@cond
-        template <typename Q, typename Less>
-        CDS_DEPRECATED("deprecated, use contains()")
-        bool find_with( Q const& key, Less pred )
-        {
-            return contains( key, pred );
-        }
-        //@endcond
 
         /// Finds the key \p key and return the item found
         /** \anchor cds_intrusive_SplitListSet_hp_get
 
         /// Finds the key \p key and return the item found
         /** \anchor cds_intrusive_SplitListSet_hp_get
@@ -867,96 +1110,12 @@ namespace cds { namespace intrusive {
             return m_Stat;
         }
 
             return m_Stat;
         }
 
-    protected:
-        //@cond
-        template <bool IsConst>
-        class iterator_type
-            :public split_list::details::iterator_type<node_traits, ordered_list, IsConst>
-        {
-            typedef split_list::details::iterator_type<node_traits, ordered_list, IsConst> iterator_base_class;
-            typedef typename iterator_base_class::list_iterator list_iterator;
-        public:
-            iterator_type()
-                : iterator_base_class()
-            {}
-
-            iterator_type( iterator_type const& src )
-                : iterator_base_class( src )
-            {}
-
-            // This ctor should be protected...
-            iterator_type( list_iterator itCur, list_iterator itEnd )
-                : iterator_base_class( itCur, itEnd )
-            {}
-        };
-        //@endcond
-    public:
-    ///@name Forward iterators (only for debugging purpose)
-    //@{
-        /// Forward iterator
-        /**
-            The forward iterator for a split-list has some features:
-            - it has no post-increment operator
-            - it depends on iterator of underlying \p OrderedList
-            - The iterator cannot be moved across thread boundary since it may contain GC's guard that is thread-private GC data.
-            - Iterator ensures thread-safety even if you delete the item that iterator points to. However, in case of concurrent
-              deleting operations it is no guarantee that you iterate all item in the set.
-              Moreover, a crash is possible when you try to iterate the next element that has been deleted by concurrent thread.
-
-            @warning Use this iterator on the concurrent container for debugging purpose only.
-        */
-        typedef iterator_type<false>    iterator;
-
-        /// Const forward iterator
-        /**
-            For iterator's features and requirements see \ref iterator
-        */
-        typedef iterator_type<true>     const_iterator;
-
-        /// Returns a forward iterator addressing the first element in a split-list
-        /**
-            For empty list \code begin() == end() \endcode
-        */
-        iterator begin()
-        {
-            return iterator( m_List.begin(), m_List.end());
-        }
-
-        /// Returns an iterator that addresses the location succeeding the last element in a split-list
-        /**
-            Do not use the value returned by <tt>end</tt> function to access any item.
-
-            The returned value can be used only to control reaching the end of the split-list.
-            For empty list \code begin() == end() \endcode
-        */
-        iterator end()
-        {
-            return iterator( m_List.end(), m_List.end());
-        }
-
-        /// Returns a forward const iterator addressing the first element in a split-list
-        const_iterator begin() const
-        {
-            return cbegin();
-        }
-        /// Returns a forward const iterator addressing the first element in a split-list
-        const_iterator cbegin() const
+        /// Returns internal statistics for \p OrderedList
+        typename OrderedList::stat const& list_statistics() const
         {
         {
-            return const_iterator( m_List.cbegin(), m_List.cend());
+            return m_List.statistics();
         }
 
         }
 
-        /// Returns an const iterator that addresses the location succeeding the last element in a split-list
-        const_iterator end() const
-        {
-            return cend();
-        }
-        /// Returns an const iterator that addresses the location succeeding the last element in a split-list
-        const_iterator cend() const
-        {
-            return const_iterator( m_List.cend(), m_List.cend());
-        }
-    //@}
-
     protected:
         //@cond
         aux_node_type * alloc_aux_node( size_t nHash )
     protected:
         //@cond
         aux_node_type * alloc_aux_node( size_t nHash )
@@ -1126,6 +1285,17 @@ namespace cds { namespace intrusive {
             return m_Stat.onFind( m_List.find_at( pHead, sv, cmp ) );
         }
 
             return m_Stat.onFind( m_List.find_at( pHead, sv, cmp ) );
         }
 
+        template <typename Q, typename Compare>
+        iterator find_iterator_( Q const& val, Compare cmp )
+        {
+            size_t nHash = hash_value( val );
+            split_list::details::search_value_type<Q const>  sv( val, split_list::regular_hash( nHash ) );
+            aux_node_type * pHead = get_bucket( nHash );
+            assert( pHead != nullptr );
+
+            return iterator( m_List.find_iterator_at( pHead, sv, cmp ), m_List.end());
+        }
+
         template <typename Q, typename Compare>
         guarded_ptr get_( Q const& val, Compare cmp )
         {
         template <typename Q, typename Compare>
         guarded_ptr get_( Q const& val, Compare cmp )
         {
@@ -1148,7 +1318,7 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Less>
         guarded_ptr get_with_( Q const& key, Less )
         {
         template <typename Q, typename Less>
         guarded_ptr get_with_( Q const& key, Less )
         {
-            return get_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>() );
+            return get_( key, typename ordered_list_adapter::template make_compare_from_less<Less>() );
         }
 
         template <typename Q, typename Compare, typename Func>
         }
 
         template <typename Q, typename Compare, typename Func>
@@ -1212,7 +1382,7 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Less>
         guarded_ptr extract_with_( Q const& key, Less )
         {
         template <typename Q, typename Less>
         guarded_ptr extract_with_( Q const& key, Less )
         {
-            return extract_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>() );
+            return extract_( key, typename ordered_list_adapter::template make_compare_from_less<Less>() );
         }
         //@endcond
 
         }
         //@endcond
 
index 0576c98affd0729b0819d914c07c393a2b81656a..8e2c44d7ef86340ceab546194f29b6d1dde4ffee 100644 (file)
@@ -70,14 +70,14 @@ namespace cds { namespace intrusive {
 
     protected:
         //@cond
 
     protected:
         //@cond
-        typedef split_list::details::rebind_list_traits<OrderedList, traits> wrapped_ordered_list;
+        typedef split_list::details::rebind_list_traits<OrderedList, traits> ordered_list_adapter;
         //@endcond
 
     public:
 #   ifdef CDS_DOXYGEN_INVOKED
         typedef OrderedList ordered_list;   ///< type of ordered list used as base for split-list
 #   else
         //@endcond
 
     public:
 #   ifdef CDS_DOXYGEN_INVOKED
         typedef OrderedList ordered_list;   ///< type of ordered list used as base for split-list
 #   else
-        typedef typename wrapped_ordered_list::result ordered_list;
+        typedef typename ordered_list_adapter::result ordered_list;
 #   endif
         typedef typename ordered_list::value_type     value_type;     ///< type of value stored in the split-list
         typedef typename ordered_list::key_comparator key_comparator; ///< key comparison functor
 #   endif
         typedef typename ordered_list::value_type     value_type;     ///< type of value stored in the split-list
         typedef typename ordered_list::key_comparator key_comparator; ///< key comparison functor
@@ -105,13 +105,13 @@ namespace cds { namespace intrusive {
             This traits is intended for converting between underlying ordered list node type \ref list_node_type
             and split-list node type \ref node_type
         */
             This traits is intended for converting between underlying ordered list node type \ref list_node_type
             and split-list node type \ref node_type
         */
-        typedef split_list::node_traits<typename ordered_list::node_traits>  node_traits;
+        typedef typename ordered_list_adapter::node_traits node_traits;
 
         /// Bucket table implementation
         typedef typename split_list::details::bucket_table_selector<
             traits::dynamic_bucket_table
             , gc
 
         /// Bucket table implementation
         typedef typename split_list::details::bucket_table_selector<
             traits::dynamic_bucket_table
             , gc
-            , node_type
+            , typename ordered_list_adapter::aux_node
             , opt::allocator< typename traits::allocator >
             , opt::memory_model< memory_model >
             , opt::free_list< typename traits::free_list >
             , opt::allocator< typename traits::allocator >
             , opt::memory_model< memory_model >
             , opt::free_list< typename traits::free_list >
@@ -361,14 +361,14 @@ namespace cds { namespace intrusive {
         bool find_with( Q& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
         bool find_with( Q& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
-            return find_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>(), f );
+            return find_( key, typename ordered_list_adapter::template make_compare_from_less<Less>(), f );
         }
         //@cond
         template <typename Q, typename Less, typename Func>
         bool find_with( Q const& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
         }
         //@cond
         template <typename Q, typename Less, typename Func>
         bool find_with( Q const& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
-            return find_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>(), f );
+            return find_( key, typename ordered_list_adapter::template make_compare_from_less<Less>(), f );
         }
         //@endcond
 
         }
         //@endcond
 
@@ -549,7 +549,7 @@ namespace cds { namespace intrusive {
             aux_node_type * pHead = get_bucket( nHash );
             assert( pHead != nullptr );
 
             aux_node_type * pHead = get_bucket( nHash );
             assert( pHead != nullptr );
 
-            auto it = m_List.find_at_( pHead, sv, typename wrapped_ordered_list::template make_compare_from_less<Less>() );
+            auto it = m_List.find_at_( pHead, sv, typename ordered_list_adapter::template make_compare_from_less<Less>() );
             m_Stat.onFind( it != m_List.end() );
             return iterator( it, m_List.end() );
         }
             m_Stat.onFind( it != m_List.end() );
             return iterator( it, m_List.end() );
         }
index d464c06204d5b708a8b4737a7ba5dd936e5fbd44..f91ffa04af6d7e0e0103317b420f01b364878cbe 100644 (file)
@@ -101,14 +101,14 @@ namespace cds { namespace intrusive {
 
     protected:
         //@cond
 
     protected:
         //@cond
-        typedef split_list::details::rebind_list_traits<OrderedList, traits> wrapped_ordered_list;
+        typedef split_list::details::rebind_list_traits<OrderedList, traits> ordered_list_adapter;
         //@endcond
 
     public:
 #   ifdef CDS_DOXYGEN_INVOKED
         typedef OrderedList         ordered_list;   ///< type of ordered list used as base for split-list
 #   else
         //@endcond
 
     public:
 #   ifdef CDS_DOXYGEN_INVOKED
         typedef OrderedList         ordered_list;   ///< type of ordered list used as base for split-list
 #   else
-        typedef typename wrapped_ordered_list::result    ordered_list;
+        typedef typename ordered_list_adapter::result    ordered_list;
 #   endif
         typedef typename ordered_list::value_type     value_type;     ///< type of value stored in the split-list
         typedef typename ordered_list::key_comparator key_comparator; ///< key compare functor
 #   endif
         typedef typename ordered_list::value_type     value_type;     ///< type of value stored in the split-list
         typedef typename ordered_list::key_comparator key_comparator; ///< key compare functor
@@ -141,13 +141,13 @@ namespace cds { namespace intrusive {
             This traits is intended for converting between underlying ordered list node type \ref list_node_type
             and split-list node type \ref node_type
         */
             This traits is intended for converting between underlying ordered list node type \ref list_node_type
             and split-list node type \ref node_type
         */
-        typedef split_list::node_traits<typename ordered_list::node_traits>  node_traits;
+        typedef typename ordered_list_adapter::node_traits node_traits;
 
         /// Bucket table implementation
         typedef typename split_list::details::bucket_table_selector<
             traits::dynamic_bucket_table
             , gc
 
         /// Bucket table implementation
         typedef typename split_list::details::bucket_table_selector<
             traits::dynamic_bucket_table
             , gc
-            , node_type
+            , typename ordered_list_adapter::aux_node
             , opt::allocator< typename traits::allocator >
             , opt::memory_model< memory_model >
             , opt::free_list< typename traits::free_list >
             , opt::allocator< typename traits::allocator >
             , opt::memory_model< memory_model >
             , opt::free_list< typename traits::free_list >
@@ -491,7 +491,7 @@ namespace cds { namespace intrusive {
         bool erase_with( Q const& key, Less pred )
         {
             CDS_UNUSED( pred );
         bool erase_with( Q const& key, Less pred )
         {
             CDS_UNUSED( pred );
-            return erase_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>() );
+            return erase_( key, typename ordered_list_adapter::template make_compare_from_less<Less>() );
         }
 
         /// Deletes the item from the set
         }
 
         /// Deletes the item from the set
@@ -531,7 +531,7 @@ namespace cds { namespace intrusive {
         bool erase_with( Q const& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
         bool erase_with( Q const& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
-            return erase_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>(), f );
+            return erase_( key, typename ordered_list_adapter::template make_compare_from_less<Less>(), f );
         }
 
         /// Extracts an item from the set
         }
 
         /// Extracts an item from the set
@@ -639,14 +639,14 @@ namespace cds { namespace intrusive {
         bool find_with( Q& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
         bool find_with( Q& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
-            return find_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>(), f );
+            return find_( key, typename ordered_list_adapter::template make_compare_from_less<Less>(), f );
         }
         //@cond
         template <typename Q, typename Less, typename Func>
         bool find_with( Q const& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
         }
         //@cond
         template <typename Q, typename Less, typename Func>
         bool find_with( Q const& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
-            return find_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>(), f );
+            return find_( key, typename ordered_list_adapter::template make_compare_from_less<Less>(), f );
         }
         //@endcond
 
         }
         //@endcond
 
@@ -683,7 +683,7 @@ namespace cds { namespace intrusive {
         bool contains( Q const& key, Less pred )
         {
             CDS_UNUSED( pred );
         bool contains( Q const& key, Less pred )
         {
             CDS_UNUSED( pred );
-            return find_value( key, typename wrapped_ordered_list::template make_compare_from_less<Less>() );
+            return find_value( key, typename ordered_list_adapter::template make_compare_from_less<Less>() );
         }
         //@cond
         template <typename Q, typename Less>
         }
         //@cond
         template <typename Q, typename Less>
@@ -741,7 +741,7 @@ namespace cds { namespace intrusive {
         raw_ptr get_with( Q const& key, Less pred )
         {
             CDS_UNUSED( pred );
         raw_ptr get_with( Q const& key, Less pred )
         {
             CDS_UNUSED( pred );
-            return get_( key, typename wrapped_ordered_list::template make_compare_from_less<Less>());
+            return get_( key, typename ordered_list_adapter::template make_compare_from_less<Less>());
         }
 
 
         }
 
 
@@ -1061,7 +1061,7 @@ namespace cds { namespace intrusive {
         value_type * extract_with_( Q const& val, Less pred )
         {
             CDS_UNUSED( pred );
         value_type * extract_with_( Q const& val, Less pred )
         {
             CDS_UNUSED( pred );
-            return extract_( val, typename wrapped_ordered_list::template make_compare_from_less<Less>());
+            return extract_( val, typename ordered_list_adapter::template make_compare_from_less<Less>());
         }
 
         template <typename Q, typename Compare>
         }
 
         template <typename Q, typename Compare>
index 912c132cb7590a221fb98c2d44a2495e500eb90a..3acac78ec254d48648803f076cc23763cd851bf0 100644 (file)
@@ -29,6 +29,8 @@
   <ItemGroup>\r
     <ClCompile Include="..\..\..\test\unit\intrusive-set\intrusive_michael_iterable_dhp.cpp" />\r
     <ClCompile Include="..\..\..\test\unit\intrusive-set\intrusive_michael_iterable_hp.cpp" />\r
   <ItemGroup>\r
     <ClCompile Include="..\..\..\test\unit\intrusive-set\intrusive_michael_iterable_dhp.cpp" />\r
     <ClCompile Include="..\..\..\test\unit\intrusive-set\intrusive_michael_iterable_hp.cpp" />\r
+    <ClCompile Include="..\..\..\test\unit\intrusive-set\intrusive_split_iterable_dhp.cpp" />\r
+    <ClCompile Include="..\..\..\test\unit\intrusive-set\intrusive_split_iterable_hp.cpp" />\r
     <ClCompile Include="..\..\..\test\unit\main.cpp" />\r
     <ClCompile Include="..\..\..\test\unit\intrusive-set\intrusive_feldman_hashset_dhp.cpp" />\r
     <ClCompile Include="..\..\..\test\unit\intrusive-set\intrusive_feldman_hashset_hp.cpp" />\r
     <ClCompile Include="..\..\..\test\unit\main.cpp" />\r
     <ClCompile Include="..\..\..\test\unit\intrusive-set\intrusive_feldman_hashset_dhp.cpp" />\r
     <ClCompile Include="..\..\..\test\unit\intrusive-set\intrusive_feldman_hashset_hp.cpp" />\r
     <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_set_nogc.h" />\r
     <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_set_rcu.h" />\r
     <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_skiplist_rcu.h" />\r
     <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_set_nogc.h" />\r
     <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_set_rcu.h" />\r
     <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_skiplist_rcu.h" />\r
+    <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_split_iterable_set.h" />\r
+    <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_split_iterable_set_hp.h" />\r
     <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_split_lazy_rcu.h" />\r
     <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_split_michael_rcu.h" />\r
     <ClInclude Include="..\..\..\test\unit\intrusive-set\test_michael_michael_rcu.h" />\r
     <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_split_lazy_rcu.h" />\r
     <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_split_michael_rcu.h" />\r
     <ClInclude Include="..\..\..\test\unit\intrusive-set\test_michael_michael_rcu.h" />\r
index 7270896768393f7db41f5b2698b26dfeee64454f..fee9f33978325f75e46dabe04d80b345fdad3b5b 100644 (file)
     <ClCompile Include="..\..\..\test\unit\intrusive-set\intrusive_michael_iterable_dhp.cpp">\r
       <Filter>Source Files\MichaelSet</Filter>\r
     </ClCompile>\r
     <ClCompile Include="..\..\..\test\unit\intrusive-set\intrusive_michael_iterable_dhp.cpp">\r
       <Filter>Source Files\MichaelSet</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="..\..\..\test\unit\intrusive-set\intrusive_split_iterable_hp.cpp">\r
+      <Filter>Source Files\SplitList</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\test\unit\intrusive-set\intrusive_split_iterable_dhp.cpp">\r
+      <Filter>Source Files\SplitList</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_set.h">\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_set.h">\r
     <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_michael_iterable_hp.h">\r
       <Filter>Header Files</Filter>\r
     </ClInclude>\r
     <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_michael_iterable_hp.h">\r
       <Filter>Header Files</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_split_iterable_set.h">\r
+      <Filter>Header Files</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\..\test\unit\intrusive-set\test_intrusive_split_iterable_set_hp.h">\r
+      <Filter>Header Files</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
   </ItemGroup>\r
 </Project>
\ No newline at end of file
index f8c0094c27d5c626d9b62556b7e0952b69ec1a44..6f038379ee3967cb7de586452a4be87df8bea863 100644 (file)
@@ -37,6 +37,8 @@ set(CDSGTEST_SET_SOURCES
     intrusive_skiplist_rcu_gpt.cpp
     intrusive_skiplist_rcu_shb.cpp
     intrusive_skiplist_rcu_sht.cpp
     intrusive_skiplist_rcu_gpt.cpp
     intrusive_skiplist_rcu_shb.cpp
     intrusive_skiplist_rcu_sht.cpp
+    intrusive_split_iterable_hp.cpp
+    intrusive_split_iterable_dhp.cpp
     intrusive_split_lazy_hp.cpp
     intrusive_split_lazy_dhp.cpp
     intrusive_split_lazy_nogc.cpp
     intrusive_split_lazy_hp.cpp
     intrusive_split_lazy_dhp.cpp
     intrusive_split_lazy_nogc.cpp
diff --git a/test/unit/intrusive-set/intrusive_split_iterable_dhp.cpp b/test/unit/intrusive-set/intrusive_split_iterable_dhp.cpp
new file mode 100644 (file)
index 0000000..fe01762
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+    This file is a part of libcds - Concurrent Data Structures library
+
+    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
+
+    Source code repo: http://github.com/khizmax/libcds/
+    Download: http://sourceforge.net/projects/libcds/files/
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this
+      list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "test_intrusive_split_iterable_set_hp.h"
+
+#include <cds/intrusive/iterable_list_dhp.h>
+#include <cds/intrusive/split_list.h>
+#include <cds/intrusive/free_list.h>
+
+namespace {
+    namespace ci = cds::intrusive;
+    typedef cds::gc::DHP gc_type;
+
+    class IntrusiveSplitListIterableSet_DHP : public cds_test::intrusive_split_iterable_set_hp
+    {
+    protected:
+        typedef cds_test::intrusive_split_iterable_set_hp base_class;
+        typedef base_class::item_type< ci::split_list::node<void>> item_type;
+
+    protected:
+        void SetUp()
+        {
+            struct list_traits : public ci::iterable_list::traits
+            {};
+            typedef ci::IterableList< gc_type, item_type, list_traits > list_type;
+            typedef ci::SplitListSet< gc_type, list_type >   set_type;
+
+            cds::gc::dhp::GarbageCollector::Construct( 16, set_type::c_nHazardPtrCount );
+            cds::threading::Manager::attachThread();
+        }
+
+        void TearDown()
+        {
+            cds::threading::Manager::detachThread();
+            cds::gc::hp::GarbageCollector::Destruct();
+        }
+    };
+
+
+    TEST_F( IntrusiveSplitListIterableSet_DHP, cmp )
+    {
+        typedef ci::IterableList< gc_type
+            , item_type
+            ,ci::iterable_list::make_traits<
+                ci::opt::compare< cmp<item_type> >
+                ,ci::opt::disposer< mock_disposer >
+            >::type
+        > bucket_type;
+
+        typedef ci::SplitListSet< gc_type, bucket_type,
+            ci::split_list::make_traits<
+                ci::opt::hash< hash_int >
+            >::type
+        > set_type;
+
+        set_type s( kSize, 2 );
+        test( s );
+    }
+
+    TEST_F( IntrusiveSplitListIterableSet_DHP, less )
+    {
+        typedef ci::IterableList< gc_type
+            , item_type
+            ,ci::iterable_list::make_traits<
+                ci::opt::less< less<item_type> >
+                ,ci::opt::disposer< mock_disposer >
+            >::type
+        > bucket_type;
+
+        typedef ci::SplitListSet< gc_type, bucket_type,
+            ci::split_list::make_traits<
+                ci::opt::hash< hash_int >
+            >::type
+        > set_type;
+
+        set_type s( kSize, 2 );
+        test( s );
+    }
+
+    TEST_F( IntrusiveSplitListIterableSet_DHP, cmpmix )
+    {
+        struct list_traits : public ci::iterable_list::traits
+        {
+            typedef base_class::less<item_type> less;
+            typedef cmp<item_type> compare;
+            typedef mock_disposer disposer;
+        };
+        typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type;
+
+        struct set_traits : public ci::split_list::traits
+        {
+            typedef hash_int hash;
+            typedef simple_item_counter item_counter;
+        };
+        typedef ci::SplitListSet< gc_type, bucket_type, set_traits > set_type;
+
+        set_type s( kSize, 2 );
+        test( s );
+    }
+
+    TEST_F( IntrusiveSplitListIterableSet_DHP, free_list )
+    {
+        struct list_traits: public ci::iterable_list::traits
+        {
+            typedef base_class::less<item_type> less;
+            typedef cmp<item_type> compare;
+            typedef mock_disposer disposer;
+        };
+        typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type;
+
+        struct set_traits: public ci::split_list::traits
+        {
+            typedef hash_int hash;
+            typedef ci::FreeList  free_list;
+        };
+        typedef ci::SplitListSet< gc_type, bucket_type, set_traits > set_type;
+
+        set_type s( kSize, 2 );
+        test( s );
+    }
+
+    TEST_F( IntrusiveSplitListIterableSet_DHP, static_bucket_table )
+    {
+        struct list_traits: public ci::iterable_list::traits
+        {
+            typedef base_class::less<item_type> less;
+            typedef cmp<item_type> compare;
+            typedef mock_disposer disposer;
+        };
+        typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type;
+
+        struct set_traits: public ci::split_list::traits
+        {
+            typedef hash_int hash;
+            enum {
+                dynamic_bucket_table = false
+            };
+        };
+        typedef ci::SplitListSet< gc_type, bucket_type, set_traits > set_type;
+
+        set_type s( kSize, 2 );
+        test( s );
+    }
+
+    TEST_F( IntrusiveSplitListIterableSet_DHP, static_bucket_table_free_list )
+    {
+        struct list_traits: public ci::iterable_list::traits
+        {
+            typedef base_class::less<item_type> less;
+            typedef cmp<item_type> compare;
+            typedef mock_disposer disposer;
+        };
+        typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type;
+
+        struct set_traits: public ci::split_list::traits
+        {
+            typedef hash_int hash;
+            enum {
+                dynamic_bucket_table = false
+            };
+            typedef ci::FreeList free_list;
+        };
+        typedef ci::SplitListSet< gc_type, bucket_type, set_traits > set_type;
+
+        set_type s( kSize, 2 );
+        test( s );
+    }
+
+    TEST_F( IntrusiveSplitListIterableSet_DHP, list_stat )
+    {
+        struct list_traits: public ci::iterable_list::traits
+        {
+            typedef base_class::less<item_type> less;
+            typedef cmp<item_type> compare;
+            typedef mock_disposer disposer;
+            typedef ci::iterable_list::stat<> stat;
+        };
+        typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type;
+
+        struct set_traits: public ci::split_list::traits
+        {
+            typedef hash_int hash;
+            typedef simple_item_counter item_counter;
+        };
+        typedef ci::SplitListSet< gc_type, bucket_type, set_traits > set_type;
+
+        set_type s( kSize, 2 );
+        test( s );
+        EXPECT_GE( s.list_statistics().m_nInsertSuccess, 0u );
+    }
+
+    TEST_F( IntrusiveSplitListIterableSet_DHP, stat )
+    {
+        struct list_traits: public ci::iterable_list::traits
+        {
+            typedef base_class::less<item_type> less;
+            typedef cmp<item_type> compare;
+            typedef mock_disposer disposer;
+        };
+        typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type;
+
+        struct set_traits: public ci::split_list::traits
+        {
+            typedef hash_int hash;
+            typedef simple_item_counter item_counter;
+            typedef ci::split_list::stat<> stat;
+        };
+        typedef ci::SplitListSet< gc_type, bucket_type, set_traits > set_type;
+
+        set_type s( kSize, 2 );
+        test( s );
+        EXPECT_GE( s.statistics().m_nInsertSuccess, 0u );
+    }
+
+
+} // namespace
diff --git a/test/unit/intrusive-set/intrusive_split_iterable_hp.cpp b/test/unit/intrusive-set/intrusive_split_iterable_hp.cpp
new file mode 100644 (file)
index 0000000..b99e811
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+    This file is a part of libcds - Concurrent Data Structures library
+
+    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
+
+    Source code repo: http://github.com/khizmax/libcds/
+    Download: http://sourceforge.net/projects/libcds/files/
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this
+      list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "test_intrusive_split_iterable_set_hp.h"
+
+#include <cds/intrusive/iterable_list_hp.h>
+#include <cds/intrusive/split_list.h>
+#include <cds/intrusive/free_list.h>
+
+namespace {
+    namespace ci = cds::intrusive;
+    typedef cds::gc::HP gc_type;
+
+    class IntrusiveSplitListIterableSet_HP : public cds_test::intrusive_split_iterable_set_hp
+    {
+    protected:
+        typedef cds_test::intrusive_split_iterable_set_hp base_class;
+        typedef base_class::item_type< ci::split_list::node<void>> item_type;
+
+    protected:
+        void SetUp()
+        {
+            struct list_traits : public ci::iterable_list::traits
+            {};
+            typedef ci::IterableList< gc_type, item_type, list_traits > list_type;
+            typedef ci::SplitListSet< gc_type, list_type >   set_type;
+
+            // +3 - for iterators
+            cds::gc::hp::GarbageCollector::Construct( set_type::c_nHazardPtrCount + 3, 1, 16 );
+            cds::threading::Manager::attachThread();
+        }
+
+        void TearDown()
+        {
+            cds::threading::Manager::detachThread();
+            cds::gc::hp::GarbageCollector::Destruct( true );
+        }
+    };
+
+
+    TEST_F( IntrusiveSplitListIterableSet_HP, cmp )
+    {
+        typedef ci::IterableList< gc_type
+            , item_type
+            ,ci::iterable_list::make_traits<
+                ci::opt::compare< cmp<item_type> >
+                ,ci::opt::disposer< mock_disposer >
+            >::type
+        > bucket_type;
+
+        typedef ci::SplitListSet< gc_type, bucket_type,
+            ci::split_list::make_traits<
+                ci::opt::hash< hash_int >
+            >::type
+        > set_type;
+
+        set_type s( kSize, 2 );
+        test( s );
+    }
+
+    TEST_F( IntrusiveSplitListIterableSet_HP, less )
+    {
+        typedef ci::IterableList< gc_type
+            , item_type
+            ,ci::iterable_list::make_traits<
+                ci::opt::less< less<item_type> >
+                ,ci::opt::disposer< mock_disposer >
+            >::type
+        > bucket_type;
+
+        typedef ci::SplitListSet< gc_type, bucket_type,
+            ci::split_list::make_traits<
+                ci::opt::hash< hash_int >
+            >::type
+        > set_type;
+
+        set_type s( kSize, 2 );
+        test( s );
+    }
+
+    TEST_F( IntrusiveSplitListIterableSet_HP, cmpmix )
+    {
+        struct list_traits : public ci::iterable_list::traits
+        {
+            typedef base_class::less<item_type> less;
+            typedef cmp<item_type> compare;
+            typedef mock_disposer disposer;
+        };
+        typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type;
+
+        struct set_traits : public ci::split_list::traits
+        {
+            typedef hash_int hash;
+            typedef simple_item_counter item_counter;
+        };
+        typedef ci::SplitListSet< gc_type, bucket_type, set_traits > set_type;
+
+        set_type s( kSize, 2 );
+        test( s );
+    }
+
+    TEST_F( IntrusiveSplitListIterableSet_HP, free_list )
+    {
+        struct list_traits: public ci::iterable_list::traits
+        {
+            typedef base_class::less<item_type> less;
+            typedef cmp<item_type> compare;
+            typedef mock_disposer disposer;
+        };
+        typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type;
+
+        struct set_traits: public ci::split_list::traits
+        {
+            typedef hash_int hash;
+            typedef ci::FreeList  free_list;
+        };
+        typedef ci::SplitListSet< gc_type, bucket_type, set_traits > set_type;
+
+        set_type s( kSize, 2 );
+        test( s );
+    }
+
+    TEST_F( IntrusiveSplitListIterableSet_HP, static_bucket_table )
+    {
+        struct list_traits: public ci::iterable_list::traits
+        {
+            typedef base_class::less<item_type> less;
+            typedef cmp<item_type> compare;
+            typedef mock_disposer disposer;
+        };
+        typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type;
+
+        struct set_traits: public ci::split_list::traits
+        {
+            typedef hash_int hash;
+            enum {
+                dynamic_bucket_table = false
+            };
+        };
+        typedef ci::SplitListSet< gc_type, bucket_type, set_traits > set_type;
+
+        set_type s( kSize, 2 );
+        test( s );
+    }
+
+    TEST_F( IntrusiveSplitListIterableSet_HP, static_bucket_table_free_list )
+    {
+        struct list_traits: public ci::iterable_list::traits
+        {
+            typedef base_class::less<item_type> less;
+            typedef cmp<item_type> compare;
+            typedef mock_disposer disposer;
+        };
+        typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type;
+
+        struct set_traits: public ci::split_list::traits
+        {
+            typedef hash_int hash;
+            enum {
+                dynamic_bucket_table = false
+            };
+            typedef ci::FreeList free_list;
+        };
+        typedef ci::SplitListSet< gc_type, bucket_type, set_traits > set_type;
+
+        set_type s( kSize, 2 );
+        test( s );
+    }
+
+    TEST_F( IntrusiveSplitListIterableSet_HP, list_stat )
+    {
+        struct list_traits: public ci::iterable_list::traits
+        {
+            typedef base_class::less<item_type> less;
+            typedef cmp<item_type> compare;
+            typedef mock_disposer disposer;
+            typedef ci::iterable_list::stat<> stat;
+        };
+        typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type;
+
+        struct set_traits: public ci::split_list::traits
+        {
+            typedef hash_int hash;
+            typedef simple_item_counter item_counter;
+        };
+        typedef ci::SplitListSet< gc_type, bucket_type, set_traits > set_type;
+
+        set_type s( kSize, 2 );
+        test( s );
+        EXPECT_GE( s.list_statistics().m_nInsertSuccess, 0u );
+    }
+
+    TEST_F( IntrusiveSplitListIterableSet_HP, stat )
+    {
+        struct list_traits: public ci::iterable_list::traits
+        {
+            typedef base_class::less<item_type> less;
+            typedef cmp<item_type> compare;
+            typedef mock_disposer disposer;
+        };
+        typedef ci::IterableList< gc_type, item_type, list_traits > bucket_type;
+
+        struct set_traits: public ci::split_list::traits
+        {
+            typedef hash_int hash;
+            typedef simple_item_counter item_counter;
+            typedef ci::split_list::stat<> stat;
+        };
+        typedef ci::SplitListSet< gc_type, bucket_type, set_traits > set_type;
+
+        set_type s( kSize, 2 );
+        test( s );
+        EXPECT_GE( s.statistics().m_nInsertSuccess, 0u );
+    }
+
+
+} // namespace
diff --git a/test/unit/intrusive-set/test_intrusive_split_iterable_set.h b/test/unit/intrusive-set/test_intrusive_split_iterable_set.h
new file mode 100644 (file)
index 0000000..28e03ef
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+    This file is a part of libcds - Concurrent Data Structures library
+
+    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
+
+    Source code repo: http://github.com/khizmax/libcds/
+    Download: http://sourceforge.net/projects/libcds/files/
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this
+      list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef CDSUNIT_SET_TEST_INTRUSIVE_SPLIT_ITERABLE_SET_H
+#define CDSUNIT_SET_TEST_INTRUSIVE_SPLIT_ITERABLE_SET_H
+
+#include <cds_test/check_size.h>
+#include <cds_test/fixture.h>
+
+#include <cds/opt/hash.h>
+#include <functional>   // ref
+
+// forward declaration
+namespace cds { namespace intrusive {}}
+
+namespace cds_test {
+
+    namespace ci = cds::intrusive;
+    namespace co = cds::opt;
+
+    class intrusive_split_iterable_set: public fixture
+    {
+    public:
+        static size_t const kSize = 100;
+
+        struct stat
+        {
+            unsigned int nDisposeCount  ;   // count of disposer calling
+            unsigned int nFindCount     ;   // count of find-functor calling
+            unsigned int nUpdateNewCount;
+            unsigned int nUpdateCount;
+            mutable unsigned int nEraseCount;
+
+            stat()
+            {
+                clear_stat();
+            }
+
+            void clear_stat()
+            {
+                memset( this, 0, sizeof( *this ) );
+            }
+        };
+
+        template <typename Base>
+        struct item_type: public stat, public Base
+        {
+            int nKey;
+            int nVal;
+
+            item_type( int k )
+                : nKey(k)
+                , nVal(0)
+            {}
+
+            int key() const 
+            { 
+                return nKey; 
+            }
+        };
+
+        struct hash_int {
+            size_t operator()( int i ) const
+            {
+                return co::v::hash<int>()( i );
+            }
+            template <typename Q>
+            size_t operator()( Q const& i ) const
+            {
+                return (*this)( i.key());
+            }
+        };
+
+        struct simple_item_counter {
+            size_t  m_nCount;
+
+            simple_item_counter()
+                : m_nCount(0)
+            {}
+
+            size_t operator ++()
+            {
+                return ++m_nCount;
+            }
+
+            size_t operator --()
+            {
+                return --m_nCount;
+            }
+
+            void reset()
+            {
+                m_nCount = 0;
+            }
+
+            operator size_t() const
+            {
+                return m_nCount;
+            }
+
+        };
+
+
+        template <typename T>
+        struct less
+        {
+            bool operator ()(const T& v1, const T& v2 ) const
+            {
+                return v1.key() < v2.key();
+            }
+
+            template <typename Q>
+            bool operator ()(const T& v1, const Q& v2 ) const
+            {
+                return v1.key() < v2;
+            }
+
+            template <typename Q>
+            bool operator ()(const Q& v1, const T& v2 ) const
+            {
+                return v1 < v2.key();
+            }
+        };
+
+        template <typename T>
+        struct cmp {
+            int operator ()(const T& v1, const T& v2 ) const
+            {
+                if ( v1.key() < v2.key() )
+                    return -1;
+                return v1.key() > v2.key() ? 1 : 0;
+            }
+
+            template <typename Q>
+            int operator ()(const T& v1, const Q& v2 ) const
+            {
+                if ( v1.key() < v2 )
+                    return -1;
+                return v1.key() > v2 ? 1 : 0;
+            }
+
+            template <typename Q>
+            int operator ()(const Q& v1, const T& v2 ) const
+            {
+                if ( v1 < v2.key() )
+                    return -1;
+                return v1 > v2.key() ? 1 : 0;
+            }
+        };
+
+        struct other_item {
+            int nKey;
+
+            explicit other_item( int k )
+                : nKey( k )
+            {}
+
+            int key() const
+            {
+                return nKey;
+            }
+        };
+
+        struct other_less {
+            template <typename Q, typename T>
+            bool operator()( Q const& lhs, T const& rhs ) const
+            {
+                return lhs.key() < rhs.key();
+            }
+        };
+
+        struct mock_disposer
+        {
+            template <typename T>
+            void operator ()( T * p )
+            {
+                ++p->nDisposeCount;
+            }
+        };
+
+    protected:
+        template <class Set>
+        void test( Set& s )
+        {
+            // Precondition: set is empty
+            // Postcondition: set is empty
+
+            ASSERT_TRUE( s.empty() );
+            ASSERT_CONTAINER_SIZE( s, 0 );
+            size_t const nSetSize = kSize;
+
+            typedef typename Set::value_type value_type;
+
+            std::vector< value_type > data;
+            std::vector< size_t> indices;
+            data.reserve( kSize );
+            indices.reserve( kSize );
+            for ( size_t key = 0; key < kSize; ++key ) {
+                data.push_back( value_type( static_cast<int>( key )));
+                indices.push_back( key );
+            }
+            shuffle( indices.begin(), indices.end() );
+
+            // insert/find
+            for ( auto idx : indices ) {
+                auto& i = data[ idx ];
+
+                ASSERT_FALSE( s.contains( i.nKey ));
+                ASSERT_FALSE( s.contains( i ));
+                ASSERT_FALSE( s.contains( other_item( i.key()), other_less()));
+                ASSERT_FALSE( s.find( i.nKey, []( value_type&, int ) {} ));
+                ASSERT_FALSE( s.find_with( other_item( i.key()), other_less(), []( value_type&, other_item const& ) {} ));
+                ASSERT_TRUE( s.find( i.nKey ) == s.end());
+                ASSERT_TRUE( s.find_with( other_item( i.key() ), other_less()) == s.end());
+
+                std::pair<bool, bool> updResult;
+
+                updResult = s.update( i, []( value_type&, value_type* )
+                {
+                    ASSERT_TRUE( false );
+                }, false );
+                EXPECT_FALSE( updResult.first );
+                EXPECT_FALSE( updResult.second );
+
+                updResult = s.upsert( i, false );
+                EXPECT_FALSE( updResult.first );
+                EXPECT_FALSE( updResult.second );
+
+                switch ( i.key() % 4 ) {
+                case 0:
+                    ASSERT_TRUE( s.insert( i ));
+                    ASSERT_FALSE( s.insert( i ));
+                    updResult = s.update( i, []( value_type& val, value_type* arg) 
+                        {
+                            ASSERT_TRUE( arg != nullptr );
+                            EXPECT_EQ( val.key(), arg->key() );
+                        }, false );
+                    EXPECT_TRUE( updResult.first );
+                    EXPECT_FALSE( updResult.second );
+                    break;
+                case 1:
+                    EXPECT_EQ( i.nUpdateNewCount, 0u );
+                    ASSERT_TRUE( s.insert( i, []( value_type& v ) { ++v.nUpdateNewCount;} ));
+                    EXPECT_EQ( i.nUpdateNewCount, 1u );
+                    ASSERT_FALSE( s.insert( i, []( value_type& v ) { ++v.nUpdateNewCount;} ) );
+                    EXPECT_EQ( i.nUpdateNewCount, 1u );
+                    i.nUpdateNewCount = 0;
+                    break;
+                case 2:
+                    updResult = s.update( i, []( value_type& /*val*/, value_type* arg )
+                    {
+                        EXPECT_TRUE( arg == nullptr );
+                    });
+                    EXPECT_TRUE( updResult.first );
+                    EXPECT_TRUE( updResult.second );
+                    break;
+                case 3:
+                    updResult = s.upsert( i );
+                    EXPECT_TRUE( updResult.first );
+                    EXPECT_TRUE( updResult.second );
+                    break;
+                }
+
+                ASSERT_TRUE( s.contains( i.nKey ) );
+                ASSERT_TRUE( s.contains( i ) );
+                ASSERT_TRUE( s.contains( other_item( i.key() ), other_less()));
+                EXPECT_EQ( i.nFindCount, 0u );
+                ASSERT_TRUE( s.find( i.nKey, []( value_type& v, int ) { ++v.nFindCount; } ));
+                EXPECT_EQ( i.nFindCount, 1u );
+                ASSERT_TRUE( s.find_with( other_item( i.key() ), other_less(), []( value_type& v, other_item const& ) { ++v.nFindCount; } ));
+                EXPECT_EQ( i.nFindCount, 2u );
+                ASSERT_TRUE( s.find( i.nKey ) != s.end() );
+                ASSERT_TRUE( s.find_with( other_item( i.key() ), other_less() ) != s.end() );
+                EXPECT_EQ( s.find( i.nKey )->nKey, i.key() );
+                EXPECT_EQ( s.find_with( other_item( i.key() ), other_less())->nKey, i.key() );
+            }
+            ASSERT_FALSE( s.empty() );
+            ASSERT_CONTAINER_SIZE( s, nSetSize );
+
+            std::for_each( data.begin(), data.end(), []( value_type& v ) { v.clear_stat(); });
+
+            // erase
+            shuffle( indices.begin(), indices.end() );
+            for ( auto idx : indices ) {
+                auto& i = data[ idx ];
+
+                ASSERT_TRUE( s.contains( i.nKey ) );
+                ASSERT_TRUE( s.contains( i ) );
+                ASSERT_TRUE( s.contains( other_item( i.key() ), other_less() ) );
+                EXPECT_EQ( i.nFindCount, 0u );
+                ASSERT_TRUE( s.find( i.nKey, []( value_type& v, int ) { ++v.nFindCount; } ) );
+                EXPECT_EQ( i.nFindCount, 1u );
+                ASSERT_TRUE( s.find_with( other_item( i.key() ), other_less(), []( value_type& v, other_item const& ) { ++v.nFindCount; } ) );
+                EXPECT_EQ( i.nFindCount, 2u );
+                ASSERT_TRUE( s.find( i.nKey ) != s.end() );
+                ASSERT_TRUE( s.find_with( other_item( i.key() ), other_less()) != s.end() );
+                EXPECT_EQ( s.find( i.nKey )->nKey, i.key() );
+                EXPECT_EQ( s.find_with( other_item( i.key() ), other_less())->nKey, i.key() );
+
+
+                value_type v( i );
+                switch ( i.key() % 6 ) {
+                case 0:
+                    ASSERT_FALSE( s.unlink( v ));
+                    ASSERT_TRUE( s.unlink( i ));
+                    ASSERT_FALSE( s.unlink( i ) );
+                    break;
+                case 1:
+                    ASSERT_TRUE( s.erase( i.key()));
+                    ASSERT_FALSE( s.erase( i.key() ) );
+                    break;
+                case 2:
+                    ASSERT_TRUE( s.erase( v ));
+                    ASSERT_FALSE( s.erase( v ) );
+                    break;
+                case 3:
+                    ASSERT_TRUE( s.erase_with( other_item( i.key()), other_less()));
+                    ASSERT_FALSE( s.erase_with( other_item( i.key() ), other_less() ) );
+                    break;
+                case 4:
+                    EXPECT_EQ( i.nEraseCount, 0u );
+                    ASSERT_TRUE( s.erase( v, []( value_type& val ) { ++val.nEraseCount; } ));
+                    EXPECT_EQ( i.nEraseCount, 1u );
+                    ASSERT_FALSE( s.erase( v, []( value_type& val ) { ++val.nEraseCount; } ));
+                    EXPECT_EQ( i.nEraseCount, 1u );
+                    break;
+                case 5:
+                    EXPECT_EQ( i.nEraseCount, 0u );
+                    ASSERT_TRUE( s.erase_with( other_item( i.key() ), other_less(), []( value_type& val ) { ++val.nEraseCount; } ));
+                    EXPECT_EQ( i.nEraseCount, 1u );
+                    ASSERT_FALSE( s.erase_with( other_item( i.key() ), other_less(), []( value_type& val ) { ++val.nEraseCount; } ));
+                    EXPECT_EQ( i.nEraseCount, 1u );
+                    break;
+                }
+
+                ASSERT_FALSE( s.contains( i.nKey ));
+                ASSERT_FALSE( s.contains( i ));
+                ASSERT_FALSE( s.contains( other_item( i.key()), other_less()));
+                ASSERT_FALSE( s.find( i.nKey, []( value_type&, int ) {} ));
+                ASSERT_FALSE( s.find_with( other_item( i.key()), other_less(), []( value_type&, other_item const& ) {} ));
+                ASSERT_TRUE( s.find( i.nKey ) == s.end() );
+                ASSERT_TRUE( s.find_with( other_item( i.key() ), other_less() ) == s.end() );
+            }
+            ASSERT_TRUE( s.empty() );
+            ASSERT_CONTAINER_SIZE( s, 0u );
+
+            // Force retiring cycle
+            Set::gc::force_dispose();
+            for ( auto& i : data ) {
+                EXPECT_EQ( i.nDisposeCount, 1u );
+            }
+
+            // clear
+            for ( auto& i : data ) {
+                i.clear_stat();
+                ASSERT_TRUE( s.insert( i ));
+            }
+            ASSERT_FALSE( s.empty() );
+            ASSERT_CONTAINER_SIZE( s, nSetSize );
+
+            // Iterator test
+            for ( auto it = s.begin(); it != s.end(); ++it ) {
+                ++it->nFindCount;
+            }
+            for ( auto it = s.cbegin(); it != s.cend(); ++it ) {
+                EXPECT_EQ( it->nFindCount, 1u );
+            }
+            for ( auto& i : data ) {
+                EXPECT_EQ( i.nFindCount, 1u );
+            }
+
+            // clear test
+            s.clear();
+
+            ASSERT_TRUE( s.empty());
+            ASSERT_CONTAINER_SIZE( s, 0u );
+            ASSERT_TRUE( s.begin() == s.end() );
+            ASSERT_TRUE( s.cbegin() == s.cend() );
+
+            // Force retiring cycle
+            Set::gc::force_dispose();
+            for ( auto& i : data ) {
+                EXPECT_EQ( i.nDisposeCount, 1u );
+            }
+
+        }
+    };
+
+} // namespace cds_test
+
+#endif // #ifndef CDSUNIT_SET_TEST_INTRUSIVE_SPLIT_ITERABLE_SET_H
diff --git a/test/unit/intrusive-set/test_intrusive_split_iterable_set_hp.h b/test/unit/intrusive-set/test_intrusive_split_iterable_set_hp.h
new file mode 100644 (file)
index 0000000..05a5b7c
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+    This file is a part of libcds - Concurrent Data Structures library
+
+    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
+
+    Source code repo: http://github.com/khizmax/libcds/
+    Download: http://sourceforge.net/projects/libcds/files/
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this
+      list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef CDSUNIT_SET_TEST_INTRUSIVE_SPLIT_ITERABLE_SET_HP_H
+#define CDSUNIT_SET_TEST_INTRUSIVE_SPLIT_ITERABLE_SET_HP_H
+
+#include "test_intrusive_split_iterable_set.h"
+
+// forward declaration
+namespace cds { namespace intrusive {}}
+
+namespace cds_test {
+
+    namespace ci = cds::intrusive;
+    namespace co = cds::opt;
+
+    class intrusive_split_iterable_set_hp: public intrusive_split_iterable_set
+    {
+        typedef intrusive_split_iterable_set base_class;
+
+    protected:
+
+        template <class Set>
+        void test( Set& s )
+        {
+            // Precondition: set is empty
+            // Postcondition: set is empty
+
+            base_class::test( s );
+
+            ASSERT_TRUE( s.empty() );
+            ASSERT_CONTAINER_SIZE( s, 0 );
+
+            typedef typename Set::value_type value_type;
+
+            std::vector< value_type > data;
+            std::vector< size_t> indices;
+            data.reserve( kSize );
+            indices.reserve( kSize );
+            for ( size_t key = 0; key < kSize; ++key ) {
+                data.push_back( value_type( static_cast<int>(key) ) );
+                indices.push_back( key );
+            }
+            shuffle( indices.begin(), indices.end() );
+
+            typename Set::guarded_ptr gp;
+
+            // get/extract from empty set
+            for ( auto idx : indices ) {
+                auto& i = data[idx];
+
+                gp = s.get( i );
+                ASSERT_TRUE( !gp );
+                gp = s.get( i.key() );
+                ASSERT_TRUE( !gp );
+                gp = s.get_with( other_item( i.key()), other_less());
+                ASSERT_TRUE( !gp );
+
+                gp = s.extract( i );
+                ASSERT_TRUE( !gp );
+                gp = s.extract( i.key());
+                ASSERT_TRUE( !gp );
+                gp = s.extract_with( other_item( i.key()), other_less());
+                ASSERT_TRUE( !gp );
+            }
+
+            // fill set
+            for ( auto& i : data ) {
+                i.nDisposeCount = 0;
+                ASSERT_TRUE( s.insert( i ) );
+            }
+
+            // get/extract
+            for ( auto idx : indices ) {
+                auto& i = data[idx];
+
+                EXPECT_EQ( i.nFindCount, 0u );
+                gp = s.get( i );
+                ASSERT_FALSE( !gp );
+                ++gp->nFindCount;
+                EXPECT_EQ( i.nFindCount, 1u );
+
+                gp = s.get( i.key() );
+                ASSERT_FALSE( !gp );
+                ++gp->nFindCount;
+                EXPECT_EQ( i.nFindCount, 2u );
+
+                gp = s.get_with( other_item( i.key()), other_less());
+                ASSERT_FALSE( !gp );
+                ++gp->nFindCount;
+                EXPECT_EQ( i.nFindCount, 3u );
+
+                EXPECT_EQ( i.nEraseCount, 0u );
+                switch ( i.key() % 3 ) {
+                case 0:
+                    gp = s.extract( i.key());
+                    break;
+                case 1:
+                    gp = s.extract( i );
+                    break;
+                case 2:
+                    gp = s.extract_with( other_item( i.key() ), other_less() );
+                    break;
+                }
+                ASSERT_FALSE( !gp );
+                ++gp->nEraseCount;
+                EXPECT_EQ( i.nEraseCount, 1u );
+
+                gp = s.extract( i );
+                ASSERT_TRUE( !gp );
+                gp = s.extract( i.key() );
+                ASSERT_TRUE( !gp );
+                gp = s.extract_with( other_item( i.key() ), other_less() );
+                ASSERT_TRUE( !gp );
+            }
+
+            gp.release();
+
+            ASSERT_TRUE( s.empty() );
+            ASSERT_CONTAINER_SIZE( s, 0 );
+
+            // Force retiring cycle
+            Set::gc::force_dispose();
+            for ( auto& i : data ) {
+                EXPECT_EQ( i.nDisposeCount, 1u );
+            }
+
+        }
+    };
+
+} // namespace cds_test
+
+#endif // #ifndef CDSUNIT_SET_TEST_INTRUSIVE_SPLIT_ITERABLE_SET_HP_H
index 4317e323e4891e382dafe654f65dff70f2fb1aec..498da03fd261ed4d50bf48d1105dc93a145996aa 100644 (file)
@@ -5,7 +5,7 @@
 
     Source code repo: http://github.com/khizmax/libcds/
     Download: http://sourceforge.net/projects/libcds/files/
 
     Source code repo: http://github.com/khizmax/libcds/
     Download: http://sourceforge.net/projects/libcds/files/
-    
+
     Redistribution and use in source and binary forms, with or without
     modification, are permitted provided that the following conditions are met:
 
     Redistribution and use in source and binary forms, with or without
     modification, are permitted provided that the following conditions are met: