ed9f5111c5b0064d865a51d07a1063cacca71574
[libcds.git] / cds / intrusive / split_list.h
1 //$$CDS-header$$
2
3 #ifndef __CDS_INTRUSIVE_SPLIT_LIST_H
4 #define __CDS_INTRUSIVE_SPLIT_LIST_H
5
6 #include <cds/intrusive/details/split_list_base.h>
7
8 namespace cds { namespace intrusive {
9
10     /// Split-ordered list
11     /** @ingroup cds_intrusive_map
12         \anchor cds_intrusive_SplitListSet_hp
13
14         Hash table implementation based on split-ordered list algorithm discovered by Ori Shalev and Nir Shavit, see
15         - [2003] Ori Shalev, Nir Shavit "Split-Ordered Lists - Lock-free Resizable Hash Tables"
16         - [2008] Nir Shavit "The Art of Multiprocessor Programming"
17
18         The split-ordered list is a lock-free implementation of an extensible unbounded hash table. It uses original
19         recursive split-ordering algorithm discovered by Ori Shalev and Nir Shavit that allows to split buckets
20         without items moving on resizing.
21
22         \anchor cds_SplitList_algo_desc
23         <b>Short description</b>
24         [from [2003] Ori Shalev, Nir Shavit "Split-Ordered Lists - Lock-free Resizable Hash Tables"]
25
26         The algorithm keeps all the items in one lock-free linked list, and gradually assigns the bucket pointers to
27         the places in the list where a sublist of \93correct\94 items can be found. A bucket is initialized upon first
28         access by assigning it to a new \93dummy\94 node (dashed contour) in the list, preceding all items that should be
29         in that bucket. A newly created bucket splits an older bucket\92s chain, reducing the access cost to its items. The
30         table uses a modulo 2**i hash (there are known techniques for \93pre-hashing\94 before a modulo 2**i hash
31         to overcome possible binary correlations among values). The table starts at size 2 and repeatedly doubles in size.
32
33         Unlike moving an item, the operation of directing a bucket pointer can be done
34         in a single CAS operation, and since items are not moved, they are never \93lost\94.
35         However, to make this approach work, one must be able to keep the items in the
36         list sorted in such a way that any bucket\92s sublist can be \93split\94 by directing a new
37         bucket pointer within it. This operation must be recursively repeatable, as every
38         split bucket may be split again and again as the hash table grows. To achieve this
39         goal the authors introduced recursive split-ordering, a new ordering on keys that keeps items
40         in a given bucket adjacent in the list throughout the repeated splitting process.
41
42         Magically, yet perhaps not surprisingly, recursive split-ordering is achieved by
43         simple binary reversal: reversing the bits of the hash key so that the new key\92s
44         most significant bits (MSB) are those that were originally its least significant.
45         The split-order keys of regular nodes are exactly the bit-reverse image of the original
46         keys after turning on their MSB. For example, items 9 and 13 are in the <tt>1 mod
47         4</tt> bucket, which can be recursively split in two by inserting a new node between
48         them.
49
50         To insert (respectively delete or search for) an item in the hash table, hash its
51         key to the appropriate bucket using recursive split-ordering, follow the pointer to
52         the appropriate location in the sorted items list, and traverse the list until the key\92s
53         proper location in the split-ordering (respectively until the key or a key indicating
54         the item is not in the list is found). Because of the combinatorial structure induced
55         by the split-ordering, this will require traversal of no more than an expected constant number of items.
56
57         The design is modular: to implement the ordered items list, you can use one of several
58         non-blocking list-based set algorithms: MichaelList, LazyList.
59
60         <b>Implementation</b>
61
62         Template parameters are:
63         - \p GC - Garbage collector used. Note the \p GC must be the same as the GC used for \p OrderedList
64         - \p OrderedList - ordered list implementation used as bucket for hash set, for example, MichaelList, LazyList.
65             The intrusive ordered list implementation specifies the type \p T stored in the hash-set, the reclamation
66             schema \p GC used by hash-set, the comparison functor for the type \p T and other features specific for
67             the ordered list.
68         - \p Traits - type traits. See split_list::type_traits for explanation.
69             Instead of defining \p Traits struct you may use option-based syntax with split_list::make_traits metafunction.
70
71         There are several specialization of the split-list class for different \p GC:
72         - for \ref cds_urcu_gc "RCU type" include <tt><cds/intrusive/split_list_rcu.h></tt> - see
73             \ref cds_intrusive_SplitListSet_rcu "RCU-based split-list"
74         - for cds::gc::nogc include <tt><cds/intrusive/split_list_nogc.h></tt> - see
75             \ref cds_intrusive_SplitListSet_nogc "persistent SplitListSet".
76
77         \anchor cds_SplitList_hash_functor
78         <b>Hash functor</b>
79
80         Some member functions of split-ordered list accept the key parameter of type \p Q which differs from \p value_type.
81         It is expected that type \p Q contains full key of \p value_type, and for equal keys of type \p Q and \p value_type
82         the hash values of these keys must be equal too.
83         The hash functor <tt>Traits::hash</tt> should accept parameters of both type:
84         \code
85         // Our node type
86         struct Foo {
87             std::string     key_    ;   // key field
88             // ... other fields
89         };
90
91         // Hash functor
92         struct fooHash {
93             size_t operator()( const std::string& s ) const
94             {
95                 return std::hash( s );
96             }
97
98             size_t operator()( const Foo& f ) const
99             {
100                 return (*this)( f.key_ );
101             }
102         };
103         \endcode
104
105         <b>How to use</b>
106
107         First, you should choose ordered list type to use in your split-list set:
108         \code
109         // For gc::HP-based MichaelList implementation
110         #include <cds/intrusive/michael_list_hp.h>
111
112         // cds::intrusive::SplitListSet declaration
113         #include <cds/intrusive/split_list.h>
114
115         // Type of set items
116             //  Note you should declare your struct based on cds::intrusive::split_list::node
117             //  which is a wrapper for ordered-list node struct.
118             //  In our case, the node type for HP-based MichaelList is cds::intrusive::michael_list::node< cds::gc::HP >
119         struct Foo: public cds::intrusive::split_list::node< cds::intrusive::michael_list::node< cds::gc::HP > >
120         {
121             std::string     key_    ;   // key field
122             unsigned        val_    ;   // value field
123             // ...  other value fields
124         };
125
126         // Declare comparator for the item
127         struct FooCmp
128         {
129             int operator()( const Foo& f1, const Foo& f2 ) const
130             {
131                 return f1.key_.compare( f2.key_ );
132             }
133         };
134
135         // Declare base ordered-list type for split-list
136         // It may be any ordered list type like MichaelList, LazyList
137         typedef cds::intrusive::MichaelList< cds::gc::HP, Foo,
138             typename cds::intrusive::michael_list::make_traits<
139                 // hook option
140                 cds::intrusive::opt::hook< cds::intrusive::michael_list::base_hook< cds::opt::gc< cds::gc::HP > > >
141                 // item comparator option
142                 ,cds::opt::compare< FooCmp >
143             >::type
144         >  Foo_list;
145         \endcode
146
147         Second, you should declare split-list set container:
148         \code
149
150         // Declare hash functor
151         // Note, the hash functor accepts parameter type Foo and std::string
152         struct FooHash {
153             size_t operator()( const Foo& f ) const
154             {
155                 return cds::opt::v::hash<std::string>()( f.key_ );
156             }
157             size_t operator()( const std::string& s ) const
158             {
159                 return cds::opt::v::hash<std::string>()( s );
160             }
161         };
162
163         // Split-list set typedef
164         typedef cds::intrusive::SplitListSet<
165             cds::gc::HP
166             ,Foo_list
167             ,typename cds::intrusive::split_list::make_traits<
168                 cds::opt::hash< FooHash >
169             >::type
170         > Foo_set;
171         \endcode
172
173         Now, you can use \p Foo_set in your application.
174         \code
175             Foo_set    fooSet;
176             Foo * foo = new Foo;
177             foo->key_ = "First";
178
179             fooSet.insert( *foo );
180
181             // and so on ...
182         \endcode
183
184     */
185     template <
186         class GC,
187         class OrderedList,
188 #   ifdef CDS_DOXYGEN_INVOKED
189         class Traits = split_list::type_traits
190 #   else
191         class Traits
192 #   endif
193     >
194     class SplitListSet
195     {
196     public:
197         typedef Traits          options ;   ///< Traits template parameters
198         typedef GC              gc      ;   ///< Garbage collector
199
200     protected:
201         //@cond
202         typedef split_list::details::rebind_list_options<OrderedList, options> wrapped_ordered_list;
203         //@endcond
204
205     public:
206 #   ifdef CDS_DOXYGEN_INVOKED
207         typedef OrderedList         ordered_list    ;   ///< type of ordered list used as base for split-list
208 #   else
209         typedef typename wrapped_ordered_list::result   ordered_list;
210 #   endif
211         typedef typename ordered_list::value_type       value_type      ;   ///< type of value stored in the split-list
212         typedef typename ordered_list::key_comparator   key_comparator  ;   ///< key comparison functor
213         typedef typename ordered_list::disposer         disposer        ;   ///< Node disposer functor
214
215         /// Hash functor for \p %value_type and all its derivatives that you use
216         typedef typename cds::opt::v::hash_selector< typename options::hash >::type   hash;
217
218         typedef typename options::item_counter          item_counter    ;   ///< Item counter type
219         typedef typename options::back_off              back_off        ;   ///< back-off strategy for spinning
220         typedef typename options::memory_model          memory_model    ;   ///< Memory ordering. See cds::opt::memory_model option
221         typedef typename ordered_list::guarded_ptr      guarded_ptr; ///< Guarded pointer
222
223     protected:
224         typedef typename ordered_list::node_type    list_node_type      ;   ///< Node type as declared in ordered list
225         typedef split_list::node<list_node_type>    node_type           ;   ///< split-list node type
226         typedef node_type                           dummy_node_type     ;   ///< dummy node type
227
228         /// Split-list node traits
229         /**
230             This traits is intended for converting between underlying ordered list node type \ref list_node_type
231             and split-list node type \ref node_type
232         */
233         typedef split_list::node_traits<typename ordered_list::node_traits>  node_traits;
234
235         //@cond
236         /// Bucket table implementation
237         typedef typename split_list::details::bucket_table_selector<
238             options::dynamic_bucket_table
239             , gc
240             , dummy_node_type
241             , opt::allocator< typename options::allocator >
242             , opt::memory_model< memory_model >
243         >::type bucket_table;
244
245         //@endcond
246
247     protected:
248         //@cond
249         /// Ordered list wrapper to access protected members
250         class ordered_list_wrapper: public ordered_list
251         {
252             typedef ordered_list base_class;
253             typedef typename base_class::auxiliary_head       bucket_head_type;
254
255         public:
256             bool insert_at( dummy_node_type * pHead, value_type& val )
257             {
258                 assert( pHead != nullptr );
259                 bucket_head_type h(pHead);
260                 return base_class::insert_at( h, val );
261             }
262
263             template <typename Func>
264             bool insert_at( dummy_node_type * pHead, value_type& val, Func f )
265             {
266                 assert( pHead != nullptr );
267                 bucket_head_type h(pHead);
268                 return base_class::insert_at( h, val, f );
269             }
270
271             template <typename Func>
272             std::pair<bool, bool> ensure_at( dummy_node_type * pHead, value_type& val, Func func )
273             {
274                 assert( pHead != nullptr );
275                 bucket_head_type h(pHead);
276                 return base_class::ensure_at( h, val, func );
277             }
278
279             bool unlink_at( dummy_node_type * pHead, value_type& val )
280             {
281                 assert( pHead != nullptr );
282                 bucket_head_type h(pHead);
283                 return base_class::unlink_at( h, val );
284             }
285
286             template <typename Q, typename Compare, typename Func>
287             bool erase_at( dummy_node_type * pHead, split_list::details::search_value_type<Q> const& val, Compare cmp, Func f )
288             {
289                 assert( pHead != nullptr );
290                 bucket_head_type h(pHead);
291                 return base_class::erase_at( h, val, cmp, f );
292             }
293
294             template <typename Q, typename Compare>
295             bool erase_at( dummy_node_type * pHead, split_list::details::search_value_type<Q> const& val, Compare cmp )
296             {
297                 assert( pHead != nullptr );
298                 bucket_head_type h(pHead);
299                 return base_class::erase_at( h, val, cmp );
300             }
301
302             template <typename Q, typename Compare>
303             bool extract_at( dummy_node_type * pHead, typename gc::Guard& guard, split_list::details::search_value_type<Q> const& val, Compare cmp )
304             {
305                 assert( pHead != nullptr );
306                 bucket_head_type h(pHead);
307                 return base_class::extract_at( h, guard, val, cmp );
308             }
309
310             template <typename Q, typename Compare, typename Func>
311             bool find_at( dummy_node_type * pHead, split_list::details::search_value_type<Q>& val, Compare cmp, Func f )
312             {
313                 assert( pHead != nullptr );
314                 bucket_head_type h(pHead);
315                 return base_class::find_at( h, val, cmp, f );
316             }
317
318             template <typename Q, typename Compare>
319             bool find_at( dummy_node_type * pHead, split_list::details::search_value_type<Q> const& val, Compare cmp )
320             {
321                 assert( pHead != nullptr );
322                 bucket_head_type h(pHead);
323                 return base_class::find_at( h, val, cmp );
324             }
325
326             template <typename Q, typename Compare>
327             bool get_at( dummy_node_type * pHead, typename gc::Guard& guard, split_list::details::search_value_type<Q> const& val, Compare cmp )
328             {
329                 assert( pHead != nullptr );
330                 bucket_head_type h(pHead);
331                 return base_class::get_at( h, guard, val, cmp );
332             }
333
334             bool insert_aux_node( dummy_node_type * pNode )
335             {
336                 return base_class::insert_aux_node( pNode );
337             }
338             bool insert_aux_node( dummy_node_type * pHead, dummy_node_type * pNode )
339             {
340                 bucket_head_type h(pHead);
341                 return base_class::insert_aux_node( h, pNode );
342             }
343         };
344         //@endcond
345
346     protected:
347         ordered_list_wrapper    m_List              ;   ///< Ordered list containing split-list items
348         bucket_table            m_Buckets           ;   ///< bucket table
349         atomics::atomic<size_t> m_nBucketCountLog2  ;   ///< log2( current bucket count )
350         item_counter            m_ItemCounter       ;   ///< Item counter
351         hash                    m_HashFunctor       ;   ///< Hash functor
352
353     protected:
354         //@cond
355         typedef cds::details::Allocator< dummy_node_type, typename options::allocator >   dummy_node_allocator;
356         static dummy_node_type * alloc_dummy_node( size_t nHash )
357         {
358             return dummy_node_allocator().New( nHash );
359         }
360         static void free_dummy_node( dummy_node_type * p )
361         {
362             dummy_node_allocator().Delete( p );
363         }
364
365         /// Calculates hash value of \p key
366         template <typename Q>
367         size_t hash_value( Q const& key ) const
368         {
369             return m_HashFunctor( key );
370         }
371
372         size_t bucket_no( size_t nHash ) const
373         {
374             return nHash & ( (1 << m_nBucketCountLog2.load(atomics::memory_order_relaxed)) - 1 );
375         }
376
377         static size_t parent_bucket( size_t nBucket )
378         {
379             assert( nBucket > 0 );
380             return nBucket & ~( 1 << bitop::MSBnz( nBucket ) );
381         }
382
383         dummy_node_type * init_bucket( size_t nBucket )
384         {
385             assert( nBucket > 0 );
386             size_t nParent = parent_bucket( nBucket );
387
388             dummy_node_type * pParentBucket = m_Buckets.bucket( nParent );
389             if ( pParentBucket == nullptr ) {
390                 pParentBucket = init_bucket( nParent );
391             }
392
393             assert( pParentBucket != nullptr );
394
395             // Allocate a dummy node for new bucket
396             {
397                 dummy_node_type * pBucket = alloc_dummy_node( split_list::dummy_hash( nBucket ) );
398                 if ( m_List.insert_aux_node( pParentBucket, pBucket ) ) {
399                     m_Buckets.bucket( nBucket, pBucket );
400                     return pBucket;
401                 }
402                 free_dummy_node( pBucket );
403             }
404
405             // Another thread set the bucket. Wait while it done
406
407             // In this point, we must wait while nBucket is empty.
408             // The compiler can decide that waiting loop can be "optimized" (stripped)
409             // To prevent this situation, we use waiting on volatile bucket_head_ptr pointer.
410             //
411             back_off bkoff;
412             while ( true ) {
413                 dummy_node_type volatile * p = m_Buckets.bucket( nBucket );
414                 if ( p != nullptr )
415                     return const_cast<dummy_node_type *>( p );
416                 bkoff();
417             }
418         }
419
420         dummy_node_type * get_bucket( size_t nHash )
421         {
422             size_t nBucket = bucket_no( nHash );
423
424             dummy_node_type * pHead = m_Buckets.bucket( nBucket );
425             if ( pHead == nullptr )
426                 pHead = init_bucket( nBucket );
427
428             assert( pHead->is_dummy() );
429
430             return pHead;
431         }
432
433         void init()
434         {
435             // GC and OrderedList::gc must be the same
436             static_assert(( std::is_same<gc, typename ordered_list::gc>::value ), "GC and OrderedList::gc must be the same");
437
438             // atomicity::empty_item_counter is not allowed as a item counter
439             static_assert(( !std::is_same<item_counter, atomicity::empty_item_counter>::value ), "atomicity::empty_item_counter is not allowed as a item counter");
440
441             // Initialize bucket 0
442             dummy_node_type * pNode = alloc_dummy_node( 0 /*split_list::dummy_hash(0)*/ );
443
444             // insert_aux_node cannot return false for empty list
445             CDS_VERIFY( m_List.insert_aux_node( pNode ));
446
447             m_Buckets.bucket( 0, pNode );
448         }
449
450         void    inc_item_count()
451         {
452             size_t sz = m_nBucketCountLog2.load(atomics::memory_order_relaxed);
453             if ( ( ++m_ItemCounter >> sz ) > m_Buckets.load_factor() && ((size_t)(1 << sz )) < m_Buckets.capacity() )
454             {
455                 m_nBucketCountLog2.compare_exchange_strong( sz, sz + 1, atomics::memory_order_seq_cst, atomics::memory_order_relaxed );
456             }
457         }
458
459         template <typename Q, typename Compare, typename Func>
460         bool find_( Q& val, Compare cmp, Func f )
461         {
462             size_t nHash = hash_value( val );
463             split_list::details::search_value_type<Q>  sv( val, split_list::regular_hash( nHash ));
464             dummy_node_type * pHead = get_bucket( nHash );
465             assert( pHead != nullptr );
466
467             return m_List.find_at( pHead, sv, cmp,
468                 [&f](value_type& item, split_list::details::search_value_type<Q>& val){ f(item, val.val ); });
469         }
470
471         template <typename Q, typename Compare>
472         bool find_( Q const& val, Compare cmp )
473         {
474             size_t nHash = hash_value( val );
475             split_list::details::search_value_type<Q const>  sv( val, split_list::regular_hash( nHash ));
476             dummy_node_type * pHead = get_bucket( nHash );
477             assert( pHead != nullptr );
478
479             return m_List.find_at( pHead, sv, cmp );
480         }
481
482         template <typename Q, typename Compare>
483         bool get_( typename gc::Guard& guard, Q const& val, Compare cmp )
484         {
485             size_t nHash = hash_value( val );
486             split_list::details::search_value_type<Q const>  sv( val, split_list::regular_hash( nHash ));
487             dummy_node_type * pHead = get_bucket( nHash );
488             assert( pHead != nullptr );
489
490             return m_List.get_at( pHead, guard, sv, cmp );
491         }
492
493         template <typename Q>
494         bool get_( typename gc::Guard& guard, Q const& key)
495         {
496             return get_( guard, key, key_comparator());
497         }
498
499         template <typename Q, typename Less>
500         bool get_with_( typename gc::Guard& guard, Q const& key, Less )
501         {
502             return get_( guard, key, typename wrapped_ordered_list::template make_compare_from_less<Less>());
503         }
504
505         template <typename Q, typename Compare, typename Func>
506         bool erase_( Q const& val, Compare cmp, Func f )
507         {
508             size_t nHash = hash_value( val );
509             split_list::details::search_value_type<Q const>  sv( val, split_list::regular_hash( nHash ));
510             dummy_node_type * pHead = get_bucket( nHash );
511             assert( pHead != nullptr );
512
513             if ( m_List.erase_at( pHead, sv, cmp, f )) {
514                 --m_ItemCounter;
515                 return true;
516             }
517             return false;
518         }
519
520         template <typename Q, typename Compare>
521         bool erase_( Q const& val, Compare cmp )
522         {
523             size_t nHash = hash_value( val );
524             split_list::details::search_value_type<Q const>  sv( val, split_list::regular_hash( nHash ));
525             dummy_node_type * pHead = get_bucket( nHash );
526             assert( pHead != nullptr );
527
528             if ( m_List.erase_at( pHead, sv, cmp ) ) {
529                 --m_ItemCounter;
530                 return true;
531             }
532             return false;
533         }
534
535         template <typename Q, typename Compare>
536         bool extract_( typename gc::Guard& guard, Q const& val, Compare cmp )
537         {
538             size_t nHash = hash_value( val );
539             split_list::details::search_value_type<Q const>  sv( val, split_list::regular_hash( nHash ));
540             dummy_node_type * pHead = get_bucket( nHash );
541             assert( pHead != nullptr );
542
543             if ( m_List.extract_at( pHead, guard, sv, cmp ) ) {
544                 --m_ItemCounter;
545                 return true;
546             }
547             return false;
548         }
549
550         template <typename Q>
551         bool extract_( typename gc::Guard& guard, Q const& key)
552         {
553             return extract_( guard, key, key_comparator());
554         }
555
556         template <typename Q, typename Less>
557         bool extract_with_( typename gc::Guard& guard, Q const& key, Less )
558         {
559             return extract_( guard, key, typename wrapped_ordered_list::template make_compare_from_less<Less>() );
560         }
561
562         //@endcond
563
564     public:
565         /// Initialize split-ordered list of default capacity
566         /**
567             The default capacity is defined in bucket table constructor.
568             See split_list::expandable_bucket_table, split_list::static_ducket_table
569             which selects by split_list::dynamic_bucket_table option.
570         */
571         SplitListSet()
572             : m_nBucketCountLog2(1)
573         {
574             init();
575         }
576
577         /// Initialize split-ordered list
578         SplitListSet(
579             size_t nItemCount           ///< estimate average of item count
580             , size_t nLoadFactor = 1    ///< load factor - average item count per bucket. Small integer up to 8, default is 1.
581             )
582             : m_Buckets( nItemCount, nLoadFactor )
583             , m_nBucketCountLog2(1)
584         {
585             init();
586         }
587
588     public:
589         /// Inserts new node
590         /**
591             The function inserts \p val in the set if it does not contain
592             an item with key equal to \p val.
593
594             Returns \p true if \p val is placed into the set, \p false otherwise.
595         */
596         bool insert( value_type& val )
597         {
598             size_t nHash = hash_value( val );
599             dummy_node_type * pHead = get_bucket( nHash );
600             assert( pHead != nullptr );
601
602             node_traits::to_node_ptr( val )->m_nHash = split_list::regular_hash( nHash );
603
604             if ( m_List.insert_at( pHead, val )) {
605                 inc_item_count();
606                 return true;
607             }
608             return false;
609         }
610
611         /// Inserts new node
612         /**
613             This function is intended for derived non-intrusive containers.
614
615             The function allows to split creating of new item into two part:
616             - create item with key only
617             - insert new item into the set
618             - if inserting is success, calls  \p f functor to initialize value-field of \p val.
619
620             The functor signature is:
621             \code
622                 void func( value_type& val );
623             \endcode
624             where \p val is the item inserted. User-defined functor \p f should guarantee that during changing
625             \p val no any other changes could be made on this set's item by concurrent threads.
626             The user-defined functor is called only if the inserting is success and may be passed by reference
627             using \p std::ref.
628         */
629         template <typename Func>
630         bool insert( value_type& val, Func f )
631         {
632             size_t nHash = hash_value( val );
633             dummy_node_type * pHead = get_bucket( nHash );
634             assert( pHead != nullptr );
635
636             node_traits::to_node_ptr( val )->m_nHash = split_list::regular_hash( nHash );
637
638             if ( m_List.insert_at( pHead, val, f )) {
639                 inc_item_count();
640                 return true;
641             }
642             return false;
643         }
644
645         /// Ensures that the \p val exists in the set
646         /**
647             The operation performs inserting or changing data with lock-free manner.
648
649             If the item \p val is not found in the set, then \p val is inserted into the set.
650             Otherwise, the functor \p func is called with item found.
651             The functor signature is:
652             \code
653                 void func( bool bNew, value_type& item, value_type& val );
654             \endcode
655             with arguments:
656             - \p bNew - \p true if the item has been inserted, \p false otherwise
657             - \p item - item of the set
658             - \p val - argument \p val passed into the \p ensure function
659             If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
660             refers to the same thing.
661
662             The functor can change non-key fields of the \p item; however, \p func must guarantee
663             that during changing no any other modifications could be made on this item by concurrent threads.
664
665             You can pass \p func argument by value or by reference using \p std::ref.
666
667             Returns std::pair<bool, bool> where \p first is \p true if operation is successfull,
668             \p second is \p true if new item has been added or \p false if the item with \p key
669             already is in the set.
670         */
671         template <typename Func>
672         std::pair<bool, bool> ensure( value_type& val, Func func )
673         {
674             size_t nHash = hash_value( val );
675             dummy_node_type * pHead = get_bucket( nHash );
676             assert( pHead != nullptr );
677
678             node_traits::to_node_ptr( val )->m_nHash = split_list::regular_hash( nHash );
679
680             std::pair<bool, bool> bRet = m_List.ensure_at( pHead, val, func );
681             if ( bRet.first && bRet.second )
682                 inc_item_count();
683             return bRet;
684         }
685
686         /// Unlinks the item \p val from the set
687         /**
688             The function searches the item \p val in the set and unlinks it from the set
689             if it is found and is equal to \p val.
690
691             Difference between \ref erase and \p unlink functions: \p erase finds <i>a key</i>
692             and deletes the item found. \p unlink finds an item by key and deletes it
693             only if \p val is an item of that set, i.e. the pointer to item found
694             is equal to <tt> &val </tt>.
695
696             The function returns \p true if success and \p false otherwise.
697         */
698         bool unlink( value_type& val )
699         {
700             size_t nHash = hash_value( val );
701             dummy_node_type * pHead = get_bucket( nHash );
702             assert( pHead != nullptr );
703
704             if ( m_List.unlink_at( pHead, val ) ) {
705                 --m_ItemCounter;
706                 return true;
707             }
708             return false;
709         }
710
711         /// Deletes the item from the set
712         /** \anchor cds_intrusive_SplitListSet_hp_erase
713             The function searches an item with key equal to \p val in the set,
714             unlinks it from the set, and returns \p true.
715             If the item with key equal to \p val is not found the function return \p false.
716
717             Difference between \ref erase and \p unlink functions: \p erase finds <i>a key</i>
718             and deletes the item found. \p unlink finds an item by key and deletes it
719             only if \p val is an item of that set, i.e. the pointer to item found
720             is equal to <tt> &val </tt>.
721
722             Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type.
723         */
724         template <typename Q>
725         bool erase( Q const& val )
726         {
727             return erase_( val, key_comparator() );
728         }
729
730         /// Deletes the item from the set with comparing functor \p pred
731         /**
732             The function is an analog of \ref cds_intrusive_SplitListSet_hp_erase "erase(Q const&)"
733             but \p pred predicate is used for key comparing.
734             \p Less has the interface like \p std::less.
735             \p pred must imply the same element order as the comparator used for building the set.
736         */
737         template <typename Q, typename Less>
738         bool erase_with( const Q& val, Less pred )
739         {
740             return erase_( val, typename wrapped_ordered_list::template make_compare_from_less<Less>() );
741         }
742
743         /// Deletes the item from the set
744         /** \anchor cds_intrusive_SplitListSet_hp_erase_func
745             The function searches an item with key equal to \p val in the set,
746             call \p f functor with item found, unlinks it from the set, and returns \p true.
747             The \ref disposer specified by \p OrderedList class template parameter is called
748             by garbage collector \p GC asynchronously.
749
750             The \p Func interface is
751             \code
752             struct functor {
753                 void operator()( value_type const& item );
754             };
755             \endcode
756             The functor can be passed by reference with <tt>boost:ref</tt>
757
758             If the item with key equal to \p val is not found the function return \p false.
759
760             Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type.
761         */
762         template <typename Q, typename Func>
763         bool erase( Q const& val, Func f )
764         {
765             return erase_( val, key_comparator(), f );
766         }
767
768         /// Deletes the item from the set with comparing functor \p pred
769         /**
770             The function is an analog of \ref cds_intrusive_SplitListSet_hp_erase_func "erase(Q const&, Func)"
771             but \p pred predicate is used for key comparing.
772             \p Less has the interface like \p std::less.
773             \p pred must imply the same element order as the comparator used for building the set.
774         */
775         template <typename Q, typename Less, typename Func>
776         bool erase_with( Q const& val, Less pred, Func f )
777         {
778             return erase_( val, typename wrapped_ordered_list::template make_compare_from_less<Less>(), f );
779         }
780
781         /// Extracts the item with specified \p key
782         /** \anchor cds_intrusive_SplitListSet_hp_extract
783             The function searches an item with key equal to \p key,
784             unlinks it from the set, and returns it in \p dest parameter.
785             If the item with key equal to \p key is not found the function returns \p false.
786
787             Note the compare functor should accept a parameter of type \p Q that may be not the same as \p value_type.
788
789             The \ref disposer specified in \p OrderedList class' template parameter is called automatically
790             by garbage collector \p GC when returned \ref guarded_ptr object will be destroyed or released.
791             @note Each \p guarded_ptr object uses the GC's guard that can be limited resource.
792
793             Usage:
794             \code
795             typedef cds::intrusive::SplitListSet< your_template_args > splitlist_set;
796             splitlist_set theSet;
797             // ...
798             {
799                 splitlist_set::guarded_ptr gp;
800                 theSet.extract( gp, 5 );
801                 // Deal with gp
802                 // ...
803
804                 // Destructor of gp releases internal HP guard
805             }
806             \endcode
807         */
808         template <typename Q>
809         bool extract( guarded_ptr& dest, Q const& key )
810         {
811             return extract_( dest.guard(), key );
812         }
813
814         /// Extracts the item using compare functor \p pred
815         /**
816             The function is an analog of \ref cds_intrusive_SplitListSet_hp_extract "extract(guarded_ptr&, Q const&)"
817             but \p pred predicate is used for key comparing.
818
819             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
820             in any order.
821             \p pred must imply the same element order as the comparator used for building the set.
822         */
823         template <typename Q, typename Less>
824         bool extract_with( guarded_ptr& dest, Q const& key, Less pred )
825         {
826             return extract_with_( dest.guard(), key, pred );
827         }
828
829
830         /// Finds the key \p val
831         /** \anchor cds_intrusive_SplitListSet_hp_find_func
832             The function searches the item with key equal to \p val and calls the functor \p f for item found.
833             The interface of \p Func functor is:
834             \code
835             struct functor {
836                 void operator()( value_type& item, Q& val );
837             };
838             \endcode
839             where \p item is the item found, \p val is the <tt>find</tt> function argument.
840
841             You can pass \p f argument by value or by reference using \p std::ref.
842
843             The functor can change non-key fields of \p item. Note that the functor is only guarantee
844             that \p item cannot be disposed during functor is executing.
845             The functor does not serialize simultaneous access to the set \p item. If such access is
846             possible you must provide your own synchronization schema on item level to exclude unsafe item modifications.
847
848             The \p val argument is non-const since it can be used as \p f functor destination i.e., the functor
849             can modify both arguments.
850
851             Note the hash functor specified for class \p Traits template parameter
852             should accept a parameter of type \p Q that can be not the same as \p value_type.
853
854             The function returns \p true if \p val is found, \p false otherwise.
855         */
856         template <typename Q, typename Func>
857         bool find( Q& val, Func f )
858         {
859             return find_( val, key_comparator(), f );
860         }
861
862         /// Finds the key \p val with \p pred predicate for comparing
863         /**
864             The function is an analog of \ref cds_intrusive_SplitListSet_hp_find_func "find(Q&, Func)"
865             but \p cmp is used for key compare.
866             \p Less has the interface like \p std::less.
867             \p cmp must imply the same element order as the comparator used for building the set.
868         */
869         template <typename Q, typename Less, typename Func>
870         bool find_with( Q& val, Less pred, Func f )
871         {
872             return find_( val, typename wrapped_ordered_list::template make_compare_from_less<Less>(), f );
873         }
874
875         /// Finds the key \p val
876         /** \anchor cds_intrusive_SplitListSet_hp_find_cfunc
877             The function searches the item with key equal to \p val and calls the functor \p f for item found.
878             The interface of \p Func functor is:
879             \code
880             struct functor {
881                 void operator()( value_type& item, Q const& val );
882             };
883             \endcode
884             where \p item is the item found, \p val is the <tt>find</tt> function argument.
885
886             You can pass \p f argument by value or by reference using \p std::ref.
887
888             The functor can change non-key fields of \p item. Note that the functor is only guarantee
889             that \p item cannot be disposed during functor is executing.
890             The functor does not serialize simultaneous access to the set \p item. If such access is
891             possible you must provide your own synchronization schema on item level to exclude unsafe item modifications.
892
893             Note the hash functor specified for class \p Traits template parameter
894             should accept a parameter of type \p Q that can be not the same as \p value_type.
895
896             The function returns \p true if \p val is found, \p false otherwise.
897         */
898         template <typename Q, typename Func>
899         bool find( Q const& val, Func f )
900         {
901             return find_( val, key_comparator(), f );
902         }
903
904         /// Finds the key \p val with \p pred predicate for comparing
905         /**
906             The function is an analog of \ref cds_intrusive_SplitListSet_hp_find_cfunc "find(Q const&, Func)"
907             but \p cmp is used for key compare.
908             \p Less has the interface like \p std::less.
909             \p cmp must imply the same element order as the comparator used for building the set.
910         */
911         template <typename Q, typename Less, typename Func>
912         bool find_with( Q const& val, Less pred, Func f )
913         {
914             return find_( val, typename wrapped_ordered_list::template make_compare_from_less<Less>(), f );
915         }
916
917         /// Finds the key \p val
918         /** \anchor cds_intrusive_SplitListSet_hp_find_val
919             The function searches the item with key equal to \p val
920             and returns \p true if it is found, and \p false otherwise.
921
922             Note the hash functor specified for class \p Traits template parameter
923             should accept a parameter of type \p Q that can be not the same as \p value_type.
924             Otherwise, you may use \p find_with functions with explicit predicate for key comparing.
925         */
926         template <typename Q>
927         bool find( Q const& val )
928         {
929             return find_( val, key_comparator() );
930         }
931
932         /// Finds the key \p val with \p pred predicate for comparing
933         /**
934             The function is an analog of \ref cds_intrusive_SplitListSet_hp_find_val "find(Q const&)"
935             but \p cmp is used for key compare.
936             \p Less has the interface like \p std::less.
937             \p cmp must imply the same element order as the comparator used for building the set.
938         */
939         template <typename Q, typename Less>
940         bool find_with( Q const& val, Less pred )
941         {
942             return find_( val, typename wrapped_ordered_list::template make_compare_from_less<Less>() );
943         }
944
945         /// Finds the key \p val and return the item found
946         /** \anchor cds_intrusive_SplitListSet_hp_get
947             The function searches the item with key equal to \p val
948             and assigns the item found to guarded pointer \p ptr.
949             The function returns \p true if \p val is found, and \p false otherwise.
950             If \p val is not found the \p ptr parameter is not changed.
951
952             The \ref disposer specified in \p OrderedList class' template parameter is called
953             by garbage collector \p GC automatically when returned \ref guarded_ptr object
954             will be destroyed or released.
955             @note Each \p guarded_ptr object uses one GC's guard which can be limited resource.
956
957             Usage:
958             \code
959             typedef cds::intrusive::SplitListSet< your_template_params >  splitlist_set;
960             splitlist_set theSet;
961             // ...
962             {
963                 splitlist_set::guarded_ptr gp;
964                 if ( theSet.get( gp, 5 )) {
965                     // Deal with gp
966                     //...
967                 }
968                 // Destructor of guarded_ptr releases internal HP guard
969             }
970             \endcode
971
972             Note the compare functor specified for \p OrderedList template parameter
973             should accept a parameter of type \p Q that can be not the same as \p value_type.
974         */
975         template <typename Q>
976         bool get( guarded_ptr& ptr, Q const& val )
977         {
978             return get_( ptr.guard(), val );
979         }
980
981         /// Finds the key \p val and return the item found
982         /**
983             The function is an analog of \ref cds_intrusive_SplitListSet_hp_get "get( guarded_ptr& ptr, Q const&)"
984             but \p pred is used for comparing the keys.
985
986             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
987             in any order.
988             \p pred must imply the same element order as the comparator used for building the set.
989         */
990         template <typename Q, typename Less>
991         bool get_with( guarded_ptr& ptr, Q const& val, Less pred )
992         {
993             return get_with_( ptr.guard(), val, pred );
994         }
995
996         /// Returns item count in the set
997         size_t size() const
998         {
999             return m_ItemCounter;
1000         }
1001
1002         /// Checks if the set is empty
1003         /**
1004             Emptiness is checked by item counting: if item count is zero then the set is empty.
1005             Thus, the correct item counting feature is an important part of split-list set implementation.
1006         */
1007         bool empty() const
1008         {
1009             return size() == 0;
1010         }
1011
1012         /// Clears the set (non-atomic)
1013         /**
1014             The function unlink all items from the set.
1015             The function is not atomic. Therefore, \p clear may be used only for debugging purposes.
1016
1017             For each item the \p disposer is called after unlinking.
1018         */
1019         void clear()
1020         {
1021             iterator it = begin();
1022             while ( it != end() ) {
1023                 iterator i(it);
1024                 ++i;
1025                 unlink( *it );
1026                 it = i;
1027             }
1028         }
1029
1030     protected:
1031         //@cond
1032         template <bool IsConst>
1033         class iterator_type
1034             :public split_list::details::iterator_type<node_traits, ordered_list, IsConst>
1035         {
1036             typedef split_list::details::iterator_type<node_traits, ordered_list, IsConst> iterator_base_class;
1037             typedef typename iterator_base_class::list_iterator list_iterator;
1038         public:
1039             iterator_type()
1040                 : iterator_base_class()
1041             {}
1042
1043             iterator_type( iterator_type const& src )
1044                 : iterator_base_class( src )
1045             {}
1046
1047             // This ctor should be protected...
1048             iterator_type( list_iterator itCur, list_iterator itEnd )
1049                 : iterator_base_class( itCur, itEnd )
1050             {}
1051         };
1052         //@endcond
1053     public:
1054         /// Forward iterator
1055         /**
1056             The forward iterator for a split-list has some features:
1057             - it has no post-increment operator
1058             - it depends on iterator of underlying \p OrderedList
1059             - The iterator cannot be moved across thread boundary since it may contain GC's guard that is thread-private GC data.
1060             - Iterator ensures thread-safety even if you delete the item that iterator points to. However, in case of concurrent
1061               deleting operations it is no guarantee that you iterate all item in the split-list.
1062
1063             Therefore, the use of iterators in concurrent environment is not good idea. Use the iterator on the concurrent container
1064             for debug purpose only.
1065         */
1066         typedef iterator_type<false>    iterator;
1067         /// Const forward iterator
1068         /**
1069             For iterator's features and requirements see \ref iterator
1070         */
1071         typedef iterator_type<true>     const_iterator;
1072
1073         /// Returns a forward iterator addressing the first element in a split-list
1074         /**
1075             For empty list \code begin() == end() \endcode
1076         */
1077         iterator begin()
1078         {
1079             return iterator( m_List.begin(), m_List.end() );
1080         }
1081
1082         /// Returns an iterator that addresses the location succeeding the last element in a split-list
1083         /**
1084             Do not use the value returned by <tt>end</tt> function to access any item.
1085
1086             The returned value can be used only to control reaching the end of the split-list.
1087             For empty list \code begin() == end() \endcode
1088         */
1089         iterator end()
1090         {
1091             return iterator( m_List.end(), m_List.end() );
1092         }
1093
1094         /// Returns a forward const iterator addressing the first element in a split-list
1095         const_iterator begin() const
1096         {
1097             return const_iterator( m_List.begin(), m_List.end() );
1098         }
1099
1100         /// Returns an const iterator that addresses the location succeeding the last element in a split-list
1101         const_iterator end() const
1102         {
1103             return const_iterator( m_List.end(), m_List.end() );
1104         }
1105
1106     };
1107
1108 }}  // namespace cds::intrusive
1109
1110 #endif // #ifndef __CDS_INTRUSIVE_SPLIT_LIST_H