e54f8237df6a4ee819353105ff76b92fb488fd49
[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 and can be passed by reference
2369             using \p std::ref
2370         */
2371         template <typename Func>
2372         bool insert( value_type& val, Func f )
2373         {
2374             hash_array arrHash;
2375             position arrPos[ c_nArity ];
2376             unsigned int nGoalTable;
2377
2378             hashing( arrHash, val );
2379             node_type * pNode = node_traits::to_node_ptr( val );
2380             store_hash( pNode, arrHash );
2381
2382             while (true) {
2383                 {
2384                     scoped_cell_lock guard( m_MutexPolicy, arrHash );
2385
2386                     if ( contains( arrPos, arrHash, val, key_predicate() ) != c_nUndefTable ) {
2387                         m_Stat.onInsertFailed();
2388                         return false;
2389                     }
2390
2391                     for ( unsigned int i = 0; i < c_nArity; ++i ) {
2392                         bucket_entry& refBucket = bucket( i, arrHash[i] );
2393                         if ( refBucket.size() < m_nProbesetThreshold ) {
2394                             refBucket.insert_after( arrPos[i].itPrev, pNode );
2395                             f( val );
2396                             ++m_ItemCounter;
2397                             m_Stat.onInsertSuccess();
2398                             return true;
2399                         }
2400                     }
2401
2402                     for ( unsigned int i = 0; i < c_nArity; ++i ) {
2403                         bucket_entry& refBucket = bucket( i, arrHash[i] );
2404                         if ( refBucket.size() < m_nProbesetSize ) {
2405                             refBucket.insert_after( arrPos[i].itPrev, pNode );
2406                             f( val );
2407                             ++m_ItemCounter;
2408                             nGoalTable = i;
2409                             assert( refBucket.size() > 1 );
2410                             copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()) );
2411                             goto do_relocate;
2412                         }
2413                     }
2414                 }
2415
2416                 m_Stat.onInsertResize();
2417                 resize();
2418             }
2419
2420         do_relocate:
2421             m_Stat.onInsertRelocate();
2422             if ( !relocate( nGoalTable, arrHash )) {
2423                 m_Stat.onInsertRelocateFault();
2424                 m_Stat.onInsertResize();
2425                 resize();
2426             }
2427
2428             m_Stat.onInsertSuccess();
2429             return true;
2430         }
2431
2432         /// Ensures that the \p val exists in the set
2433         /**
2434             The operation performs inserting or changing data with lock-free manner.
2435
2436             If the item \p val not found in the set, then \p val is inserted into the set.
2437             Otherwise, the functor \p func is called with item found.
2438             The functor signature is:
2439             \code
2440                 void func( bool bNew, value_type& item, value_type& val );
2441             \endcode
2442             with arguments:
2443             - \p bNew - \p true if the item has been inserted, \p false otherwise
2444             - \p item - item of the set
2445             - \p val - argument \p val passed into the \p ensure function
2446             If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
2447             refers to the same thing.
2448
2449             The functor may change non-key fields of the \p item.
2450
2451             You may pass \p func argument by reference using \p std::ref.
2452
2453             Returns <tt> std::pair<bool, bool> </tt> where \p first is \p true if operation is successful,
2454             \p second is \p true if new item has been added or \p false if the item with \p key
2455             already is in the set.
2456         */
2457         template <typename Func>
2458         std::pair<bool, bool> ensure( value_type& val, Func func )
2459         {
2460             hash_array arrHash;
2461             position arrPos[ c_nArity ];
2462             unsigned int nGoalTable;
2463
2464             hashing( arrHash, val );
2465             node_type * pNode = node_traits::to_node_ptr( val );
2466             store_hash( pNode, arrHash );
2467
2468             while (true) {
2469                 {
2470                     scoped_cell_lock guard( m_MutexPolicy, arrHash );
2471
2472                     unsigned int nTable = contains( arrPos, arrHash, val, key_predicate() );
2473                     if ( nTable != c_nUndefTable ) {
2474                         func( false, *node_traits::to_value_ptr( *arrPos[nTable].itFound ), val );
2475                         m_Stat.onEnsureExist();
2476                         return std::make_pair( true, false );
2477                     }
2478
2479                     node_type * pNode = node_traits::to_node_ptr( val );
2480                     store_hash( pNode, arrHash );
2481
2482                     for ( unsigned int i = 0; i < c_nArity; ++i ) {
2483                         bucket_entry& refBucket = bucket( i, arrHash[i] );
2484                         if ( refBucket.size() < m_nProbesetThreshold ) {
2485                             refBucket.insert_after( arrPos[i].itPrev, pNode );
2486                             func( true, val, val );
2487                             ++m_ItemCounter;
2488                             m_Stat.onEnsureSuccess();
2489                             return std::make_pair( true, true );
2490                         }
2491                     }
2492
2493                     for ( unsigned int i = 0; i < c_nArity; ++i ) {
2494                         bucket_entry& refBucket = bucket( i, arrHash[i] );
2495                         if ( refBucket.size() < m_nProbesetSize ) {
2496                             refBucket.insert_after( arrPos[i].itPrev, pNode );
2497                             func( true, val, val );
2498                             ++m_ItemCounter;
2499                             nGoalTable = i;
2500                             assert( refBucket.size() > 1 );
2501                             copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()) );
2502                             goto do_relocate;
2503                         }
2504                     }
2505                 }
2506
2507                 m_Stat.onEnsureResize();
2508                 resize();
2509             }
2510
2511         do_relocate:
2512             m_Stat.onEnsureRelocate();
2513             if ( !relocate( nGoalTable, arrHash )) {
2514                 m_Stat.onEnsureRelocateFault();
2515                 m_Stat.onEnsureResize();
2516                 resize();
2517             }
2518
2519             m_Stat.onEnsureSuccess();
2520             return std::make_pair( true, true );
2521         }
2522
2523         /// Unlink the item \p val from the set
2524         /**
2525             The function searches the item \p val in the set and unlink it
2526             if it is found and is equal to \p val (here, the equality means that
2527             \p val belongs to the set: if \p item is an item found then
2528             unlink is successful iif <tt>&val == &item</tt>)
2529
2530             The function returns \p true if success and \p false otherwise.
2531         */
2532         bool unlink( value_type& val )
2533         {
2534             hash_array arrHash;
2535             hashing( arrHash, val );
2536             position arrPos[ c_nArity ];
2537
2538             {
2539                 scoped_cell_lock guard( m_MutexPolicy, arrHash );
2540
2541                 unsigned int nTable = contains( arrPos, arrHash, val, key_predicate() );
2542                 if ( nTable != c_nUndefTable && node_traits::to_value_ptr(*arrPos[nTable].itFound) == &val ) {
2543                     bucket( nTable, arrHash[nTable]).remove( arrPos[nTable].itPrev, arrPos[nTable].itFound );
2544                     --m_ItemCounter;
2545                     m_Stat.onUnlinkSuccess();
2546                     return true;
2547                 }
2548             }
2549
2550             m_Stat.onUnlinkFailed();
2551             return false;
2552         }
2553
2554         /// Deletes the item from the set
2555         /** \anchor cds_intrusive_CuckooSet_erase
2556             The function searches an item with key equal to \p val in the set,
2557             unlinks it from the set, and returns a pointer to unlinked item.
2558
2559             If the item with key equal to \p val is not found the function return \p nullptr.
2560
2561             Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type.
2562         */
2563         template <typename Q>
2564         value_type * erase( Q const& val )
2565         {
2566             return erase( val, [](value_type const&) {} );
2567         }
2568
2569         /// Deletes the item from the set using \p pred predicate for searching
2570         /**
2571             The function is an analog of \ref cds_intrusive_CuckooSet_erase "erase(Q const&)"
2572             but \p pred is used for key comparing.
2573             If cuckoo set is ordered, then \p Predicate should have the interface and semantics like \p std::less.
2574             If cuckoo set is unordered, then \p Predicate should have the interface and semantics like \p std::equal_to.
2575             \p Predicate must imply the same element order as the comparator used for building the set.
2576         */
2577         template <typename Q, typename Predicate>
2578         value_type * erase_with( Q const& val, Predicate pred )
2579         {
2580             return erase_( val, typename predicate_wrapper<Predicate>::type(), [](value_type const&) {} );
2581         }
2582
2583         /// Delete the item from the set
2584         /** \anchor cds_intrusive_CuckooSet_erase_func
2585             The function searches an item with key equal to \p val in the set,
2586             call \p f functor with item found, unlinks it from the set, and returns a pointer to unlinked item.
2587
2588             The \p Func interface is
2589             \code
2590             struct functor {
2591                 void operator()( value_type const& item );
2592             };
2593             \endcode
2594             The functor may be passed by reference with <tt>boost:ref</tt>
2595
2596             If the item with key equal to \p val is not found the function return \p nullptr.
2597
2598             Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type.
2599         */
2600         template <typename Q, typename Func>
2601         value_type * erase( Q const& val, Func f )
2602         {
2603             return erase_( val, key_predicate(), f );
2604         }
2605
2606         /// Deletes the item from the set using \p pred predicate for searching
2607         /**
2608             The function is an analog of \ref cds_intrusive_CuckooSet_erase_func "erase(Q const&, Func)"
2609             but \p pred is used for key comparing.
2610             If you use ordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::less.
2611             If you use unordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::equal_to.
2612             \p Predicate must imply the same element order as the comparator used for building the set.
2613         */
2614         template <typename Q, typename Predicate, typename Func>
2615         value_type * erase_with( Q const& val, Predicate pred, Func f )
2616         {
2617             return erase_( val, typename predicate_wrapper<Predicate>::type(), f );
2618         }
2619
2620         /// Find the key \p val
2621         /** \anchor cds_intrusive_CuckooSet_find_func
2622             The function searches the item with key equal to \p val and calls the functor \p f for item found.
2623             The interface of \p Func functor is:
2624             \code
2625             struct functor {
2626                 void operator()( value_type& item, Q& val );
2627             };
2628             \endcode
2629             where \p item is the item found, \p val is the <tt>find</tt> function argument.
2630
2631             You can pass \p f argument by reference using \p std::ref.
2632
2633             The functor may change non-key fields of \p item.
2634
2635             The \p val argument is non-const since it can be used as \p f functor destination i.e., the functor
2636             may modify both arguments.
2637
2638             Note the hash functor specified for class \p Traits template parameter
2639             should accept a parameter of type \p Q that can be not the same as \p value_type.
2640
2641             The function returns \p true if \p val is found, \p false otherwise.
2642         */
2643         template <typename Q, typename Func>
2644         bool find( Q& val, Func f )
2645         {
2646             return find_( val, key_predicate(), f );
2647         }
2648
2649         /// Find the key \p val using \p pred predicate for comparing
2650         /**
2651             The function is an analog of \ref cds_intrusive_CuckooSet_find_func "find(Q&, Func)"
2652             but \p pred is used for key comparison.
2653             If you use ordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::less.
2654             If you use unordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::equal_to.
2655             \p pred must imply the same element order as the comparator used for building the set.
2656         */
2657         template <typename Q, typename Predicate, typename Func>
2658         bool find_with( Q& val, Predicate pred, Func f )
2659         {
2660             return find_( val, typename predicate_wrapper<Predicate>::type(), f );
2661         }
2662
2663         /// Find the key \p val
2664         /** \anchor cds_intrusive_CuckooSet_find_cfunc
2665             The function searches the item with key equal to \p val and calls the functor \p f for item found.
2666             The interface of \p Func functor is:
2667             \code
2668             struct functor {
2669                 void operator()( value_type& item, Q const& val );
2670             };
2671             \endcode
2672             where \p item is the item found, \p val is the <tt>find</tt> function argument.
2673
2674             You can pass \p f argument by reference using \p std::ref.
2675
2676             The functor may change non-key fields of \p item.
2677
2678             The \p val argument is non-const since it can be used as \p f functor destination i.e., the functor
2679             may modify both arguments.
2680
2681             The function returns \p true if \p val is found, \p false otherwise.
2682         */
2683         template <typename Q, typename Func>
2684         bool find( Q const& val, Func f )
2685         {
2686             return find_( val, key_predicate(), f );
2687         }
2688
2689         /// Find the key \p val using \p pred predicate for comparing
2690         /**
2691             The function is an analog of \ref cds_intrusive_CuckooSet_find_cfunc "find(Q const&, Func)"
2692             but \p pred is used for key comparison.
2693             If you use ordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::less.
2694             If you use unordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::equal_to.
2695             \p pred must imply the same element order as the comparator used for building the set.
2696         */
2697         template <typename Q, typename Predicate, typename Func>
2698         bool find_with( Q const& val, Predicate pred, Func f )
2699         {
2700             return find_( val, typename predicate_wrapper<Predicate>::type(), f );
2701         }
2702
2703         /// Find the key \p val
2704         /** \anchor cds_intrusive_CuckooSet_find_val
2705             The function searches the item with key equal to \p val
2706             and returns \p true if it is found, and \p false otherwise.
2707
2708             Note the hash functor specified for class \p Traits template parameter
2709             should accept a parameter of type \p Q that can be not the same as \p value_type.
2710         */
2711         template <typename Q>
2712         bool find( Q const& val )
2713         {
2714             return find( val, [](value_type&, Q const& ) {} );
2715         }
2716
2717         /// Find the key \p val using \p pred predicate for comparing
2718         /**
2719             The function is an analog of \ref cds_intrusive_CuckooSet_find_val "find(Q const&)"
2720             but \p pred is used for key comparison.
2721             If you use ordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::less.
2722             If you use unordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::equal_to.
2723             \p pred must imply the same element order as the comparator used for building the set.
2724         */
2725         template <typename Q, typename Predicate>
2726         bool find_with( Q const& val, Predicate pred )
2727         {
2728             return find_with( val, typename predicate_wrapper<Predicate>::type(), [](value_type& , Q const& ) {} );
2729         }
2730
2731         /// Clears the set
2732         /**
2733             The function unlinks all items from the set.
2734             For any item \ref disposer is called
2735         */
2736         void clear()
2737         {
2738             clear_and_dispose( disposer() );
2739         }
2740
2741         /// Clears the set and calls \p disposer for each item
2742         /**
2743             The function unlinks all items from the set calling \p disposer for each item.
2744             \p Disposer functor interface is:
2745             \code
2746             struct Disposer{
2747                 void operator()( value_type * p );
2748             };
2749             \endcode
2750
2751             The \ref disposer specified in \p Traits options is not called.
2752         */
2753         template <typename Disposer>
2754         void clear_and_dispose( Disposer oDisposer )
2755         {
2756             // locks entire array
2757             scoped_full_lock sl( m_MutexPolicy );
2758
2759             for ( unsigned int i = 0; i < c_nArity; ++i ) {
2760                 bucket_entry * pEntry = m_BucketTable[i];
2761                 bucket_entry * pEnd = pEntry + m_nBucketMask + 1;
2762                 for ( ; pEntry != pEnd ; ++pEntry ) {
2763                     pEntry->clear( [&oDisposer]( node_type * pNode ){ oDisposer( node_traits::to_value_ptr( pNode )) ; } );
2764                 }
2765             }
2766             m_ItemCounter.reset();
2767         }
2768
2769         /// Checks if the set is empty
2770         /**
2771             Emptiness is checked by item counting: if item count is zero then the set is empty.
2772         */
2773         bool empty() const
2774         {
2775             return size() == 0;
2776         }
2777
2778         /// Returns item count in the set
2779         size_t size() const
2780         {
2781             return m_ItemCounter;
2782         }
2783
2784         /// Returns the size of hash table
2785         /**
2786             The hash table size is non-constant and can be increased via resizing.
2787         */
2788         size_t bucket_count() const
2789         {
2790             return m_nBucketMask + 1;
2791         }
2792
2793         /// Returns lock array size
2794         size_t lock_count() const
2795         {
2796             return m_MutexPolicy.lock_count();
2797         }
2798
2799         /// Returns const reference to internal statistics
2800         stat const& statistics() const
2801         {
2802             return m_Stat;
2803         }
2804
2805         /// Returns const reference to mutex policy internal statistics
2806         typename mutex_policy::statistics_type const& mutex_policy_statistics() const
2807         {
2808             return m_MutexPolicy.statistics();
2809         }
2810     };
2811 }} // namespace cds::intrusive
2812
2813 #endif // #ifndef __CDS_INTRUSIVE_CUCKOO_SET_H