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