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