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