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