Remove std::ref and boost::ref from docs
[libcds.git] / cds / intrusive / cuckoo_set.h
1 //$$CDS-header$$
2
3 #ifndef __CDS_INTRUSIVE_CUCKOO_SET_H
4 #define __CDS_INTRUSIVE_CUCKOO_SET_H
5
6 #include <memory>
7 #include <type_traits>
8 #include <mutex>
9 #include <functional>   // ref
10 #include <cds/intrusive/details/base.h>
11 #include <cds/opt/compare.h>
12 #include <cds/opt/hash.h>
13 #include <cds/lock/array.h>
14 #include <cds/os/thread.h>
15 #include <cds/lock/spinlock.h>
16
17
18 namespace cds { namespace intrusive {
19
20     /// CuckooSet-related definitions
21     namespace cuckoo {
22
23         /// Option to define probeset type
24         /**
25             The option specifies probeset type for the CuckooSet.
26             Available values:
27             - \p cds::intrusive::cuckoo::list - the probeset is a single-linked list.
28                 The node contains pointer to next node in probeset.
29             - \p cds::intrusive::cuckoo::vector<Capacity> - the probeset is a vector
30                 with constant-size \p Capacity where \p Capacity is an <tt>unsigned int</tt> constant.
31                 The node does not contain any auxiliary data.
32         */
33         template <typename Type>
34         struct probeset_type
35         {
36             //@cond
37             template <typename Base>
38             struct pack: public Base {
39                 typedef Type    probeset_type;
40             };
41             //@endcond
42         };
43
44         /// Option specifying whether to store hash values in the node
45         /**
46              This option reserves additional space in the hook to store the hash value of the object once it's introduced in the container.
47              When this option is used, the unordered container will store the calculated hash value in the hook and rehashing operations won't need
48              to recalculate the hash of the value. This option will improve the performance of unordered containers
49              when rehashing is frequent or hashing the value is a slow operation
50
51              The \p Count template parameter defines the size of hash array. Remember that cuckoo hashing implies at least two
52              hash values per item.
53
54              Possible values of \p Count:
55              - 0 - no hash storing in the node
56              - greater that 1 - store hash values.
57
58              Value 1 is deprecated.
59         */
60         template <unsigned int Count>
61         struct store_hash
62         {
63             //@cond
64             template <typename Base>
65             struct pack: public Base {
66                 static unsigned int const store_hash = Count;
67             };
68             //@endcond
69         };
70
71
72         //@cond
73         // Probeset type placeholders
74         struct list_probeset_class;
75         struct vector_probeset_class;
76         //@endcond
77
78         //@cond
79         // Probeset type declarations.
80         struct list;
81         template <unsigned int Capacity>
82         struct vector
83         {
84             static unsigned int const c_nCapacity = Capacity;
85         };
86         //@endcond
87
88         /// CuckooSet node
89         /**
90             Template arguments:
91             - \p ProbesetType - type of probeset. Can be \p cds::intrusive::cuckoo::list
92                 or \p cds::intrusive::cuckoo::vector<Capacity>.
93             - \p StoreHashCount - constant that defines whether to store node hash values.
94                 See cuckoo::store_hash option for explanation
95             - Tag - a tag used to distinguish between different implementation when two or more
96                 \p node is needed in single struct.
97         */
98         template <typename ProbesetType = cuckoo::list, unsigned int StoreHashCount = 0, typename Tag = opt::none>
99         struct node
100 #ifdef CDS_DOXYGEN_INVOKED
101         {
102             typedef ProbesetType    probeset_type   ;   ///< Probeset type
103             typedef Tag             tag             ;   ///< Tag
104             static unsigned int const hash_array_size = StoreHashCount ;    ///< The size of hash array
105         }
106 #endif
107 ;
108
109         //@cond
110         template <typename Tag>
111         struct node< cuckoo::list, 0, Tag>
112         {
113             typedef list_probeset_class probeset_class;
114             typedef cuckoo::list        probeset_type;
115             typedef Tag                 tag;
116             static unsigned int const hash_array_size = 0;
117             static unsigned int const probeset_size = 0;
118
119             node *  m_pNext;
120
121             CDS_CONSTEXPR node() CDS_NOEXCEPT
122                 : m_pNext( nullptr )
123             {}
124
125             void store_hash( size_t * )
126             {}
127
128             size_t * get_hash() const
129             {
130                 // This node type does not store hash values!!!
131                 assert(false);
132                 return nullptr;
133             }
134
135             void clear()
136             {
137                 m_pNext = nullptr;
138             }
139         };
140
141         template <unsigned int StoreHashCount, typename Tag>
142         struct node< cuckoo::list, StoreHashCount, Tag>
143         {
144             typedef list_probeset_class probeset_class;
145             typedef cuckoo::list        probeset_type;
146             typedef Tag                 tag;
147             static unsigned int const hash_array_size = StoreHashCount;
148             static unsigned int const probeset_size = 0;
149
150             node *  m_pNext;
151             size_t  m_arrHash[ hash_array_size ];
152
153             node() CDS_NOEXCEPT
154                 : m_pNext( nullptr )
155             {
156                 memset( m_arrHash, 0, sizeof(m_arrHash));
157             }
158
159             void store_hash( size_t * pHashes )
160             {
161                 memcpy( m_arrHash, pHashes, hash_array_size );
162             }
163
164             size_t * get_hash() const
165             {
166                 return const_cast<size_t *>( m_arrHash );
167             }
168
169             void clear()
170             {
171                 m_pNext = nullptr;
172             }
173
174         };
175
176         template <unsigned int VectorSize, typename Tag>
177         struct node< cuckoo::vector<VectorSize>, 0, Tag>
178         {
179             typedef vector_probeset_class       probeset_class;
180             typedef cuckoo::vector<VectorSize>  probeset_type;
181             typedef Tag                         tag;
182             static unsigned int const hash_array_size = 0;
183             static unsigned int const probeset_size = probeset_type::c_nCapacity;
184
185             node() CDS_NOEXCEPT
186             {}
187
188             void store_hash( size_t * )
189             {}
190
191             size_t * get_hash() const
192             {
193                 // This node type does not store hash values!!!
194                 assert(false);
195                 return nullptr;
196             }
197
198             void clear()
199             {}
200         };
201
202         template <unsigned int VectorSize, unsigned int StoreHashCount, typename Tag>
203         struct node< cuckoo::vector<VectorSize>, StoreHashCount, Tag>
204         {
205             typedef vector_probeset_class       probeset_class;
206             typedef cuckoo::vector<VectorSize>  probeset_type;
207             typedef Tag                         tag;
208             static unsigned int const hash_array_size = StoreHashCount;
209             static unsigned int const probeset_size = probeset_type::c_nCapacity;
210
211             size_t  m_arrHash[ hash_array_size ];
212
213             node() CDS_NOEXCEPT
214             {
215                 memset( m_arrHash, 0, sizeof(m_arrHash));
216             }
217
218             void store_hash( size_t * pHashes )
219             {
220                 memcpy( m_arrHash, pHashes, hash_array_size );
221             }
222
223             size_t * get_hash() const
224             {
225                 return const_cast<size_t *>( m_arrHash );
226             }
227
228             void clear()
229             {}
230         };
231         //@endcond
232
233
234         //@cond
235         struct default_hook {
236             typedef cuckoo::list probeset_type;
237             static unsigned int const store_hash = 0;
238             typedef opt::none   tag;
239         };
240
241         template < typename HookType, typename... Options>
242         struct hook
243         {
244             typedef typename opt::make_options< default_hook, Options...>::type  options;
245
246             typedef typename options::probeset_type probeset_type;
247             typedef typename options::tag tag;
248             static unsigned int const store_hash = options::store_hash;
249
250             typedef node<probeset_type, store_hash, tag>   node_type;
251
252             typedef HookType        hook_type;
253         };
254         //@endcond
255
256         /// Base hook
257         /**
258             \p Options are:
259             - cuckoo::probeset_type - probeset type. Defaul is \p cuckoo::list
260             - cuckoo::store_hash - store hash values in the node or not. Default is 0 (no storing)
261             - opt::tag - tag to distinguish different nodes in one struct. Default is opt::none
262         */
263         template < typename... Options >
264         struct base_hook: public hook< opt::base_hook_tag, Options... >
265         {};
266
267         /// Member hook
268         /**
269             \p MemberOffset defines offset in bytes of \ref node member into your structure.
270             Use \p offsetof macro to define \p MemberOffset
271
272             \p Options are:
273             - cuckoo::probeset_type - probeset type. Defaul is \p cuckoo::list
274             - cuckoo::store_hash - store hash values in the node or not. Default is 0 (no storing)
275             - opt::tag - tag to distinguish different nodes in one struct. Default is opt::none
276         */
277         template < size_t MemberOffset, typename... Options >
278         struct member_hook: public hook< opt::member_hook_tag, Options... >
279         {
280             //@cond
281             static const size_t c_nMemberOffset = MemberOffset;
282             //@endcond
283         };
284
285         /// Traits hook
286         /**
287             \p NodeTraits defines type traits for node.
288             See \ref node_traits for \p NodeTraits interface description
289
290             \p Options are:
291             - cuckoo::probeset_type - probeset type. Defaul is \p cuckoo::list
292             - cuckoo::store_hash - store hash values in the node or not. Default is 0 (no storing)
293             - opt::tag - tag to distinguish different nodes in one struct. Default is opt::none
294         */
295         template <typename NodeTraits, typename... Options >
296         struct traits_hook: public hook< opt::traits_hook_tag, Options... >
297         {
298             //@cond
299             typedef NodeTraits node_traits;
300             //@endcond
301         };
302
303         /// Internal statistics for \ref striping mutex policy
304         struct striping_stat {
305             typedef cds::atomicity::event_counter   counter_type;   ///< Counter type
306
307             counter_type   m_nCellLockCount    ;  ///< Count of obtaining cell lock
308             counter_type   m_nCellTryLockCount ;  ///< Count of cell \p try_lock attempts
309             counter_type   m_nFullLockCount    ;  ///< Count of obtaining full lock
310             counter_type   m_nResizeLockCount  ;  ///< Count of obtaining resize lock
311             counter_type   m_nResizeCount      ;  ///< Count of resize event
312
313             //@cond
314             void    onCellLock()    { ++m_nCellLockCount; }
315             void    onCellTryLock() { ++m_nCellTryLockCount; }
316             void    onFullLock()    { ++m_nFullLockCount; }
317             void    onResizeLock()  { ++m_nResizeLockCount;   }
318             void    onResize()      { ++m_nResizeCount; }
319             //@endcond
320         };
321
322         /// Dummy internal statistics for \ref striping mutex policy
323         struct empty_striping_stat {
324             //@cond
325             void    onCellLock()    const {}
326             void    onCellTryLock() const {}
327             void    onFullLock()    const {}
328             void    onResizeLock()  const {}
329             void    onResize()      const {}
330             //@endcond
331         };
332
333         /// Lock striping concurrent access policy
334         /**
335             This is one of available opt::mutex_policy option type for CuckooSet
336
337             Lock striping is very simple technique.
338             The cuckoo set consists of the bucket tables and the array of locks.
339             There is single lock array for each bucket table, at least, the count of bucket table is 2.
340             Initially, the capacity of lock array and each bucket table is the same.
341             When set is resized, bucket table capacity will be doubled but lock array will not.
342             The lock \p i protects each bucket \p j, where <tt> j = i mod L </tt>,
343             where \p L - the size of lock array.
344
345             The policy contains an internal array of \p RecursiveLock locks.
346
347             Template arguments:
348             - \p RecursiveLock - the type of recursive mutex. The default is \p std::recursive_mutex. The mutex type should be default-constructible.
349                 Note that a recursive spin-lock is not suitable for lock striping for performance reason.
350             - \p Arity - unsigned int constant that specifies an arity. The arity is the count of hash functors, i.e., the
351                 count of lock arrays. Default value is 2.
352             - \p Alloc - allocator type used for lock array memory allocation. Default is \p CDS_DEFAULT_ALLOCATOR.
353             - \p Stat - internal statistics type. Note that this template argument is automatically selected by \ref CuckooSet
354                 class according to its \p opt::stat option.
355         */
356         template <
357             class RecursiveLock = std::recursive_mutex,
358             unsigned int Arity = 2,
359             class Alloc = CDS_DEFAULT_ALLOCATOR,
360             class Stat = empty_striping_stat
361         >
362         class striping
363         {
364         public:
365             typedef RecursiveLock   lock_type       ;   ///< lock type
366             typedef Alloc           allocator_type  ;   ///< allocator type
367             static unsigned int const c_nArity = Arity ;    ///< the arity
368             typedef Stat            statistics_type ;   ///< Internal statistics type (\ref striping_stat or \ref empty_striping_stat)
369
370             //@cond
371             typedef striping_stat       real_stat;
372             typedef empty_striping_stat empty_stat;
373
374             template <typename Stat2>
375             struct rebind_statistics {
376                 typedef striping<lock_type, c_nArity, allocator_type, Stat2> other;
377             };
378             //@endcond
379
380             typedef cds::lock::array< lock_type, cds::lock::pow2_select_policy, allocator_type >    lock_array_type ;   ///< lock array type
381
382         protected:
383             //@cond
384             class lock_array: public lock_array_type
385             {
386             public:
387                 // placeholder ctor
388                 lock_array(): lock_array_type( typename lock_array_type::select_cell_policy(2) ) {}
389
390                 // real ctor
391                 lock_array( size_t nCapacity ): lock_array_type( nCapacity, typename lock_array_type::select_cell_policy(nCapacity) ) {}
392             };
393
394             class scoped_lock: public std::unique_lock< lock_array_type >
395             {
396                 typedef std::unique_lock< lock_array_type > base_class;
397             public:
398                 scoped_lock( lock_array& arrLock, size_t nHash ): base_class( arrLock, nHash ) {}
399             };
400             //@endcond
401
402         protected:
403             //@cond
404             lock_array      m_Locks[c_nArity] ;   ///< array of lock_array_type
405             statistics_type m_Stat              ; ///< internal statistics
406             //@endcond
407
408         public:
409             //@cond
410             class scoped_cell_lock {
411                 lock_type * m_guard[c_nArity];
412
413             public:
414                 scoped_cell_lock( striping& policy, size_t const* arrHash )
415                 {
416                     for ( unsigned int i = 0; i < c_nArity; ++i ) {
417                         m_guard[i] = &( policy.m_Locks[i].at( policy.m_Locks[i].lock( arrHash[i] )));
418                     }
419                     policy.m_Stat.onCellLock();
420                 }
421
422                 ~scoped_cell_lock()
423                 {
424                     for ( unsigned int i = 0; i < c_nArity; ++i )
425                         m_guard[i]->unlock();
426                 }
427             };
428
429             class scoped_cell_trylock
430             {
431                 typedef typename lock_array_type::lock_type lock_type;
432
433                 lock_type * m_guard[c_nArity];
434                 bool        m_bLocked;
435
436             public:
437                 scoped_cell_trylock( striping& policy, size_t const* arrHash )
438                 {
439                     size_t nCell = policy.m_Locks[0].try_lock( arrHash[0] );
440                     m_bLocked = nCell != lock_array_type::c_nUnspecifiedCell;
441                     if ( m_bLocked ) {
442                         m_guard[0] = &(policy.m_Locks[0].at(nCell));
443                         for ( unsigned int i = 1; i < c_nArity; ++i ) {
444                             m_guard[i] = &( policy.m_Locks[i].at( policy.m_Locks[i].lock( arrHash[i] )) );
445                         }
446                     }
447                     else {
448                         std::fill( m_guard, m_guard + c_nArity, nullptr );
449                     }
450                     policy.m_Stat.onCellTryLock();
451                 }
452                 ~scoped_cell_trylock()
453                 {
454                     if ( m_bLocked ) {
455                         for ( unsigned int i = 0; i < c_nArity; ++i )
456                             m_guard[i]->unlock();
457                     }
458                 }
459
460                 bool locked() const
461                 {
462                     return m_bLocked;
463                 }
464             };
465
466             class scoped_full_lock {
467                 std::unique_lock< lock_array_type >   m_guard;
468             public:
469                 scoped_full_lock( striping& policy )
470                     : m_guard( policy.m_Locks[0] )
471                 {
472                     policy.m_Stat.onFullLock();
473                 }
474
475                 /// Ctor for scoped_resize_lock - no statistics is incremented
476                 scoped_full_lock( striping& policy, bool )
477                     : m_guard( policy.m_Locks[0] )
478                 {}
479             };
480
481             class scoped_resize_lock: public scoped_full_lock {
482             public:
483                 scoped_resize_lock( striping& policy )
484                     : scoped_full_lock( policy, false )
485                 {
486                     policy.m_Stat.onResizeLock();
487                 }
488             };
489             //@endcond
490
491         public:
492             /// Constructor
493             striping(
494                 size_t nLockCount          ///< The size of lock array. Must be power of two.
495             )
496             {
497                 // Trick: initialize the array of locks
498                 for ( unsigned int i = 0; i < c_nArity; ++i ) {
499                     lock_array * pArr = m_Locks + i;
500                     pArr->lock_array::~lock_array();
501                     new ( pArr ) lock_array( nLockCount );
502                 }
503             }
504
505             /// Returns lock array size
506             /**
507                 Lock array size is unchanged during \p striping object lifetime
508             */
509             size_t lock_count() const
510             {
511                 return m_Locks[0].size();
512             }
513
514             //@cond
515             void resize( size_t )
516             {
517                 m_Stat.onResize();
518             }
519             //@endcond
520
521             /// Returns the arity of striping mutex policy
522             CDS_CONSTEXPR unsigned int arity() const CDS_NOEXCEPT
523             {
524                 return c_nArity;
525             }
526
527             /// Returns internal statistics
528             statistics_type const& statistics() const
529             {
530                 return m_Stat;
531             }
532         };
533
534         /// Internal statistics for \ref refinable mutex policy
535         struct refinable_stat {
536             typedef cds::atomicity::event_counter   counter_type    ;   ///< Counter type
537
538             counter_type   m_nCellLockCount         ;   ///< Count of obtaining cell lock
539             counter_type   m_nCellLockWaitResizing  ;   ///< Count of loop iteration to wait for resizing
540             counter_type   m_nCellLockArrayChanged  ;   ///< Count of event "Lock array has been changed when obtaining cell lock"
541             counter_type   m_nCellLockFailed        ;   ///< Count of event "Cell lock failed because of the array is owned by other thread"
542
543             counter_type   m_nSecondCellLockCount   ;   ///< Count of obtaining cell lock when another cell is already locked
544             counter_type   m_nSecondCellLockFailed  ;   ///< Count of unsuccess obtaining cell lock when another cell is already locked
545
546             counter_type   m_nFullLockCount         ;   ///< Count of obtaining full lock
547             counter_type   m_nFullLockIter          ;   ///< Count of unsuccessfull iteration to obtain full lock
548
549             counter_type   m_nResizeLockCount       ;   ///< Count of obtaining resize lock
550             counter_type   m_nResizeLockIter        ;   ///< Count of unsuccessfull iteration to obtain resize lock
551             counter_type   m_nResizeLockArrayChanged;   ///< Count of event "Lock array has been changed when obtaining resize lock"
552             counter_type   m_nResizeCount           ;   ///< Count of resize event
553
554             //@cond
555             void    onCellLock()    { ++m_nCellLockCount; }
556             void    onCellWaitResizing() { ++m_nCellLockWaitResizing; }
557             void    onCellArrayChanged() { ++m_nCellLockArrayChanged; }
558             void    onCellLockFailed()   { ++m_nCellLockFailed; }
559             void    onSecondCellLock()   { ++m_nSecondCellLockCount; }
560             void    onSecondCellLockFailed() { ++m_nSecondCellLockFailed; }
561             void    onFullLock()         { ++m_nFullLockCount; }
562             void    onFullLockIter()     { ++m_nFullLockIter; }
563             void    onResizeLock()       { ++m_nResizeLockCount;   }
564             void    onResizeLockIter()   { ++m_nResizeLockIter; }
565             void    onResizeLockArrayChanged() { ++m_nResizeLockArrayChanged; }
566             void    onResize()           { ++m_nResizeCount; }
567             //@endcond
568         };
569
570         /// Dummy internal statistics for \ref refinable mutex policy
571         struct empty_refinable_stat {
572             //@cond
573             void    onCellLock()            const {}
574             void    onCellWaitResizing()    const {}
575             void    onCellArrayChanged()    const {}
576             void    onCellLockFailed()      const {}
577             void    onSecondCellLock()      const {}
578             void    onSecondCellLockFailed() const {}
579             void    onFullLock()            const {}
580             void    onFullLockIter()        const {}
581             void    onResizeLock()          const {}
582             void    onResizeLockIter()      const {}
583             void    onResizeLockArrayChanged() const {}
584             void    onResize()              const {}
585             //@endcond
586         };
587
588         /// Refinable concurrent access policy
589         /**
590             This is one of available opt::mutex_policy option type for CuckooSet
591
592             Refining is like a striping technique (see cuckoo::striping)
593             but it allows growing the size of lock array when resizing the hash table.
594             So, the sizes of hash table and lock array are equal.
595
596             Template arguments:
597             - \p RecursiveLock - the type of mutex. Reentrant (recursive) mutex is required.
598                 The default is \p std::recursive_mutex. The mutex type should be default-constructible.
599             - \p Arity - unsigned int constant that specifies an arity. The arity is the count of hash functors, i.e., the
600                 count of lock arrays. Default value is 2.
601             - \p BackOff - back-off strategy. Default is cds::backoff::yield
602             - \p Alloc - allocator type used for lock array memory allocation. Default is \p CDS_DEFAULT_ALLOCATOR.
603             - \p Stat - internal statistics type. Note that this template argument is automatically selected by \ref CuckooSet
604                 class according to its \p opt::stat option.
605         */
606         template <
607             class RecursiveLock = std::recursive_mutex,
608             unsigned int Arity = 2,
609             typename BackOff = cds::backoff::yield,
610             class Alloc = CDS_DEFAULT_ALLOCATOR,
611             class Stat = empty_refinable_stat
612         >
613         class refinable
614         {
615         public:
616             typedef RecursiveLock   lock_type       ;   ///< lock type
617             typedef Alloc           allocator_type  ;   ///< allocator type
618             typedef BackOff         back_off        ;   ///< back-off strategy
619             typedef Stat            statistics_type ;   ///< internal statistics type
620             static unsigned int const c_nArity = Arity; ///< the arity
621
622             //@cond
623             typedef refinable_stat          real_stat;
624             typedef empty_refinable_stat    empty_stat;
625
626             template <typename Stat2>
627             struct rebind_statistics {
628                 typedef refinable< lock_type, c_nArity, back_off, allocator_type, Stat2>    other;
629             };
630             //@endcond
631
632         protected:
633             //@cond
634             typedef cds::lock::trivial_select_policy  lock_selection_policy;
635
636             class lock_array_type
637                 : public cds::lock::array< lock_type, lock_selection_policy, allocator_type >
638                 , public std::enable_shared_from_this< lock_array_type >
639             {
640                 typedef cds::lock::array< lock_type, lock_selection_policy, allocator_type >    lock_array_base;
641             public:
642                 lock_array_type( size_t nCapacity )
643                     : lock_array_base( nCapacity )
644                 {}
645             };
646             typedef std::shared_ptr< lock_array_type >  lock_array_ptr;
647             typedef cds::details::Allocator< lock_array_type, allocator_type >  lock_array_allocator;
648
649             typedef unsigned long long  owner_t;
650             typedef cds::OS::ThreadId   threadId_t;
651
652             typedef cds::lock::Spin     spinlock_type;
653             typedef std::unique_lock< spinlock_type > scoped_spinlock;
654             //@endcond
655
656         protected:
657             //@cond
658             static owner_t const c_nOwnerMask = (((owner_t) 1) << (sizeof(owner_t) * 8 - 1)) - 1;
659
660             atomics::atomic< owner_t >   m_Owner     ;   ///< owner mark (thread id + boolean flag)
661             atomics::atomic<size_t>      m_nCapacity ;   ///< lock array capacity
662             lock_array_ptr                  m_arrLocks[ c_nArity ]  ; ///< Lock array. The capacity of array is specified in constructor.
663             spinlock_type                   m_access    ;   ///< access to m_arrLocks
664             statistics_type                 m_Stat      ;   ///< internal statistics
665             //@endcond
666
667         protected:
668             //@cond
669             struct lock_array_disposer {
670                 void operator()( lock_array_type * pArr )
671                 {
672                     lock_array_allocator().Delete( pArr );
673                 }
674             };
675
676             lock_array_ptr create_lock_array( size_t nCapacity )
677             {
678                 return lock_array_ptr( lock_array_allocator().New( nCapacity ), lock_array_disposer() );
679             }
680
681             void acquire( size_t const * arrHash, lock_array_ptr * pLockArr, lock_type ** parrLock )
682             {
683                 owner_t me = (owner_t) cds::OS::getCurrentThreadId();
684                 owner_t who;
685
686                 back_off bkoff;
687                 while ( true ) {
688
689                     {
690                         scoped_spinlock sl(m_access);
691                         for ( unsigned int i = 0; i < c_nArity; ++i )
692                             pLockArr[i] = m_arrLocks[i];
693                     }
694
695                     // wait while resizing
696                     while ( true ) {
697                         who = m_Owner.load( atomics::memory_order_acquire );
698                         if ( !( who & 1 ) || (who >> 1) == (me & c_nOwnerMask) )
699                             break;
700                         bkoff();
701                         m_Stat.onCellWaitResizing();
702                     }
703
704                     if ( pLockArr[0] != m_arrLocks[0] ) {
705                         m_Stat.onCellArrayChanged();
706                     }
707                     else {
708
709                         size_t const nMask = pLockArr[0]->size() - 1;
710                         assert( cds::beans::is_power2( nMask + 1 ));
711
712                         for ( unsigned int i = 0; i < c_nArity; ++i ) {
713                             parrLock[i] = &( pLockArr[i]->at( arrHash[i] & nMask ));
714                             parrLock[i]->lock();
715                         }
716
717                         who = m_Owner.load( atomics::memory_order_acquire );
718                         if ( ( !(who & 1) || (who >> 1) == (me & c_nOwnerMask) ) && m_arrLocks[0] == pLockArr[0] ) {
719                             m_Stat.onCellLock();
720                             return;
721                         }
722
723                         for ( unsigned int i = 0; i < c_nArity; ++i ) {
724                             parrLock[i]->unlock();
725                         }
726
727                         m_Stat.onCellLockFailed();
728                     }
729
730                     // clears pLockArr can lead to calling dtor for each item of pLockArr[i] that may be a heavy-weighted operation
731                     // (each pLockArr[i] is a shared pointer to array of a ton of mutexes)
732                     // It is better to do this before the next loop iteration where we will use spin-locked assignment to pLockArr
733                     // Destructing a lot of mutexes under spin-lock is a bad solution
734                     for ( unsigned int i = 0; i < c_nArity; ++i )
735                         pLockArr[i].reset();
736                 }
737             }
738
739             bool try_second_acquire( size_t const * arrHash, lock_type ** parrLock )
740             {
741                 // It is assumed that the current thread already has a lock
742                 // and requires a second lock for other hash
743
744                 size_t const nMask = m_nCapacity.load(atomics::memory_order_acquire) - 1;
745                 size_t nCell = m_arrLocks[0]->try_lock( arrHash[0] & nMask);
746                 if ( nCell == lock_array_type::c_nUnspecifiedCell ) {
747                     m_Stat.onSecondCellLockFailed();
748                     return false;
749                 }
750                 parrLock[0] = &(m_arrLocks[0]->at(nCell));
751
752                 for ( unsigned int i = 1; i < c_nArity; ++i ) {
753                     parrLock[i] = &( m_arrLocks[i]->at( m_arrLocks[i]->lock( arrHash[i] & nMask)) );
754                 }
755
756                 m_Stat.onSecondCellLock();
757                 return true;
758             }
759
760             void acquire_all()
761             {
762                 owner_t me = (owner_t) cds::OS::getCurrentThreadId();
763
764                 back_off bkoff;
765                 while ( true ) {
766                     owner_t ownNull = 0;
767                     if ( m_Owner.compare_exchange_strong( ownNull, (me << 1) | 1, atomics::memory_order_acq_rel, atomics::memory_order_relaxed )) {
768                         m_arrLocks[0]->lock_all();
769
770                         m_Stat.onFullLock();
771                         return;
772                     }
773                     bkoff();
774                     m_Stat.onFullLockIter();
775                 }
776             }
777
778             void release_all()
779             {
780                 m_arrLocks[0]->unlock_all();
781                 m_Owner.store( 0, atomics::memory_order_release );
782             }
783
784             void acquire_resize( lock_array_ptr * pOldLocks )
785             {
786                 owner_t me = (owner_t) cds::OS::getCurrentThreadId();
787
788                 while ( true ) {
789                     {
790                         scoped_spinlock sl(m_access);
791                         for ( unsigned int i = 0; i < c_nArity; ++i )
792                             pOldLocks[i] = m_arrLocks[i];
793                     }
794
795                     // global lock
796                     owner_t ownNull = 0;
797                     if ( m_Owner.compare_exchange_strong( ownNull, (me << 1) | 1, atomics::memory_order_acq_rel, atomics::memory_order_relaxed )) {
798                         if ( pOldLocks[0] != m_arrLocks[0] ) {
799                             m_Owner.store( 0, atomics::memory_order_release );
800                             m_Stat.onResizeLockArrayChanged();
801                         }
802                         else {
803                             pOldLocks[0]->lock_all();
804                             m_Stat.onResizeLock();
805                             return;
806                         }
807                     }
808                     else
809                         m_Stat.onResizeLockIter();
810
811                     // clears pOldLocks can lead to calling dtor for each item of pOldLocks[i] that may be a heavy-weighted operation
812                     // (each pOldLocks[i] is a shared pointer to array of a ton of mutexes)
813                     // It is better to do this before the next loop iteration where we will use spin-locked assignment to pOldLocks
814                     // Destructing a lot of mutexes under spin-lock is a bad solution
815                     for ( unsigned int i = 0; i < c_nArity; ++i )
816                         pOldLocks[i].reset();
817                 }
818             }
819
820             void release_resize( lock_array_ptr * pOldLocks )
821             {
822                 m_Owner.store( 0, atomics::memory_order_release );
823                 pOldLocks[0]->unlock_all();
824             }
825             //@endcond
826
827         public:
828             //@cond
829             class scoped_cell_lock {
830                 lock_type * m_arrLock[ c_nArity ];
831                 lock_array_ptr  m_arrLockArr[ c_nArity ];
832
833             public:
834                 scoped_cell_lock( refinable& policy, size_t const* arrHash )
835                 {
836                     policy.acquire( arrHash, m_arrLockArr, m_arrLock );
837                 }
838
839                 ~scoped_cell_lock()
840                 {
841                     for ( unsigned int i = 0; i < c_nArity; ++i )
842                         m_arrLock[i]->unlock();
843                 }
844             };
845
846             class scoped_cell_trylock {
847                 lock_type * m_arrLock[ c_nArity ];
848                 bool        m_bLocked;
849
850             public:
851                 scoped_cell_trylock( refinable& policy, size_t const* arrHash )
852                 {
853                     m_bLocked = policy.try_second_acquire( arrHash, m_arrLock );
854                 }
855
856                 ~scoped_cell_trylock()
857                 {
858                     if ( m_bLocked ) {
859                         for ( unsigned int i = 0; i < c_nArity; ++i )
860                             m_arrLock[i]->unlock();
861                     }
862                 }
863
864                 bool locked() const
865                 {
866                     return m_bLocked;
867                 }
868             };
869
870             class scoped_full_lock {
871                 refinable& m_policy;
872             public:
873                 scoped_full_lock( refinable& policy )
874                     : m_policy( policy )
875                 {
876                     policy.acquire_all();
877                 }
878                 ~scoped_full_lock()
879                 {
880                     m_policy.release_all();
881                 }
882             };
883
884             class scoped_resize_lock
885             {
886                 refinable&      m_policy;
887                 lock_array_ptr  m_arrLocks[ c_nArity ];
888             public:
889                 scoped_resize_lock( refinable& policy )
890                     : m_policy(policy)
891                 {
892                     policy.acquire_resize( m_arrLocks );
893                 }
894                 ~scoped_resize_lock()
895                 {
896                     m_policy.release_resize( m_arrLocks );
897                 }
898             };
899             //@endcond
900
901         public:
902             /// Constructor
903             refinable(
904                 size_t nLockCount   ///< The size of lock array. Must be power of two.
905             )   : m_Owner(0)
906                 , m_nCapacity( nLockCount )
907             {
908                 assert( cds::beans::is_power2( nLockCount ));
909                 for ( unsigned int i = 0; i < c_nArity; ++i )
910                     m_arrLocks[i] = create_lock_array( nLockCount );
911             }
912
913             //@cond
914             void resize( size_t nCapacity )
915             {
916                 lock_array_ptr pNew[ c_nArity ];
917                 for ( unsigned int i = 0; i < c_nArity; ++i )
918                     pNew[i] = create_lock_array( nCapacity );
919
920                 /*
921                 // Assignment m_arrLocks[i] = pNew[i] may call heavy-weighted dtor for each item of m_arrLocks
922                 // that is unacceptable under spin-lock
923                 // So, we store copy of m_arrLocks in pOld
924                 lock_array_ptr pOld[ c_nArity ];
925                 for ( unsigned int i = 0; i < c_nArity; ++i )
926                     pOld[i] = m_arrLocks[i];
927
928                 // m_arrLocks assignment will not lead to calling dtor of each item of m_arrLocks
929                 // since copy of m_arrLocks locates in pOld and assignment will not be too painful for spin-lock
930                 */
931
932                 {
933                     scoped_spinlock sl(m_access);
934                     for ( unsigned int i = 0; i < c_nArity; ++i )
935                         m_arrLocks[i] = pNew[i];
936                 }
937                 m_nCapacity.store( nCapacity, atomics::memory_order_release );
938
939                 m_Stat.onResize();
940             }
941             //@endcond
942
943             /// Returns lock array size
944             /**
945                 Lock array size is not a constant for \p refinable policy and can be changed when the set is resized.
946             */
947             size_t lock_count() const
948             {
949                 return m_nCapacity.load(atomics::memory_order_relaxed);
950             }
951
952             /// Returns the arity of \p refinable mutex policy
953             CDS_CONSTEXPR unsigned int arity() const CDS_NOEXCEPT
954             {
955                 return c_nArity;
956             }
957
958             /// Returns internal statistics
959             statistics_type const& statistics() const
960             {
961                 return m_Stat;
962             }
963         };
964
965         /// CuckooSet internal statistics
966         struct stat {
967             typedef cds::atomicity::event_counter   counter_type ;  ///< Counter type
968
969             counter_type    m_nRelocateCallCount    ; ///< Count of \p relocate function call
970             counter_type    m_nRelocateRoundCount   ; ///< Count of attempts to relocate items
971             counter_type    m_nFalseRelocateCount   ; ///< Count of unneeded attempts of \p relocate call
972             counter_type    m_nSuccessRelocateCount ; ///< Count of successfull item relocating
973             counter_type    m_nRelocateAboveThresholdCount; ///< Count of item relocating above probeset threshold
974             counter_type    m_nFailedRelocateCount  ;   ///< Count of failed relocation attemp (when all probeset is full)
975
976             counter_type    m_nResizeCallCount      ;   ///< Count of \p resize function call
977             counter_type    m_nFalseResizeCount     ;   ///< Count of false \p resize function call (when other thread has been resized the set)
978             counter_type    m_nResizeSuccessNodeMove;   ///< Count of successfull node moving when resizing
979             counter_type    m_nResizeRelocateCall   ;   ///< Count of \p relocate function call from \p resize function
980
981             counter_type    m_nInsertSuccess        ;   ///< Count of successfull \p insert function call
982             counter_type    m_nInsertFailed         ;   ///< Count of failed \p insert function call
983             counter_type    m_nInsertResizeCount    ;   ///< Count of \p resize function call from \p insert
984             counter_type    m_nInsertRelocateCount  ;   ///< Count of \p relocate function call from \p insert
985             counter_type    m_nInsertRelocateFault  ;   ///< Count of failed \p relocate function call from \p insert
986
987             counter_type    m_nEnsureExistCount     ;   ///< Count of call \p ensure function for existing node
988             counter_type    m_nEnsureSuccessCount   ;   ///< Count of successfull \p insert function call for new node
989             counter_type    m_nEnsureResizeCount    ;   ///< Count of \p resize function call from \p ensure
990             counter_type    m_nEnsureRelocateCount  ;   ///< Count of \p relocate function call from \p ensure
991             counter_type    m_nEnsureRelocateFault  ;   ///< Count of failed \p relocate function call from \p ensure
992
993             counter_type    m_nUnlinkSuccess        ;   ///< Count of success \p unlink function call
994             counter_type    m_nUnlinkFailed         ;   ///< Count of failed \p unlink function call
995
996             counter_type    m_nEraseSuccess         ;   ///< Count of success \p erase function call
997             counter_type    m_nEraseFailed          ;   ///< Count of failed \p erase function call
998
999             counter_type    m_nFindSuccess         ;   ///< Count of success \p find function call
1000             counter_type    m_nFindFailed          ;   ///< Count of failed \p find function call
1001
1002             counter_type    m_nFindEqualSuccess         ;   ///< Count of success \p find_equal function call
1003             counter_type    m_nFindEqualFailed          ;   ///< Count of failed \p find_equal function call
1004
1005             counter_type    m_nFindWithSuccess         ;   ///< Count of success \p find_with function call
1006             counter_type    m_nFindWithFailed          ;   ///< Count of failed \p find_with function call
1007
1008             //@cond
1009             void    onRelocateCall()        { ++m_nRelocateCallCount; }
1010             void    onRelocateRound()       { ++m_nRelocateRoundCount; }
1011             void    onFalseRelocateRound()  { ++m_nFalseRelocateCount; }
1012             void    onSuccessRelocateRound(){ ++m_nSuccessRelocateCount; }
1013             void    onRelocateAboveThresholdRound() { ++m_nRelocateAboveThresholdCount; }
1014             void    onFailedRelocate()      { ++m_nFailedRelocateCount; }
1015
1016             void    onResizeCall()          { ++m_nResizeCallCount; }
1017             void    onFalseResizeCall()     { ++m_nFalseResizeCount; }
1018             void    onResizeSuccessMove()   { ++m_nResizeSuccessNodeMove; }
1019             void    onResizeRelocateCall()  { ++m_nResizeRelocateCall; }
1020
1021             void    onInsertSuccess()       { ++m_nInsertSuccess; }
1022             void    onInsertFailed()        { ++m_nInsertFailed; }
1023             void    onInsertResize()        { ++m_nInsertResizeCount; }
1024             void    onInsertRelocate()      { ++m_nInsertRelocateCount; }
1025             void    onInsertRelocateFault() { ++m_nInsertRelocateFault; }
1026
1027             void    onEnsureExist()         { ++m_nEnsureExistCount; }
1028             void    onEnsureSuccess()       { ++m_nEnsureSuccessCount; }
1029             void    onEnsureResize()        { ++m_nEnsureResizeCount; }
1030             void    onEnsureRelocate()      { ++m_nEnsureRelocateCount; }
1031             void    onEnsureRelocateFault() { ++m_nEnsureRelocateFault; }
1032
1033             void    onUnlinkSuccess()       { ++m_nUnlinkSuccess; }
1034             void    onUnlinkFailed()        { ++m_nUnlinkFailed; }
1035
1036             void    onEraseSuccess()        { ++m_nEraseSuccess; }
1037             void    onEraseFailed()         { ++m_nEraseFailed; }
1038
1039             void    onFindSuccess()         { ++m_nFindSuccess; }
1040             void    onFindFailed()          { ++m_nFindFailed; }
1041
1042             void    onFindWithSuccess()     { ++m_nFindWithSuccess; }
1043             void    onFindWithFailed()      { ++m_nFindWithFailed; }
1044             //@endcond
1045         };
1046
1047         /// CuckooSet empty internal statistics
1048         struct empty_stat {
1049             //@cond
1050             void    onRelocateCall()        const {}
1051             void    onRelocateRound()       const {}
1052             void    onFalseRelocateRound()  const {}
1053             void    onSuccessRelocateRound()const {}
1054             void    onRelocateAboveThresholdRound() const {}
1055             void    onFailedRelocate()      const {}
1056
1057             void    onResizeCall()          const {}
1058             void    onFalseResizeCall()     const {}
1059             void    onResizeSuccessMove()   const {}
1060             void    onResizeRelocateCall()  const {}
1061
1062             void    onInsertSuccess()       const {}
1063             void    onInsertFailed()        const {}
1064             void    onInsertResize()        const {}
1065             void    onInsertRelocate()      const {}
1066             void    onInsertRelocateFault() const {}
1067
1068             void    onEnsureExist()         const {}
1069             void    onEnsureSuccess()       const {}
1070             void    onEnsureResize()        const {}
1071             void    onEnsureRelocate()      const {}
1072             void    onEnsureRelocateFault() const {}
1073
1074             void    onUnlinkSuccess()       const {}
1075             void    onUnlinkFailed()        const {}
1076
1077             void    onEraseSuccess()        const {}
1078             void    onEraseFailed()         const {}
1079
1080             void    onFindSuccess()         const {}
1081             void    onFindFailed()          const {}
1082
1083             void    onFindWithSuccess()     const {}
1084             void    onFindWithFailed()      const {}
1085             //@endcond
1086         };
1087
1088         /// Type traits for CuckooSet class
1089         struct type_traits
1090         {
1091             /// Hook used
1092             /**
1093                 Possible values are: cuckoo::base_hook, cuckoo::member_hook, cuckoo::traits_hook.
1094             */
1095             typedef base_hook<>         hook;
1096
1097             /// Hash functors tuple
1098             /**
1099                 This is mandatory type and has no predefined one.
1100
1101                 At least, two hash functors should be provided. All hash functor
1102                 should be orthogonal (different): for each <tt> i,j: i != j => h[i](x) != h[j](x) </tt>.
1103                 The hash functors are defined as <tt> std::tuple< H1, H2, ... Hn > </tt>:
1104                 \@code cds::opt::hash< std::tuple< h1, h2 > > \@endcode
1105                 The number of hash functors specifies the number \p k - the count of hash tables in cuckoo hashing.
1106                 Up to 10 different hash functors are supported.
1107             */
1108             typedef cds::opt::none      hash;
1109
1110             /// Concurrent access policy
1111             /**
1112                 Available opt::mutex_policy types:
1113                 - cuckoo::striping - simple, but the lock array is not resizable
1114                 - cuckoo::refinable - resizable lock array, but more complex access to set data.
1115
1116                 Default is cuckoo::striping.
1117             */
1118             typedef cuckoo::striping<>               mutex_policy;
1119
1120             /// Key equality functor
1121             /**
1122                 Default is <tt>std::equal_to<T></tt>
1123             */
1124             typedef opt::none                       equal_to;
1125
1126             /// Key comparison functor
1127             /**
1128                 No default functor is provided. If the option is not specified, the \p less is used.
1129             */
1130             typedef opt::none                       compare;
1131
1132             /// specifies binary predicate used for key comparison.
1133             /**
1134                 Default is \p std::less<T>.
1135             */
1136             typedef opt::none                       less;
1137
1138             /// Item counter
1139             /**
1140                 The type for item counting feature.
1141                 Default is cds::atomicity::item_counter
1142
1143                 Only atomic item counter type is allowed.
1144             */
1145             typedef atomicity::item_counter             item_counter;
1146
1147             /// Allocator type
1148             /**
1149                 The allocator type for allocating bucket tables.
1150             */
1151             typedef CDS_DEFAULT_ALLOCATOR       allocator;
1152
1153             /// Disposer
1154             /**
1155                 The disposer functor is used in CuckooSet::clear member function
1156                 to free set's node.
1157             */
1158             typedef intrusive::opt::v::empty_disposer   disposer;
1159
1160             /// Internal statistics. Available statistics: cuckoo::stat, cuckoo::empty_stat
1161             typedef empty_stat                  stat;
1162         };
1163
1164         /// Metafunction converting option list to CuckooSet traits
1165         /**
1166             This is a wrapper for <tt> cds::opt::make_options< type_traits, Options...> </tt>
1167             \p Options list see \ref CuckooSet.
1168         */
1169         template <typename... Options>
1170         struct make_traits {
1171             typedef typename cds::opt::make_options<
1172                 typename cds::opt::find_type_traits< cuckoo::type_traits, Options... >::type
1173                 ,Options...
1174             >::type   type ;    ///< Result of metafunction
1175         };
1176
1177         //@cond
1178         namespace details {
1179             template <typename Node, typename Probeset>
1180             class bucket_entry;
1181
1182             template <typename Node>
1183             class bucket_entry<Node, cuckoo::list>
1184             {
1185             public:
1186                 typedef Node                        node_type;
1187                 typedef cuckoo::list_probeset_class probeset_class;
1188                 typedef cuckoo::list                probeset_type;
1189
1190             protected:
1191                 node_type *     pHead;
1192                 unsigned int    nSize;
1193
1194             public:
1195                 class iterator
1196                 {
1197                     node_type * pNode;
1198                     friend class bucket_entry;
1199
1200                 public:
1201                     iterator()
1202                         : pNode( nullptr )
1203                     {}
1204                     iterator( node_type * p )
1205                         : pNode( p )
1206                     {}
1207                     iterator( iterator const& it)
1208                         : pNode( it.pNode )
1209                     {}
1210
1211                     iterator& operator=( iterator const& it )
1212                     {
1213                         pNode = it.pNode;
1214                         return *this;
1215                     }
1216
1217                     iterator& operator=( node_type * p )
1218                     {
1219                         pNode = p;
1220                         return *this;
1221                     }
1222
1223                     node_type * operator->()
1224                     {
1225                         return pNode;
1226                     }
1227                     node_type& operator*()
1228                     {
1229                         assert( pNode != nullptr );
1230                         return *pNode;
1231                     }
1232
1233                     // preinc
1234                     iterator& operator ++()
1235                     {
1236                         if ( pNode )
1237                             pNode = pNode->m_pNext;
1238                         return *this;
1239                     }
1240
1241                     bool operator==(iterator const& it ) const
1242                     {
1243                         return pNode == it.pNode;
1244                     }
1245                     bool operator!=(iterator const& it ) const
1246                     {
1247                         return !( *this == it );
1248                     }
1249                 };
1250
1251             public:
1252                 bucket_entry()
1253                     : pHead( nullptr )
1254                     , nSize(0)
1255                 {
1256                     static_assert(( std::is_same<typename node_type::probeset_type, probeset_type>::value ), "Incompatible node type" );
1257                 }
1258
1259                 iterator begin()
1260                 {
1261                     return iterator(pHead);
1262                 }
1263                 iterator end()
1264                 {
1265                     return iterator();
1266                 }
1267
1268                 void insert_after( iterator it, node_type * p )
1269                 {
1270                     node_type * pPrev = it.pNode;
1271                     if ( pPrev ) {
1272                         p->m_pNext = pPrev->m_pNext;
1273                         pPrev->m_pNext = p;
1274                     }
1275                     else {
1276                         // insert as head
1277                         p->m_pNext = pHead;
1278                         pHead = p;
1279                     }
1280                     ++nSize;
1281                 }
1282
1283                 void remove( iterator itPrev, iterator itWhat )
1284                 {
1285                     node_type * pPrev = itPrev.pNode;
1286                     node_type * pWhat = itWhat.pNode;
1287                     assert( (!pPrev && pWhat == pHead) || (pPrev && pPrev->m_pNext == pWhat) );
1288
1289                     if ( pPrev )
1290                         pPrev->m_pNext = pWhat->m_pNext;
1291                     else {
1292                         assert( pWhat == pHead );
1293                         pHead = pHead->m_pNext;
1294                     }
1295                     pWhat->clear();
1296                     --nSize;
1297                 }
1298
1299                 void clear()
1300                 {
1301                     node_type * pNext;
1302                     for ( node_type * pNode = pHead; pNode; pNode = pNext ) {
1303                         pNext = pNode->m_pNext;
1304                         pNode->clear();
1305                     }
1306
1307                     nSize = 0;
1308                     pHead = nullptr;
1309                 }
1310
1311                 template <typename Disposer>
1312                 void clear( Disposer disp )
1313                 {
1314                     node_type * pNext;
1315                     for ( node_type * pNode = pHead; pNode; pNode = pNext ) {
1316                         pNext = pNode->m_pNext;
1317                         pNode->clear();
1318                         disp( pNode );
1319                     }
1320
1321                     nSize = 0;
1322                     pHead = nullptr;
1323                 }
1324
1325                 unsigned int size() const
1326                 {
1327                     return nSize;
1328                 }
1329             };
1330
1331             template <typename Node, unsigned int Capacity>
1332             class bucket_entry<Node, cuckoo::vector<Capacity> >
1333             {
1334             public:
1335                 typedef Node                            node_type;
1336                 typedef cuckoo::vector_probeset_class   probeset_class;
1337                 typedef cuckoo::vector<Capacity>        probeset_type;
1338
1339                 static unsigned int const c_nCapacity = probeset_type::c_nCapacity;
1340
1341             protected:
1342                 node_type *     m_arrNode[c_nCapacity];
1343                 unsigned int    m_nSize;
1344
1345                 void shift_up( unsigned int nFrom )
1346                 {
1347                     assert( m_nSize < c_nCapacity );
1348
1349                     // std alorithm
1350                     if ( nFrom < m_nSize )
1351                         std::copy_backward( m_arrNode + nFrom, m_arrNode + m_nSize, m_arrNode + m_nSize + 1 );
1352
1353                     // alternative: low-level byte copying
1354                     //memmove( m_arrNode + nFrom + 1, m_arrNode + nFrom, (m_nSize - nFrom) * sizeof(m_arrNode[0]) );
1355                 }
1356
1357                 void shift_down( node_type ** pFrom )
1358                 {
1359                     assert( m_arrNode <= pFrom && pFrom < m_arrNode + m_nSize);
1360                     // std algo
1361                     std::copy( pFrom + 1, m_arrNode + m_nSize, pFrom  );
1362
1363                     // alternative: low-level byte copying
1364                     //memmove( pFrom + 1, pFrom, (m_nSize - nFrom - 1) * sizeof(m_arrNode[0]));
1365                 }
1366             public:
1367                 class iterator
1368                 {
1369                     node_type **    pArr;
1370                     friend class bucket_entry;
1371
1372                 public:
1373                     iterator()
1374                         : pArr( nullptr )
1375                     {}
1376                     iterator( node_type ** p )
1377                         : pArr(p)
1378                     {}
1379                     iterator( iterator const& it)
1380                         : pArr( it.pArr )
1381                     {}
1382
1383                     iterator& operator=( iterator const& it )
1384                     {
1385                         pArr = it.pArr;
1386                         return *this;
1387                     }
1388
1389                     node_type * operator->()
1390                     {
1391                         assert( pArr != nullptr );
1392                         return *pArr;
1393                     }
1394                     node_type& operator*()
1395                     {
1396                         assert( pArr != nullptr );
1397                         assert( *pArr != nullptr );
1398                         return *(*pArr);
1399                     }
1400
1401                     // preinc
1402                     iterator& operator ++()
1403                     {
1404                         ++pArr;
1405                         return *this;
1406                     }
1407
1408                     bool operator==(iterator const& it ) const
1409                     {
1410                         return pArr == it.pArr;
1411                     }
1412                     bool operator!=(iterator const& it ) const
1413                     {
1414                         return !( *this == it );
1415                     }
1416                 };
1417
1418             public:
1419                 bucket_entry()
1420                     : m_nSize(0)
1421                 {
1422                     memset( m_arrNode, 0, sizeof(m_arrNode));
1423                     static_assert(( std::is_same<typename node_type::probeset_type, probeset_type>::value ), "Incompatible node type" );
1424                 }
1425
1426                 iterator begin()
1427                 {
1428                     return iterator(m_arrNode);
1429                 }
1430                 iterator end()
1431                 {
1432                     return iterator(m_arrNode + size());
1433                 }
1434
1435                 void insert_after( iterator it, node_type * p )
1436                 {
1437                     assert( m_nSize < c_nCapacity );
1438                     assert( !it.pArr || (m_arrNode <= it.pArr && it.pArr <= m_arrNode + m_nSize));
1439
1440                     if ( it.pArr ) {
1441                         shift_up( (unsigned int)(it.pArr - m_arrNode) + 1 );
1442                         *(it.pArr + 1) = p;
1443                     }
1444                     else {
1445                         shift_up(0);
1446                         m_arrNode[0] = p;
1447                     }
1448                     ++m_nSize;
1449                 }
1450
1451                 void remove( iterator /*itPrev*/, iterator itWhat )
1452                 {
1453                     itWhat->clear();
1454                     shift_down( itWhat.pArr );
1455                     --m_nSize;
1456                 }
1457
1458                 void clear()
1459                 {
1460                     m_nSize = 0;
1461                 }
1462
1463                 template <typename Disposer>
1464                 void clear( Disposer disp )
1465                 {
1466                     for ( unsigned int i = 0; i < m_nSize; ++i ) {
1467                         disp( m_arrNode[i] );
1468                     }
1469                     m_nSize = 0;
1470                 }
1471
1472                 unsigned int size() const
1473                 {
1474                     return m_nSize;
1475                 }
1476             };
1477
1478             template <typename Node, unsigned int ArraySize>
1479             struct hash_ops {
1480                 static void store( Node * pNode, size_t * pHashes )
1481                 {
1482                     memcpy( pNode->m_arrHash, pHashes, sizeof(size_t) * ArraySize );
1483                 }
1484                 static bool equal_to( Node& node, unsigned int nTable, size_t nHash )
1485                 {
1486                     return node.m_arrHash[nTable] == nHash;
1487                 }
1488             };
1489             template <typename Node>
1490             struct hash_ops<Node, 0>
1491             {
1492                 static void store( Node * /*pNode*/, size_t * /*pHashes*/ )
1493                 {}
1494                 static bool equal_to( Node& /*node*/, unsigned int /*nTable*/, size_t /*nHash*/ )
1495                 {
1496                     return true;
1497                 }
1498             };
1499
1500             template <typename NodeTraits, bool Ordered>
1501             struct contains;
1502
1503             template <typename NodeTraits>
1504             struct contains<NodeTraits, true>
1505             {
1506                 template <typename BucketEntry, typename Position, typename Q, typename Compare>
1507                 static bool find( BucketEntry& probeset, Position& pos, unsigned int nTable, size_t nHash, Q const& val, Compare cmp )
1508                 {
1509                     // Ordered version
1510                     typedef typename BucketEntry::iterator bucket_iterator;
1511
1512                     bucket_iterator itPrev;
1513
1514                     for ( bucket_iterator it = probeset.begin(), itEnd = probeset.end(); it != itEnd; ++it ) {
1515                         int cmpRes = cmp( *NodeTraits::to_value_ptr(*it), val );
1516                         if ( cmpRes >= 0 ) {
1517                             pos.itFound = it;
1518                             pos.itPrev = itPrev;
1519                             return cmpRes == 0;
1520                         }
1521
1522                         itPrev = it;
1523                     }
1524
1525                     pos.itPrev = itPrev;
1526                     pos.itFound = probeset.end();
1527                     return false;
1528                 }
1529             };
1530
1531             template <typename NodeTraits>
1532             struct contains<NodeTraits, false>
1533             {
1534                 template <typename BucketEntry, typename Position, typename Q, typename EqualTo>
1535                 static bool find( BucketEntry& probeset, Position& pos, unsigned int nTable, size_t nHash, Q const& val, EqualTo eq )
1536                 {
1537                     // Unordered version
1538                     typedef typename BucketEntry::iterator  bucket_iterator;
1539                     typedef typename BucketEntry::node_type node_type;
1540
1541                     bucket_iterator itPrev;
1542
1543                     for ( bucket_iterator it = probeset.begin(), itEnd = probeset.end(); it != itEnd; ++it ) {
1544                         if ( hash_ops<node_type, node_type::hash_array_size>::equal_to( *it, nTable, nHash ) && eq( *NodeTraits::to_value_ptr(*it), val )) {
1545                             pos.itFound = it;
1546                             pos.itPrev = itPrev;
1547                             return true;
1548                         }
1549                         itPrev = it;
1550                     }
1551
1552                     pos.itPrev = itPrev;
1553                     pos.itFound = probeset.end();
1554                     return false;
1555                 }
1556             };
1557
1558         }   // namespace details
1559         //@endcond
1560
1561     } // namespace cuckoo
1562
1563     /// Cuckoo hash set
1564     /** @ingroup cds_intrusive_map
1565
1566         Source
1567             - [2007] M.Herlihy, N.Shavit, M.Tzafrir "Concurrent Cuckoo Hashing. Technical report"
1568             - [2008] Maurice Herlihy, Nir Shavit "The Art of Multiprocessor Programming"
1569
1570         <b>About Cuckoo hashing</b>
1571
1572             [From <i>"The Art of Multiprocessor Programming"</i>]
1573             Cuckoo hashing is a hashing algorithm in which a newly added item displaces any earlier item
1574             occupying the same slot. For brevity, a table is a k-entry array of items. For a hash set f size
1575             N = 2k we use a two-entry array of tables, and two independent hash functions,
1576             <tt> h0, h1: KeyRange -> 0,...,k-1</tt>
1577             mapping the set of possible keys to entries in he array. To test whether a value \p x is in the set,
1578             <tt>find(x)</tt> tests whether either <tt>table[0][h0(x)]</tt> or <tt>table[1][h1(x)]</tt> is
1579             equal to \p x. Similarly, <tt>erase(x)</tt>checks whether \p x is in either <tt>table[0][h0(x)]</tt>
1580             or <tt>table[1][h1(x)]</tt>, ad removes it if found.
1581
1582             The <tt>insert(x)</tt> successively "kicks out" conflicting items until every key has a slot.
1583             To add \p x, the method swaps \p x with \p y, the current occupant of <tt>table[0][h0(x)]</tt>.
1584             If the prior value was \p nullptr, it is done. Otherwise, it swaps the newly nest-less value \p y
1585             for the current occupant of <tt>table[1][h1(y)]</tt> in the same way. As before, if the prior value
1586             was \p nullptr, it is done. Otherwise, the method continues swapping entries (alternating tables)
1587             until it finds an empty slot. We might not find an empty slot, either because the table is full,
1588             or because the sequence of displacement forms a cycle. We therefore need an upper limit on the
1589             number of successive displacements we are willing to undertake. When this limit is exceeded,
1590             we resize the hash table, choose new hash functions and start over.
1591
1592             For concurrent cuckoo hashing, rather than organizing the set as a two-dimensional table of
1593             items, we use two-dimensional table of probe sets, where a probe set is a constant-sized set
1594             of items with the same hash code. Each probe set holds at most \p PROBE_SIZE items, but the algorithm
1595             tries to ensure that when the set is quiescent (i.e no method call in progress) each probe set
1596             holds no more than <tt>THRESHOLD < PROBE_SET</tt> items. While method calls are in-flight, a probe
1597             set may temporarily hold more than \p THRESHOLD but never more than \p PROBE_SET items.
1598
1599             In current implementation, a probe set can be defined either as a (single-linked) list
1600             or as a fixed-sized vector, optionally ordered.
1601
1602             In description above two-table cuckoo hashing (<tt>k = 2</tt>) has been considered.
1603             We can generalize this approach for <tt>k >= 2</tt> when we have \p k hash functions
1604             <tt>h[0], ... h[k-1]</tt> and \p k tables <tt>table[0], ... table[k-1]</tt>.
1605
1606             The search in probe set is linear, the complexity is <tt> O(PROBE_SET) </tt>.
1607             The probe set may be ordered or not. Ordered probe set can be more efficient since
1608             the average search complexity is <tt>O(PROBE_SET/2)</tt>.
1609             However, the overhead of sorting can eliminate a gain of ordered search.
1610
1611             The probe set is ordered if opt::compare or opt::less is specified in \p Traits template
1612             parameter. Otherwise, the probe set is unordered and \p Traits must contain
1613             opt::equal_to option.
1614
1615             The cds::intrusive::cuckoo namespace contains \p %CuckooSet-related declarations.
1616
1617         Template arguments:
1618         - \p T - the type stored in the set.  The type must be based on cuckoo::node (for cuckoo::base_hook)
1619             or it must have a member of type %cuckoo::node (for cuckoo::member_hook),
1620             or it must be convertible to \p %cuckoo::node (for cuckoo::traits_hook)
1621         - \p Traits - type traits. See cuckoo::type_traits for explanation. It is possible to declare option-based
1622             set with cuckoo::make_traits metafunction result as \p Traits template argument.
1623
1624         Template argument list \p Options... of cuckoo::make_traits metafunction are:
1625         - intrusive::opt::hook - hook used. Possible values are: cuckoo::base_hook, cuckoo::member_hook, cuckoo::traits_hook.
1626             If the option is not specified, <tt>%cuckoo::base_hook<></tt> is used.
1627         - opt::hash - hash functor tuple, mandatory option. At least, two hash functors should be provided. All hash functor
1628             should be orthogonal (different): for each <tt> i,j: i != j => h[i](x) != h[j](x) </tt>.
1629             The hash functors are passed as <tt> std::tuple< H1, H2, ... Hn > </tt>. The number of hash functors specifies
1630             the number \p k - the count of hash tables in cuckoo hashing. If the compiler supports variadic templates
1631             then k is unlimited, otherwise up to 10 different hash functors are supported.
1632         - opt::mutex_policy - concurrent access policy.
1633             Available policies: cuckoo::striping, cuckoo::refinable.
1634             Default is cuckoo::striping.
1635         - opt::equal_to - key equality functor like \p std::equal_to.
1636             If this functor is defined then the probe-set will be unordered.
1637             If opt::compare or opt::less option is specified too, then the probe-set will be ordered
1638             and opt::equal_to will be ignored.
1639         - opt::compare - key comparison functor. No default functor is provided.
1640             If the option is not specified, the opt::less is used.
1641             If opt::compare or opt::less option is specified, then the probe-set will be ordered.
1642         - opt::less - specifies binary predicate used for key comparison. Default is \p std::less<T>.
1643             If opt::compare or opt::less option is specified, then the probe-set will be ordered.
1644         - opt::item_counter - the type of item counting feature. Default is \ref atomicity::item_counter
1645             The item counter should be atomic.
1646         - opt::allocator - the allocator type using for allocating bucket tables.
1647             Default is \p CDS_DEFAULT_ALLOCATOR
1648         - intrusive::opt::disposer - the disposer type used in \ref clear() member function for
1649             freeing nodes. Default is intrusive::opt::v::empty_disposer
1650         - opt::stat - internal statistics. Possibly types: cuckoo::stat, cuckoo::empty_stat.
1651             Default is cuckoo::empty_stat
1652
1653         The probe set options cuckoo::probeset_type and cuckoo::store_hash are taken from \p node type
1654         specified by \p opt::hook option.
1655
1656         <b>How to use</b>
1657
1658         You should incorporate cuckoo::node into your struct \p T and provide
1659         appropriate cuckoo::type_traits::hook in your \p Traits template parameters. Usually, for \p Traits you
1660         define a struct based on cuckoo::type_traits.
1661
1662         Example for base hook and list-based probe-set:
1663         \code
1664         #include <cds/intrusive/cuckoo_set.h>
1665
1666         // Data stored in cuckoo set
1667         // We use list as probe-set container and store hash values in the node
1668         // (since we use two hash functions we should store 2 hash values per node)
1669         struct my_data: public cds::intrusive::cuckoo::node< cds::intrusive::cuckoo::list, 2 >
1670         {
1671             // key field
1672             std::string     strKey;
1673
1674             // other data
1675             // ...
1676         };
1677
1678         // Provide equal_to functor for my_data since we will use unordered probe-set
1679         struct my_data_equal_to {
1680             bool operator()( const my_data& d1, const my_data& d2 ) const
1681             {
1682                 return d1.strKey.compare( d2.strKey ) == 0;
1683             }
1684
1685             bool operator()( const my_data& d, const std::string& s ) const
1686             {
1687                 return d.strKey.compare(s) == 0;
1688             }
1689
1690             bool operator()( const std::string& s, const my_data& d ) const
1691             {
1692                 return s.compare( d.strKey ) == 0;
1693             }
1694         };
1695
1696         // Provide two hash functor for my_data
1697         struct hash1 {
1698             size_t operator()(std::string const& s) const
1699             {
1700                 return cds::opt::v::hash<std::string>( s );
1701             }
1702             size_t operator()( my_data const& d ) const
1703             {
1704                 return (*this)( d.strKey );
1705             }
1706         };
1707
1708         struct hash2: private hash1 {
1709             size_t operator()(std::string const& s) const
1710             {
1711                 size_t h = ~( hash1::operator()(s));
1712                 return ~h + 0x9e3779b9 + (h << 6) + (h >> 2);
1713             }
1714             size_t operator()( my_data const& d ) const
1715             {
1716                 return (*this)( d.strKey );
1717             }
1718         };
1719
1720         // Declare type traits
1721         struct my_traits: public cds::intrusive::cuckoo::type_traits
1722         {
1723             typedef cds::intrusive::cuckoo::base_hook<
1724                 cds::intrusive::cuckoo::probeset_type< my_data::probeset_type >
1725                 ,cds::intrusive::cuckoo::store_hash< my_data::hash_array_size >
1726             >   hook;
1727             typedef my_data_equa_to equal_to;
1728             typedef std::tuple< hash1, hash2 >  hash;
1729         };
1730
1731         // Declare CuckooSet type
1732         typedef cds::intrusive::CuckooSet< my_data, my_traits > my_cuckoo_set;
1733
1734         // Equal option-based declaration
1735         typedef cds::intrusive::CuckooSet< my_data,
1736             cds::intrusive::cuckoo::make_traits<
1737                 cds::intrusive::opt::hook< cds::intrusive::cuckoo::base_hook<
1738                     cds::intrusive::cuckoo::probeset_type< my_data::probeset_type >
1739                     ,cds::intrusive::cuckoo::store_hash< my_data::hash_array_size >
1740                 > >
1741                 ,cds::opt::hash< std::tuple< hash1, hash2 > >
1742                 ,cds::opt::equal_to< my_data_equal_to >
1743             >::type
1744         > opt_cuckoo_set;
1745         \endcode
1746
1747         If we provide \p compare function instead of \p equal_to for \p my_data
1748         we get as a result a cuckoo set with ordered probe set that may improve
1749         performance.
1750         Example for base hook and ordered vector-based probe-set:
1751
1752         \code
1753         #include <cds/intrusive/cuckoo_set.h>
1754
1755         // Data stored in cuckoo set
1756         // We use a vector of capacity 4 as probe-set container and store hash values in the node
1757         // (since we use two hash functions we should store 2 hash values per node)
1758         struct my_data: public cds::intrusive::cuckoo::node< cds::intrusive::cuckoo::vector<4>, 2 >
1759         {
1760             // key field
1761             std::string     strKey;
1762
1763             // other data
1764             // ...
1765         };
1766
1767         // Provide compare functor for my_data since we want to use ordered probe-set
1768         struct my_data_compare {
1769             int operator()( const my_data& d1, const my_data& d2 ) const
1770             {
1771                 return d1.strKey.compare( d2.strKey );
1772             }
1773
1774             int operator()( const my_data& d, const std::string& s ) const
1775             {
1776                 return d.strKey.compare(s);
1777             }
1778
1779             int operator()( const std::string& s, const my_data& d ) const
1780             {
1781                 return s.compare( d.strKey );
1782             }
1783         };
1784
1785         // Provide two hash functor for my_data
1786         struct hash1 {
1787             size_t operator()(std::string const& s) const
1788             {
1789                 return cds::opt::v::hash<std::string>( s );
1790             }
1791             size_t operator()( my_data const& d ) const
1792             {
1793                 return (*this)( d.strKey );
1794             }
1795         };
1796
1797         struct hash2: private hash1 {
1798             size_t operator()(std::string const& s) const
1799             {
1800                 size_t h = ~( hash1::operator()(s));
1801                 return ~h + 0x9e3779b9 + (h << 6) + (h >> 2);
1802             }
1803             size_t operator()( my_data const& d ) const
1804             {
1805                 return (*this)( d.strKey );
1806             }
1807         };
1808
1809         // Declare type traits
1810         struct my_traits: public cds::intrusive::cuckoo::type_traits
1811         {
1812             typedef cds::intrusive::cuckoo::base_hook<
1813                 cds::intrusive::cuckoo::probeset_type< my_data::probeset_type >
1814                 ,cds::intrusive::cuckoo::store_hash< my_data::hash_array_size >
1815             >   hook;
1816             typedef my_data_compare compare;
1817             typedef std::tuple< hash1, hash2 >  hash;
1818         };
1819
1820         // Declare CuckooSet type
1821         typedef cds::intrusive::CuckooSet< my_data, my_traits > my_cuckoo_set;
1822
1823         // Equal option-based declaration
1824         typedef cds::intrusive::CuckooSet< my_data,
1825             cds::intrusive::cuckoo::make_traits<
1826                 cds::intrusive::opt::hook< cds::intrusive::cuckoo::base_hook<
1827                     cds::intrusive::cuckoo::probeset_type< my_data::probeset_type >
1828                     ,cds::intrusive::cuckoo::store_hash< my_data::hash_array_size >
1829                 > >
1830                 ,cds::opt::hash< std::tuple< hash1, hash2 > >
1831                 ,cds::opt::compare< my_data_compare >
1832             >::type
1833         > opt_cuckoo_set;
1834         \endcode
1835
1836     */
1837     template <typename T, typename Traits = cuckoo::type_traits>
1838     class CuckooSet
1839     {
1840     public:
1841         typedef T   value_type  ;   ///< The value type stored in the set
1842         typedef Traits options  ;   ///< Set traits
1843
1844         typedef typename options::hook      hook        ;   ///< hook type
1845         typedef typename hook::node_type    node_type   ;   ///< node type
1846         typedef typename get_node_traits< value_type, node_type, hook>::type node_traits ;    ///< node traits
1847
1848         typedef typename options::hash              hash ;   ///< hash functor tuple wrapped for internal use
1849         typedef typename hash::hash_tuple_type      hash_tuple_type ; ///< Type of hash tuple
1850
1851         typedef typename options::stat              stat    ;   ///< internal statistics type
1852
1853         typedef typename options::mutex_policy      original_mutex_policy   ;   ///< Concurrent access policy, see cuckoo::type_traits::mutex_policy
1854
1855         /// Actual mutex policy
1856         /**
1857             Actual mutex policy is built from mutex policy type provided by \p Traits template argument (see cuckoo::type_traits::mutex_policy)
1858             but mutex policy internal statistics is conformed with cukoo::type_traits::stat type provided by \p Traits:
1859             - if \p %cuckoo::type_traits::stat is cuckoo::empty_stat then mutex policy statistics is already empty one
1860             - otherwise real mutex policy statistics is used
1861         */
1862         typedef typename original_mutex_policy::template rebind_statistics<
1863             typename std::conditional<
1864                 std::is_same< stat, cuckoo::empty_stat >::value
1865                 ,typename original_mutex_policy::empty_stat
1866                 ,typename original_mutex_policy::real_stat
1867             >::type
1868         >::other    mutex_policy;
1869
1870         static bool const c_isSorted = !( std::is_same< typename options::compare, opt::none >::value
1871                 && std::is_same< typename options::less, opt::none >::value ) ; ///< whether the probe set should be ordered
1872         static size_t const c_nArity = hash::size ; ///< the arity of cuckoo hashing: the number of hash functors provided; minimum 2.
1873
1874         /// Key equality functor; used only for unordered probe-set
1875         typedef typename opt::details::make_equal_to< value_type, options, !c_isSorted>::type key_equal_to;
1876
1877         /// key comparing functor based on opt::compare and opt::less option setter. Used only for ordered probe set
1878         typedef typename opt::details::make_comparator< value_type, options >::type key_comparator;
1879
1880         /// allocator type
1881         typedef typename options::allocator     allocator;
1882
1883         /// item counter type
1884         typedef typename options::item_counter  item_counter;
1885
1886         /// node disposer
1887         typedef typename options::disposer      disposer;
1888
1889     protected:
1890         //@cond
1891         typedef typename node_type::probeset_class  probeset_class;
1892         typedef typename node_type::probeset_type   probeset_type;
1893         static unsigned int const c_nNodeHashArraySize = node_type::hash_array_size;
1894
1895         typedef typename mutex_policy::scoped_cell_lock     scoped_cell_lock;
1896         typedef typename mutex_policy::scoped_cell_trylock  scoped_cell_trylock;
1897         typedef typename mutex_policy::scoped_full_lock     scoped_full_lock;
1898         typedef typename mutex_policy::scoped_resize_lock   scoped_resize_lock;
1899
1900         typedef cuckoo::details::bucket_entry< node_type, probeset_type >   bucket_entry;
1901         typedef typename bucket_entry::iterator                     bucket_iterator;
1902         typedef cds::details::Allocator< bucket_entry, allocator >  bucket_table_allocator;
1903
1904         typedef size_t  hash_array[c_nArity]    ;   ///< hash array
1905
1906         struct position {
1907             bucket_iterator     itPrev;
1908             bucket_iterator     itFound;
1909         };
1910
1911         typedef typename std::conditional< c_isSorted
1912             , cuckoo::details::contains< node_traits, true >
1913             , cuckoo::details::contains< node_traits, false >
1914         >::type contains_action;
1915
1916         template <typename Predicate>
1917         struct predicate_wrapper {
1918             typedef typename std::conditional< c_isSorted, cds::opt::details::make_comparator_from_less<Predicate>, Predicate>::type   type;
1919         };
1920
1921         typedef typename std::conditional< c_isSorted, key_comparator, key_equal_to >::type key_predicate;
1922         //@endcond
1923
1924     public:
1925         static unsigned int const   c_nDefaultProbesetSize = 4  ;   ///< default probeset size
1926         static size_t const         c_nDefaultInitialSize = 16  ;   ///< default initial size
1927         static unsigned int const   c_nRelocateLimit = c_nArity * 2 - 1 ;   ///< Count of attempts to relocate before giving up
1928
1929     protected:
1930         bucket_entry *      m_BucketTable[ c_nArity ] ; ///< Bucket tables
1931
1932         size_t              m_nBucketMask           ;   ///< Hash bitmask; bucket table size minus 1.
1933         unsigned int const  m_nProbesetSize         ;   ///< Probe set size
1934         unsigned int const  m_nProbesetThreshold    ;   ///< Probe set threshold
1935
1936         hash            m_Hash              ;   ///< Hash functor tuple
1937         mutex_policy    m_MutexPolicy       ;   ///< concurrent access policy
1938         item_counter    m_ItemCounter       ;   ///< item counter
1939         mutable stat    m_Stat              ;   ///< internal statistics
1940
1941     protected:
1942         //@cond
1943         static void check_common_constraints()
1944         {
1945             static_assert( (c_nArity == mutex_policy::c_nArity), "The count of hash functors must be equal to mutex_policy arity" );
1946         }
1947
1948         void check_probeset_properties() const
1949         {
1950             assert( m_nProbesetThreshold < m_nProbesetSize );
1951
1952             // if probe set type is cuckoo::vector<N> then m_nProbesetSize == N
1953             assert( node_type::probeset_size == 0 || node_type::probeset_size == m_nProbesetSize );
1954         }
1955
1956         template <typename Q>
1957         void hashing( size_t * pHashes, Q const& v ) const
1958         {
1959             m_Hash( pHashes, v );
1960         }
1961
1962         void copy_hash( size_t * pHashes, value_type const& v ) const
1963         {
1964             if ( c_nNodeHashArraySize )
1965                 memcpy( pHashes, node_traits::to_node_ptr( v )->get_hash(), sizeof(pHashes[0]) * c_nNodeHashArraySize );
1966             else
1967                 hashing( pHashes, v );
1968         }
1969
1970         bucket_entry& bucket( unsigned int nTable, size_t nHash )
1971         {
1972             assert( nTable < c_nArity );
1973             return m_BucketTable[nTable][nHash & m_nBucketMask];
1974         }
1975
1976         static void store_hash( node_type * pNode, size_t * pHashes )
1977         {
1978             cuckoo::details::hash_ops< node_type, c_nNodeHashArraySize >::store( pNode, pHashes );
1979             //memcpy( pNode->m_arrHash, pHashes, sizeof(size_t) * c_nArity );
1980         }
1981
1982         static bool equal_hash( node_type& node, unsigned int nTable, size_t nHash )
1983         {
1984             return cuckoo::details::hash_ops< node_type, c_nNodeHashArraySize >::equal_to( node, nTable, nHash );
1985         }
1986
1987         void allocate_bucket_tables( size_t nSize )
1988         {
1989             assert( cds::beans::is_power2( nSize ) );
1990
1991             m_nBucketMask = nSize - 1;
1992             bucket_table_allocator alloc;
1993             for ( unsigned int i = 0; i < c_nArity; ++i )
1994                 m_BucketTable[i] = alloc.NewArray( nSize );
1995         }
1996
1997         static void free_bucket_tables( bucket_entry ** pTable, size_t nCapacity )
1998         {
1999             bucket_table_allocator alloc;
2000             for ( unsigned int i = 0; i < c_nArity; ++i ) {
2001                 alloc.Delete( pTable[i], nCapacity );
2002                 pTable[i] = nullptr;
2003             }
2004         }
2005         void free_bucket_tables()
2006         {
2007             free_bucket_tables( m_BucketTable, m_nBucketMask + 1 );
2008         }
2009
2010         static unsigned int const c_nUndefTable = (unsigned int) -1;
2011         template <typename Q, typename Predicate >
2012         unsigned int contains( position * arrPos, size_t * arrHash, Q const& val, Predicate pred )
2013         {
2014             // Buckets must be locked
2015
2016             for ( unsigned int i = 0; i < c_nArity; ++i ) {
2017                 bucket_entry& probeset = bucket( i, arrHash[i] );
2018                 if ( contains_action::find( probeset, arrPos[i], i, arrHash[i], val, pred ))
2019                     return i;
2020             }
2021             return c_nUndefTable;
2022         }
2023
2024         template <typename Q, typename Predicate, typename Func>
2025         value_type * erase_( Q const& val, Predicate pred, Func f )
2026         {
2027             hash_array arrHash;
2028             hashing( arrHash, val );
2029             position arrPos[ c_nArity ];
2030
2031             {
2032                 scoped_cell_lock guard( m_MutexPolicy, arrHash );
2033
2034                 unsigned int nTable = contains( arrPos, arrHash, val, pred );
2035                 if ( nTable != c_nUndefTable ) {
2036                     node_type& node = *arrPos[nTable].itFound;
2037                     f( *node_traits::to_value_ptr(node) );
2038                     bucket( nTable, arrHash[nTable]).remove( arrPos[nTable].itPrev, arrPos[nTable].itFound );
2039                     --m_ItemCounter;
2040                     m_Stat.onEraseSuccess();
2041                     return node_traits::to_value_ptr( node );
2042                 }
2043             }
2044
2045             m_Stat.onEraseFailed();
2046             return nullptr;
2047         }
2048
2049         template <typename Q, typename Predicate, typename Func>
2050         bool find_( Q& val, Predicate pred, Func f )
2051         {
2052             hash_array arrHash;
2053             position arrPos[ c_nArity ];
2054             hashing( arrHash, val );
2055             scoped_cell_lock sl( m_MutexPolicy, arrHash );
2056
2057             unsigned int nTable = contains( arrPos, arrHash, val, pred );
2058             if ( nTable != c_nUndefTable ) {
2059                 f( *node_traits::to_value_ptr( *arrPos[nTable].itFound ), val );
2060                 m_Stat.onFindSuccess();
2061                 return true;
2062             }
2063
2064             m_Stat.onFindFailed();
2065             return false;
2066         }
2067
2068         bool relocate( unsigned int nTable, size_t * arrGoalHash )
2069         {
2070             // arrGoalHash contains hash values for relocating element
2071             // Relocating element is first one from bucket( nTable, arrGoalHash[nTable] ) probeset
2072
2073             m_Stat.onRelocateCall();
2074
2075             hash_array arrHash;
2076             value_type * pVal;
2077             for ( unsigned int nRound = 0; nRound < c_nRelocateLimit; ++nRound ) {
2078                 m_Stat.onRelocateRound();
2079
2080                 while ( true ) {
2081                     scoped_cell_lock guard( m_MutexPolicy, arrGoalHash );
2082
2083                     bucket_entry& refBucket = bucket( nTable, arrGoalHash[nTable] );
2084                     if ( refBucket.size() < m_nProbesetThreshold ) {
2085                         // probeset is not above the threshold
2086                         m_Stat.onFalseRelocateRound();
2087                         return true;
2088                     }
2089
2090                     pVal = node_traits::to_value_ptr( *refBucket.begin() );
2091                     copy_hash( arrHash, *pVal );
2092
2093                     scoped_cell_trylock guard2( m_MutexPolicy, arrHash );
2094                     if ( !guard2.locked() )
2095                         continue ;  // try one more time
2096
2097                     refBucket.remove( typename bucket_entry::iterator(), refBucket.begin() );
2098
2099                     unsigned int i = (nTable + 1) % c_nArity;
2100
2101                     // try insert into free probeset
2102                     while ( i != nTable ) {
2103                         bucket_entry& bkt = bucket( i, arrHash[i] );
2104                         if ( bkt.size() < m_nProbesetThreshold ) {
2105                             position pos;
2106                             contains_action::find( bkt, pos, i, arrHash[i], *pVal, key_predicate() ) ; // must return false!
2107                             bkt.insert_after( pos.itPrev, node_traits::to_node_ptr( pVal ));
2108                             m_Stat.onSuccessRelocateRound();
2109                             return true;
2110                         }
2111                         i = ( i + 1 ) % c_nArity;
2112                     }
2113
2114                     // try insert into partial probeset
2115                     i = (nTable + 1) % c_nArity;
2116                     while ( i != nTable ) {
2117                         bucket_entry& bkt = bucket( i, arrHash[i] );
2118                         if ( bkt.size() < m_nProbesetSize ) {
2119                             position pos;
2120                             contains_action::find( bkt, pos, i, arrHash[i], *pVal, key_predicate() ) ; // must return false!
2121                             bkt.insert_after( pos.itPrev, node_traits::to_node_ptr( pVal ));
2122                             nTable = i;
2123                             memcpy( arrGoalHash, arrHash, sizeof(arrHash));
2124                             m_Stat.onRelocateAboveThresholdRound();
2125                             goto next_iteration;
2126                         }
2127                         i = (i + 1) % c_nArity;
2128                     }
2129
2130                     // all probeset is full, relocating fault
2131                     refBucket.insert_after( typename bucket_entry::iterator(), node_traits::to_node_ptr( pVal ));
2132                     m_Stat.onFailedRelocate();
2133                     return false;
2134                 }
2135
2136             next_iteration:;
2137             }
2138             return false;
2139         }
2140
2141         void resize()
2142         {
2143             m_Stat.onResizeCall();
2144
2145             size_t nOldCapacity = bucket_count();
2146             bucket_entry *      pOldTable[ c_nArity ];
2147             {
2148                 scoped_resize_lock guard( m_MutexPolicy );
2149
2150                 if ( nOldCapacity != bucket_count() ) {
2151                     m_Stat.onFalseResizeCall();
2152                     return;
2153                 }
2154
2155                 size_t nCapacity = nOldCapacity * 2;
2156
2157                 m_MutexPolicy.resize( nCapacity );
2158                 memcpy( pOldTable, m_BucketTable, sizeof(pOldTable));
2159                 allocate_bucket_tables( nCapacity );
2160
2161                 typedef typename bucket_entry::iterator bucket_iterator;
2162                 hash_array arrHash;
2163                 position arrPos[ c_nArity ];
2164
2165                 for ( unsigned int nTable = 0; nTable < c_nArity; ++nTable ) {
2166                     bucket_entry * pTable = pOldTable[nTable];
2167                     for ( size_t k = 0; k < nOldCapacity; ++k ) {
2168                         bucket_iterator itNext;
2169                         for ( bucket_iterator it = pTable[k].begin(), itEnd = pTable[k].end(); it != itEnd; it = itNext ) {
2170                             itNext = it;
2171                             ++itNext;
2172
2173                             value_type& val = *node_traits::to_value_ptr( *it );
2174                             copy_hash( arrHash, val );
2175                             contains( arrPos, arrHash, val, key_predicate() ) ; // must return c_nUndefTable
2176
2177                             for ( unsigned int i = 0; i < c_nArity; ++i ) {
2178                                 bucket_entry& refBucket = bucket( i, arrHash[i] );
2179                                 if ( refBucket.size() < m_nProbesetThreshold ) {
2180                                     refBucket.insert_after( arrPos[i].itPrev, &*it );
2181                                     m_Stat.onResizeSuccessMove();
2182                                     goto do_next;
2183                                 }
2184                             }
2185
2186                             for ( unsigned int i = 0; i < c_nArity; ++i ) {
2187                                 bucket_entry& refBucket = bucket( i, arrHash[i] );
2188                                 if ( refBucket.size() < m_nProbesetSize ) {
2189                                     refBucket.insert_after( arrPos[i].itPrev, &*it );
2190                                     assert( refBucket.size() > 1 );
2191                                     copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()) );
2192                                     m_Stat.onResizeRelocateCall();
2193                                     relocate( i, arrHash );
2194                                     break;
2195                                 }
2196                             }
2197                         do_next:;
2198                         }
2199                     }
2200                 }
2201             }
2202             free_bucket_tables( pOldTable, nOldCapacity );
2203         }
2204
2205         CDS_CONSTEXPR static unsigned int calc_probeset_size( unsigned int nProbesetSize ) CDS_NOEXCEPT
2206         {
2207             return nProbesetSize
2208                 ? nProbesetSize
2209                 : ( node_type::probeset_size ? node_type::probeset_size : c_nDefaultProbesetSize )
2210 ;
2211         }
2212         //@endcond
2213
2214     public:
2215         /// Default constructor
2216         /**
2217             Initial size = \ref c_nDefaultInitialSize
2218
2219             Probe set size:
2220             - \p c_nDefaultProbesetSize if \p probeset_type is \p cuckoo::list
2221             - \p Capacity if \p probeset_type is <tt> cuckoo::vector<Capacity> </tt>
2222
2223             Probe set threshold = probe set size - 1
2224         */
2225         CuckooSet()
2226             : m_nProbesetSize( calc_probeset_size(0) )
2227             , m_nProbesetThreshold( m_nProbesetSize - 1 )
2228             , m_MutexPolicy( c_nDefaultInitialSize )
2229         {
2230             check_common_constraints();
2231             check_probeset_properties();
2232
2233             allocate_bucket_tables( c_nDefaultInitialSize );
2234         }
2235
2236         /// Constructs the set object with given probe set size and threshold
2237         /**
2238             If probe set type is <tt> cuckoo::vector<Capacity> </tt> vector
2239             then \p nProbesetSize should be equal to vector's \p Capacity.
2240         */
2241         CuckooSet(
2242             size_t nInitialSize                 ///< Initial set size; if 0 - use default initial size \ref c_nDefaultInitialSize
2243             , unsigned int nProbesetSize        ///< probe set size
2244             , unsigned int nProbesetThreshold = 0   ///< probe set threshold, <tt>nProbesetThreshold < nProbesetSize</tt>. If 0, nProbesetThreshold = nProbesetSize - 1
2245         )
2246             : m_nProbesetSize( calc_probeset_size(nProbesetSize) )
2247             , m_nProbesetThreshold( nProbesetThreshold ? nProbesetThreshold : m_nProbesetSize - 1 )
2248             , m_MutexPolicy( cds::beans::ceil2(nInitialSize ? nInitialSize : c_nDefaultInitialSize ))
2249         {
2250             check_common_constraints();
2251             check_probeset_properties();
2252
2253             allocate_bucket_tables( nInitialSize ? cds::beans::ceil2( nInitialSize ) : c_nDefaultInitialSize );
2254         }
2255
2256         /// Constructs the set object with given hash functor tuple
2257         /**
2258             The probe set size and threshold are set as default, see CuckooSet()
2259         */
2260         CuckooSet(
2261             hash_tuple_type const& h    ///< hash functor tuple of type <tt>std::tuple<H1, H2, ... Hn></tt> where <tt> n == \ref c_nArity </tt>
2262         )
2263             : m_nProbesetSize( calc_probeset_size(0) )
2264             , m_nProbesetThreshold( m_nProbesetSize -1 )
2265             , m_Hash( h )
2266             , m_MutexPolicy( c_nDefaultInitialSize )
2267         {
2268             check_common_constraints();
2269             check_probeset_properties();
2270
2271             allocate_bucket_tables( c_nDefaultInitialSize );
2272         }
2273
2274         /// Constructs the set object with given probe set properties and hash functor tuple
2275         /**
2276             If probe set type is <tt> cuckoo::vector<Capacity> </tt> vector
2277             then \p nProbesetSize should be equal to vector's \p Capacity.
2278         */
2279         CuckooSet(
2280             size_t nInitialSize                 ///< Initial set size; if 0 - use default initial size \ref c_nDefaultInitialSize
2281             , unsigned int nProbesetSize        ///< probe set size, positive integer
2282             , unsigned int nProbesetThreshold   ///< probe set threshold, <tt>nProbesetThreshold < nProbesetSize</tt>. If 0, nProbesetThreshold = nProbesetSize - 1
2283             , hash_tuple_type const& h    ///< hash functor tuple of type <tt>std::tuple<H1, H2, ... Hn></tt> where <tt> n == \ref c_nArity </tt>
2284         )
2285             : m_nProbesetSize( calc_probeset_size(nProbesetSize) )
2286             , m_nProbesetThreshold( nProbesetThreshold ? nProbesetThreshold : m_nProbesetSize - 1)
2287             , m_Hash( h )
2288             , m_MutexPolicy( cds::beans::ceil2(nInitialSize ? nInitialSize : c_nDefaultInitialSize ))
2289         {
2290             check_common_constraints();
2291             check_probeset_properties();
2292
2293             allocate_bucket_tables( nInitialSize ? cds::beans::ceil2( nInitialSize ) : c_nDefaultInitialSize );
2294         }
2295
2296         /// Constructs the set object with given hash functor tuple (move semantics)
2297         /**
2298             The probe set size and threshold are set as default, see CuckooSet()
2299         */
2300         CuckooSet(
2301             hash_tuple_type&& h     ///< hash functor tuple of type <tt>std::tuple<H1, H2, ... Hn></tt> where <tt> n == \ref c_nArity </tt>
2302         )
2303             : m_nProbesetSize( calc_probeset_size(0) )
2304             , m_nProbesetThreshold( m_nProbesetSize / 2 )
2305             , m_Hash( std::forward<hash_tuple_type>(h) )
2306             , m_MutexPolicy( c_nDefaultInitialSize )
2307         {
2308             check_common_constraints();
2309             check_probeset_properties();
2310
2311             allocate_bucket_tables( c_nDefaultInitialSize );
2312         }
2313
2314         /// Constructs the set object with given probe set properties and hash functor tuple (move semantics)
2315         /**
2316             If probe set type is <tt> cuckoo::vector<Capacity> </tt> vector
2317             then \p nProbesetSize should be equal to vector's \p Capacity.
2318         */
2319         CuckooSet(
2320             size_t nInitialSize                 ///< Initial set size; if 0 - use default initial size \ref c_nDefaultInitialSize
2321             , unsigned int nProbesetSize        ///< probe set size, positive integer
2322             , unsigned int nProbesetThreshold   ///< probe set threshold, <tt>nProbesetThreshold < nProbesetSize</tt>. If 0, nProbesetThreshold = nProbesetSize - 1
2323             , hash_tuple_type&& h    ///< hash functor tuple of type <tt>std::tuple<H1, H2, ... Hn></tt> where <tt> n == \ref c_nArity </tt>
2324         )
2325             : m_nProbesetSize( calc_probeset_size(nProbesetSize) )
2326             , m_nProbesetThreshold( nProbesetThreshold ? nProbesetThreshold : m_nProbesetSize - 1)
2327             , m_Hash( std::forward<hash_tuple_type>(h) )
2328             , m_MutexPolicy( cds::beans::ceil2(nInitialSize ? nInitialSize : c_nDefaultInitialSize ))
2329         {
2330             check_common_constraints();
2331             check_probeset_properties();
2332
2333             allocate_bucket_tables( nInitialSize ? cds::beans::ceil2( nInitialSize ) : c_nDefaultInitialSize );
2334         }
2335
2336         /// Destructor
2337         ~CuckooSet()
2338         {
2339             free_bucket_tables();
2340         }
2341
2342     public:
2343         /// Inserts new node
2344         /**
2345             The function inserts \p val in the set if it does not contain
2346             an item with key equal to \p val.
2347
2348             Returns \p true if \p val is placed into the set, \p false otherwise.
2349         */
2350         bool insert( value_type& val )
2351         {
2352             return insert( val, []( value_type& ) {} );
2353         }
2354
2355         /// Inserts new node
2356         /**
2357             The function allows to split creating of new item into two part:
2358             - create item with key only
2359             - insert new item into the set
2360             - if inserting is success, calls  \p f functor to initialize value-field of \p val.
2361
2362             The functor signature is:
2363             \code
2364                 void func( value_type& val );
2365             \endcode
2366             where \p val is the item inserted.
2367
2368             The user-defined functor is called only if the inserting is success.
2369         */
2370         template <typename Func>
2371         bool insert( value_type& val, Func f )
2372         {
2373             hash_array arrHash;
2374             position arrPos[ c_nArity ];
2375             unsigned int nGoalTable;
2376
2377             hashing( arrHash, val );
2378             node_type * pNode = node_traits::to_node_ptr( val );
2379             store_hash( pNode, arrHash );
2380
2381             while (true) {
2382                 {
2383                     scoped_cell_lock guard( m_MutexPolicy, arrHash );
2384
2385                     if ( contains( arrPos, arrHash, val, key_predicate() ) != c_nUndefTable ) {
2386                         m_Stat.onInsertFailed();
2387                         return false;
2388                     }
2389
2390                     for ( unsigned int i = 0; i < c_nArity; ++i ) {
2391                         bucket_entry& refBucket = bucket( i, arrHash[i] );
2392                         if ( refBucket.size() < m_nProbesetThreshold ) {
2393                             refBucket.insert_after( arrPos[i].itPrev, pNode );
2394                             f( val );
2395                             ++m_ItemCounter;
2396                             m_Stat.onInsertSuccess();
2397                             return true;
2398                         }
2399                     }
2400
2401                     for ( unsigned int i = 0; i < c_nArity; ++i ) {
2402                         bucket_entry& refBucket = bucket( i, arrHash[i] );
2403                         if ( refBucket.size() < m_nProbesetSize ) {
2404                             refBucket.insert_after( arrPos[i].itPrev, pNode );
2405                             f( val );
2406                             ++m_ItemCounter;
2407                             nGoalTable = i;
2408                             assert( refBucket.size() > 1 );
2409                             copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()) );
2410                             goto do_relocate;
2411                         }
2412                     }
2413                 }
2414
2415                 m_Stat.onInsertResize();
2416                 resize();
2417             }
2418
2419         do_relocate:
2420             m_Stat.onInsertRelocate();
2421             if ( !relocate( nGoalTable, arrHash )) {
2422                 m_Stat.onInsertRelocateFault();
2423                 m_Stat.onInsertResize();
2424                 resize();
2425             }
2426
2427             m_Stat.onInsertSuccess();
2428             return true;
2429         }
2430
2431         /// Ensures that the \p val exists in the set
2432         /**
2433             The operation performs inserting or changing data with lock-free manner.
2434
2435             If the item \p val not found in the set, then \p val is inserted into the set.
2436             Otherwise, the functor \p func is called with item found.
2437             The functor signature is:
2438             \code
2439                 void func( bool bNew, value_type& item, value_type& val );
2440             \endcode
2441             with arguments:
2442             - \p bNew - \p true if the item has been inserted, \p false otherwise
2443             - \p item - item of the set
2444             - \p val - argument \p val passed into the \p ensure function
2445             If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
2446             refers to the same thing.
2447
2448             The functor may change non-key fields of the \p item.
2449
2450             Returns <tt> std::pair<bool, bool> </tt> where \p first is \p true if operation is successful,
2451             \p second is \p true if new item has been added or \p false if the item with \p key
2452             already is in the set.
2453         */
2454         template <typename Func>
2455         std::pair<bool, bool> ensure( value_type& val, Func func )
2456         {
2457             hash_array arrHash;
2458             position arrPos[ c_nArity ];
2459             unsigned int nGoalTable;
2460
2461             hashing( arrHash, val );
2462             node_type * pNode = node_traits::to_node_ptr( val );
2463             store_hash( pNode, arrHash );
2464
2465             while (true) {
2466                 {
2467                     scoped_cell_lock guard( m_MutexPolicy, arrHash );
2468
2469                     unsigned int nTable = contains( arrPos, arrHash, val, key_predicate() );
2470                     if ( nTable != c_nUndefTable ) {
2471                         func( false, *node_traits::to_value_ptr( *arrPos[nTable].itFound ), val );
2472                         m_Stat.onEnsureExist();
2473                         return std::make_pair( true, false );
2474                     }
2475
2476                     node_type * pNode = node_traits::to_node_ptr( val );
2477                     store_hash( pNode, arrHash );
2478
2479                     for ( unsigned int i = 0; i < c_nArity; ++i ) {
2480                         bucket_entry& refBucket = bucket( i, arrHash[i] );
2481                         if ( refBucket.size() < m_nProbesetThreshold ) {
2482                             refBucket.insert_after( arrPos[i].itPrev, pNode );
2483                             func( true, val, val );
2484                             ++m_ItemCounter;
2485                             m_Stat.onEnsureSuccess();
2486                             return std::make_pair( true, true );
2487                         }
2488                     }
2489
2490                     for ( unsigned int i = 0; i < c_nArity; ++i ) {
2491                         bucket_entry& refBucket = bucket( i, arrHash[i] );
2492                         if ( refBucket.size() < m_nProbesetSize ) {
2493                             refBucket.insert_after( arrPos[i].itPrev, pNode );
2494                             func( true, val, val );
2495                             ++m_ItemCounter;
2496                             nGoalTable = i;
2497                             assert( refBucket.size() > 1 );
2498                             copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()) );
2499                             goto do_relocate;
2500                         }
2501                     }
2502                 }
2503
2504                 m_Stat.onEnsureResize();
2505                 resize();
2506             }
2507
2508         do_relocate:
2509             m_Stat.onEnsureRelocate();
2510             if ( !relocate( nGoalTable, arrHash )) {
2511                 m_Stat.onEnsureRelocateFault();
2512                 m_Stat.onEnsureResize();
2513                 resize();
2514             }
2515
2516             m_Stat.onEnsureSuccess();
2517             return std::make_pair( true, true );
2518         }
2519
2520         /// Unlink the item \p val from the set
2521         /**
2522             The function searches the item \p val in the set and unlink it
2523             if it is found and is equal to \p val (here, the equality means that
2524             \p val belongs to the set: if \p item is an item found then
2525             unlink is successful iif <tt>&val == &item</tt>)
2526
2527             The function returns \p true if success and \p false otherwise.
2528         */
2529         bool unlink( value_type& val )
2530         {
2531             hash_array arrHash;
2532             hashing( arrHash, val );
2533             position arrPos[ c_nArity ];
2534
2535             {
2536                 scoped_cell_lock guard( m_MutexPolicy, arrHash );
2537
2538                 unsigned int nTable = contains( arrPos, arrHash, val, key_predicate() );
2539                 if ( nTable != c_nUndefTable && node_traits::to_value_ptr(*arrPos[nTable].itFound) == &val ) {
2540                     bucket( nTable, arrHash[nTable]).remove( arrPos[nTable].itPrev, arrPos[nTable].itFound );
2541                     --m_ItemCounter;
2542                     m_Stat.onUnlinkSuccess();
2543                     return true;
2544                 }
2545             }
2546
2547             m_Stat.onUnlinkFailed();
2548             return false;
2549         }
2550
2551         /// Deletes the item from the set
2552         /** \anchor cds_intrusive_CuckooSet_erase
2553             The function searches an item with key equal to \p val in the set,
2554             unlinks it from the set, and returns a pointer to unlinked item.
2555
2556             If the item with key equal to \p val is not found the function return \p nullptr.
2557
2558             Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type.
2559         */
2560         template <typename Q>
2561         value_type * erase( Q const& val )
2562         {
2563             return erase( val, [](value_type const&) {} );
2564         }
2565
2566         /// Deletes the item from the set using \p pred predicate for searching
2567         /**
2568             The function is an analog of \ref cds_intrusive_CuckooSet_erase "erase(Q const&)"
2569             but \p pred is used for key comparing.
2570             If cuckoo set is ordered, then \p Predicate should have the interface and semantics like \p std::less.
2571             If cuckoo set is unordered, then \p Predicate should have the interface and semantics like \p std::equal_to.
2572             \p Predicate must imply the same element order as the comparator used for building the set.
2573         */
2574         template <typename Q, typename Predicate>
2575         value_type * erase_with( Q const& val, Predicate pred )
2576         {
2577             return erase_( val, typename predicate_wrapper<Predicate>::type(), [](value_type const&) {} );
2578         }
2579
2580         /// Delete the item from the set
2581         /** \anchor cds_intrusive_CuckooSet_erase_func
2582             The function searches an item with key equal to \p val in the set,
2583             call \p f functor with item found, unlinks it from the set, and returns a pointer to unlinked item.
2584
2585             The \p Func interface is
2586             \code
2587             struct functor {
2588                 void operator()( value_type const& item );
2589             };
2590             \endcode
2591
2592             If the item with key equal to \p val is not found the function return \p nullptr.
2593
2594             Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type.
2595         */
2596         template <typename Q, typename Func>
2597         value_type * erase( Q const& val, Func f )
2598         {
2599             return erase_( val, key_predicate(), f );
2600         }
2601
2602         /// Deletes the item from the set using \p pred predicate for searching
2603         /**
2604             The function is an analog of \ref cds_intrusive_CuckooSet_erase_func "erase(Q const&, Func)"
2605             but \p pred is used for key comparing.
2606             If you use ordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::less.
2607             If you use unordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::equal_to.
2608             \p Predicate must imply the same element order as the comparator used for building the set.
2609         */
2610         template <typename Q, typename Predicate, typename Func>
2611         value_type * erase_with( Q const& val, Predicate pred, Func f )
2612         {
2613             return erase_( val, typename predicate_wrapper<Predicate>::type(), f );
2614         }
2615
2616         /// Find the key \p val
2617         /** \anchor cds_intrusive_CuckooSet_find_func
2618             The function searches the item with key equal to \p val and calls the functor \p f for item found.
2619             The interface of \p Func functor is:
2620             \code
2621             struct functor {
2622                 void operator()( value_type& item, Q& val );
2623             };
2624             \endcode
2625             where \p item is the item found, \p val is the <tt>find</tt> function argument.
2626
2627             The functor may change non-key fields of \p item.
2628
2629             The \p val argument is non-const since it can be used as \p f functor destination i.e., the functor
2630             may modify both arguments.
2631
2632             Note the hash functor specified for class \p Traits template parameter
2633             should accept a parameter of type \p Q that can be not the same as \p value_type.
2634
2635             The function returns \p true if \p val is found, \p false otherwise.
2636         */
2637         template <typename Q, typename Func>
2638         bool find( Q& val, Func f )
2639         {
2640             return find_( val, key_predicate(), f );
2641         }
2642
2643         /// Find the key \p val using \p pred predicate for comparing
2644         /**
2645             The function is an analog of \ref cds_intrusive_CuckooSet_find_func "find(Q&, Func)"
2646             but \p pred is used for key comparison.
2647             If you use ordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::less.
2648             If you use unordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::equal_to.
2649             \p pred must imply the same element order as the comparator used for building the set.
2650         */
2651         template <typename Q, typename Predicate, typename Func>
2652         bool find_with( Q& val, Predicate pred, Func f )
2653         {
2654             return find_( val, typename predicate_wrapper<Predicate>::type(), f );
2655         }
2656
2657         /// Find the key \p val
2658         /** \anchor cds_intrusive_CuckooSet_find_cfunc
2659             The function searches the item with key equal to \p val and calls the functor \p f for item found.
2660             The interface of \p Func functor is:
2661             \code
2662             struct functor {
2663                 void operator()( value_type& item, Q const& val );
2664             };
2665             \endcode
2666             where \p item is the item found, \p val is the <tt>find</tt> function argument.
2667
2668             The functor may change non-key fields of \p item.
2669
2670             The \p val argument is non-const since it can be used as \p f functor destination i.e., the functor
2671             may modify both arguments.
2672
2673             The function returns \p true if \p val is found, \p false otherwise.
2674         */
2675         template <typename Q, typename Func>
2676         bool find( Q const& val, Func f )
2677         {
2678             return find_( val, key_predicate(), f );
2679         }
2680
2681         /// Find the key \p val using \p pred predicate for comparing
2682         /**
2683             The function is an analog of \ref cds_intrusive_CuckooSet_find_cfunc "find(Q const&, Func)"
2684             but \p pred is used for key comparison.
2685             If you use ordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::less.
2686             If you use unordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::equal_to.
2687             \p pred must imply the same element order as the comparator used for building the set.
2688         */
2689         template <typename Q, typename Predicate, typename Func>
2690         bool find_with( Q const& val, Predicate pred, Func f )
2691         {
2692             return find_( val, typename predicate_wrapper<Predicate>::type(), f );
2693         }
2694
2695         /// Find the key \p val
2696         /** \anchor cds_intrusive_CuckooSet_find_val
2697             The function searches the item with key equal to \p val
2698             and returns \p true if it is found, and \p false otherwise.
2699
2700             Note the hash functor specified for class \p Traits template parameter
2701             should accept a parameter of type \p Q that can be not the same as \p value_type.
2702         */
2703         template <typename Q>
2704         bool find( Q const& val )
2705         {
2706             return find( val, [](value_type&, Q const& ) {} );
2707         }
2708
2709         /// Find the key \p val using \p pred predicate for comparing
2710         /**
2711             The function is an analog of \ref cds_intrusive_CuckooSet_find_val "find(Q const&)"
2712             but \p pred is used for key comparison.
2713             If you use ordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::less.
2714             If you use unordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::equal_to.
2715             \p pred must imply the same element order as the comparator used for building the set.
2716         */
2717         template <typename Q, typename Predicate>
2718         bool find_with( Q const& val, Predicate pred )
2719         {
2720             return find_with( val, typename predicate_wrapper<Predicate>::type(), [](value_type& , Q const& ) {} );
2721         }
2722
2723         /// Clears the set
2724         /**
2725             The function unlinks all items from the set.
2726             For any item \ref disposer is called
2727         */
2728         void clear()
2729         {
2730             clear_and_dispose( disposer() );
2731         }
2732
2733         /// Clears the set and calls \p disposer for each item
2734         /**
2735             The function unlinks all items from the set calling \p disposer for each item.
2736             \p Disposer functor interface is:
2737             \code
2738             struct Disposer{
2739                 void operator()( value_type * p );
2740             };
2741             \endcode
2742
2743             The \ref disposer specified in \p Traits options is not called.
2744         */
2745         template <typename Disposer>
2746         void clear_and_dispose( Disposer oDisposer )
2747         {
2748             // locks entire array
2749             scoped_full_lock sl( m_MutexPolicy );
2750
2751             for ( unsigned int i = 0; i < c_nArity; ++i ) {
2752                 bucket_entry * pEntry = m_BucketTable[i];
2753                 bucket_entry * pEnd = pEntry + m_nBucketMask + 1;
2754                 for ( ; pEntry != pEnd ; ++pEntry ) {
2755                     pEntry->clear( [&oDisposer]( node_type * pNode ){ oDisposer( node_traits::to_value_ptr( pNode )) ; } );
2756                 }
2757             }
2758             m_ItemCounter.reset();
2759         }
2760
2761         /// Checks if the set is empty
2762         /**
2763             Emptiness is checked by item counting: if item count is zero then the set is empty.
2764         */
2765         bool empty() const
2766         {
2767             return size() == 0;
2768         }
2769
2770         /// Returns item count in the set
2771         size_t size() const
2772         {
2773             return m_ItemCounter;
2774         }
2775
2776         /// Returns the size of hash table
2777         /**
2778             The hash table size is non-constant and can be increased via resizing.
2779         */
2780         size_t bucket_count() const
2781         {
2782             return m_nBucketMask + 1;
2783         }
2784
2785         /// Returns lock array size
2786         size_t lock_count() const
2787         {
2788             return m_MutexPolicy.lock_count();
2789         }
2790
2791         /// Returns const reference to internal statistics
2792         stat const& statistics() const
2793         {
2794             return m_Stat;
2795         }
2796
2797         /// Returns const reference to mutex policy internal statistics
2798         typename mutex_policy::statistics_type const& mutex_policy_statistics() const
2799         {
2800             return m_MutexPolicy.statistics();
2801         }
2802     };
2803 }} // namespace cds::intrusive
2804
2805 #endif // #ifndef __CDS_INTRUSIVE_CUCKOO_SET_H