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