Merge pull request #24 from krinkinmu/unordered-list-wip
authorMax Khizhinsky <libcds.dev@gmail.com>
Mon, 30 Mar 2015 11:34:35 +0000 (14:34 +0300)
committerMax Khizhinsky <libcds.dev@gmail.com>
Mon, 30 Mar 2015 11:34:35 +0000 (14:34 +0300)
Unordered nogc lazy list implementation.

20 files changed:
cds/container/details/lazy_list_base.h
cds/container/details/make_lazy_kvlist.h
cds/container/details/make_lazy_list.h
cds/container/lazy_kvlist_nogc.h
cds/container/lazy_list_nogc.h
cds/intrusive/details/lazy_list_base.h
cds/intrusive/lazy_list_nogc.h
cds/opt/compare.h
projects/source.test-hdr.mk
projects/source.test-hdr.offsetof.mk
tests/test-hdr/map/hdr_map.h
tests/test-hdr/map/hdr_michael_map_lazy_nogc.cpp
tests/test-hdr/set/hdr_michael_set_lazy_nogc.cpp
tests/test-hdr/set/hdr_set.h
tests/test-hdr/unordered_list/hdr_intrusive_lazy.h [new file with mode: 0644]
tests/test-hdr/unordered_list/hdr_intrusive_lazy_nogc.cpp [new file with mode: 0644]
tests/test-hdr/unordered_list/hdr_lazy.h [new file with mode: 0644]
tests/test-hdr/unordered_list/hdr_lazy_kv.h [new file with mode: 0644]
tests/test-hdr/unordered_list/hdr_lazy_kv_nogc.cpp [new file with mode: 0644]
tests/test-hdr/unordered_list/hdr_lazy_nogc.cpp [new file with mode: 0644]

index 74fede7bcbc82cd71340de53e4707a93a29afa42..ad0ad2a8068bb6c742089c75c7c41ddbd1225ce0 100644 (file)
@@ -34,6 +34,20 @@ namespace cds { namespace container {
             */
             typedef opt::none                       less;
 
+            /// Specifies binary functor used for comparing keys for equality
+            /**
+                No default functor is provided. If \p equal_to option is not spcified, \p compare is used, if \p compare is not
+                specified, \p less is used.
+            */
+            typedef opt::none                       equal_to;
+
+            /// Specifies list ordering policy.
+            /**
+                If \p sort is \p true, than list maintains items in sorted order, otherwise items are unordered. Default is \p true.
+                Note that if \p sort is \p falsem than lookup operations scan entire list.
+            */
+            static const bool sort = true;
+
             /// Lock type used to lock modifying items
             /**
                 Default is cds::sync::spin
@@ -76,6 +90,9 @@ namespace cds { namespace container {
             - \p opt::compare - key compare functor. No default functor is provided.
                 If the option is not specified, the \p opt::less is used.
             - \p opt::less - specifies binary predicate used for key compare. Default is \p std::less<T>.
+            - \p opt::equal_to - specifies binary functor for comparing keys for equality. No default is provided. If \p equal_to is
+                not specified, \p compare is used, if \p compare is not specified, \p less is used.
+            - \p opt::sort - specifies ordering policy. Default value is \p true.
             - \p opt::back_off - back-off strategy used. If the option is not specified, \p cds::backoff::Default is used.
             - \p opt::item_counter - the type of item counting feature. Default is disabled (\p atomicity::empty_item_counter).
                 To enable item counting use \p atomicity::item_counter.
index 5b0131824c16b24b21998735c36e78785c4abfc8..ff3ea15c2242507319ea0f1dc8cbc8df7bc3dba3 100644 (file)
@@ -64,17 +64,24 @@ namespace cds { namespace container {
             };
 
             typedef typename opt::details::make_comparator< key_type, original_type_traits >::type key_comparator;
+            typedef typename opt::details::make_equal_to< key_type, original_type_traits >::type equal_to_comparator;
 
             template <typename Less>
             struct less_wrapper {
                 typedef cds::details::compare_wrapper< node_type, cds::opt::details::make_comparator_from_less<Less>, key_field_accessor >    type;
             };
 
+            template <typename Equal>
+            struct equal_to_wrapper {
+                typedef cds::details::predicate_wrapper< node_type, Equal, key_field_accessor >    type;
+            };
+
             struct intrusive_traits: public original_type_traits
             {
                 typedef intrusive::lazy_list::base_hook< opt::gc<gc> >  hook;
                 typedef node_deallocator disposer;
                 typedef cds::details::compare_wrapper< node_type, key_comparator, key_field_accessor > compare;
+                typedef cds::details::predicate_wrapper< node_type, equal_to_comparator, key_field_accessor > equal_to;
                 static const opt::link_check_type link_checker = cds::intrusive::lazy_list::traits::link_checker;
             };
 
index ee853a61326e11d5baf145244f3b4d551f344beb..578e484c14fd6b7d97ebd02667c336c40c7e8256 100644 (file)
@@ -46,6 +46,7 @@ namespace cds { namespace container {
             };
 
             typedef typename opt::details::make_comparator< value_type, original_type_traits >::type key_comparator;
+            typedef typename opt::details::make_equal_to< value_type, original_type_traits >::type equal_to_comparator;
 
             struct value_accessor {
                 value_type const & operator()( node_type const & node ) const
@@ -59,6 +60,11 @@ namespace cds { namespace container {
                 typedef cds::details::compare_wrapper< node_type, cds::opt::details::make_comparator_from_less<Less>, value_accessor > type;
             };
 
+            template <typename Equal>
+            struct equal_to_wrapper {
+                typedef cds::details::predicate_wrapper< node_type, Equal, value_accessor > type;
+            };
+
             struct intrusive_traits: public original_type_traits
             {
                 typedef intrusive::lazy_list::base_hook< opt::gc<gc> >  hook;
@@ -66,6 +72,7 @@ namespace cds { namespace container {
                 static CDS_CONSTEXPR const opt::link_check_type link_checker = cds::intrusive::lazy_list::traits::link_checker;
 
                 typedef cds::details::compare_wrapper< node_type, key_comparator, value_accessor > compare;
+                typedef cds::details::predicate_wrapper< node_type, equal_to_comparator, value_accessor > equal_to;
             };
 
             typedef intrusive::LazyList<gc, node_type, intrusive_traits>  type;
index 7a93b72e1f6ee0c10b8354941ef6f33d2c88e2e7..d582180bd16dfb70be3355775c941ca92a30dc58 100644 (file)
@@ -41,6 +41,7 @@ namespace cds { namespace container {
         //@endcond
 
     public:
+        typedef Traits traits;
         typedef cds::gc::nogc gc; ///< Garbage collector
 #ifdef CDS_DOXYGEN_INVOKED
         typedef Key                                 key_type        ;   ///< Key type
@@ -59,11 +60,11 @@ namespace cds { namespace container {
 
     protected:
         //@cond
-        typedef typename base_class::value_type   node_type;
-        typedef typename maker::cxx_allocator     cxx_allocator;
-        typedef typename maker::node_deallocator  node_deallocator;
-        typedef typename maker::intrusive_traits::compare  intrusive_key_comparator;
-        typedef typename base_class::node_type    head_type;
+        typedef typename base_class::value_type     node_type;
+        typedef typename maker::cxx_allocator       cxx_allocator;
+        typedef typename maker::node_deallocator    node_deallocator;
+        typedef typename base_class::predicate_type intrusive_predicate_type;
+        typedef typename base_class::node_type      head_type;
         //@endcond
 
     protected:
@@ -408,7 +409,7 @@ namespace cds { namespace container {
         template <typename Q>
         iterator find( Q const& key )
         {
-            return node_to_iterator( find_at( head(), key, intrusive_key_comparator() ) );
+            return node_to_iterator( find_at( head(), key, intrusive_predicate_type() ) );
         }
 
         /// Finds the key \p val using \p pred predicate for searching
@@ -418,13 +419,26 @@ namespace cds { namespace container {
             \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 list.
         */
-        template <typename Q, typename Less>
-        iterator find_with( Q const& key, Less pred )
+        template <typename Q, typename Less, bool Sort = traits::sort>
+        typename std::enable_if<Sort, iterator>::type find_with( Q const& key, Less pred )
         {
             CDS_UNUSED( pred );
             return node_to_iterator( find_at( head(), key, typename maker::template less_wrapper<Less>::type() ) );
         }
 
+        /// Finds the key \p val using \p equal predicate for searching
+        /**
+            The function is an analog of \ref cds_nonintrusive_LazyKVList_nogc_find "find(Q const&)"
+            but \p equal is used for key comparing.
+            \p Equal functor has the interface like \p std::equal_to.
+        */
+        template <typename Q, typename Equal, bool Sort = traits::sort>
+        typename std::enable_if<!Sort, iterator>::type find_with( Q const& key, Equal equal )
+        {
+            CDS_UNUSED( equal );
+            return node_to_iterator( find_at( head(), key, typename maker::template equal_to_wrapper<Equal>::type() ) );
+        }
+
         /// Check if the list is empty
         bool empty() const
         {
index f3ed777d039411033bf634364082c686257dde12..88860169c5c63fcd0e384b25506f886e37ccdaab 100644 (file)
@@ -44,18 +44,19 @@ namespace cds { namespace container {
         typedef T      value_type; ///< Type of value stored in the list
         typedef Traits traits;     ///< List traits
 
-        typedef typename base_class::back_off     back_off;       ///< Back-off strategy used
-        typedef typename maker::allocator_type    allocator_type; ///< Allocator type used for allocate/deallocate the nodes
-        typedef typename base_class::item_counter item_counter;   ///< Item counting policy used
-        typedef typename maker::key_comparator    key_comparator; ///< key comparison functor
-        typedef typename base_class::memory_model memory_model;   ///< Memory ordering. See cds::opt::memory_model option
+        typedef typename base_class::back_off       back_off;            ///< Back-off strategy used
+        typedef typename maker::allocator_type      allocator_type;      ///< Allocator type used for allocate/deallocate the nodes
+        typedef typename base_class::item_counter   item_counter;        ///< Item counting policy used
+        typedef typename maker::key_comparator      key_comparator;      ///< key comparison functor
+        typedef typename maker::equal_to_comparator equal_to_comparator; ///< key equality comparision functor
+        typedef typename base_class::memory_model   memory_model;        ///< Memory ordering. See cds::opt::memory_model option
 
     protected:
         //@cond
-        typedef typename base_class::value_type   node_type;
-        typedef typename maker::cxx_allocator     cxx_allocator;
-        typedef typename maker::node_deallocator  node_deallocator;
-        typedef typename maker::intrusive_traits::compare intrusive_key_comparator;
+        typedef typename base_class::value_type     node_type;
+        typedef typename maker::cxx_allocator       cxx_allocator;
+        typedef typename maker::node_deallocator    node_deallocator;
+        typedef typename base_class::predicate_type intrusive_predicate_type;
 
         typedef typename base_class::node_type      head_type;
         //@endcond
@@ -315,7 +316,7 @@ namespace cds { namespace container {
         template <typename Q>
         iterator find( Q const& key )
         {
-            return node_to_iterator( find_at( head(), key, intrusive_key_comparator() ));
+            return node_to_iterator( find_at( head(), key, intrusive_predicate_type() ));
         }
 
         /// Finds the key \p val using \p pred predicate for searching
@@ -325,13 +326,26 @@ namespace cds { namespace container {
             \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 list.
         */
-        template <typename Q, typename Less>
-        iterator find_with( Q const& key, Less pred )
+        template <typename Q, typename Less, bool Sort = traits::sort>
+        typename std::enable_if<Sort, iterator>::type find_with( Q const& key, Less pred )
         {
             CDS_UNUSED( pred );
             return node_to_iterator( find_at( head(), key, typename maker::template less_wrapper<Less>::type() ));
         }
 
+        /// Finds the key \p val using \p equal predicate for searching
+        /**
+            The function is an analog of \ref cds_nonintrusive_LazyList_nogc_find "find(Q const&)"
+            but \p pred is used for key comparing.
+            \p Equal functor has the interface like \p std::equal_to.
+        */
+        template <typename Q, typename Equal, bool Sort = traits::sort>
+        typename std::enable_if<!Sort, iterator>::type find_with( Q const& key, Equal equal )
+        {
+            CDS_UNUSED( equal );
+            return node_to_iterator( find_at( head(), key, typename maker::template equal_to_wrapper<Equal>::type() ));
+        }
+
         /// Check if the list is empty
         bool empty() const
         {
index 8a497c10be4fda29dfc171c8b1ccdb11ad449ea1..bc6ff520c5820ce081865b4498d09001ae8de697 100644 (file)
@@ -208,6 +208,20 @@ namespace cds { namespace intrusive {
             */
             typedef opt::none                       less;
 
+            /// Specifies binary functor used for comparing keys for equality
+            /**
+                If \p equal_to option is not specified, \p compare is used, if \p compare is not specified, \p less is used,
+                if \p less is not specified, then \p std::equal_to<T> is used.
+            */
+            typedef opt::none                       equal_to;
+
+            /// Specifies list ordering policy
+            /**
+                If \p sort is \p true, than list maintains items in sorted order, otherwise items are unordered. Default is \p true.
+                Note that if \p sort is \p false, than lookup operations scan entire list.
+            */
+            static const bool sort = true;
+
             /// Back-off strategy
             typedef cds::backoff::Default           back_off;
 
@@ -245,6 +259,9 @@ namespace cds { namespace intrusive {
             - \p opt::compare - key comparison functor. No default functor is provided.
                 If the option is not specified, the \p opt::less is used.
             - \p opt::less - specifies binary predicate used for key comparison. Default is \p std::less<T>.
+            - \p opt::equal_to - specifies binary functor for comparing keys for equality. If \p equal_to is not specified, \p compare is
+                used, \p compare is not specified, \p less is used.
+            - \p opt::sort - specifies ordering policy. Default value is \p true.
             - \p opt::back_off - back-off strategy used. If the option is not specified, the \p cds::backoff::Default is used.
             - \p opt::disposer - the functor used for dispose removed items. Default is \p opt::v::empty_disposer. Due the nature
                 of GC schema the disposer may be called asynchronously.
index ae950445a01169f8895f5f545e001e3a9f0fd2d1..467d795500dcae4a9a20a45f9b69182daba12f03 100644 (file)
@@ -69,8 +69,12 @@ namespace cds { namespace intrusive {
 
 #   ifdef CDS_DOXYGEN_INVOKED
         typedef implementation_defined key_comparator  ;    ///< key comparison functor based on opt::compare and opt::less option setter.
+        typedef implementation_defined equal_to_comparator;
+        typedef implementation_defined predicate_type;
 #   else
         typedef typename opt::details::make_comparator< value_type, traits >::type key_comparator;
+        typedef typename opt::details::make_equal_to< value_type, traits >::type equal_to_comparator;
+        typedef typename std::conditional< traits::sort, key_comparator, equal_to_comparator >::type predicate_type;
 #   endif
         typedef typename traits::back_off  back_off;   ///< Back-off strategy
         typedef typename traits::disposer  disposer;   ///< disposer
@@ -382,36 +386,56 @@ namespace cds { namespace intrusive {
         template <typename Q, typename Func>
         bool find( Q& key, Func f )
         {
-            return find_at( &m_Head, key, key_comparator(), f );
+            return find_at( &m_Head, key, predicate_type(), f );
         }
         //@cond
         template <typename Q, typename Func>
         bool find( Q const& key, Func f )
         {
-            return find_at( &m_Head, key, key_comparator(), f );
+            return find_at( &m_Head, key, predicate_type(), f );
         }
         //@endcond
 
-        /// Finds the key \p key using \p pred predicate for searching
+        /// Finds the key \p key using \p less predicate for searching. Disabled for unordered lists.
         /**
             The function is an analog of \ref cds_intrusive_LazyList_nogc_find_func "find(Q&, Func)"
             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 list.
         */
-        template <typename Q, typename Less, typename Func>
-        bool find_with( Q& key, Less pred, Func f )
+        template <typename Q, typename Less, typename Func, bool Sort = traits::sort>
+        typename std::enable_if<Sort, bool>::type find_with( Q& key, Less less, Func f )
         {
-            CDS_UNUSED( pred );
+            CDS_UNUSED( less );
             return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>(), f );
         }
+
+        /// Finds the key \p key using \p equal predicate for searching. Disabled for ordered lists.
+        /**
+            The function is an analog of \ref cds_intrusive_LazyList_nogc_find_func "find(Q&, Func)"
+            but \p equal is used for key comparing.
+            \p Equal functor has the interface like \p std::equal_to.
+        */
+        template <typename Q, typename Equal, typename Func, bool Sort = traits::sort>
+        typename std::enable_if<!Sort, bool>::type find_with( Q& key, Equal equal, Func f )
+        {
+            CDS_UNUSED( equal );
+            return find_at( &m_Head, key, Equal(), f );
+        }
         //@cond
-        template <typename Q, typename Less, typename Func>
-        bool find_with( Q const& key, Less pred, Func f )
+        template <typename Q, typename Less, typename Func, bool Sort = traits::sort>
+        typename std::enable_if<Sort, bool>::type find_with( Q const& key, Less pred, Func f )
         {
             CDS_UNUSED( pred );
             return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>(), f );
         }
+
+        template <typename Q, typename Equal, typename Func, bool Sort = traits::sort>
+        typename std::enable_if<!Sort, bool>::type find_with( Q const& key, Equal equal, Func f )
+        {
+            CDS_UNUSED( equal );
+            return find_at( &m_Head, key, Equal(), f );
+        }
         //@endcond
 
         /// Finds the key \p key
@@ -422,23 +446,36 @@ namespace cds { namespace intrusive {
         template <typename Q>
         value_type * find( Q const& key )
         {
-            return find_at( &m_Head, key, key_comparator() );
+            return find_at( &m_Head, key, predicate_type() );
         }
 
-        /// Finds the key \p key using \p pred predicate for searching
+        /// Finds the key \p key using \p pred predicate for searching. Disabled for unordered lists.
         /**
             The function is an analog of \ref cds_intrusive_LazyList_nogc_find_val "find(Q const&)"
             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 list.
         */
-        template <typename Q, typename Less>
-        value_type * find_with( Q const& key, Less pred )
+        template <typename Q, typename Less, bool Sort = traits::sort>
+        typename std::enable_if<Sort, value_type *>::type find_with( Q const& key, Less pred )
         {
             CDS_UNUSED( pred );
             return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>() );
         }
 
+        /// Finds the key \p key using \p equal predicate for searching. Disabled for ordered lists.
+        /**
+            The function is an analog of \ref cds_intrusive_LazyList_nogc_find_val "find(Q const&)"
+            but \p equal is used for key comparing.
+            \p Equal functor has the interface like \p std::equal_to.
+        */
+        template <typename Q, typename Equal, bool Sort = traits::sort>
+        typename std::enable_if<!Sort, value_type *>::type find_with( Q const& key, Equal equal )
+        {
+            CDS_UNUSED( equal );
+            return find_at( &m_Head, key, equal );
+        }
+
         /// Clears the list
         /**
             The function unlink all items from the list.
@@ -510,14 +547,14 @@ namespace cds { namespace intrusive {
         {
             link_checker::is_empty( node_traits::to_node_ptr( val ) );
             position pos;
-            key_comparator  cmp;
+            predicate_type pred;
 
             while ( true ) {
-                search( pHead, val, pos, key_comparator() );
+                search( pHead, val, pos, pred );
                 {
                     auto_lock_position alp( pos );
                     if ( validate( pos.pPred, pos.pCur )) {
-                        if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
+                        if ( pos.pCur != &m_Tail && equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred ) ) {
                             // failed: key already in list
                             return false;
                         }
@@ -543,14 +580,14 @@ namespace cds { namespace intrusive {
         std::pair<iterator, bool> ensure_at_( node_type * pHead, value_type& val, Func func )
         {
             position pos;
-            key_comparator  cmp;
+            predicate_type pred;
 
             while ( true ) {
-                search( pHead, val, pos, key_comparator() );
+                search( pHead, val, pos, pred );
                 {
                     auto_lock_position alp( pos );
                     if ( validate( pos.pPred, pos.pCur )) {
-                        if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
+                        if ( pos.pCur != &m_Tail && equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred ) ) {
                             // key already in the list
 
                             func( false, *node_traits::to_value_ptr( *pos.pCur ) , val );
@@ -577,15 +614,15 @@ namespace cds { namespace intrusive {
             return std::make_pair( ret.first != end(), ret.second );
         }
 
-        template <typename Q, typename Compare, typename Func>
-        bool find_at( node_type * pHead, Q& val, Compare cmp, Func f )
+        template <typename Q, typename Pred, typename Func>
+        bool find_at( node_type * pHead, Q& val, Pred pred, Func f )
         {
             position pos;
 
-            search( pHead, val, pos, cmp );
+            search( pHead, val, pos, pred );
             if ( pos.pCur != &m_Tail ) {
                 std::unique_lock< typename node_type::lock_type> al( pos.pCur->m_Lock );
-                if ( cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 )
+                if ( equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred ) )
                 {
                     f( *node_traits::to_value_ptr( *pos.pCur ), val );
                     return true;
@@ -594,23 +631,23 @@ namespace cds { namespace intrusive {
             return false;
         }
 
-        template <typename Q, typename Compare>
-        value_type * find_at( node_type * pHead, Q& val, Compare cmp)
+        template <typename Q, typename Pred>
+        value_type * find_at( node_type * pHead, Q& val, Pred pred)
         {
-            iterator it = find_at_( pHead, val, cmp );
+            iterator it = find_at_( pHead, val, pred );
             if ( it != end() )
                 return &*it;
             return nullptr;
         }
 
-        template <typename Q, typename Compare>
-        iterator find_at_( node_type * pHead, Q& val, Compare cmp)
+        template <typename Q, typename Pred>
+        iterator find_at_( node_type * pHead, Q& val, Pred pred)
         {
             position pos;
 
-            search( pHead, val, pos, cmp );
+            search( pHead, val, pos, pred );
             if ( pos.pCur != &m_Tail ) {
-                if ( cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 )
+                if ( equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred ) )
                     return iterator( pos.pCur );
             }
             return end();
@@ -620,8 +657,25 @@ namespace cds { namespace intrusive {
 
     protected:
         //@cond
-        template <typename Q, typename Compare>
-        void search( node_type * pHead, const Q& key, position& pos, Compare cmp )
+        template <typename Q, typename Equal, bool Sort = traits::sort>
+        typename std::enable_if<!Sort, void>::type search( node_type * pHead, const Q& key, position& pos, Equal eq )
+        {
+            const node_type * pTail = &m_Tail;
+
+            node_type * pCur = pHead;
+            node_type * pPrev = pHead;
+
+            while ( pCur != pTail && ( pCur == pHead || !equal( *node_traits::to_value_ptr( *pCur ), key, eq ) )) {
+                pPrev = pCur;
+                pCur = pCur->m_pNext.load(memory_model::memory_order_acquire);
+            }
+
+            pos.pCur = pCur;
+            pos.pPred = pPrev;
+        }
+
+        template <typename Q, typename Compare, bool Sort = traits::sort>
+        typename std::enable_if<Sort, void>::type search( node_type * pHead, const Q& key, position& pos, Compare cmp )
         {
             const node_type * pTail = &m_Tail;
 
@@ -637,6 +691,18 @@ namespace cds { namespace intrusive {
             pos.pPred = pPrev;
         }
 
+        template <typename L, typename R, typename Equal, bool Sort = traits::sort>
+        static typename std::enable_if<!Sort, bool>::type equal( L const& l, R const& r, Equal eq )
+        {
+            return eq(l, r);
+        }
+
+        template <typename L, typename R, typename Compare, bool Sort = traits::sort>
+        static typename std::enable_if<Sort, bool>::type equal( L const& l, R const& r, Compare cmp )
+        {
+            return cmp(l, r) == 0;
+        }
+
         static bool validate( node_type * pPred, node_type * pCur )
         {
             return pPred->m_pNext.load(memory_model::memory_order_acquire) == pCur;
index 3bcfc9617e2c8c831c6193b62b8549f231ca156e..1243f5be3777fe996c483a84784e71c483c0471f 100644 (file)
@@ -244,16 +244,52 @@ namespace cds { namespace opt {
 
     //@cond
     namespace details {
+        template <typename Compare>
+        struct make_equal_to_from_compare
+        {
+            typedef Compare compare_functor;
+
+            template <typename T, typename Q>
+            bool operator()( T const& t, Q const& q ) const
+            {
+                compare_functor cmp;
+                return cmp(t, q) == 0;
+            }
+        };
+
+        template <typename Less>
+        struct make_equal_to_from_less
+        {
+            typedef Less less_functor;
+
+            template <typename T, typename Q>
+            bool operator()( T const& t, Q const& q ) const
+            {
+                less_functor less;
+                return !less(t, q) && !less(q, t);
+            }
+        };
+
         template <typename T, typename Traits, bool Forced = true>
         struct make_equal_to
         {
             typedef typename Traits::equal_to equal_to;
+            typedef typename Traits::compare  compare;
+            typedef typename Traits::less     less;
 
             typedef typename std::conditional<
                 std::is_same< equal_to, opt::none >::value,
-                typename std::conditional< Forced, std::equal_to<T>, opt::none >::type,
-                equal_to
-            >::type type;
+                typename std::conditional<
+                    std::is_same< compare, opt::none >::value,
+                    typename std::conditional<
+                        std::is_same< less, opt::none >::value,
+                        typename std::conditional<
+                            Forced,
+                            std::equal_to<T>,
+                            opt::none >::type,
+                        make_equal_to_from_less< less > >::type,
+                    make_equal_to_from_compare< compare > >::type,
+                equal_to >::type type;
         };
     }
     //@endcond
index 349bde712c0c50a08582005323d2b16929d0bfae..7f6a46b7c188cc8723a68039157e8cdfb76f59f6 100644 (file)
@@ -94,6 +94,10 @@ CDS_TESTHDR_ORDLIST := \
     tests/test-hdr/ordered_list/hdr_michael_kv_rcu_shb.cpp \
     tests/test-hdr/ordered_list/hdr_michael_kv_rcu_sht.cpp
 
+CDS_TESTHDR_UNORDLIST := \
+    tests/test-hdr/unordered_list/hdr_lazy_nogc.cpp \
+    tests/test-hdr/unordered_list/hdr_lazy_kv_nogc.cpp
+
 CDS_TESTHDR_PQUEUE := \
     tests/test-hdr/priority_queue/hdr_intrusive_mspqueue_dyn.cpp \
     tests/test-hdr/priority_queue/hdr_intrusive_mspqueue_static.cpp \
@@ -276,7 +280,8 @@ CDS_TESTHDR_SOURCES := \
     $(CDS_TESTHDR_MAP) \
     $(CDS_TESTHDR_DEQUE) \
     $(CDS_TESTHDR_ORDLIST) \
+    $(CDS_TESTHDR_UNORDLIST) \
     $(CDS_TESTHDR_SET) \
     $(CDS_TESTHDR_TREE) \
     $(CDS_TESTHDR_MISC)
-    
\ No newline at end of file
+    
index 887527e20af41d2d5f7bf446a0e61f647bf89aef..cca42544a53f31eee02b64bff8d2949a29b4a89e 100644 (file)
@@ -66,6 +66,9 @@ CDS_TESTHDR_OFFSETOF_ORDLIST := \
     tests/test-hdr/ordered_list/hdr_intrusive_michael_list_rcu_shb.cpp \
     tests/test-hdr/ordered_list/hdr_intrusive_michael_list_rcu_sht.cpp
 
+CDS_TESTHDR_OFFSETOF_UNORDLIST := \
+    tests/test-hdr/unordered_list/hdr_intrusive_lazy_nogc.cpp
+
 CDS_TESTHDR_OFFSETOF_QUEUE := \
     tests/test-hdr/queue/hdr_intrusive_basketqueue_hp.cpp \
     tests/test-hdr/queue/hdr_intrusive_basketqueue_dhp.cpp \
@@ -98,4 +101,5 @@ CDS_TESTHDR_OFFSETOF_SOURCES := \
        $(CDS_TESTHDR_OFFSETOF_MAP) \
        $(CDS_TESTHDR_OFFSETOF_SET) \
        $(CDS_TESTHDR_OFFSETOF_ORDLIST) \
-       $(CDS_TESTHDR_OFFSETOF_TREE)
\ No newline at end of file
+       $(CDS_TESTHDR_OFFSETOF_UNORDLIST) \
+       $(CDS_TESTHDR_OFFSETOF_TREE)
index 611533ad6dacf6dc42999c3b05b4a2c74f663970..327090db17e15ed72a10f62f0973d1e5582f27f0 100644 (file)
@@ -635,6 +635,158 @@ namespace map {
             }
         }
 
+        template <class Map>
+        void test_int_nogc_unordered()
+        {
+            typedef typename Map::iterator          iterator;
+            typedef typename Map::const_iterator    const_iterator;
+
+            {
+                Map m( 52, 4 );
+
+                CPPUNIT_ASSERT( m.empty() );
+                CPPUNIT_ASSERT( check_size( m, 0 ));
+
+                CPPUNIT_ASSERT( m.find(10) == m.end() );
+                iterator it = m.insert( 10 );
+                CPPUNIT_ASSERT( it != m.end() );
+                CPPUNIT_ASSERT( !m.empty() );
+                CPPUNIT_ASSERT( check_size( m, 1 ));
+                CPPUNIT_ASSERT( m.find(10) == it );
+                CPPUNIT_ASSERT( it->first == 10 );
+                CPPUNIT_ASSERT( it->second.m_val == 0 );
+
+                CPPUNIT_ASSERT( m.find(100) == m.end() );
+                it = m.insert( 100, 200 );
+                CPPUNIT_ASSERT( it != m.end() );
+                CPPUNIT_ASSERT( !m.empty() );
+                CPPUNIT_ASSERT( check_size( m, 2 ));
+                CPPUNIT_ASSERT( m.find_with(100, equal()) == it );
+                CPPUNIT_ASSERT( it->first == 100 );
+                CPPUNIT_ASSERT( it->second.m_val == 200 );
+
+                CPPUNIT_ASSERT( m.find(55) == m.end() );
+                it = m.insert_with( 55, insert_functor<Map>() );
+                CPPUNIT_ASSERT( it != m.end() );
+                CPPUNIT_ASSERT( !m.empty() );
+                CPPUNIT_ASSERT( check_size( m, 3 ));
+                CPPUNIT_ASSERT( m.find(55) == it );
+                CPPUNIT_ASSERT( it->first == 55 );
+                CPPUNIT_ASSERT( it->second.m_val == 55 * 3 );
+
+                CPPUNIT_ASSERT( m.insert( 55 ) == m.end() );
+                CPPUNIT_ASSERT( m.insert( 55, 10 ) == m.end() );
+                CPPUNIT_ASSERT( m.insert_with( 55, insert_functor<Map>()) == m.end() );
+
+                CPPUNIT_ASSERT( m.find(10) != m.end() );
+                std::pair<iterator, bool> ensureResult = m.ensure( 10 );
+                CPPUNIT_ASSERT( ensureResult.first != m.end() );
+                CPPUNIT_ASSERT( !ensureResult.second  );
+                CPPUNIT_ASSERT( !m.empty() );
+                ensureResult.first->second.m_val = ensureResult.first->first * 5;
+                CPPUNIT_ASSERT( check_size( m, 3 ));
+                CPPUNIT_ASSERT( m.find(10) == ensureResult.first );
+                it = m.find(10);
+                CPPUNIT_ASSERT( it != m.end() );
+                CPPUNIT_ASSERT( it->second.m_val == 50 );
+
+                CPPUNIT_ASSERT( m.find(120) == m.end() );
+                ensureResult = m.ensure( 120 );
+                CPPUNIT_ASSERT( ensureResult.first != m.end() );
+                CPPUNIT_ASSERT( ensureResult.second  );
+                CPPUNIT_ASSERT( !m.empty() );
+                CPPUNIT_ASSERT( check_size( m, 4 ));
+                ensureResult.first->second.m_val = ensureResult.first->first * 5;
+                CPPUNIT_ASSERT( m.find_with(120, equal()) == ensureResult.first );
+                it = m.find_with(120, equal());
+                CPPUNIT_ASSERT( it != m.end() );
+                CPPUNIT_ASSERT( it->second.m_val == 120 * 5 );
+                CPPUNIT_ASSERT( m.find_with(120, equal()) == m.find(120) );
+
+                // emplace test
+                it = m.emplace( 151 ) ;  // key = 151,  val = 0
+                CPPUNIT_ASSERT( it != m.end() );
+                CPPUNIT_ASSERT( it->first == 151 );
+                CPPUNIT_ASSERT( it->second.m_val == 0 );
+
+                it = m.emplace( 174, 471 ) ; // key == 174, val = 471
+                CPPUNIT_ASSERT( it != m.end() );
+                CPPUNIT_ASSERT( it->first == 174 );
+                CPPUNIT_ASSERT( it->second.m_val == 471 );
+
+                it = m.emplace( 190, value_type(91)) ; // key == 190, val = 19
+                CPPUNIT_ASSERT( it != m.end() );
+                CPPUNIT_ASSERT( it->first == 190 );
+                CPPUNIT_ASSERT( it->second.m_val == 91 );
+
+                it = m.emplace( 151, 1051 );
+                CPPUNIT_ASSERT( it == m.end());
+
+                it = m.find( 174 );
+                CPPUNIT_ASSERT( it != m.end() );
+                CPPUNIT_ASSERT( it->first == 174 );
+                CPPUNIT_ASSERT( it->second.m_val == 471 );
+
+                it = m.find( 190 );
+                CPPUNIT_ASSERT( it != m.end() );
+                CPPUNIT_ASSERT( it->first == 190 );
+                CPPUNIT_ASSERT( it->second.m_val == 91 );
+
+                it = m.find( 151 );
+                CPPUNIT_ASSERT( it != m.end() );
+                CPPUNIT_ASSERT( it->first == 151 );
+                CPPUNIT_ASSERT( it->second.m_val == 0 );
+            }
+
+            // iterator test
+
+            {
+                Map m( 52, 4 );
+
+                for ( int i = 0; i < 500; ++i ) {
+                    CPPUNIT_ASSERT( m.insert( i, i * 2 ) != m.end() );
+                }
+                CPPUNIT_ASSERT( check_size( m, 500 ));
+
+                {
+                    typename Map::iterator it( m.begin() );
+                    typename Map::const_iterator cit( m.cbegin() );
+                    CPPUNIT_CHECK( it == cit );
+                    CPPUNIT_CHECK( it != m.end() );
+                    CPPUNIT_CHECK( it != m.cend() );
+                    CPPUNIT_CHECK( cit != m.end() );
+                    CPPUNIT_CHECK( cit != m.cend() );
+                    ++it;
+                    CPPUNIT_CHECK( it != cit );
+                    CPPUNIT_CHECK( it != m.end() );
+                    CPPUNIT_CHECK( it != m.cend() );
+                    CPPUNIT_CHECK( cit != m.end() );
+                    CPPUNIT_CHECK( cit != m.cend() );
+                    ++cit;
+                    CPPUNIT_CHECK( it == cit );
+                    CPPUNIT_CHECK( it != m.end() );
+                    CPPUNIT_CHECK( it != m.cend() );
+                    CPPUNIT_CHECK( cit != m.end() );
+                    CPPUNIT_CHECK( cit != m.cend() );
+                }
+
+
+                for ( iterator it = m.begin(), itEnd = m.end(); it != itEnd; ++it ) {
+                    iterator it2 = it;
+                    CPPUNIT_CHECK( it2 == it );
+                    CPPUNIT_CHECK( it2 != itEnd );
+                    CPPUNIT_ASSERT( it->first * 2 == (*it).second.m_val );
+                    it->second = it->first;
+                }
+
+                Map const& refMap = m;
+                for ( const_iterator it = refMap.begin(), itEnd = refMap.end(); it != itEnd; ++it ) {
+                    CPPUNIT_ASSERT( it->first == it->second.m_val );
+                    CPPUNIT_ASSERT( (*it).first == (*it).second.m_val );
+                }
+            }
+        }
+
         template <class Map>
         void test_iter()
         {
@@ -752,6 +904,7 @@ namespace map {
 
         void Lazy_nogc_cmp();
         void Lazy_nogc_less();
+        void Lazy_nogc_equal();
         void Lazy_nogc_cmpmix();
 
         void Split_HP_cmp();
@@ -897,6 +1050,7 @@ namespace map {
 
             CPPUNIT_TEST(Lazy_nogc_cmp)
             CPPUNIT_TEST(Lazy_nogc_less)
+            CPPUNIT_TEST(Lazy_nogc_equal)
             CPPUNIT_TEST(Lazy_nogc_cmpmix)
 
             CPPUNIT_TEST(Split_HP_cmp)
index eba7232a4ddf92ce43dbfbe6db33a50f5400c65b..9c9fb03c23030e95e70fcb126a905a09da5a4b1b 100644 (file)
@@ -26,6 +26,12 @@ namespace map {
             typedef HashMapHdrTest::cmp   compare;
             typedef HashMapHdrTest::less  less;
         };
+
+        struct nogc_equal_traits: public cc::lazy_list::traits
+        {
+            typedef HashMapHdrTest::equal  equal_to;
+            static const bool sort = false;
+        };
     }
 
     void HashMapHdrTest::Lazy_nogc_cmp()
@@ -64,6 +70,24 @@ namespace map {
         test_int_nogc< opt_map >();
     }
 
+    void HashMapHdrTest::Lazy_nogc_equal()
+    {
+        typedef cc::LazyKVList< cds::gc::nogc, int, HashMapHdrTest::value_type, nogc_equal_traits > list;
+
+        // traits-based version
+        typedef cc::MichaelHashMap< cds::gc::nogc, list, map_traits > map;
+        test_int_nogc_unordered< map >();
+
+        // option-based version
+        typedef cc::MichaelHashMap< cds::gc::nogc, list,
+            cc::michael_map::make_traits<
+                cc::opt::hash< hash_int >
+                ,cc::opt::item_counter< simple_item_counter >
+            >::type
+        > opt_map;
+        test_int_nogc_unordered< opt_map >();
+    }
+
     void HashMapHdrTest::Lazy_nogc_cmpmix()
     {
         typedef cc::LazyKVList< cds::gc::nogc, int, HashMapHdrTest::value_type, nogc_cmpmix_traits > list;
index 4d988f438440970ded0c141b5bd50613b10d15b8..ac4de70051a998cb23522a855fea4676dcf2b58a 100644 (file)
@@ -23,6 +23,12 @@ namespace set {
             typedef HashSetHdrTest::less<HashSetHdrTest::item>   less;
         };
 
+        struct nogc_equal_traits: public cc::lazy_list::traits
+        {
+            typedef HashSetHdrTest::equal<HashSetHdrTest::item>   equal_to;
+            static const bool sort = false;
+        };
+
         struct nogc_cmpmix_traits: public cc::lazy_list::traits
         {
             typedef HashSetHdrTest::cmp<HashSetHdrTest::item>   compare;
@@ -66,6 +72,24 @@ namespace set {
         test_int_nogc< opt_set >();
     }
 
+    void HashSetHdrTest::Lazy_nogc_equal()
+    {
+        typedef cc::LazyList< cds::gc::nogc, item, nogc_equal_traits > list;
+
+        // traits-based version
+        typedef cc::MichaelHashSet< cds::gc::nogc, list, set_traits > set;
+        test_int_nogc_unordered< set >();
+
+        // option-based version
+        typedef cc::MichaelHashSet< cds::gc::nogc, list,
+            cc::michael_set::make_traits<
+                cc::opt::hash< hash_int >
+                ,cc::opt::item_counter< simple_item_counter >
+            >::type
+        > opt_set;
+        test_int_nogc_unordered< opt_set >();
+    }
+
     void HashSetHdrTest::Lazy_nogc_cmpmix()
     {
         typedef cc::LazyList< cds::gc::nogc, item, nogc_cmpmix_traits > list;
index 61dc034cb88f340c5a376cd0d664926c0260b577..643b7c25ed819821b706bb643024e9eaba872b45 100644 (file)
@@ -803,6 +803,145 @@ namespace set {
             }
         }
 
+        template <class Set>
+        void test_int_nogc_unordered()
+        {
+            typedef typename Set::value_type        value_type;
+            typedef typename Set::iterator          iterator;
+            typedef typename Set::const_iterator    const_iterator;
+
+            {
+                Set s( 52, 4 );
+                iterator it;
+
+                CPPUNIT_ASSERT( s.empty() );
+                CPPUNIT_ASSERT( check_size( s, 0 ));
+
+                // insert
+                it = s.insert( 10 );
+                CPPUNIT_ASSERT( it != s.end() );
+                CPPUNIT_ASSERT( it->key() == 10 );
+                CPPUNIT_ASSERT( it->val() == 10 );
+                CPPUNIT_ASSERT( !s.empty() );
+                CPPUNIT_ASSERT( check_size( s, 1 ));
+                CPPUNIT_ASSERT( s.insert( 10 ) == s.end() );
+
+                it = s.insert( std::make_pair( 50, 25 ));
+                CPPUNIT_ASSERT( it != s.end() );
+                CPPUNIT_ASSERT( it->key() == 50 );
+                CPPUNIT_ASSERT( it->val() == 25 );
+                CPPUNIT_ASSERT( !s.empty() );
+                CPPUNIT_ASSERT( check_size( s, 2 ));
+                CPPUNIT_ASSERT( s.insert( 50 ) == s.end() );
+
+                // ensure
+                std::pair< iterator, bool>  ensureResult;
+                ensureResult = s.ensure( 20 );
+                CPPUNIT_ASSERT( ensureResult.first != s.end() );
+                CPPUNIT_ASSERT( ensureResult.second  );
+                CPPUNIT_ASSERT( ensureResult.first->key() == 20 );
+                CPPUNIT_ASSERT( ensureResult.first->val() == 20 );
+                CPPUNIT_ASSERT( !s.empty() );
+                CPPUNIT_ASSERT( check_size( s, 3 ));
+
+                ensureResult = s.ensure( std::make_pair( 20, 200 ));
+                CPPUNIT_ASSERT( ensureResult.first != s.end() );
+                CPPUNIT_ASSERT( !ensureResult.second  );
+                CPPUNIT_ASSERT( ensureResult.first->key() == 20 );
+                CPPUNIT_ASSERT( ensureResult.first->val() == 20 );
+                CPPUNIT_ASSERT( !s.empty() );
+                CPPUNIT_ASSERT( check_size( s, 3 ));
+                ensureResult.first->nVal = 22;
+
+                ensureResult = s.ensure( std::make_pair( 30, 33 ));
+                CPPUNIT_ASSERT( ensureResult.first != s.end() );
+                CPPUNIT_ASSERT( ensureResult.second  );
+                CPPUNIT_ASSERT( ensureResult.first->key() == 30 );
+                CPPUNIT_ASSERT( ensureResult.first->val() == 33 );
+                CPPUNIT_ASSERT( !s.empty() );
+                CPPUNIT_ASSERT( check_size( s, 4 ));
+
+                // find
+                it = s.find( 10 );
+                CPPUNIT_ASSERT( it != s.end() );
+                CPPUNIT_ASSERT( it->key() == 10 );
+                CPPUNIT_ASSERT( it->val() == 10 );
+
+                it = s.find_with( 20, equal<value_type>() );
+                CPPUNIT_ASSERT( it != s.end() );
+                CPPUNIT_ASSERT( it->key() == 20 );
+                CPPUNIT_ASSERT( it->val() == 22 );
+
+                it = s.find( 30 );
+                CPPUNIT_ASSERT( it != s.end() );
+                CPPUNIT_ASSERT( it->key() == 30 );
+                CPPUNIT_ASSERT( it->val() == 33 );
+
+                it = s.find( 40 );
+                CPPUNIT_ASSERT( it == s.end() );
+
+                it = s.find( 50 );
+                CPPUNIT_ASSERT( it != s.end() );
+                CPPUNIT_ASSERT( it->key() == 50 );
+                CPPUNIT_ASSERT( it->val() == 25 );
+
+                // emplace test
+                it = s.emplace( 151 ) ;  // key = 151,  val = 151
+                CPPUNIT_ASSERT( it != s.end() );
+                CPPUNIT_ASSERT( it->key() == 151 );
+                CPPUNIT_ASSERT( it->val() == 151 );
+
+                it = s.emplace( 174, 471 ) ; // key == 174, val = 471
+                CPPUNIT_ASSERT( it != s.end() );
+                CPPUNIT_ASSERT( it->key() == 174 );
+                CPPUNIT_ASSERT( it->val() == 471 );
+
+                it = s.emplace( std::make_pair( 190, 91 )) ; // key == 190, val = 91
+                CPPUNIT_ASSERT( it != s.end() );
+                CPPUNIT_ASSERT( it->key() == 190 );
+                CPPUNIT_ASSERT( it->val() == 91 );
+
+                it = s.find( 174 );
+                CPPUNIT_ASSERT( it != s.end() );
+                CPPUNIT_ASSERT( it->key() == 174 );
+                CPPUNIT_ASSERT( it->val() == 471 );
+
+                it = s.find_with( 190, equal<value_type>() );
+                CPPUNIT_ASSERT( it != s.end() );
+                CPPUNIT_ASSERT( it->key() == 190 );
+                CPPUNIT_ASSERT( it->val() == 91 );
+
+                it = s.find( 151 );
+                CPPUNIT_ASSERT( it != s.end() );
+                CPPUNIT_ASSERT( it->key() == 151 );
+                CPPUNIT_ASSERT( it->val() == 151 );
+
+                //s.clear();
+                //CPPUNIT_ASSERT( s.empty() );
+                //CPPUNIT_ASSERT( check_size( s, 0 ));
+            }
+
+            {
+                Set s( 52, 4 );
+
+                // iterator test
+                for ( int i = 0; i < 500; ++i ) {
+                    CPPUNIT_ASSERT( s.insert( std::make_pair( i, i * 2) ) != s.end() );
+                }
+                for ( iterator it = s.begin(), itEnd = s.end(); it != itEnd; ++it ) {
+                    iterator it2 = it;
+                    CPPUNIT_CHECK( it2 == it );
+                    CPPUNIT_CHECK( it2 != itEnd );
+                    CPPUNIT_ASSERT( (*it).nKey * 2 == it->nVal );
+                    it->nVal = (*it).nKey;
+                }
+
+                Set const& refSet = s;
+                for ( const_iterator it = refSet.begin(), itEnd = refSet.end(); it != itEnd; ++it ) {
+                    CPPUNIT_ASSERT( (*it).nKey == it->nVal );
+                }
+            }
+        }
         template <class Set>
         void test_iter()
         {
@@ -922,6 +1061,7 @@ namespace set {
 
         void Lazy_nogc_cmp();
         void Lazy_nogc_less();
+        void Lazy_nogc_equal();
         void Lazy_nogc_cmpmix();
 
         void Split_HP_cmp();
@@ -1068,6 +1208,7 @@ namespace set {
 
             CPPUNIT_TEST(Lazy_nogc_cmp)
             CPPUNIT_TEST(Lazy_nogc_less)
+            CPPUNIT_TEST(Lazy_nogc_equal)
             CPPUNIT_TEST(Lazy_nogc_cmpmix)
 
             CPPUNIT_TEST(Split_HP_cmp)
diff --git a/tests/test-hdr/unordered_list/hdr_intrusive_lazy.h b/tests/test-hdr/unordered_list/hdr_intrusive_lazy.h
new file mode 100644 (file)
index 0000000..9f1f96c
--- /dev/null
@@ -0,0 +1,354 @@
+//$$CDS-header$$
+
+#ifndef CDSTEST_HDR_INTRUSIVE_LAZY_H
+#define CDSTEST_HDR_INTRUSIVE_LAZY_H
+
+#include "cppunit/cppunit_proxy.h"
+#include <cds/intrusive/details/lazy_list_base.h>
+
+namespace unordlist {
+    namespace ci = cds::intrusive;
+    namespace co = cds::opt;
+
+    struct stat {
+        int nDisposeCount;
+        int nEnsureExistsCall;
+        int nEnsureNewCall;
+        int nFindCall;
+        int nEraseCall;
+
+        stat()
+            : nDisposeCount(0)
+            , nEnsureExistsCall(0)
+            , nEnsureNewCall(0)
+            , nFindCall(0)
+            , nEraseCall(0)
+        {}
+
+        stat( const stat& s )
+        {
+            *this = s;
+        }
+
+        stat& operator =(const stat& s)
+        {
+            memcpy( this, &s, sizeof(s));
+            return *this;
+        }
+    };
+
+    template <typename GC>
+    struct base_int_item: public ci::lazy_list::node< GC >
+    {
+        int nKey;
+        int nVal;
+
+        mutable stat    s;
+
+        base_int_item()
+        {}
+
+        base_int_item(int key, int val)
+            : nKey( key )
+            , nVal(val)
+            , s()
+        {}
+
+        base_int_item(const base_int_item& v )
+            : nKey( v.nKey )
+            , nVal( v.nVal )
+            , s()
+        {}
+
+        const int& key() const
+        {
+            return nKey;
+        }
+
+        operator int() const
+        { return nKey; }
+    };
+
+    template <typename GC>
+    struct member_int_item
+    {
+        int nKey;
+        int nVal;
+
+        ci::lazy_list::node< GC > hMember;
+
+        mutable stat s;
+
+        member_int_item()
+        {}
+
+        member_int_item(int key, int val)
+            : nKey( key )
+            , nVal(val)
+            , s()
+        {}
+
+        member_int_item(const member_int_item& v )
+            : nKey( v.nKey )
+            , nVal( v.nVal )
+            , s()
+        {}
+
+        const int& key() const
+        {
+            return nKey;
+        }
+
+        operator int() const
+        { return nKey; }
+    };
+
+    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;
+        }
+    };
+
+    template <typename T>
+    struct equal_to {
+        bool operator()( T const& l, T const& r ) const
+        {
+            return l.key() == r.key();
+        }
+
+        template <typename Q>
+        bool operator()( Q const& l, T const& r ) const
+        {
+            return l == r.key();
+        }
+
+        template <typename Q>
+        bool operator()( T const& l, Q const& r ) const
+        {
+            return l.key() == r;
+        }
+    };
+
+    struct faked_disposer
+    {
+        template <typename T>
+        void operator ()( T * p )
+        {
+            ++p->s.nDisposeCount;
+        }
+    };
+
+    struct ensure_functor
+    {
+        template <typename T>
+        void operator ()(bool bNew, T& item, T& /*val*/ )
+        {
+            if ( bNew )
+                ++item.s.nEnsureNewCall;
+            else
+                ++item.s.nEnsureExistsCall;
+        }
+    };
+
+    struct find_functor
+    {
+        template <typename T, typename Q>
+        void operator ()( T& item, Q& /*val*/ )
+        {
+            ++item.s.nFindCall;
+        }
+    };
+
+    class UnorderedIntrusiveLazyListHeaderTest: public CppUnitMini::TestCase
+    {
+    public:
+        template <class UnordList>
+        void test_nogc_int()
+        {
+            typedef typename UnordList::value_type    value_type;
+            {
+                value_type v1( 10, 50 );
+                value_type v2( 5, 25  );
+                value_type v3( 20, 100 );
+                {
+                    UnordList l;
+                    CPPUNIT_ASSERT( l.empty() );
+
+                    CPPUNIT_ASSERT( l.insert( v1 ));   // true
+                    CPPUNIT_ASSERT( l.find( v1.key() ) == &v1 );
+
+                    CPPUNIT_ASSERT( v1.s.nFindCall == 0 );
+                    CPPUNIT_ASSERT( l.find( v1.key(), find_functor() ));
+                    CPPUNIT_ASSERT( v1.s.nFindCall == 1 );
+
+                    CPPUNIT_ASSERT( l.find_with( v2.key(), equal_to<value_type>() ) == nullptr );
+                    CPPUNIT_ASSERT( l.find( v3.key() ) == nullptr );
+                    CPPUNIT_ASSERT( !l.empty() );
+
+                    //CPPUNIT_ASSERT( !l.insert( v1 ))    ;   // assertion "is_empty" is raised
+
+                    {
+                        value_type v( v1 );
+                        CPPUNIT_ASSERT( !l.insert( v )) ;   // false
+                    }
+
+                    std::pair<bool, bool> ret = l.ensure( v2, ensure_functor() );
+                    CPPUNIT_ASSERT( ret.first );
+                    CPPUNIT_ASSERT( ret.second );
+                    CPPUNIT_ASSERT( v2.s.nEnsureNewCall == 1 );
+                    CPPUNIT_ASSERT( v2.s.nEnsureExistsCall == 0 );
+
+                    //CPPUNIT_ASSERT( !l.insert( v2 ))    ;   // assertion "is_empty"
+
+                    CPPUNIT_ASSERT( l.find( v1.key() ) == &v1 ) ;   // true
+
+                    CPPUNIT_ASSERT( v1.s.nFindCall == 1 );
+                    CPPUNIT_ASSERT( l.find( v1.key(), find_functor() ));
+                    CPPUNIT_ASSERT( v1.s.nFindCall == 2 );
+
+                    CPPUNIT_ASSERT( l.find_with( v2.key(), equal_to<value_type>() ) == &v2 );
+
+                    CPPUNIT_ASSERT( v2.s.nFindCall == 0 );
+                    CPPUNIT_ASSERT( l.find_with( v2.key(), equal_to<value_type>(), find_functor() ));
+                    CPPUNIT_ASSERT( v2.s.nFindCall == 1 );
+
+                    CPPUNIT_ASSERT( !l.find( v3.key() ));
+
+                    {
+                        value_type v( v2 );
+                        ret = l.ensure( v, ensure_functor() );
+
+                        CPPUNIT_ASSERT( ret.first );
+                        CPPUNIT_ASSERT( !ret.second );
+                        CPPUNIT_ASSERT( v2.s.nEnsureExistsCall == 1 );
+                        CPPUNIT_ASSERT( v.s.nEnsureExistsCall == 0 && v.s.nEnsureNewCall == 0 );
+                    }
+
+                    CPPUNIT_ASSERT( !l.empty() );
+
+                    CPPUNIT_ASSERT( l.insert( v3 ))     ;   // true
+                    CPPUNIT_ASSERT( l.find( v3.key() ) == &v3 );
+
+                    CPPUNIT_ASSERT( v3.s.nFindCall == 0 );
+                    CPPUNIT_ASSERT( l.find( v3.key(), find_functor() ));
+                    CPPUNIT_ASSERT( v3.s.nFindCall == 1 );
+
+                    {
+                        typename UnordList::iterator it = l.begin();
+                        typename UnordList::const_iterator cit = l.cbegin();
+                        CPPUNIT_ASSERT( it != l.end() );
+                        CPPUNIT_ASSERT( it != l.cend() );
+                        CPPUNIT_ASSERT( cit != l.end() );
+                        CPPUNIT_ASSERT( cit != l.cend() );
+                        CPPUNIT_ASSERT( cit == it );
+
+                        CPPUNIT_ASSERT( it->nKey == v1.nKey );
+                        CPPUNIT_ASSERT( it->nVal == v1.nVal );
+                        CPPUNIT_ASSERT( ++it != l.end() );
+                        CPPUNIT_ASSERT( it->nKey == v2.nKey );
+                        CPPUNIT_ASSERT( it->nVal == v2.nVal );
+                        CPPUNIT_ASSERT( it++ != l.end() );
+                        CPPUNIT_ASSERT( it->nKey == v3.nKey );
+                        CPPUNIT_ASSERT( it->nVal == v3.nVal );
+                        CPPUNIT_ASSERT( it++ != l.end() );
+                        CPPUNIT_ASSERT( it == l.end() );
+                    }
+
+                    {
+                        UnordList const & lref = l;
+                        typename UnordList::const_iterator it = lref.begin();
+                        CPPUNIT_ASSERT( it != l.end() );
+                        CPPUNIT_ASSERT( it->nKey == v1.nKey );
+                        CPPUNIT_ASSERT( it->nVal == v1.nVal );
+                        CPPUNIT_ASSERT( ++it != lref.end() );
+                        CPPUNIT_ASSERT( it->nKey == v2.nKey );
+                        CPPUNIT_ASSERT( it->nVal == v2.nVal );
+                        CPPUNIT_ASSERT( it++ != l.end() );
+                        CPPUNIT_ASSERT( it->nKey == v3.nKey );
+                        CPPUNIT_ASSERT( it->nVal == v3.nVal );
+                        CPPUNIT_ASSERT( it++ != lref.end() );
+                        CPPUNIT_ASSERT( it == l.end() );
+                    }
+                }
+
+                // Disposer called on list destruction
+                CPPUNIT_ASSERT( v1.s.nDisposeCount == 1 );
+                CPPUNIT_ASSERT( v2.s.nDisposeCount == 1 );
+                CPPUNIT_ASSERT( v3.s.nDisposeCount == 1 );
+            }
+        }
+
+        void nogc_base_cmp();
+        void nogc_base_less();
+        void nogc_base_equal_to();
+        void nogc_base_cmpmix();
+        void nogc_base_equal_to_mix();
+        void nogc_base_ic();
+        void nogc_member_cmp();
+        void nogc_member_less();
+        void nogc_member_equal_to();
+        void nogc_member_cmpmix();
+        void nogc_member_equal_to_mix();
+        void nogc_member_ic();
+
+        CPPUNIT_TEST_SUITE(UnorderedIntrusiveLazyListHeaderTest)
+
+            CPPUNIT_TEST(nogc_base_cmp)
+            CPPUNIT_TEST(nogc_base_less)
+            CPPUNIT_TEST(nogc_base_equal_to)
+            CPPUNIT_TEST(nogc_base_cmpmix)
+            CPPUNIT_TEST(nogc_base_equal_to_mix)
+            CPPUNIT_TEST(nogc_base_ic)
+            CPPUNIT_TEST(nogc_member_cmp)
+            CPPUNIT_TEST(nogc_member_less)
+            CPPUNIT_TEST(nogc_member_equal_to)
+            CPPUNIT_TEST(nogc_member_cmpmix)
+            CPPUNIT_TEST(nogc_member_equal_to_mix)
+            CPPUNIT_TEST(nogc_member_ic)
+
+        CPPUNIT_TEST_SUITE_END()
+    };
+}   // namespace unordlist
+
+#endif // #ifndef CDSTEST_HDR_INTRUSIVE_LAZY_H
diff --git a/tests/test-hdr/unordered_list/hdr_intrusive_lazy_nogc.cpp b/tests/test-hdr/unordered_list/hdr_intrusive_lazy_nogc.cpp
new file mode 100644 (file)
index 0000000..d519e60
--- /dev/null
@@ -0,0 +1,168 @@
+//$$CDS-header$$
+
+#include "unordered_list/hdr_intrusive_lazy.h"
+#include <cds/intrusive/lazy_list_nogc.h>
+
+namespace unordlist {
+    namespace {
+        typedef base_int_item< cds::gc::nogc > base_item;
+        typedef member_int_item< cds::gc::nogc > member_item;
+
+        struct cmp_traits : public ci::lazy_list::traits {
+            typedef ci::lazy_list::base_hook< co::gc<cds::gc::nogc> > hook;
+            typedef unordlist::cmp<base_item> compare;
+            typedef faked_disposer disposer;
+            static const bool sort = false;
+        };
+
+        struct less_traits: public ci::lazy_list::traits {
+            typedef ci::lazy_list::base_hook< co::gc<cds::gc::nogc> > hook;
+            typedef unordlist::less<base_item> less;
+            typedef faked_disposer disposer;
+            static const bool sort = false;
+        };
+
+        struct equal_to_traits: public ci::lazy_list::traits {
+            typedef ci::lazy_list::base_hook< co::gc<cds::gc::nogc> > hook;
+            typedef unordlist::equal_to<base_item> equal_to;
+            typedef faked_disposer disposer;
+            static const bool sort = false;
+        };
+
+        typedef typename ci::lazy_list::make_traits<
+            ci::opt::hook< ci::lazy_list::base_hook< co::gc<cds::gc::nogc> > >
+            ,co::less< less<base_item> >
+            ,co::compare< cmp<base_item> >
+            ,ci::opt::disposer< faked_disposer >
+            ,co::sort< false > >::type cmpmix_traits;
+
+        typedef typename ci::lazy_list::make_traits<
+            ci::opt::hook< ci::lazy_list::base_hook< co::gc<cds::gc::nogc> > >
+            ,co::compare< cmp<base_item> >
+            ,co::equal_to< equal_to<base_item> >
+            ,ci::opt::disposer< faked_disposer >
+            ,co::sort< false > >::type equal_to_mix_traits;
+
+        typedef typename ci::lazy_list::make_traits<
+            ci::opt::hook< ci::lazy_list::base_hook< co::gc<cds::gc::nogc> > >
+            ,co::equal_to< equal_to<base_item> >
+            ,ci::opt::disposer< faked_disposer >
+            ,co::item_counter< cds::atomicity::item_counter >
+            ,co::sort< false > >::type ic_traits;
+
+        typedef typename ci::lazy_list::make_traits<
+            ci::opt::hook< ci::lazy_list::member_hook<
+                offsetof( member_item, hMember )
+                ,co::gc<cds::gc::nogc> > >
+            ,co::compare< cmp<member_item> >
+            ,ci::opt::disposer< faked_disposer >
+            ,co::sort< false > >::type member_cmp_traits;
+
+        typedef typename ci::lazy_list::make_traits<
+            ci::opt::hook< ci::lazy_list::member_hook<
+                offsetof( member_item, hMember )
+                ,co::gc<cds::gc::nogc> > >
+            ,co::less< less<member_item> >
+            ,ci::opt::disposer< faked_disposer >
+            ,co::sort< false > >::type member_less_traits;
+
+        typedef typename ci::lazy_list::make_traits<
+            ci::opt::hook< ci::lazy_list::member_hook<
+                offsetof( member_item, hMember )
+                ,co::gc<cds::gc::nogc> > >
+            ,co::equal_to< equal_to<member_item> >
+            ,ci::opt::disposer< faked_disposer >
+            ,co::sort< false > >::type member_equal_to_traits;
+
+        typedef typename ci::lazy_list::make_traits<
+            ci::opt::hook< ci::lazy_list::member_hook<
+                offsetof( member_item, hMember )
+                ,co::gc<cds::gc::nogc> > >
+            ,co::less< less<member_item> >
+            ,co::compare< cmp<member_item> >
+            ,ci::opt::disposer< faked_disposer >
+            ,co::sort< false > >::type member_cmpmix_traits;
+
+        typedef typename ci::lazy_list::make_traits<
+            ci::opt::hook< ci::lazy_list::member_hook<
+                offsetof( member_item, hMember )
+                ,co::gc<cds::gc::nogc> > >
+            ,co::compare< cmp<member_item> >
+            ,co::equal_to< equal_to<member_item> >
+            ,ci::opt::disposer< faked_disposer >
+            ,co::sort< false > >::type member_equal_to_mix_traits;
+
+        typedef typename ci::lazy_list::make_traits<
+            ci::opt::hook< ci::lazy_list::member_hook<
+                offsetof( member_item, hMember ),
+                co::gc<cds::gc::nogc> > >
+            ,co::equal_to< equal_to<member_item> >
+            ,ci::opt::disposer< faked_disposer >
+            ,co::item_counter< cds::atomicity::item_counter >
+            ,co::sort< false > >::type member_ic_traits;
+
+    }
+    void UnorderedIntrusiveLazyListHeaderTest::nogc_base_cmp()
+    {
+        typedef ci::LazyList< cds::gc::nogc, base_item, cmp_traits > list;
+        test_nogc_int<list>();
+    }
+    void UnorderedIntrusiveLazyListHeaderTest::nogc_base_less()
+    {
+        typedef ci::LazyList< cds::gc::nogc, base_item, less_traits > list;
+        test_nogc_int<list>();
+    }
+    void UnorderedIntrusiveLazyListHeaderTest::nogc_base_equal_to()
+    {
+        typedef ci::LazyList< cds::gc::nogc, base_item, equal_to_traits > list;
+        test_nogc_int<list>();
+    }
+    void UnorderedIntrusiveLazyListHeaderTest::nogc_base_cmpmix()
+    {
+        typedef ci::LazyList< cds::gc::nogc, base_item, cmpmix_traits > list;
+        test_nogc_int<list>();
+    }
+    void UnorderedIntrusiveLazyListHeaderTest::nogc_base_equal_to_mix()
+    {
+        typedef ci::LazyList< cds::gc::nogc, base_item, equal_to_mix_traits > list;
+        test_nogc_int<list>();
+    }
+    void UnorderedIntrusiveLazyListHeaderTest::nogc_base_ic()
+    {
+        typedef ci::LazyList< cds::gc::nogc, base_item, ic_traits > list;
+        test_nogc_int<list>();
+    }
+    void UnorderedIntrusiveLazyListHeaderTest::nogc_member_cmp()
+    {
+        typedef ci::LazyList< cds::gc::nogc, member_item, member_cmp_traits > list;
+        test_nogc_int<list>();
+    }
+    void UnorderedIntrusiveLazyListHeaderTest::nogc_member_less()
+    {
+        typedef ci::LazyList< cds::gc::nogc, member_item, member_less_traits > list;
+        test_nogc_int<list>();
+    }
+    void UnorderedIntrusiveLazyListHeaderTest::nogc_member_equal_to()
+    {
+        typedef ci::LazyList< cds::gc::nogc, member_item, member_equal_to_traits > list;
+        test_nogc_int<list>();
+    }
+    void UnorderedIntrusiveLazyListHeaderTest::nogc_member_cmpmix()
+    {
+        typedef ci::LazyList< cds::gc::nogc, member_item, member_cmpmix_traits > list;
+        test_nogc_int<list>();
+    }
+    void UnorderedIntrusiveLazyListHeaderTest::nogc_member_equal_to_mix()
+    {
+        typedef ci::LazyList< cds::gc::nogc, member_item, member_equal_to_mix_traits > list;
+        test_nogc_int<list>();
+    }
+    void UnorderedIntrusiveLazyListHeaderTest::nogc_member_ic()
+    {
+        typedef ci::LazyList< cds::gc::nogc, member_item, member_ic_traits > list;
+        test_nogc_int<list>();
+    }
+
+} // namespace unordlist
+
+CPPUNIT_TEST_SUITE_REGISTRATION(unordlist::UnorderedIntrusiveLazyListHeaderTest);
diff --git a/tests/test-hdr/unordered_list/hdr_lazy.h b/tests/test-hdr/unordered_list/hdr_lazy.h
new file mode 100644 (file)
index 0000000..7321c08
--- /dev/null
@@ -0,0 +1,238 @@
+//$$CDS-header$$
+
+#ifndef CDSTEST_HDR_LAZY_H
+#define CDSTEST_HDR_LAZY_H
+
+#include "cppunit/cppunit_proxy.h"
+#include <cds/container/details/lazy_list_base.h>
+
+namespace unordlist {
+    namespace cc = cds::container;
+    namespace co = cds::container::opt;
+
+    class UnorderedLazyListTestHeader: public CppUnitMini::TestCase
+    {
+    public:
+        struct stat {
+            int nEnsureExistsCall;
+            int nEnsureNewCall;
+
+            stat()
+            {
+                nEnsureExistsCall
+                    = nEnsureNewCall
+                    = 0;
+            }
+        };
+
+        struct item {
+            int     nKey;
+            int     nVal;
+
+            stat    s;
+
+            item(int key)
+                : nKey( key )
+                , nVal( key * 2 )
+                , s()
+            {}
+
+            item(int key, int val)
+                : nKey( key )
+                , nVal(val)
+                , s()
+            {}
+
+            item( item const& v )
+                : nKey( v.nKey )
+                , nVal( v.nVal )
+                , s()
+            {}
+
+            int key() const
+            {
+                return nKey;
+            }
+        };
+
+        template <typename T>
+        struct lt
+        {
+            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;
+            }
+        };
+
+        template <typename T>
+        struct equal_to {
+            int operator ()(const T& v1, const T& v2 ) const
+            {
+                return v1.key() == v2.key();
+            }
+
+            template <typename Q>
+            int operator ()(const T& v1, const Q& v2 ) const
+            {
+                return v1.key() == v2;
+            }
+
+            template <typename Q>
+            int operator ()(const Q& v1, const T& v2 ) const
+            {
+                return v1 == v2.key();
+            }
+        };
+
+    protected:
+        template <class UnordList>
+        void nogc_test()
+        {
+            typedef UnordList list;
+            typedef typename list::value_type    value_type;
+            typedef std::pair<typename list::iterator, bool> ensure_result;
+
+            typename list::iterator it;
+
+            list l;
+            CPPUNIT_ASSERT( l.empty() );
+            CPPUNIT_ASSERT( l.insert(50) != l.end() );
+            CPPUNIT_ASSERT( !l.empty() );
+
+            ensure_result eres = l.ensure( item(100, 33) );
+            CPPUNIT_ASSERT( eres.second );
+            CPPUNIT_ASSERT( eres.first != l.end() );
+            CPPUNIT_ASSERT( l.insert( item(150) ) != l.end() );
+
+            CPPUNIT_ASSERT( l.insert(100) == l.end() );
+            eres = l.ensure( item(50, 33) );
+            CPPUNIT_ASSERT( !eres.second );
+            CPPUNIT_ASSERT( eres.first->nVal == eres.first->nKey * 2 );
+            eres.first->nVal = 63;
+
+            it = l.find( 33 );
+            CPPUNIT_ASSERT( it == l.end() );
+
+            it = l.find( 50 );
+            CPPUNIT_ASSERT( it != l.end() );
+            CPPUNIT_ASSERT( it->nKey == 50 );
+            CPPUNIT_ASSERT( it->nVal == 63 );
+
+            it = l.find( 100 );
+            CPPUNIT_ASSERT( it != l.end() );
+            CPPUNIT_ASSERT( it->nKey == 100 );
+            CPPUNIT_ASSERT( it->nVal == 33 );
+
+            it = l.find_with( 150, equal_to<value_type>() );
+            CPPUNIT_ASSERT( it != l.end() );
+            CPPUNIT_ASSERT( it->nKey == 150 );
+            CPPUNIT_ASSERT( it->nVal == it->nKey * 2 );
+
+            CPPUNIT_ASSERT( !l.empty() );
+            l.clear();
+            CPPUNIT_ASSERT( l.empty() );
+
+            // insert test
+            CPPUNIT_ASSERT( l.emplace( 501 ) != l.end());
+            CPPUNIT_ASSERT( l.emplace( 251, 152 ) != l.end());
+            CPPUNIT_ASSERT( l.emplace( item( 1001 )) != l.end());
+
+            // insert failed - such key exists
+            CPPUNIT_ASSERT( l.emplace( 501, 2 ) == l.end());
+            CPPUNIT_ASSERT( l.emplace( 251, 10) == l.end());
+
+            it = l.find( 501 );
+            CPPUNIT_ASSERT( it != l.end() );
+            CPPUNIT_ASSERT( it->nKey == 501 );
+            CPPUNIT_ASSERT( it->nVal == 501 * 2 );
+
+            it = l.find( 1001 );
+            CPPUNIT_ASSERT( it != l.end() );
+            CPPUNIT_ASSERT( it->nKey == 1001 );
+            CPPUNIT_ASSERT( it->nVal == 1001 * 2 );
+
+            {
+                typename UnordList::iterator it( l.begin() );
+                typename UnordList::const_iterator cit( l.cbegin() );
+                CPPUNIT_CHECK( it == cit );
+                CPPUNIT_CHECK( it != l.end() );
+                CPPUNIT_CHECK( it != l.cend() );
+                CPPUNIT_CHECK( cit != l.end() );
+                CPPUNIT_CHECK( cit != l.cend() );
+                ++it;
+                CPPUNIT_CHECK( it != cit );
+                CPPUNIT_CHECK( it != l.end() );
+                CPPUNIT_CHECK( it != l.cend() );
+                CPPUNIT_CHECK( cit != l.end() );
+                CPPUNIT_CHECK( cit != l.cend() );
+                ++cit;
+                CPPUNIT_CHECK( it == cit );
+                CPPUNIT_CHECK( it != l.end() );
+                CPPUNIT_CHECK( it != l.cend() );
+                CPPUNIT_CHECK( cit != l.end() );
+                CPPUNIT_CHECK( cit != l.cend() );
+            }
+
+
+            l.clear();
+            CPPUNIT_ASSERT( l.empty() );
+        }
+
+        void NOGC_cmp();
+        void NOGC_less();
+        void NOGC_equal_to();
+        void NOGC_cmpmix();
+        void NOGC_equal_to_mix();
+        void NOGC_ic();
+
+        CPPUNIT_TEST_SUITE(UnorderedLazyListTestHeader)
+            CPPUNIT_TEST(NOGC_cmp)
+            CPPUNIT_TEST(NOGC_less)
+            CPPUNIT_TEST(NOGC_equal_to)
+            CPPUNIT_TEST(NOGC_cmpmix)
+            CPPUNIT_TEST(NOGC_equal_to_mix)
+            CPPUNIT_TEST(NOGC_ic)
+        CPPUNIT_TEST_SUITE_END()
+    };
+
+}   // namespace unordlist
+
+#endif // #ifndef CDSTEST_HDR_LAZY_H
diff --git a/tests/test-hdr/unordered_list/hdr_lazy_kv.h b/tests/test-hdr/unordered_list/hdr_lazy_kv.h
new file mode 100644 (file)
index 0000000..ab37f57
--- /dev/null
@@ -0,0 +1,264 @@
+//$$CDS-header$$
+
+#ifndef CDSTEST_HDR_LAZY_KV_H
+#define CDSTEST_HDR_LAZY_KV_H
+
+#include "cppunit/cppunit_proxy.h"
+#include <cds/container/details/lazy_list_base.h>
+
+namespace unordlist {
+    namespace cc = cds::container;
+    namespace co = cds::container::opt;
+
+    class UnorderedLazyKVListTestHeader: public CppUnitMini::TestCase
+    {
+    public:
+        typedef int key_type;
+        struct value_type {
+            int m_val;
+
+            value_type()
+                : m_val(0)
+            {}
+
+            value_type( int n )
+                : m_val( n )
+            {}
+        };
+
+        template <typename T>
+        struct lt
+        {
+            bool operator ()(const T& v1, const T& v2 ) const
+            {
+                return v1 < v2;
+            }
+        };
+
+        template <typename T>
+        struct cmp {
+            int operator ()(const T& v1, const T& v2 ) const
+            {
+                if ( v1 < v2 )
+                    return -1;
+                return v1 > v2 ? 1 : 0;
+            }
+        };
+
+        template <typename T>
+        struct eq {
+            bool operator ()(const T& v1, const T& v2 ) const
+            {
+                return v1 == v2;
+            }
+        };
+
+        struct insert_functor {
+            template <typename T>
+            void operator()( T& pair )
+            {
+                pair.second.m_val = pair.first * 10;
+            }
+        };
+
+    protected:
+        template <class UnordList>
+        void nogc_test()
+        {
+            typedef typename UnordList::value_type    value_type;
+            typedef typename UnordList::iterator      iterator;
+
+            {
+                UnordList l;
+                iterator it;
+
+                CPPUNIT_ASSERT( l.empty() );
+
+                // insert / find test
+                CPPUNIT_ASSERT( l.find( 100 ) == l.end() );
+                CPPUNIT_ASSERT( l.insert( 100 ) != l.end() );
+                CPPUNIT_ASSERT( !l.empty() );
+                it = l.find( 100 );
+                CPPUNIT_ASSERT( it != l.end() );
+                CPPUNIT_ASSERT( it.key() == 100 );
+                CPPUNIT_ASSERT( it.val().m_val == 0 );
+
+                CPPUNIT_ASSERT( l.find_with( 50, eq<key_type>() ) == l.end() );
+                CPPUNIT_ASSERT( l.insert( 50, 500 ) != l.end());
+                it = l.find( 50 );
+                CPPUNIT_ASSERT( it != l.end() );
+                CPPUNIT_ASSERT( it.key() == 50 );
+                CPPUNIT_ASSERT( it.val().m_val == 500 );
+
+                CPPUNIT_ASSERT( l.insert( 50, 5 ) == l.end() );
+                it = l.find( 50 );
+                CPPUNIT_ASSERT( it != l.end() );
+                CPPUNIT_ASSERT( it.key() == 50 );
+                CPPUNIT_ASSERT( it.val().m_val == 500 );
+                CPPUNIT_ASSERT( !l.empty() );
+
+                CPPUNIT_ASSERT( l.find( 150 ) == l.end() );
+                CPPUNIT_ASSERT( l.insert_with( 150, insert_functor() ) != l.end() );
+                it = l.find( 150 );
+                CPPUNIT_ASSERT( it != l.end() );
+                CPPUNIT_ASSERT( it.key() == 150 );
+                CPPUNIT_ASSERT( it.val().m_val == 1500 );
+                it = l.find( 100 );
+                CPPUNIT_ASSERT( it != l.end() );
+                CPPUNIT_ASSERT( it.key() == 100 );
+                CPPUNIT_ASSERT( it.val().m_val == 0 );
+                it = l.find( 50 );
+                CPPUNIT_ASSERT( it != l.end() );
+                CPPUNIT_ASSERT( it.key() == 50 );
+                CPPUNIT_ASSERT( it.val().m_val == 500 );
+                it.val().m_val = 25;
+                it = l.find( 50 );
+                CPPUNIT_ASSERT( it != l.end() );
+                CPPUNIT_ASSERT( it.key() == 50 );
+                CPPUNIT_ASSERT( it.val().m_val == 25 );
+                CPPUNIT_ASSERT( !l.empty() );
+
+                // ensure existing item
+                std::pair<iterator, bool> ensureResult;
+                ensureResult = l.ensure( 100 );
+                CPPUNIT_ASSERT( !ensureResult.second );
+                CPPUNIT_ASSERT( ensureResult.first.key() == 100 );
+                CPPUNIT_ASSERT( ensureResult.first.val().m_val == 0   );
+                ensureResult.first.val().m_val = 5;
+                it = l.find( 100 );
+                CPPUNIT_ASSERT( it != l.end() );
+                CPPUNIT_ASSERT( it.key() == 100 );
+                CPPUNIT_ASSERT( it.val().m_val == 5 );
+
+                CPPUNIT_ASSERT( !l.empty() );
+
+                // ensure new item
+                ensureResult = l.ensure( 1000 );
+                CPPUNIT_ASSERT( ensureResult.second );
+                CPPUNIT_ASSERT( ensureResult.first.key() == 1000 );
+                CPPUNIT_ASSERT( ensureResult.first.val().m_val == 0   );
+                ensureResult.first.val().m_val = 33;
+                ensureResult = l.ensure( 1000 );
+                CPPUNIT_ASSERT( !ensureResult.second );
+                CPPUNIT_ASSERT( ensureResult.first.key() == 1000 );
+                CPPUNIT_ASSERT( ensureResult.first.val().m_val == 33   );
+
+                // clear test
+                l.clear();
+                CPPUNIT_ASSERT( l.empty() );
+
+                // insert test
+                CPPUNIT_ASSERT( l.emplace( 501 ) != l.end());
+                CPPUNIT_ASSERT( l.emplace( 251, 152 ) != l.end());
+
+                // insert failed - such key exists
+                CPPUNIT_ASSERT( l.emplace( 501, 2 ) == l.end());
+                CPPUNIT_ASSERT( l.emplace( 251, 10) == l.end());
+
+                it = l.find(501);
+                CPPUNIT_ASSERT( it != l.end() );
+                CPPUNIT_ASSERT( it.key() == 501 );
+                CPPUNIT_ASSERT( it.val().m_val == 0 );
+
+                it = l.find(251);
+                CPPUNIT_ASSERT( it != l.end() );
+                CPPUNIT_ASSERT( it.key() == 251 );
+                CPPUNIT_ASSERT( it.val().m_val == 152 );
+
+                l.clear();
+                CPPUNIT_ASSERT( l.empty() );
+
+                // Iterator test
+                {
+                    int nCount = 100;
+                    for ( int i = 0; i < nCount; ++i )
+                        CPPUNIT_ASSERT( l.insert(i, i * 2 ) != l.end() );
+
+                    {
+                        typename UnordList::iterator it( l.begin() );
+                        typename UnordList::const_iterator cit( l.cbegin() );
+                        CPPUNIT_CHECK( it == cit );
+                        CPPUNIT_CHECK( it != l.end() );
+                        CPPUNIT_CHECK( it != l.cend() );
+                        CPPUNIT_CHECK( cit != l.end() );
+                        CPPUNIT_CHECK( cit != l.cend() );
+                        ++it;
+                        CPPUNIT_CHECK( it != cit );
+                        CPPUNIT_CHECK( it != l.end() );
+                        CPPUNIT_CHECK( it != l.cend() );
+                        CPPUNIT_CHECK( cit != l.end() );
+                        CPPUNIT_CHECK( cit != l.cend() );
+                        ++cit;
+                        CPPUNIT_CHECK( it == cit );
+                        CPPUNIT_CHECK( it != l.end() );
+                        CPPUNIT_CHECK( it != l.cend() );
+                        CPPUNIT_CHECK( cit != l.end() );
+                        CPPUNIT_CHECK( cit != l.cend() );
+                    }
+
+                    int i = 0;
+                    for ( typename UnordList::iterator iter = l.begin(), itEnd = l.end(); iter != itEnd; ++iter, ++i ) {
+                        CPPUNIT_ASSERT( iter.key() == i );
+                        CPPUNIT_ASSERT( iter->first == i );
+                        CPPUNIT_ASSERT( (*iter).first == i );
+
+                        CPPUNIT_ASSERT( iter.val().m_val == i * 2 );
+                        CPPUNIT_ASSERT( iter->second.m_val == i * 2 );
+                        CPPUNIT_ASSERT( (*iter).second.m_val == i * 2 );
+
+                        iter.val().m_val = i * 3;
+                    }
+
+                    // Check that we have visited all items
+                    for ( int i = 0; i < nCount; ++i ) {
+                        it = l.find( i );
+                        CPPUNIT_ASSERT( it != l.end() );
+                        CPPUNIT_ASSERT( it.key() == i );
+                        CPPUNIT_ASSERT( it.val().m_val == i * 3 );
+                    }
+
+                    l.clear();
+                    CPPUNIT_ASSERT( l.empty() );
+
+                    // Const iterator
+                    for ( int i = 0; i < nCount; ++i )
+                        CPPUNIT_ASSERT( l.insert(i, i * 7) != l.end() );
+
+                    i = 0;
+                    const UnordList& rl = l;
+                    for ( typename UnordList::const_iterator iter = rl.begin(), itEnd = rl.end(); iter != itEnd; ++iter, ++i ) {
+                        CPPUNIT_ASSERT( iter.key() == i );
+                        CPPUNIT_ASSERT( iter->first == i );
+                        CPPUNIT_ASSERT( (*iter).first == i );
+
+                        CPPUNIT_ASSERT( iter.val().m_val == i * 7 );
+                        CPPUNIT_ASSERT( iter->second.m_val == i * 7 );
+                        CPPUNIT_ASSERT( (*iter).second.m_val == i * 7 );
+                        // it.val().m_val = i * 3    ; // error: const-iterator
+                    }
+
+                    l.clear();
+                    CPPUNIT_ASSERT( l.empty() );
+                }
+
+            }
+        }
+
+        void NOGC_cmp();
+        void NOGC_less();
+        void NOGC_equal_to();
+        void NOGC_cmpmix();
+        void NOGC_ic();
+
+        CPPUNIT_TEST_SUITE(UnorderedLazyKVListTestHeader)
+            CPPUNIT_TEST(NOGC_cmp)
+            CPPUNIT_TEST(NOGC_less)
+            CPPUNIT_TEST(NOGC_equal_to)
+            CPPUNIT_TEST(NOGC_cmpmix)
+            CPPUNIT_TEST(NOGC_ic)
+        CPPUNIT_TEST_SUITE_END()
+    };
+
+}   // namespace unordlist
+
+#endif // #ifndef CDSTEST_HDR_LAZY_KV_H
diff --git a/tests/test-hdr/unordered_list/hdr_lazy_kv_nogc.cpp b/tests/test-hdr/unordered_list/hdr_lazy_kv_nogc.cpp
new file mode 100644 (file)
index 0000000..66e0179
--- /dev/null
@@ -0,0 +1,132 @@
+//$$CDS-header$$
+
+#include "unordered_list/hdr_lazy_kv.h"
+#include <cds/container/lazy_kvlist_nogc.h>
+
+namespace unordlist {
+    namespace {
+        struct NOGC_cmp_traits: public cc::lazy_list::traits
+        {
+            typedef UnorderedLazyKVListTestHeader::cmp<UnorderedLazyKVListTestHeader::key_type>   compare;
+            static const bool sort = false;
+        };
+
+    }
+    void UnorderedLazyKVListTestHeader::NOGC_cmp()
+    {
+        // traits-based version
+        typedef cc::LazyKVList< cds::gc::nogc, key_type, value_type, NOGC_cmp_traits > list;
+        nogc_test< list >();
+
+        // option-based version
+        typedef cc::LazyKVList< cds::gc::nogc,
+            key_type,
+            value_type,
+            cc::lazy_list::make_traits<
+                cc::opt::compare< cmp<key_type> >
+                ,cc::opt::sort<false>
+            >::type
+        > opt_list;
+        nogc_test< opt_list >();
+    }
+
+    namespace {
+        struct NOGC_less_traits : public cc::lazy_list::traits
+        {
+            typedef UnorderedLazyKVListTestHeader::lt<UnorderedLazyKVListTestHeader::key_type>   less;
+            static const bool sort = false;
+        };
+    }
+    void UnorderedLazyKVListTestHeader::NOGC_less()
+    {
+        // traits-based version
+        typedef cc::LazyKVList< cds::gc::nogc, key_type, value_type, NOGC_less_traits > list;
+        nogc_test< list >();
+
+        // option-based version
+        typedef cc::LazyKVList< cds::gc::nogc, key_type, value_type,
+            cc::lazy_list::make_traits<
+                cc::opt::less< lt<key_type> >
+                ,cc::opt::sort<false>
+            >::type
+        > opt_list;
+        nogc_test< opt_list >();
+    }
+
+    namespace {
+        struct NOGC_equal_to_traits : public cc::lazy_list::traits
+        {
+            typedef UnorderedLazyKVListTestHeader::eq<UnorderedLazyKVListTestHeader::key_type>   equal_to;
+            static const bool sort = false;
+        };
+    }
+    void UnorderedLazyKVListTestHeader::NOGC_equal_to()
+    {
+        // traits-based version
+        typedef cc::LazyKVList< cds::gc::nogc, key_type, value_type, NOGC_equal_to_traits > list;
+        nogc_test< list >();
+
+        // option-based version
+        typedef cc::LazyKVList< cds::gc::nogc, key_type, value_type,
+            cc::lazy_list::make_traits<
+                cc::opt::equal_to< eq<key_type> >
+                ,cc::opt::sort<false>
+            >::type
+        > opt_list;
+        nogc_test< opt_list >();
+    }
+
+    namespace {
+        struct NOGC_cmpmix_traits : public cc::lazy_list::traits
+        {
+            typedef UnorderedLazyKVListTestHeader::cmp<UnorderedLazyKVListTestHeader::key_type>   compare;
+            typedef UnorderedLazyKVListTestHeader::lt<UnorderedLazyKVListTestHeader::key_type>  less;
+            typedef UnorderedLazyKVListTestHeader::eq<UnorderedLazyKVListTestHeader::key_type>   equal_to;
+            static const bool sort = false;
+        };
+    }
+    void UnorderedLazyKVListTestHeader::NOGC_cmpmix()
+    {
+        // traits-based version
+        typedef cc::LazyKVList< cds::gc::nogc, key_type, value_type, NOGC_cmpmix_traits > list;
+        nogc_test< list >();
+
+        // option-based version
+        typedef cc::LazyKVList< cds::gc::nogc, key_type, value_type,
+            cc::lazy_list::make_traits<
+                cc::opt::compare< cmp<key_type> >
+                ,cc::opt::less< lt<key_type> >
+                ,cc::opt::equal_to< eq<key_type> >
+                ,cc::opt::sort<false>
+            >::type
+        > opt_list;
+        nogc_test< opt_list >();
+    }
+
+    namespace {
+        struct NOGC_ic_traits : public cc::lazy_list::traits
+        {
+            typedef UnorderedLazyKVListTestHeader::eq<UnorderedLazyKVListTestHeader::key_type>   equal_to;
+            typedef cds::atomicity::item_counter item_counter;
+            static const bool sort = false;
+        };
+    }
+    void UnorderedLazyKVListTestHeader::NOGC_ic()
+    {
+        // traits-based version
+        typedef cc::LazyKVList< cds::gc::nogc, key_type, value_type, NOGC_ic_traits > list;
+        nogc_test< list >();
+
+        // option-based version
+        typedef cc::LazyKVList< cds::gc::nogc, key_type, value_type,
+            cc::lazy_list::make_traits<
+                cc::opt::equal_to< eq<key_type> >
+                ,cc::opt::item_counter< cds::atomicity::item_counter >
+                ,cc::opt::sort<false>
+            >::type
+        > opt_list;
+        nogc_test< opt_list >();
+    }
+
+}   // namespace unordlist
+CPPUNIT_TEST_SUITE_REGISTRATION(unordlist::UnorderedLazyKVListTestHeader);
diff --git a/tests/test-hdr/unordered_list/hdr_lazy_nogc.cpp b/tests/test-hdr/unordered_list/hdr_lazy_nogc.cpp
new file mode 100644 (file)
index 0000000..c1538ab
--- /dev/null
@@ -0,0 +1,153 @@
+//$$CDS-header$$
+
+#include "unordered_list/hdr_lazy.h"
+#include <cds/container/lazy_list_nogc.h>
+
+namespace unordlist {
+    namespace {
+        struct NOGC_cmp_traits : public cc::lazy_list::traits
+        {
+            typedef UnorderedLazyListTestHeader::cmp<UnorderedLazyListTestHeader::item>   compare;
+            static const bool sort = false;
+        };
+    }
+    void UnorderedLazyListTestHeader::NOGC_cmp()
+    {
+        // traits-based version
+        typedef cc::LazyList< cds::gc::nogc, item, NOGC_cmp_traits > list;
+        nogc_test< list >();
+
+        // option-based version
+        typedef cc::LazyList< cds::gc::nogc, item,
+            cc::lazy_list::make_traits<
+                cc::opt::compare< cmp<item> >
+                ,cc::opt::sort<false>
+            >::type
+        > opt_list;
+        nogc_test< opt_list >();
+    }
+
+    namespace {
+        struct NOGC_less_traits : public cc::lazy_list::traits
+        {
+            typedef UnorderedLazyListTestHeader::lt<UnorderedLazyListTestHeader::item>   less;
+            static const bool sort = false;
+        };
+    }
+    void UnorderedLazyListTestHeader::NOGC_less()
+    {
+        // traits-based version
+        typedef cc::LazyList< cds::gc::nogc, item, NOGC_less_traits > list;
+        nogc_test< list >();
+
+        // option-based version
+        typedef cc::LazyList< cds::gc::nogc, item,
+            cc::lazy_list::make_traits<
+                cc::opt::less< lt<item> >
+                ,cc::opt::sort<false>
+            >::type
+        > opt_list;
+        nogc_test< opt_list >();
+    }
+
+    namespace {
+        struct NOGC_equal_to_traits : public cc::lazy_list::traits
+        {
+            typedef UnorderedLazyListTestHeader::equal_to<UnorderedLazyListTestHeader::item> equal_to;
+            static const bool sort = false;
+        };
+    }
+    void UnorderedLazyListTestHeader::NOGC_equal_to()
+    {
+        // traits-based version
+        typedef cc::LazyList< cds::gc::nogc, item, NOGC_equal_to_traits > list;
+        nogc_test< list >();
+
+        // option-based version
+        typedef cc::LazyList< cds::gc::nogc, item,
+            cc::lazy_list::make_traits<
+                cc::opt::equal_to< equal_to<item> >
+                ,cc::opt::sort<false>
+            >::type
+        > opt_list;
+        nogc_test< opt_list >();
+    }
+
+    namespace {
+        struct NOGC_cmpmix_traits : public cc::lazy_list::traits
+        {
+            typedef UnorderedLazyListTestHeader::cmp<UnorderedLazyListTestHeader::item>   compare;
+            typedef UnorderedLazyListTestHeader::lt<UnorderedLazyListTestHeader::item>  less;
+            static const bool sort = false;
+        };
+    }
+    void UnorderedLazyListTestHeader::NOGC_cmpmix()
+    {
+        // traits-based version
+        typedef cc::LazyList< cds::gc::nogc, item, NOGC_cmpmix_traits > list;
+        nogc_test< list >();
+
+        // option-based version
+        typedef cc::LazyList< cds::gc::nogc, item,
+            cc::lazy_list::make_traits<
+                cc::opt::compare< cmp<item> >
+                ,cc::opt::less< lt<item> >
+                ,cc::opt::sort<false>
+            >::type
+        > opt_list;
+        nogc_test< opt_list >();
+    }
+
+    namespace {
+        struct NOGC_equal_to_mix_traits : public cc::lazy_list::traits
+        {
+            typedef UnorderedLazyListTestHeader::cmp<UnorderedLazyListTestHeader::item> compare;
+            typedef UnorderedLazyListTestHeader::lt<UnorderedLazyListTestHeader::item> less;
+            typedef UnorderedLazyListTestHeader::equal_to<UnorderedLazyListTestHeader::item> equal_to;
+            static const bool sort = false;
+        };
+    }
+    void UnorderedLazyListTestHeader::NOGC_equal_to_mix()
+    {
+        // traits-based version
+        typedef cc::LazyList< cds::gc::nogc, item, NOGC_equal_to_mix_traits > list;
+        nogc_test< list >();
+
+        // option-based version
+        typedef cc::LazyList< cds::gc::nogc, item,
+            cc::lazy_list::make_traits<
+                cc::opt::compare< cmp<item> >
+                ,cc::opt::less< lt<item> >
+                ,cc::opt::equal_to< equal_to<item> >
+                ,cc::opt::sort<false>
+            >::type
+        > opt_list;
+        nogc_test< opt_list >();
+    }
+    namespace {
+        struct NOGC_ic_traits : public cc::lazy_list::traits
+        {
+            typedef UnorderedLazyListTestHeader::equal_to<UnorderedLazyListTestHeader::item> equal_to;
+            typedef cds::atomicity::item_counter item_counter;
+            static const bool sort = false;
+        };
+    }
+    void UnorderedLazyListTestHeader::NOGC_ic()
+    {
+        // traits-based version
+        typedef cc::LazyList< cds::gc::nogc, item, NOGC_ic_traits > list;
+        nogc_test< list >();
+
+        // option-based version
+        typedef cc::LazyList< cds::gc::nogc, item,
+            cc::lazy_list::make_traits<
+                cc::opt::equal_to< equal_to<item> >
+                ,cc::opt::item_counter< cds::atomicity::item_counter >
+                ,cc::opt::sort<false>
+            >::type
+        > opt_list;
+        nogc_test< opt_list >();
+    }
+
+}   // namespace unordlist
+CPPUNIT_TEST_SUITE_REGISTRATION(unordlist::UnorderedLazyListTestHeader);