Fixed -Wshadow warning
[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-2017
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 \p opt::mutex_policy option type for \p CuckooSet
619
620             Refining is like a striping technique (see \p 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 \p cds::backoff::Default
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::Default,
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                     // Seems, there is a false positive in std::shared_ptr deallocation in uninstrumented libc++
701                     // see, for example, https://groups.google.com/forum/#!topic/thread-sanitizer/eHu4dE_z7Cc
702                     // https://reviews.llvm.org/D21609
703                     CDS_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
704                     lock_array_allocator().Delete( pArr );
705                     CDS_TSAN_ANNOTATE_IGNORE_WRITES_END;
706                 }
707             };
708
709             lock_array_ptr create_lock_array( size_t nCapacity )
710             {
711                 return lock_array_ptr( lock_array_allocator().New( nCapacity ), lock_array_disposer());
712             }
713
714             void acquire( size_t const * arrHash, lock_array_ptr * pLockArr, lock_type ** parrLock )
715             {
716                 owner_t me = (owner_t) cds::OS::get_current_thread_id();
717                 owner_t who;
718                 size_t cur_capacity;
719
720                 back_off bkoff;
721                 while ( true ) {
722
723                     {
724                         scoped_spinlock sl(m_access);
725                         for ( unsigned int i = 0; i < c_nArity; ++i )
726                             pLockArr[i] = m_arrLocks[i];
727                         cur_capacity = m_nCapacity.load( atomics::memory_order_acquire );
728                     }
729
730                     // wait while resizing
731                     while ( true ) {
732                         who = m_Owner.load( atomics::memory_order_acquire );
733                         if ( !( who & 1 ) || (who >> 1) == (me & c_nOwnerMask))
734                             break;
735                         bkoff();
736                         m_Stat.onCellWaitResizing();
737                     }
738
739                     if ( cur_capacity == m_nCapacity.load( atomics::memory_order_acquire )) {
740
741                         size_t const nMask = pLockArr[0]->size() - 1;
742                         assert( cds::beans::is_power2( nMask + 1 ));
743
744                         for ( unsigned int i = 0; i < c_nArity; ++i ) {
745                             parrLock[i] = &( pLockArr[i]->at( arrHash[i] & nMask ));
746                             parrLock[i]->lock();
747                         }
748
749                         who = m_Owner.load( atomics::memory_order_acquire );
750                         if ( ( !(who & 1) || (who >> 1) == (me & c_nOwnerMask)) && cur_capacity == m_nCapacity.load( atomics::memory_order_acquire )) {
751                             m_Stat.onCellLock();
752                             return;
753                         }
754
755                         for ( unsigned int i = 0; i < c_nArity; ++i )
756                             parrLock[i]->unlock();
757
758                         m_Stat.onCellLockFailed();
759                     }
760                     else
761                         m_Stat.onCellArrayChanged();
762
763                     // clears pLockArr can lead to calling dtor for each item of pLockArr[i] that may be a heavy-weighted operation
764                     // (each pLockArr[i] is a shared pointer to array of a ton of mutexes)
765                     // It is better to do this before the next loop iteration where we will use spin-locked assignment to pLockArr
766                     // However, destructing a lot of mutexes under spin-lock is a bad solution
767                     for ( unsigned int i = 0; i < c_nArity; ++i )
768                         pLockArr[i].reset();
769                 }
770             }
771
772             bool try_second_acquire( size_t const * arrHash, lock_type ** parrLock )
773             {
774                 // It is assumed that the current thread already has a lock
775                 // and requires a second lock for other hash
776
777                 size_t const nMask = m_nCapacity.load(atomics::memory_order_acquire) - 1;
778                 size_t nCell = m_arrLocks[0]->try_lock( arrHash[0] & nMask);
779                 if ( nCell == lock_array_type::c_nUnspecifiedCell ) {
780                     m_Stat.onSecondCellLockFailed();
781                     return false;
782                 }
783                 parrLock[0] = &(m_arrLocks[0]->at(nCell));
784
785                 for ( unsigned int i = 1; i < c_nArity; ++i ) {
786                     parrLock[i] = &( m_arrLocks[i]->at( m_arrLocks[i]->lock( arrHash[i] & nMask)));
787                 }
788
789                 m_Stat.onSecondCellLock();
790                 return true;
791             }
792
793             void acquire_all()
794             {
795                 owner_t me = (owner_t) cds::OS::get_current_thread_id();
796
797                 back_off bkoff;
798                 while ( true ) {
799                     owner_t ownNull = 0;
800                     if ( m_Owner.compare_exchange_strong( ownNull, (me << 1) | 1, atomics::memory_order_acq_rel, atomics::memory_order_relaxed )) {
801                         m_arrLocks[0]->lock_all();
802
803                         m_Stat.onFullLock();
804                         return;
805                     }
806                     bkoff();
807                     m_Stat.onFullLockIter();
808                 }
809             }
810
811             void release_all()
812             {
813                 m_arrLocks[0]->unlock_all();
814                 m_Owner.store( 0, atomics::memory_order_release );
815             }
816
817             void acquire_resize( lock_array_ptr * pOldLocks )
818             {
819                 owner_t me = (owner_t) cds::OS::get_current_thread_id();
820                 size_t cur_capacity;
821
822                 while ( true ) {
823                     {
824                         scoped_spinlock sl(m_access);
825                         for ( unsigned int i = 0; i < c_nArity; ++i )
826                             pOldLocks[i] = m_arrLocks[i];
827                         cur_capacity = m_nCapacity.load( atomics::memory_order_acquire );
828                     }
829
830                     // global lock
831                     owner_t ownNull = 0;
832                     if ( m_Owner.compare_exchange_strong( ownNull, (me << 1) | 1, atomics::memory_order_acq_rel, atomics::memory_order_relaxed )) {
833                         if ( cur_capacity == m_nCapacity.load( atomics::memory_order_acquire )) {
834                             pOldLocks[0]->lock_all();
835                             m_Stat.onResizeLock();
836                             return;
837                         }
838
839                         m_Owner.store( 0, atomics::memory_order_release );
840                         m_Stat.onResizeLockArrayChanged();
841                     }
842                     else
843                         m_Stat.onResizeLockIter();
844
845                     // clears pOldLocks can lead to calling dtor for each item of pOldLocks[i] that may be a heavy-weighted operation
846                     // (each pOldLocks[i] is a shared pointer to array of a ton of mutexes)
847                     // It is better to do this before the next loop iteration where we will use spin-locked assignment to pOldLocks
848                     // However, destructing a lot of mutexes under spin-lock is a bad solution
849                     for ( unsigned int i = 0; i < c_nArity; ++i )
850                         pOldLocks[i].reset();
851                 }
852             }
853
854             void release_resize( lock_array_ptr * pOldLocks )
855             {
856                 m_Owner.store( 0, atomics::memory_order_release );
857                 pOldLocks[0]->unlock_all();
858             }
859             //@endcond
860
861         public:
862             //@cond
863             class scoped_cell_lock {
864                 lock_type * m_arrLock[ c_nArity ];
865                 lock_array_ptr  m_arrLockArr[ c_nArity ];
866
867             public:
868                 scoped_cell_lock( refinable& policy, size_t const* arrHash )
869                 {
870                     policy.acquire( arrHash, m_arrLockArr, m_arrLock );
871                 }
872
873                 ~scoped_cell_lock()
874                 {
875                     for ( unsigned int i = 0; i < c_nArity; ++i )
876                         m_arrLock[i]->unlock();
877                 }
878             };
879
880             class scoped_cell_trylock {
881                 lock_type * m_arrLock[ c_nArity ];
882                 bool        m_bLocked;
883
884             public:
885                 scoped_cell_trylock( refinable& policy, size_t const* arrHash )
886                 {
887                     m_bLocked = policy.try_second_acquire( arrHash, m_arrLock );
888                 }
889
890                 ~scoped_cell_trylock()
891                 {
892                     if ( m_bLocked ) {
893                         for ( unsigned int i = 0; i < c_nArity; ++i )
894                             m_arrLock[i]->unlock();
895                     }
896                 }
897
898                 bool locked() const
899                 {
900                     return m_bLocked;
901                 }
902             };
903
904             class scoped_full_lock {
905                 refinable& m_policy;
906             public:
907                 scoped_full_lock( refinable& policy )
908                     : m_policy( policy )
909                 {
910                     policy.acquire_all();
911                 }
912                 ~scoped_full_lock()
913                 {
914                     m_policy.release_all();
915                 }
916             };
917
918             class scoped_resize_lock
919             {
920                 refinable&      m_policy;
921                 lock_array_ptr  m_arrLocks[ c_nArity ];
922             public:
923                 scoped_resize_lock( refinable& policy )
924                     : m_policy(policy)
925                 {
926                     policy.acquire_resize( m_arrLocks );
927                 }
928                 ~scoped_resize_lock()
929                 {
930                     m_policy.release_resize( m_arrLocks );
931                 }
932             };
933             //@endcond
934
935         public:
936             /// Constructor
937             refinable(
938                 size_t nLockCount   ///< The size of lock array. Must be power of two.
939             )   : m_Owner(0)
940                 , m_nCapacity( nLockCount )
941             {
942                 assert( cds::beans::is_power2( nLockCount ));
943                 for ( unsigned int i = 0; i < c_nArity; ++i )
944                     m_arrLocks[i] = create_lock_array( nLockCount );
945             }
946
947             //@cond
948             void resize( size_t nCapacity )
949             {
950                 lock_array_ptr pNew[ c_nArity ];
951                 for ( unsigned int i = 0; i < c_nArity; ++i )
952                     pNew[i] = create_lock_array( nCapacity );
953
954                 {
955                     scoped_spinlock sl(m_access);
956                     m_nCapacity.store( nCapacity, atomics::memory_order_release );
957                     for ( unsigned int i = 0; i < c_nArity; ++i )
958                         m_arrLocks[i] = pNew[i];
959                 }
960
961                 m_Stat.onResize();
962             }
963             //@endcond
964
965             /// Returns lock array size
966             /**
967                 Lock array size is not a constant for \p refinable policy and can be changed when the set is resized.
968             */
969             size_t lock_count() const
970             {
971                 return m_nCapacity.load(atomics::memory_order_relaxed);
972             }
973
974             /// Returns the arity of \p refinable mutex policy
975             CDS_CONSTEXPR unsigned int arity() const CDS_NOEXCEPT
976             {
977                 return c_nArity;
978             }
979
980             /// Returns internal statistics
981             statistics_type const& statistics() const
982             {
983                 return m_Stat;
984             }
985         };
986
987         /// \p CuckooSet internal statistics
988         struct stat {
989             typedef cds::atomicity::event_counter   counter_type ;  ///< Counter type
990
991             counter_type    m_nRelocateCallCount    ; ///< Count of \p relocate() function call
992             counter_type    m_nRelocateRoundCount   ; ///< Count of attempts to relocate items
993             counter_type    m_nFalseRelocateCount   ; ///< Count of unneeded attempts of \p relocate call
994             counter_type    m_nSuccessRelocateCount ; ///< Count of successful item relocating
995             counter_type    m_nRelocateAboveThresholdCount; ///< Count of item relocating above probeset threshold
996             counter_type    m_nFailedRelocateCount  ;   ///< Count of failed relocation attemp (when all probeset is full)
997
998             counter_type    m_nResizeCallCount      ;   ///< Count of \p resize() function call
999             counter_type    m_nFalseResizeCount     ;   ///< Count of false \p resize() function call (when other thread has been resized the set)
1000             counter_type    m_nResizeSuccessNodeMove;   ///< Count of successful node moving when resizing
1001             counter_type    m_nResizeRelocateCall   ;   ///< Count of \p relocate() function call from \p resize function
1002
1003             counter_type    m_nInsertSuccess        ;   ///< Count of successful \p insert() function call
1004             counter_type    m_nInsertFailed         ;   ///< Count of failed \p insert() function call
1005             counter_type    m_nInsertResizeCount    ;   ///< Count of \p resize() function call from \p insert()
1006             counter_type    m_nInsertRelocateCount  ;   ///< Count of \p relocate() function call from \p insert()
1007             counter_type    m_nInsertRelocateFault  ;   ///< Count of failed \p relocate() function call from \p insert()
1008
1009             counter_type    m_nUpdateExistCount     ;   ///< Count of call \p update() function for existing node
1010             counter_type    m_nUpdateSuccessCount   ;   ///< Count of successful \p insert() function call for new node
1011             counter_type    m_nUpdateResizeCount    ;   ///< Count of \p resize() function call from \p update()
1012             counter_type    m_nUpdateRelocateCount  ;   ///< Count of \p relocate() function call from \p update()
1013             counter_type    m_nUpdateRelocateFault  ;   ///< Count of failed \p relocate() function call from \p update()
1014
1015             counter_type    m_nUnlinkSuccess        ;   ///< Count of success \p unlink() function call
1016             counter_type    m_nUnlinkFailed         ;   ///< Count of failed \p unlink() function call
1017
1018             counter_type    m_nEraseSuccess         ;   ///< Count of success \p erase() function call
1019             counter_type    m_nEraseFailed          ;   ///< Count of failed \p erase() function call
1020
1021             counter_type    m_nFindSuccess         ;   ///< Count of success \p find() function call
1022             counter_type    m_nFindFailed          ;   ///< Count of failed \p find() function call
1023
1024             counter_type    m_nFindEqualSuccess         ;   ///< Count of success \p find_equal() function call
1025             counter_type    m_nFindEqualFailed          ;   ///< Count of failed \p find_equal() function call
1026
1027             counter_type    m_nFindWithSuccess         ;   ///< Count of success \p find_with() function call
1028             counter_type    m_nFindWithFailed          ;   ///< Count of failed \p find_with() function call
1029
1030             //@cond
1031             void    onRelocateCall()        { ++m_nRelocateCallCount; }
1032             void    onRelocateRound()       { ++m_nRelocateRoundCount; }
1033             void    onFalseRelocateRound()  { ++m_nFalseRelocateCount; }
1034             void    onSuccessRelocateRound(){ ++m_nSuccessRelocateCount; }
1035             void    onRelocateAboveThresholdRound() { ++m_nRelocateAboveThresholdCount; }
1036             void    onFailedRelocate()      { ++m_nFailedRelocateCount; }
1037
1038             void    onResizeCall()          { ++m_nResizeCallCount; }
1039             void    onFalseResizeCall()     { ++m_nFalseResizeCount; }
1040             void    onResizeSuccessMove()   { ++m_nResizeSuccessNodeMove; }
1041             void    onResizeRelocateCall()  { ++m_nResizeRelocateCall; }
1042
1043             void    onInsertSuccess()       { ++m_nInsertSuccess; }
1044             void    onInsertFailed()        { ++m_nInsertFailed; }
1045             void    onInsertResize()        { ++m_nInsertResizeCount; }
1046             void    onInsertRelocate()      { ++m_nInsertRelocateCount; }
1047             void    onInsertRelocateFault() { ++m_nInsertRelocateFault; }
1048
1049             void    onUpdateExist()         { ++m_nUpdateExistCount; }
1050             void    onUpdateSuccess()       { ++m_nUpdateSuccessCount; }
1051             void    onUpdateResize()        { ++m_nUpdateResizeCount; }
1052             void    onUpdateRelocate()      { ++m_nUpdateRelocateCount; }
1053             void    onUpdateRelocateFault() { ++m_nUpdateRelocateFault; }
1054
1055             void    onUnlinkSuccess()       { ++m_nUnlinkSuccess; }
1056             void    onUnlinkFailed()        { ++m_nUnlinkFailed; }
1057
1058             void    onEraseSuccess()        { ++m_nEraseSuccess; }
1059             void    onEraseFailed()         { ++m_nEraseFailed; }
1060
1061             void    onFindSuccess()         { ++m_nFindSuccess; }
1062             void    onFindFailed()          { ++m_nFindFailed; }
1063
1064             void    onFindWithSuccess()     { ++m_nFindWithSuccess; }
1065             void    onFindWithFailed()      { ++m_nFindWithFailed; }
1066             //@endcond
1067         };
1068
1069         /// CuckooSet empty internal statistics
1070         struct empty_stat {
1071             //@cond
1072             void    onRelocateCall()        const {}
1073             void    onRelocateRound()       const {}
1074             void    onFalseRelocateRound()  const {}
1075             void    onSuccessRelocateRound()const {}
1076             void    onRelocateAboveThresholdRound() const {}
1077             void    onFailedRelocate()      const {}
1078
1079             void    onResizeCall()          const {}
1080             void    onFalseResizeCall()     const {}
1081             void    onResizeSuccessMove()   const {}
1082             void    onResizeRelocateCall()  const {}
1083
1084             void    onInsertSuccess()       const {}
1085             void    onInsertFailed()        const {}
1086             void    onInsertResize()        const {}
1087             void    onInsertRelocate()      const {}
1088             void    onInsertRelocateFault() const {}
1089
1090             void    onUpdateExist()         const {}
1091             void    onUpdateSuccess()       const {}
1092             void    onUpdateResize()        const {}
1093             void    onUpdateRelocate()      const {}
1094             void    onUpdateRelocateFault() const {}
1095
1096             void    onUnlinkSuccess()       const {}
1097             void    onUnlinkFailed()        const {}
1098
1099             void    onEraseSuccess()        const {}
1100             void    onEraseFailed()         const {}
1101
1102             void    onFindSuccess()         const {}
1103             void    onFindFailed()          const {}
1104
1105             void    onFindWithSuccess()     const {}
1106             void    onFindWithFailed()      const {}
1107             //@endcond
1108         };
1109
1110         /// Type traits for CuckooSet class
1111         struct traits
1112         {
1113             /// Hook used
1114             /**
1115                 Possible values are: cuckoo::base_hook, cuckoo::member_hook, cuckoo::traits_hook.
1116             */
1117             typedef base_hook<>         hook;
1118
1119             /// Hash functors tuple
1120             /**
1121                 This is mandatory type and has no predefined one.
1122
1123                 At least, two hash functors should be provided. All hash functor
1124                 should be orthogonal (different): for each <tt> i,j: i != j => h[i](x) != h[j](x) </tt>.
1125                 The hash functors are defined as <tt> std::tuple< H1, H2, ... Hn > </tt>:
1126                 \@code cds::opt::hash< std::tuple< h1, h2 > > \@endcode
1127                 The number of hash functors specifies the number \p k - the count of hash tables in cuckoo hashing.
1128
1129                 To specify hash tuple in traits you should use \p cds::opt::hash_tuple:
1130                 \code
1131                 struct my_traits: public cds::intrusive::cuckoo::traits {
1132                     typedef cds::opt::hash_tuple< hash1, hash2 > hash;
1133                 };
1134                 \endcode
1135             */
1136             typedef cds::opt::none      hash;
1137
1138             /// Concurrent access policy
1139             /**
1140                 Available opt::mutex_policy types:
1141                 - \p cuckoo::striping - simple, but the lock array is not resizable
1142                 - \p cuckoo::refinable - resizable lock array, but more complex access to set data.
1143
1144                 Default is \p cuckoo::striping.
1145             */
1146             typedef cuckoo::striping<>               mutex_policy;
1147
1148             /// Key equality functor
1149             /**
1150                 Default is <tt>std::equal_to<T></tt>
1151             */
1152             typedef opt::none                       equal_to;
1153
1154             /// Key comparing functor
1155             /**
1156                 No default functor is provided. If the option is not specified, the \p less is used.
1157             */
1158             typedef opt::none                       compare;
1159
1160             /// specifies binary predicate used for key comparison.
1161             /**
1162                 Default is \p std::less<T>.
1163             */
1164             typedef opt::none                       less;
1165
1166             /// Item counter
1167             /**
1168                 The type for item counting feature.
1169                 Default is \p cds::atomicity::item_counter
1170
1171                 Only atomic item counter type is allowed.
1172             */
1173             typedef atomicity::item_counter             item_counter;
1174
1175             /// Allocator type
1176             /**
1177                 The allocator type for allocating bucket tables.
1178             */
1179             typedef CDS_DEFAULT_ALLOCATOR       allocator;
1180
1181             /// Disposer
1182             /**
1183                 The disposer functor is used in \p CuckooSet::clear() member function
1184                 to free set's node.
1185             */
1186             typedef intrusive::opt::v::empty_disposer   disposer;
1187
1188             /// Internal statistics. Available statistics: \p cuckoo::stat, \p cuckoo::empty_stat
1189             typedef empty_stat                  stat;
1190         };
1191
1192         /// Metafunction converting option list to \p CuckooSet traits
1193         /**
1194             Template argument list \p Options... are:
1195             - \p intrusive::opt::hook - hook used. Possible values are: \p cuckoo::base_hook, \p cuckoo::member_hook,
1196                 \p cuckoo::traits_hook.
1197                 If the option is not specified, <tt>%cuckoo::base_hook<></tt> is used.
1198             - \p opt::hash - hash functor tuple, mandatory option. At least, two hash functors should be provided. All hash functor
1199                 should be orthogonal (different): for each <tt> i,j: i != j => h[i](x) != h[j](x) </tt>.
1200                 The hash functors are passed as <tt> std::tuple< H1, H2, ... Hn > </tt>. The number of hash functors specifies
1201                 the number \p k - the count of hash tables in cuckoo hashing.
1202             - \p opt::mutex_policy - concurrent access policy.
1203                 Available policies: \p cuckoo::striping, \p cuckoo::refinable.
1204                 Default is \p %cuckoo::striping.
1205             - \p opt::equal_to - key equality functor like \p std::equal_to.
1206                 If this functor is defined then the probe-set will be unordered.
1207                 If \p %opt::compare or \p %opt::less option is specified too, then the probe-set will be ordered
1208                 and \p %opt::equal_to will be ignored.
1209             - \p opt::compare - key comparison functor. No default functor is provided.
1210                 If the option is not specified, the \p %opt::less is used.
1211                 If \p %opt::compare or \p %opt::less option is specified, then the probe-set will be ordered.
1212             - \p opt::less - specifies binary predicate used for key comparison. Default is \p std::less<T>.
1213                 If \p %opt::compare or \p %opt::less option is specified, then the probe-set will be ordered.
1214             - \p opt::item_counter - the type of item counting feature. Default is \p atomicity::item_counter
1215                 The item counter should be atomic.
1216             - \p opt::allocator - the allocator type using for allocating bucket tables.
1217                 Default is \ref CDS_DEFAULT_ALLOCATOR
1218             - \p intrusive::opt::disposer - the disposer type used in \p clear() member function for
1219                 freeing nodes. Default is \p intrusive::opt::v::empty_disposer
1220             - \p opt::stat - internal statistics. Possibly types: \p cuckoo::stat, \p cuckoo::empty_stat.
1221                 Default is \p %cuckoo::empty_stat
1222
1223             The probe set traits \p cuckoo::probeset_type and \p cuckoo::store_hash are taken from \p node type
1224             specified by \p opt::hook option.
1225         */
1226         template <typename... Options>
1227         struct make_traits {
1228             typedef typename cds::opt::make_options<
1229                 typename cds::opt::find_type_traits< cuckoo::traits, Options... >::type
1230                 ,Options...
1231             >::type   type ;    ///< Result of metafunction
1232         };
1233
1234         //@cond
1235         namespace details {
1236             template <typename Node, typename Probeset>
1237             class bucket_entry;
1238
1239             template <typename Node>
1240             class bucket_entry<Node, cuckoo::list>
1241             {
1242             public:
1243                 typedef Node                        node_type;
1244                 typedef cuckoo::list_probeset_class probeset_class;
1245                 typedef cuckoo::list                probeset_type;
1246
1247             protected:
1248                 node_type *     pHead;
1249                 unsigned int    nSize;
1250
1251             public:
1252                 class iterator
1253                 {
1254                     node_type * pNode;
1255                     friend class bucket_entry;
1256
1257                 public:
1258                     iterator()
1259                         : pNode( nullptr )
1260                     {}
1261                     iterator( node_type * p )
1262                         : pNode( p )
1263                     {}
1264                     iterator( iterator const& it)
1265                         : pNode( it.pNode )
1266                     {}
1267
1268                     iterator& operator=( iterator const& it )
1269                     {
1270                         pNode = it.pNode;
1271                         return *this;
1272                     }
1273
1274                     iterator& operator=( node_type * p )
1275                     {
1276                         pNode = p;
1277                         return *this;
1278                     }
1279
1280                     node_type * operator->()
1281                     {
1282                         return pNode;
1283                     }
1284                     node_type& operator*()
1285                     {
1286                         assert( pNode != nullptr );
1287                         return *pNode;
1288                     }
1289
1290                     // preinc
1291                     iterator& operator ++()
1292                     {
1293                         if ( pNode )
1294                             pNode = pNode->m_pNext;
1295                         return *this;
1296                     }
1297
1298                     bool operator==(iterator const& it ) const
1299                     {
1300                         return pNode == it.pNode;
1301                     }
1302                     bool operator!=(iterator const& it ) const
1303                     {
1304                         return !( *this == it );
1305                     }
1306                 };
1307
1308             public:
1309                 bucket_entry()
1310                     : pHead( nullptr )
1311                     , nSize(0)
1312                 {
1313                     static_assert(( std::is_same<typename node_type::probeset_type, probeset_type>::value ), "Incompatible node type" );
1314                 }
1315
1316                 iterator begin()
1317                 {
1318                     return iterator(pHead);
1319                 }
1320                 iterator end()
1321                 {
1322                     return iterator();
1323                 }
1324
1325                 void insert_after( iterator it, node_type * p )
1326                 {
1327                     node_type * pPrev = it.pNode;
1328                     if ( pPrev ) {
1329                         p->m_pNext = pPrev->m_pNext;
1330                         pPrev->m_pNext = p;
1331                     }
1332                     else {
1333                         // insert as head
1334                         p->m_pNext = pHead;
1335                         pHead = p;
1336                     }
1337                     ++nSize;
1338                 }
1339
1340                 void remove( iterator itPrev, iterator itWhat )
1341                 {
1342                     node_type * pPrev = itPrev.pNode;
1343                     node_type * pWhat = itWhat.pNode;
1344                     assert( (!pPrev && pWhat == pHead) || (pPrev && pPrev->m_pNext == pWhat));
1345
1346                     if ( pPrev )
1347                         pPrev->m_pNext = pWhat->m_pNext;
1348                     else {
1349                         assert( pWhat == pHead );
1350                         pHead = pHead->m_pNext;
1351                     }
1352                     pWhat->clear();
1353                     --nSize;
1354                 }
1355
1356                 void clear()
1357                 {
1358                     node_type * pNext;
1359                     for ( node_type * pNode = pHead; pNode; pNode = pNext ) {
1360                         pNext = pNode->m_pNext;
1361                         pNode->clear();
1362                     }
1363
1364                     nSize = 0;
1365                     pHead = nullptr;
1366                 }
1367
1368                 template <typename Disposer>
1369                 void clear( Disposer disp )
1370                 {
1371                     node_type * pNext;
1372                     for ( node_type * pNode = pHead; pNode; pNode = pNext ) {
1373                         pNext = pNode->m_pNext;
1374                         pNode->clear();
1375                         disp( pNode );
1376                     }
1377
1378                     nSize = 0;
1379                     pHead = nullptr;
1380                 }
1381
1382                 unsigned int size() const
1383                 {
1384                     return nSize;
1385                 }
1386             };
1387
1388             template <typename Node, unsigned int Capacity>
1389             class bucket_entry<Node, cuckoo::vector<Capacity>>
1390             {
1391             public:
1392                 typedef Node                            node_type;
1393                 typedef cuckoo::vector_probeset_class   probeset_class;
1394                 typedef cuckoo::vector<Capacity>        probeset_type;
1395
1396                 static unsigned int const c_nCapacity = probeset_type::c_nCapacity;
1397
1398             protected:
1399                 node_type *     m_arrNode[c_nCapacity];
1400                 unsigned int    m_nSize;
1401
1402                 void shift_up( unsigned int nFrom )
1403                 {
1404                     assert( m_nSize < c_nCapacity );
1405
1406                     if ( nFrom < m_nSize )
1407                         std::copy_backward( m_arrNode + nFrom, m_arrNode + m_nSize, m_arrNode + m_nSize + 1 );
1408                 }
1409
1410                 void shift_down( node_type ** pFrom )
1411                 {
1412                     assert( m_arrNode <= pFrom && pFrom < m_arrNode + m_nSize);
1413                     std::copy( pFrom + 1, m_arrNode + m_nSize, pFrom );
1414                 }
1415             public:
1416                 class iterator
1417                 {
1418                     node_type **    pArr;
1419                     friend class bucket_entry;
1420
1421                 public:
1422                     iterator()
1423                         : pArr( nullptr )
1424                     {}
1425                     iterator( node_type ** p )
1426                         : pArr(p)
1427                     {}
1428                     iterator( iterator const& it)
1429                         : pArr( it.pArr )
1430                     {}
1431
1432                     iterator& operator=( iterator const& it )
1433                     {
1434                         pArr = it.pArr;
1435                         return *this;
1436                     }
1437
1438                     node_type * operator->()
1439                     {
1440                         assert( pArr != nullptr );
1441                         return *pArr;
1442                     }
1443                     node_type& operator*()
1444                     {
1445                         assert( pArr != nullptr );
1446                         assert( *pArr != nullptr );
1447                         return *(*pArr);
1448                     }
1449
1450                     // preinc
1451                     iterator& operator ++()
1452                     {
1453                         ++pArr;
1454                         return *this;
1455                     }
1456
1457                     bool operator==(iterator const& it ) const
1458                     {
1459                         return pArr == it.pArr;
1460                     }
1461                     bool operator!=(iterator const& it ) const
1462                     {
1463                         return !( *this == it );
1464                     }
1465                 };
1466
1467             public:
1468                 bucket_entry()
1469                     : m_nSize(0)
1470                 {
1471                     memset( m_arrNode, 0, sizeof(m_arrNode));
1472                     static_assert(( std::is_same<typename node_type::probeset_type, probeset_type>::value ), "Incompatible node type" );
1473                 }
1474
1475                 iterator begin()
1476                 {
1477                     return iterator(m_arrNode);
1478                 }
1479                 iterator end()
1480                 {
1481                     return iterator(m_arrNode + size());
1482                 }
1483
1484                 void insert_after( iterator it, node_type * p )
1485                 {
1486                     assert( m_nSize < c_nCapacity );
1487                     assert( !it.pArr || (m_arrNode <= it.pArr && it.pArr <= m_arrNode + m_nSize));
1488
1489                     if ( it.pArr ) {
1490                         shift_up( static_cast<unsigned int>(it.pArr - m_arrNode) + 1 );
1491                         it.pArr[1] = p;
1492                     }
1493                     else {
1494                         shift_up(0);
1495                         m_arrNode[0] = p;
1496                     }
1497                     ++m_nSize;
1498                 }
1499
1500                 void remove( iterator /*itPrev*/, iterator itWhat )
1501                 {
1502                     itWhat->clear();
1503                     shift_down( itWhat.pArr );
1504                     --m_nSize;
1505                 }
1506
1507                 void clear()
1508                 {
1509                     m_nSize = 0;
1510                 }
1511
1512                 template <typename Disposer>
1513                 void clear( Disposer disp )
1514                 {
1515                     for ( unsigned int i = 0; i < m_nSize; ++i ) {
1516                         disp( m_arrNode[i] );
1517                     }
1518                     m_nSize = 0;
1519                 }
1520
1521                 unsigned int size() const
1522                 {
1523                     return m_nSize;
1524                 }
1525             };
1526
1527             template <typename Node, unsigned int ArraySize>
1528             struct hash_ops {
1529                 static void store( Node * pNode, size_t * pHashes )
1530                 {
1531                     memcpy( pNode->m_arrHash, pHashes, sizeof(pHashes[0]) * ArraySize );
1532                 }
1533                 static bool equal_to( Node& node, unsigned int nTable, size_t nHash )
1534                 {
1535                     return node.m_arrHash[nTable] == nHash;
1536                 }
1537             };
1538             template <typename Node>
1539             struct hash_ops<Node, 0>
1540             {
1541                 static void store( Node * /*pNode*/, size_t * /*pHashes*/ )
1542                 {}
1543                 static bool equal_to( Node& /*node*/, unsigned int /*nTable*/, size_t /*nHash*/ )
1544                 {
1545                     return true;
1546                 }
1547             };
1548
1549             template <typename NodeTraits, bool Ordered>
1550             struct contains;
1551
1552             template <typename NodeTraits>
1553             struct contains<NodeTraits, true>
1554             {
1555                 template <typename BucketEntry, typename Position, typename Q, typename Compare>
1556                 static bool find( BucketEntry& probeset, Position& pos, unsigned int /*nTable*/, size_t /*nHash*/, Q const& val, Compare cmp )
1557                 {
1558                     // Ordered version
1559                     typedef typename BucketEntry::iterator bucket_iterator;
1560
1561                     bucket_iterator itPrev;
1562
1563                     for ( bucket_iterator it = probeset.begin(), itEnd = probeset.end(); it != itEnd; ++it ) {
1564                         int cmpRes = cmp( *NodeTraits::to_value_ptr(*it), val );
1565                         if ( cmpRes >= 0 ) {
1566                             pos.itFound = it;
1567                             pos.itPrev = itPrev;
1568                             return cmpRes == 0;
1569                         }
1570
1571                         itPrev = it;
1572                     }
1573
1574                     pos.itPrev = itPrev;
1575                     pos.itFound = probeset.end();
1576                     return false;
1577                 }
1578             };
1579
1580             template <typename NodeTraits>
1581             struct contains<NodeTraits, false>
1582             {
1583                 template <typename BucketEntry, typename Position, typename Q, typename EqualTo>
1584                 static bool find( BucketEntry& probeset, Position& pos, unsigned int nTable, size_t nHash, Q const& val, EqualTo eq )
1585                 {
1586                     // Unordered version
1587                     typedef typename BucketEntry::iterator  bucket_iterator;
1588                     typedef typename BucketEntry::node_type node_type;
1589
1590                     bucket_iterator itPrev;
1591
1592                     for ( bucket_iterator it = probeset.begin(), itEnd = probeset.end(); it != itEnd; ++it ) {
1593                         if ( hash_ops<node_type, node_type::hash_array_size>::equal_to( *it, nTable, nHash ) && eq( *NodeTraits::to_value_ptr(*it), val )) {
1594                             pos.itFound = it;
1595                             pos.itPrev = itPrev;
1596                             return true;
1597                         }
1598                         itPrev = it;
1599                     }
1600
1601                     pos.itPrev = itPrev;
1602                     pos.itFound = probeset.end();
1603                     return false;
1604                 }
1605             };
1606
1607         }   // namespace details
1608         //@endcond
1609
1610     } // namespace cuckoo
1611
1612     /// Cuckoo hash set
1613     /** @ingroup cds_intrusive_map
1614
1615         Source
1616             - [2007] M.Herlihy, N.Shavit, M.Tzafrir "Concurrent Cuckoo Hashing. Technical report"
1617             - [2008] Maurice Herlihy, Nir Shavit "The Art of Multiprocessor Programming"
1618
1619         <b>About Cuckoo hashing</b>
1620
1621             [From <i>"The Art of Multiprocessor Programming"</i>]
1622             <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
1623             occupying the same slot. For brevity, a table is a k-entry array of items. For a hash set of size
1624             N = 2k we use a two-entry array of tables, and two independent hash functions,
1625             <tt> h0, h1: KeyRange -> 0,...,k-1</tt>
1626             mapping the set of possible keys to entries in he array. To test whether a value \p x is in the set,
1627             <tt>find(x)</tt> tests whether either <tt>table[0][h0(x)]</tt> or <tt>table[1][h1(x)]</tt> is
1628             equal to \p x. Similarly, <tt>erase(x)</tt>checks whether \p x is in either <tt>table[0][h0(x)]</tt>
1629             or <tt>table[1][h1(x)]</tt>, ad removes it if found.
1630
1631             The <tt>insert(x)</tt> successively "kicks out" conflicting items until every key has a slot.
1632             To add \p x, the method swaps \p x with \p y, the current occupant of <tt>table[0][h0(x)]</tt>.
1633             If the prior value was \p nullptr, it is done. Otherwise, it swaps the newly nest-less value \p y
1634             for the current occupant of <tt>table[1][h1(y)]</tt> in the same way. As before, if the prior value
1635             was \p nullptr, it is done. Otherwise, the method continues swapping entries (alternating tables)
1636             until it finds an empty slot. We might not find an empty slot, either because the table is full,
1637             or because the sequence of displacement forms a cycle. We therefore need an upper limit on the
1638             number of successive displacements we are willing to undertake. When this limit is exceeded,
1639             we resize the hash table, choose new hash functions and start over.
1640
1641             For concurrent cuckoo hashing, rather than organizing the set as a two-dimensional table of
1642             items, we use two-dimensional table of probe sets, where a probe set is a constant-sized set
1643             of items with the same hash code. Each probe set holds at most \p PROBE_SIZE items, but the algorithm
1644             tries to ensure that when the set is quiescent (i.e no method call in progress) each probe set
1645             holds no more than <tt>THRESHOLD < PROBE_SET</tt> items. While method calls are in-flight, a probe
1646             set may temporarily hold more than \p THRESHOLD but never more than \p PROBE_SET items.
1647
1648             In current implementation, a probe set can be defined either as a (single-linked) list
1649             or as a fixed-sized vector, optionally ordered.
1650
1651             In description above two-table cuckoo hashing (<tt>k = 2</tt>) has been considered.
1652             We can generalize this approach for <tt>k >= 2</tt> when we have \p k hash functions
1653             <tt>h[0], ... h[k-1]</tt> and \p k tables <tt>table[0], ... table[k-1]</tt>.
1654
1655             The search in probe set is linear, the complexity is <tt> O(PROBE_SET) </tt>.
1656             The probe set may be ordered or not. Ordered probe set can be more efficient since
1657             the average search complexity is <tt>O(PROBE_SET/2)</tt>.
1658             However, the overhead of sorting can eliminate a gain of ordered search.
1659
1660             The probe set is ordered if \p compare or \p less is specified in \p Traits template
1661             parameter. Otherwise, the probe set is unordered and \p Traits should provide
1662             \p equal_to predicate.
1663
1664             The \p cds::intrusive::cuckoo namespace contains \p %CuckooSet-related declarations.
1665
1666         Template arguments:
1667         - \p T - the type stored in the set. The type must be based on \p cuckoo::node (for \p cuckoo::base_hook)
1668             or it must have a member of type %cuckoo::node (for \p cuckoo::member_hook),
1669             or it must be convertible to \p %cuckoo::node (for \p cuckoo::traits_hook)
1670         - \p Traits - type traits, default is \p cuckoo::traits. It is possible to declare option-based
1671             set with \p cuckoo::make_traits metafunction result as \p Traits template argument.
1672
1673         <b>How to use</b>
1674
1675         You should incorporate \p cuckoo::node into your struct \p T and provide
1676         appropriate \p cuckoo::traits::hook in your \p Traits template parameters.
1677         Usually, for \p Traits you define a struct based on \p cuckoo::traits.
1678
1679         Example for base hook and list-based probe-set:
1680         \code
1681         #include <cds/intrusive/cuckoo_set.h>
1682
1683         // Data stored in cuckoo set
1684         // We use list as probe-set container and store hash values in the node
1685         // (since we use two hash functions we should store 2 hash values per node)
1686         struct my_data: public cds::intrusive::cuckoo::node< cds::intrusive::cuckoo::list, 2 >
1687         {
1688             // key field
1689             std::string     strKey;
1690
1691             // other data
1692             // ...
1693         };
1694
1695         // Provide equal_to functor for my_data since we will use unordered probe-set
1696         struct my_data_equal_to {
1697             bool operator()( const my_data& d1, const my_data& d2 ) const
1698             {
1699                 return d1.strKey.compare( d2.strKey ) == 0;
1700             }
1701
1702             bool operator()( const my_data& d, const std::string& s ) const
1703             {
1704                 return d.strKey.compare(s) == 0;
1705             }
1706
1707             bool operator()( const std::string& s, const my_data& d ) const
1708             {
1709                 return s.compare( d.strKey ) == 0;
1710             }
1711         };
1712
1713         // Provide two hash functor for my_data
1714         struct hash1 {
1715             size_t operator()(std::string const& s) const
1716             {
1717                 return cds::opt::v::hash<std::string>( s );
1718             }
1719             size_t operator()( my_data const& d ) const
1720             {
1721                 return (*this)( d.strKey );
1722             }
1723         };
1724
1725         struct hash2: private hash1 {
1726             size_t operator()(std::string const& s) const
1727             {
1728                 size_t h = ~( hash1::operator()(s));
1729                 return ~h + 0x9e3779b9 + (h << 6) + (h >> 2);
1730             }
1731             size_t operator()( my_data const& d ) const
1732             {
1733                 return (*this)( d.strKey );
1734             }
1735         };
1736
1737         // Declare type traits
1738         struct my_traits: public cds::intrusive::cuckoo::traits
1739         {
1740             typedef cds::intrusive::cuckoo::base_hook<
1741                 cds::intrusive::cuckoo::probeset_type< my_data::probeset_type >
1742                 ,cds::intrusive::cuckoo::store_hash< my_data::hash_array_size >
1743             >   hook;
1744             typedef my_data_equa_to equal_to;
1745             typedef cds::opt::hash_tuple< hash1, hash2 > hash;
1746         };
1747
1748         // Declare CuckooSet type
1749         typedef cds::intrusive::CuckooSet< my_data, my_traits > my_cuckoo_set;
1750
1751         // Equal option-based declaration
1752         typedef cds::intrusive::CuckooSet< my_data,
1753             cds::intrusive::cuckoo::make_traits<
1754                 cds::intrusive::opt::hook< cds::intrusive::cuckoo::base_hook<
1755                     cds::intrusive::cuckoo::probeset_type< my_data::probeset_type >
1756                     ,cds::intrusive::cuckoo::store_hash< my_data::hash_array_size >
1757                 > >
1758                 ,cds::opt::hash< std::tuple< hash1, hash2 > >
1759                 ,cds::opt::equal_to< my_data_equal_to >
1760             >::type
1761         > opt_cuckoo_set;
1762         \endcode
1763
1764         If we provide \p compare function instead of \p equal_to for \p my_data
1765         we get as a result a cuckoo set with ordered probe set that may improve
1766         performance.
1767         Example for base hook and ordered vector-based probe-set:
1768
1769         \code
1770         #include <cds/intrusive/cuckoo_set.h>
1771
1772         // Data stored in cuckoo set
1773         // We use a vector of capacity 4 as probe-set container and store hash values in the node
1774         // (since we use two hash functions we should store 2 hash values per node)
1775         struct my_data: public cds::intrusive::cuckoo::node< cds::intrusive::cuckoo::vector<4>, 2 >
1776         {
1777             // key field
1778             std::string     strKey;
1779
1780             // other data
1781             // ...
1782         };
1783
1784         // Provide compare functor for my_data since we want to use ordered probe-set
1785         struct my_data_compare {
1786             int operator()( const my_data& d1, const my_data& d2 ) const
1787             {
1788                 return d1.strKey.compare( d2.strKey );
1789             }
1790
1791             int operator()( const my_data& d, const std::string& s ) const
1792             {
1793                 return d.strKey.compare(s);
1794             }
1795
1796             int operator()( const std::string& s, const my_data& d ) const
1797             {
1798                 return s.compare( d.strKey );
1799             }
1800         };
1801
1802         // Provide two hash functor for my_data
1803         struct hash1 {
1804             size_t operator()(std::string const& s) const
1805             {
1806                 return cds::opt::v::hash<std::string>( s );
1807             }
1808             size_t operator()( my_data const& d ) const
1809             {
1810                 return (*this)( d.strKey );
1811             }
1812         };
1813
1814         struct hash2: private hash1 {
1815             size_t operator()(std::string const& s) const
1816             {
1817                 size_t h = ~( hash1::operator()(s));
1818                 return ~h + 0x9e3779b9 + (h << 6) + (h >> 2);
1819             }
1820             size_t operator()( my_data const& d ) const
1821             {
1822                 return (*this)( d.strKey );
1823             }
1824         };
1825
1826         // Declare type traits
1827         struct my_traits: public cds::intrusive::cuckoo::traits
1828         {
1829             typedef cds::intrusive::cuckoo::base_hook<
1830                 cds::intrusive::cuckoo::probeset_type< my_data::probeset_type >
1831                 ,cds::intrusive::cuckoo::store_hash< my_data::hash_array_size >
1832             >   hook;
1833             typedef my_data_compare compare;
1834             typedef cds::opt::hash_tuple< hash1, hash2 > hash;
1835         };
1836
1837         // Declare CuckooSet type
1838         typedef cds::intrusive::CuckooSet< my_data, my_traits > my_cuckoo_set;
1839
1840         // Equal option-based declaration
1841         typedef cds::intrusive::CuckooSet< my_data,
1842             cds::intrusive::cuckoo::make_traits<
1843                 cds::intrusive::opt::hook< cds::intrusive::cuckoo::base_hook<
1844                     cds::intrusive::cuckoo::probeset_type< my_data::probeset_type >
1845                     ,cds::intrusive::cuckoo::store_hash< my_data::hash_array_size >
1846                 > >
1847                 ,cds::opt::hash< std::tuple< hash1, hash2 > >
1848                 ,cds::opt::compare< my_data_compare >
1849             >::type
1850         > opt_cuckoo_set;
1851         \endcode
1852
1853     */
1854     template <typename T, typename Traits = cuckoo::traits>
1855     class CuckooSet
1856     {
1857     public:
1858         typedef T   value_type;   ///< The value type stored in the set
1859         typedef Traits traits;    ///< Set traits
1860
1861         typedef typename traits::hook       hook;        ///< hook type
1862         typedef typename hook::node_type    node_type;   ///< node type
1863         typedef typename get_node_traits< value_type, node_type, hook>::type node_traits;    ///< node traits
1864
1865         typedef typename traits::hash          hash;   ///< hash functor tuple wrapped for internal use
1866         typedef typename hash::hash_tuple_type hash_tuple_type; ///< Type of hash tuple
1867
1868         typedef typename traits::stat          stat;   ///< internal statistics type
1869
1870         typedef typename traits::mutex_policy  original_mutex_policy; ///< Concurrent access policy, see \p cuckoo::traits::mutex_policy
1871
1872         //@cond
1873         typedef typename original_mutex_policy::template rebind_statistics<
1874             typename std::conditional<
1875                 std::is_same< stat, cuckoo::empty_stat >::value
1876                 ,typename original_mutex_policy::empty_stat
1877                 ,typename original_mutex_policy::real_stat
1878             >::type
1879         >::other    mutex_policy;
1880         //@endcond
1881
1882         /// Probe set should be ordered or not
1883         /**
1884             If \p Traits specifies \p cmpare or \p less functor then the set is ordered.
1885             Otherwise, it is unordered and \p Traits should provide \p equal_to functor.
1886         */
1887         static bool const c_isSorted = !( std::is_same< typename traits::compare, opt::none >::value
1888                 && std::is_same< typename traits::less, opt::none >::value );
1889         static size_t const c_nArity = hash::size ; ///< the arity of cuckoo hashing: the number of hash functors provided; minimum 2.
1890
1891         /// Key equality functor; used only for unordered probe-set
1892         typedef typename opt::details::make_equal_to< value_type, traits, !c_isSorted>::type key_equal_to;
1893
1894         /// key comparing functor based on \p opt::compare and \p opt::less option setter. Used only for ordered probe set
1895         typedef typename opt::details::make_comparator< value_type, traits >::type key_comparator;
1896
1897         /// allocator type
1898         typedef typename traits::allocator     allocator;
1899
1900         /// item counter type
1901         typedef typename traits::item_counter  item_counter;
1902
1903         /// node disposer
1904         typedef typename traits::disposer      disposer;
1905
1906     protected:
1907         //@cond
1908         typedef typename node_type::probeset_class  probeset_class;
1909         typedef typename node_type::probeset_type   probeset_type;
1910         static unsigned int const c_nNodeHashArraySize = node_type::hash_array_size;
1911
1912         typedef typename mutex_policy::scoped_cell_lock     scoped_cell_lock;
1913         typedef typename mutex_policy::scoped_cell_trylock  scoped_cell_trylock;
1914         typedef typename mutex_policy::scoped_full_lock     scoped_full_lock;
1915         typedef typename mutex_policy::scoped_resize_lock   scoped_resize_lock;
1916
1917         typedef cuckoo::details::bucket_entry< node_type, probeset_type >   bucket_entry;
1918         typedef typename bucket_entry::iterator                     bucket_iterator;
1919         typedef cds::details::Allocator< bucket_entry, allocator >  bucket_table_allocator;
1920
1921         typedef size_t  hash_array[c_nArity]    ;   ///< hash array
1922
1923         struct position {
1924             bucket_iterator     itPrev;
1925             bucket_iterator     itFound;
1926         };
1927
1928         typedef cuckoo::details::contains< node_traits, c_isSorted > contains_action;
1929
1930         template <typename Predicate>
1931         struct predicate_wrapper {
1932             typedef typename std::conditional< c_isSorted, cds::opt::details::make_comparator_from_less<Predicate>, Predicate>::type   type;
1933         };
1934
1935         typedef typename std::conditional< c_isSorted, key_comparator, key_equal_to >::type key_predicate;
1936         //@endcond
1937
1938     public:
1939         static unsigned int const   c_nDefaultProbesetSize = 4;   ///< default probeset size
1940         static size_t const         c_nDefaultInitialSize = 16;   ///< default initial size
1941         static unsigned int const   c_nRelocateLimit = c_nArity * 2 - 1; ///< Count of attempts to relocate before giving up
1942
1943     protected:
1944         bucket_entry *      m_BucketTable[ c_nArity ] ; ///< Bucket tables
1945
1946         atomics::atomic<size_t> m_nBucketMask           ;   ///< Hash bitmask; bucket table size minus 1.
1947         unsigned int const      m_nProbesetSize         ;   ///< Probe set size
1948         unsigned int const      m_nProbesetThreshold    ;   ///< Probe set threshold
1949
1950         hash            m_Hash              ;   ///< Hash functor tuple
1951         mutex_policy    m_MutexPolicy       ;   ///< concurrent access policy
1952         item_counter    m_ItemCounter       ;   ///< item counter
1953         mutable stat    m_Stat              ;   ///< internal statistics
1954
1955     protected:
1956         //@cond
1957         static void check_common_constraints()
1958         {
1959             static_assert( (c_nArity == mutex_policy::c_nArity), "The count of hash functors must be equal to mutex_policy arity" );
1960         }
1961
1962         void check_probeset_properties() const
1963         {
1964             assert( m_nProbesetThreshold < m_nProbesetSize );
1965
1966             // if probe set type is cuckoo::vector<N> then m_nProbesetSize == N
1967             assert( node_type::probeset_size == 0 || node_type::probeset_size == m_nProbesetSize );
1968         }
1969
1970         template <typename Q>
1971         void hashing( size_t * pHashes, Q const& v ) const
1972         {
1973             m_Hash( pHashes, v );
1974         }
1975
1976         void copy_hash( size_t * pHashes, value_type const& v ) const
1977         {
1978             if ( c_nNodeHashArraySize )
1979                 memcpy( pHashes, node_traits::to_node_ptr( v )->get_hash(), sizeof(pHashes[0]) * c_nNodeHashArraySize );
1980             else
1981                 hashing( pHashes, v );
1982         }
1983
1984         bucket_entry& bucket( unsigned int nTable, size_t nHash )
1985         {
1986             assert( nTable < c_nArity );
1987             return m_BucketTable[nTable][nHash & m_nBucketMask.load( atomics::memory_order_relaxed ) ];
1988         }
1989
1990         static void store_hash( node_type * pNode, size_t * pHashes )
1991         {
1992             cuckoo::details::hash_ops< node_type, c_nNodeHashArraySize >::store( pNode, pHashes );
1993         }
1994
1995         static bool equal_hash( node_type& node, unsigned int nTable, size_t nHash )
1996         {
1997             return cuckoo::details::hash_ops< node_type, c_nNodeHashArraySize >::equal_to( node, nTable, nHash );
1998         }
1999
2000         void allocate_bucket_tables( size_t nSize )
2001         {
2002             assert( cds::beans::is_power2( nSize ));
2003
2004             m_nBucketMask.store( nSize - 1, atomics::memory_order_release );
2005             bucket_table_allocator alloc;
2006             for ( unsigned int i = 0; i < c_nArity; ++i )
2007                 m_BucketTable[i] = alloc.NewArray( nSize );
2008         }
2009
2010         static void free_bucket_tables( bucket_entry ** pTable, size_t nCapacity )
2011         {
2012             bucket_table_allocator alloc;
2013             for ( unsigned int i = 0; i < c_nArity; ++i ) {
2014                 alloc.Delete( pTable[i], nCapacity );
2015                 pTable[i] = nullptr;
2016             }
2017         }
2018         void free_bucket_tables()
2019         {
2020             free_bucket_tables( m_BucketTable, m_nBucketMask.load( atomics::memory_order_relaxed ) + 1 );
2021         }
2022
2023         static CDS_CONSTEXPR unsigned int const c_nUndefTable = (unsigned int) -1;
2024         template <typename Q, typename Predicate >
2025         unsigned int contains( position * arrPos, size_t * arrHash, Q const& val, Predicate pred )
2026         {
2027             // Buckets must be locked
2028
2029             for ( unsigned int i = 0; i < c_nArity; ++i ) {
2030                 bucket_entry& probeset = bucket( i, arrHash[i] );
2031                 if ( contains_action::find( probeset, arrPos[i], i, arrHash[i], val, pred ))
2032                     return i;
2033             }
2034             return c_nUndefTable;
2035         }
2036
2037         template <typename Q, typename Predicate, typename Func>
2038         value_type * erase_( Q const& val, Predicate pred, Func f )
2039         {
2040             hash_array arrHash;
2041             hashing( arrHash, val );
2042             position arrPos[ c_nArity ];
2043
2044             {
2045                 scoped_cell_lock guard( m_MutexPolicy, arrHash );
2046
2047                 unsigned int nTable = contains( arrPos, arrHash, val, pred );
2048                 if ( nTable != c_nUndefTable ) {
2049                     node_type& node = *arrPos[nTable].itFound;
2050                     f( *node_traits::to_value_ptr(node));
2051                     bucket( nTable, arrHash[nTable]).remove( arrPos[nTable].itPrev, arrPos[nTable].itFound );
2052                     --m_ItemCounter;
2053                     m_Stat.onEraseSuccess();
2054                     return node_traits::to_value_ptr( node );
2055                 }
2056             }
2057
2058             m_Stat.onEraseFailed();
2059             return nullptr;
2060         }
2061
2062         template <typename Q, typename Predicate, typename Func>
2063         bool find_( Q& val, Predicate pred, Func f )
2064         {
2065             hash_array arrHash;
2066             position arrPos[ c_nArity ];
2067             hashing( arrHash, val );
2068             scoped_cell_lock sl( m_MutexPolicy, arrHash );
2069
2070             unsigned int nTable = contains( arrPos, arrHash, val, pred );
2071             if ( nTable != c_nUndefTable ) {
2072                 f( *node_traits::to_value_ptr( *arrPos[nTable].itFound ), val );
2073                 m_Stat.onFindSuccess();
2074                 return true;
2075             }
2076
2077             m_Stat.onFindFailed();
2078             return false;
2079         }
2080
2081         bool relocate( unsigned int nTable, size_t * arrGoalHash )
2082         {
2083             // arrGoalHash contains hash values for relocating element
2084             // Relocating element is first one from bucket( nTable, arrGoalHash[nTable] ) probeset
2085
2086             m_Stat.onRelocateCall();
2087
2088             hash_array arrHash;
2089             value_type * pVal;
2090             for ( unsigned int nRound = 0; nRound < c_nRelocateLimit; ++nRound ) {
2091                 m_Stat.onRelocateRound();
2092
2093                 while ( true ) {
2094                     scoped_cell_lock guard( m_MutexPolicy, arrGoalHash );
2095
2096                     bucket_entry& refBucket = bucket( nTable, arrGoalHash[nTable] );
2097                     if ( refBucket.size() < m_nProbesetThreshold ) {
2098                         // probeset is not above the threshold
2099                         m_Stat.onFalseRelocateRound();
2100                         return true;
2101                     }
2102
2103                     pVal = node_traits::to_value_ptr( *refBucket.begin());
2104                     copy_hash( arrHash, *pVal );
2105
2106                     scoped_cell_trylock guard2( m_MutexPolicy, arrHash );
2107                     if ( !guard2.locked())
2108                         continue ;  // try one more time
2109
2110                     refBucket.remove( typename bucket_entry::iterator(), refBucket.begin());
2111
2112                     unsigned int i = (nTable + 1) % c_nArity;
2113
2114                     // try insert into free probeset
2115                     while ( i != nTable ) {
2116                         bucket_entry& bkt = bucket( i, arrHash[i] );
2117                         if ( bkt.size() < m_nProbesetThreshold ) {
2118                             position pos;
2119                             contains_action::find( bkt, pos, i, arrHash[i], *pVal, key_predicate()) ; // must return false!
2120                             bkt.insert_after( pos.itPrev, node_traits::to_node_ptr( pVal ));
2121                             m_Stat.onSuccessRelocateRound();
2122                             return true;
2123                         }
2124                         i = ( i + 1 ) % c_nArity;
2125                     }
2126
2127                     // try insert into partial probeset
2128                     i = (nTable + 1) % c_nArity;
2129                     while ( i != nTable ) {
2130                         bucket_entry& bkt = bucket( i, arrHash[i] );
2131                         if ( bkt.size() < m_nProbesetSize ) {
2132                             position pos;
2133                             contains_action::find( bkt, pos, i, arrHash[i], *pVal, key_predicate()) ; // must return false!
2134                             bkt.insert_after( pos.itPrev, node_traits::to_node_ptr( pVal ));
2135                             nTable = i;
2136                             memcpy( arrGoalHash, arrHash, sizeof(arrHash));
2137                             m_Stat.onRelocateAboveThresholdRound();
2138                             goto next_iteration;
2139                         }
2140                         i = (i + 1) % c_nArity;
2141                     }
2142
2143                     // all probeset is full, relocating fault
2144                     refBucket.insert_after( typename bucket_entry::iterator(), node_traits::to_node_ptr( pVal ));
2145                     m_Stat.onFailedRelocate();
2146                     return false;
2147                 }
2148
2149             next_iteration:;
2150             }
2151             return false;
2152         }
2153
2154         void resize()
2155         {
2156             m_Stat.onResizeCall();
2157
2158             size_t nOldCapacity = bucket_count( atomics::memory_order_acquire );
2159             bucket_entry *      pOldTable[ c_nArity ];
2160             {
2161                 scoped_resize_lock guard( m_MutexPolicy );
2162
2163                 if ( nOldCapacity != bucket_count()) {
2164                     m_Stat.onFalseResizeCall();
2165                     return;
2166                 }
2167
2168                 size_t nCapacity = nOldCapacity * 2;
2169
2170                 m_MutexPolicy.resize( nCapacity );
2171                 memcpy( pOldTable, m_BucketTable, sizeof(pOldTable));
2172                 allocate_bucket_tables( nCapacity );
2173
2174                 hash_array arrHash;
2175                 position arrPos[ c_nArity ];
2176
2177                 for ( unsigned int nTable = 0; nTable < c_nArity; ++nTable ) {
2178                     bucket_entry * pTable = pOldTable[nTable];
2179                     for ( size_t k = 0; k < nOldCapacity; ++k ) {
2180                         bucket_iterator itNext;
2181                         for ( bucket_iterator it = pTable[k].begin(), itEnd = pTable[k].end(); it != itEnd; it = itNext ) {
2182                             itNext = it;
2183                             ++itNext;
2184
2185                             value_type& val = *node_traits::to_value_ptr( *it );
2186                             copy_hash( arrHash, val );
2187                             contains( arrPos, arrHash, val, key_predicate()) ; // must return c_nUndefTable
2188
2189                             for ( unsigned int i = 0; i < c_nArity; ++i ) {
2190                                 bucket_entry& refBucket = bucket( i, arrHash[i] );
2191                                 if ( refBucket.size() < m_nProbesetThreshold ) {
2192                                     refBucket.insert_after( arrPos[i].itPrev, &*it );
2193                                     m_Stat.onResizeSuccessMove();
2194                                     goto do_next;
2195                                 }
2196                             }
2197
2198                             for ( unsigned int i = 0; i < c_nArity; ++i ) {
2199                                 bucket_entry& refBucket = bucket( i, arrHash[i] );
2200                                 if ( refBucket.size() < m_nProbesetSize ) {
2201                                     refBucket.insert_after( arrPos[i].itPrev, &*it );
2202                                     assert( refBucket.size() > 1 );
2203                                     copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()));
2204                                     m_Stat.onResizeRelocateCall();
2205                                     relocate( i, arrHash );
2206                                     break;
2207                                 }
2208                             }
2209                         do_next:;
2210                         }
2211                     }
2212                 }
2213             }
2214             free_bucket_tables( pOldTable, nOldCapacity );
2215         }
2216
2217         CDS_CONSTEXPR static unsigned int calc_probeset_size( unsigned int nProbesetSize ) CDS_NOEXCEPT
2218         {
2219             return std::is_same< probeset_class, cuckoo::vector_probeset_class >::value
2220                 ? node_type::probeset_size
2221                 : (nProbesetSize
2222                     ? nProbesetSize
2223                     : ( node_type::probeset_size ? node_type::probeset_size : c_nDefaultProbesetSize ));
2224         }
2225         //@endcond
2226
2227     public:
2228         /// Default constructor
2229         /**
2230             Initial size = \ref c_nDefaultInitialSize
2231
2232             Probe set size:
2233             - \p c_nDefaultProbesetSize if \p probeset_type is \p cuckoo::list
2234             - \p Capacity if \p probeset_type is <tt> cuckoo::vector<Capacity> </tt>
2235
2236             Probe set threshold = probe set size - 1
2237         */
2238         CuckooSet()
2239             : m_nProbesetSize( calc_probeset_size(0))
2240             , m_nProbesetThreshold( m_nProbesetSize - 1 )
2241             , m_MutexPolicy( c_nDefaultInitialSize )
2242         {
2243             check_common_constraints();
2244             check_probeset_properties();
2245
2246             allocate_bucket_tables( c_nDefaultInitialSize );
2247         }
2248
2249         /// Constructs the set object with given probe set size and threshold
2250         /**
2251             If probe set type is <tt> cuckoo::vector<Capacity> </tt> vector
2252             then \p nProbesetSize is ignored since it should be equal to vector's \p Capacity.
2253         */
2254         CuckooSet(
2255             size_t nInitialSize                 ///< Initial set size; if 0 - use default initial size \p c_nDefaultInitialSize
2256             , unsigned int nProbesetSize        ///< probe set size
2257             , unsigned int nProbesetThreshold = 0   ///< probe set threshold, <tt>nProbesetThreshold < nProbesetSize</tt>. If 0, <tt>nProbesetThreshold = nProbesetSize - 1</tt>
2258         )
2259             : m_nProbesetSize( calc_probeset_size(nProbesetSize))
2260             , m_nProbesetThreshold( nProbesetThreshold ? nProbesetThreshold : m_nProbesetSize - 1 )
2261             , m_MutexPolicy( cds::beans::ceil2(nInitialSize ? nInitialSize : c_nDefaultInitialSize ))
2262         {
2263             check_common_constraints();
2264             check_probeset_properties();
2265
2266             allocate_bucket_tables( nInitialSize ? cds::beans::ceil2( nInitialSize ) : c_nDefaultInitialSize );
2267         }
2268
2269         /// Constructs the set object with given hash functor tuple
2270         /**
2271             The probe set size and threshold are set as default, see \p CuckooSet()
2272         */
2273         CuckooSet(
2274             hash_tuple_type const& h    ///< hash functor tuple of type <tt>std::tuple<H1, H2, ... Hn></tt> where <tt> n == \ref c_nArity </tt>
2275         )
2276             : m_nProbesetSize( calc_probeset_size(0))
2277             , m_nProbesetThreshold( m_nProbesetSize -1 )
2278             , m_Hash( h )
2279             , m_MutexPolicy( c_nDefaultInitialSize )
2280         {
2281             check_common_constraints();
2282             check_probeset_properties();
2283
2284             allocate_bucket_tables( c_nDefaultInitialSize );
2285         }
2286
2287         /// Constructs the set object with given probe set properties and hash functor tuple
2288         /**
2289             If probe set type is <tt> cuckoo::vector<Capacity> </tt> vector
2290             then \p nProbesetSize should be equal to vector's \p Capacity.
2291         */
2292         CuckooSet(
2293             size_t nInitialSize                 ///< Initial set size; if 0 - use default initial size \p c_nDefaultInitialSize
2294             , unsigned int nProbesetSize        ///< probe set size, positive integer
2295             , unsigned int nProbesetThreshold   ///< probe set threshold, <tt>nProbesetThreshold < nProbesetSize</tt>. If 0, <tt>nProbesetThreshold = nProbesetSize - 1</tt>
2296             , hash_tuple_type const& h    ///< hash functor tuple of type <tt>std::tuple<H1, H2, ... Hn></tt> where <tt> n == \ref c_nArity </tt>
2297         )
2298             : m_nProbesetSize( calc_probeset_size(nProbesetSize))
2299             , m_nProbesetThreshold( nProbesetThreshold ? nProbesetThreshold : m_nProbesetSize - 1)
2300             , m_Hash( h )
2301             , m_MutexPolicy( cds::beans::ceil2(nInitialSize ? nInitialSize : c_nDefaultInitialSize ))
2302         {
2303             check_common_constraints();
2304             check_probeset_properties();
2305
2306             allocate_bucket_tables( nInitialSize ? cds::beans::ceil2( nInitialSize ) : c_nDefaultInitialSize );
2307         }
2308
2309         /// Constructs the set object with given hash functor tuple (move semantics)
2310         /**
2311             The probe set size and threshold are set as default, see \p CuckooSet()
2312         */
2313         CuckooSet(
2314             hash_tuple_type&& h     ///< hash functor tuple of type <tt>std::tuple<H1, H2, ... Hn></tt> where <tt> n == \ref c_nArity </tt>
2315         )
2316             : m_nProbesetSize( calc_probeset_size(0))
2317             , m_nProbesetThreshold( m_nProbesetSize / 2 )
2318             , m_Hash( std::forward<hash_tuple_type>(h))
2319             , m_MutexPolicy( c_nDefaultInitialSize )
2320         {
2321             check_common_constraints();
2322             check_probeset_properties();
2323
2324             allocate_bucket_tables( c_nDefaultInitialSize );
2325         }
2326
2327         /// Constructs the set object with given probe set properties and hash functor tuple (move semantics)
2328         /**
2329             If probe set type is <tt> cuckoo::vector<Capacity> </tt> vector
2330             then \p nProbesetSize should be equal to vector's \p Capacity.
2331         */
2332         CuckooSet(
2333             size_t nInitialSize                 ///< Initial set size; if 0 - use default initial size \p c_nDefaultInitialSize
2334             , unsigned int nProbesetSize        ///< probe set size, positive integer
2335             , unsigned int nProbesetThreshold   ///< probe set threshold, <tt>nProbesetThreshold < nProbesetSize</tt>. If 0, <tt>nProbesetThreshold = nProbesetSize - 1</tt>
2336             , hash_tuple_type&& h    ///< hash functor tuple of type <tt>std::tuple<H1, H2, ... Hn></tt> where <tt> n == \ref c_nArity </tt>
2337         )
2338             : m_nProbesetSize( calc_probeset_size(nProbesetSize))
2339             , m_nProbesetThreshold( nProbesetThreshold ? nProbesetThreshold : m_nProbesetSize - 1)
2340             , m_Hash( std::forward<hash_tuple_type>(h))
2341             , m_MutexPolicy( cds::beans::ceil2(nInitialSize ? nInitialSize : c_nDefaultInitialSize ))
2342         {
2343             check_common_constraints();
2344             check_probeset_properties();
2345
2346             allocate_bucket_tables( nInitialSize ? cds::beans::ceil2( nInitialSize ) : c_nDefaultInitialSize );
2347         }
2348
2349         /// Destructor
2350         ~CuckooSet()
2351         {
2352             free_bucket_tables();
2353         }
2354
2355     public:
2356         /// Inserts new node
2357         /**
2358             The function inserts \p val in the set if it does not contain an item with key equal to \p val.
2359
2360             Returns \p true if \p val is inserted into the set, \p false otherwise.
2361         */
2362         bool insert( value_type& val )
2363         {
2364             return insert( val, []( value_type& ) {} );
2365         }
2366
2367         /// Inserts new node
2368         /**
2369             The function allows to split creating of new item into two part:
2370             - create item with key only
2371             - insert new item into the set
2372             - if inserting is success, calls  \p f functor to initialize value-field of \p val.
2373
2374             The functor signature is:
2375             \code
2376                 void func( value_type& val );
2377             \endcode
2378             where \p val is the item inserted.
2379
2380             The user-defined functor is called only if the inserting is success.
2381         */
2382         template <typename Func>
2383         bool insert( value_type& val, Func f )
2384         {
2385             hash_array arrHash;
2386             position arrPos[ c_nArity ];
2387             unsigned int nGoalTable;
2388
2389             hashing( arrHash, val );
2390             node_type * pNode = node_traits::to_node_ptr( val );
2391             store_hash( pNode, arrHash );
2392
2393             while (true) {
2394                 {
2395                     scoped_cell_lock guard( m_MutexPolicy, arrHash );
2396
2397                     if ( contains( arrPos, arrHash, val, key_predicate()) != c_nUndefTable ) {
2398                         m_Stat.onInsertFailed();
2399                         return false;
2400                     }
2401
2402                     for ( unsigned int i = 0; i < c_nArity; ++i ) {
2403                         bucket_entry& refBucket = bucket( i, arrHash[i] );
2404                         if ( refBucket.size() < m_nProbesetThreshold ) {
2405                             refBucket.insert_after( arrPos[i].itPrev, pNode );
2406                             f( val );
2407                             ++m_ItemCounter;
2408                             m_Stat.onInsertSuccess();
2409                             return true;
2410                         }
2411                     }
2412
2413                     for ( unsigned int i = 0; i < c_nArity; ++i ) {
2414                         bucket_entry& refBucket = bucket( i, arrHash[i] );
2415                         if ( refBucket.size() < m_nProbesetSize ) {
2416                             refBucket.insert_after( arrPos[i].itPrev, pNode );
2417                             f( val );
2418                             ++m_ItemCounter;
2419                             nGoalTable = i;
2420                             assert( refBucket.size() > 1 );
2421                             copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()));
2422                             goto do_relocate;
2423                         }
2424                     }
2425                 }
2426
2427                 m_Stat.onInsertResize();
2428                 resize();
2429             }
2430
2431         do_relocate:
2432             m_Stat.onInsertRelocate();
2433             if ( !relocate( nGoalTable, arrHash )) {
2434                 m_Stat.onInsertRelocateFault();
2435                 m_Stat.onInsertResize();
2436                 resize();
2437             }
2438
2439             m_Stat.onInsertSuccess();
2440             return true;
2441         }
2442
2443         /// Updates the node
2444         /**
2445             The operation performs inserting or changing data with lock-free manner.
2446
2447             If the item \p val is not found in the set, then \p val is inserted into the set
2448             iff \p bAllowInsert is \p true.
2449             Otherwise, the functor \p func is called with item found.
2450             The functor \p func signature is:
2451             \code
2452                 void func( bool bNew, value_type& item, value_type& val );
2453             \endcode
2454             with arguments:
2455             - \p bNew - \p true if the item has been inserted, \p false otherwise
2456             - \p item - item of the set
2457             - \p val - argument \p val passed into the \p %update() function
2458             If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
2459             refer to the same thing.
2460
2461             The functor may change non-key fields of the \p item.
2462
2463             Returns std::pair<bool, bool> where \p first is \p true if operation is successful,
2464             i.e. the node has been inserted or updated,
2465             \p second is \p true if new item has been added or \p false if the item with \p key
2466             already exists.
2467         */
2468         template <typename Func>
2469         std::pair<bool, bool> update( value_type& val, Func func, bool bAllowInsert = true )
2470         {
2471             hash_array arrHash;
2472             position arrPos[ c_nArity ];
2473             unsigned int nGoalTable;
2474
2475             hashing( arrHash, val );
2476             node_type * pNode = node_traits::to_node_ptr( val );
2477             store_hash( pNode, arrHash );
2478
2479             while (true) {
2480                 {
2481                     scoped_cell_lock guard( m_MutexPolicy, arrHash );
2482
2483                     unsigned int nTable = contains( arrPos, arrHash, val, key_predicate());
2484                     if ( nTable != c_nUndefTable ) {
2485                         func( false, *node_traits::to_value_ptr( *arrPos[nTable].itFound ), val );
2486                         m_Stat.onUpdateExist();
2487                         return std::make_pair( true, false );
2488                     }
2489
2490                     if ( !bAllowInsert )
2491                         return std::make_pair( false, false );
2492
2493                     //node_type * pNode = node_traits::to_node_ptr( val );
2494                     //store_hash( pNode, arrHash );
2495
2496                     for ( unsigned int i = 0; i < c_nArity; ++i ) {
2497                         bucket_entry& refBucket = bucket( i, arrHash[i] );
2498                         if ( refBucket.size() < m_nProbesetThreshold ) {
2499                             refBucket.insert_after( arrPos[i].itPrev, pNode );
2500                             func( true, val, val );
2501                             ++m_ItemCounter;
2502                             m_Stat.onUpdateSuccess();
2503                             return std::make_pair( true, true );
2504                         }
2505                     }
2506
2507                     for ( unsigned int i = 0; i < c_nArity; ++i ) {
2508                         bucket_entry& refBucket = bucket( i, arrHash[i] );
2509                         if ( refBucket.size() < m_nProbesetSize ) {
2510                             refBucket.insert_after( arrPos[i].itPrev, pNode );
2511                             func( true, val, val );
2512                             ++m_ItemCounter;
2513                             nGoalTable = i;
2514                             assert( refBucket.size() > 1 );
2515                             copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()));
2516                             goto do_relocate;
2517                         }
2518                     }
2519                 }
2520
2521                 m_Stat.onUpdateResize();
2522                 resize();
2523             }
2524
2525         do_relocate:
2526             m_Stat.onUpdateRelocate();
2527             if ( !relocate( nGoalTable, arrHash )) {
2528                 m_Stat.onUpdateRelocateFault();
2529                 m_Stat.onUpdateResize();
2530                 resize();
2531             }
2532
2533             m_Stat.onUpdateSuccess();
2534             return std::make_pair( true, true );
2535         }
2536         //@cond
2537         template <typename Func>
2538         CDS_DEPRECATED("ensure() is deprecated, use update()")
2539         std::pair<bool, bool> ensure( value_type& val, Func func )
2540         {
2541             return update( val, func, true );
2542         }
2543         //@endcond
2544
2545         /// Unlink the item \p val from the set
2546         /**
2547             The function searches the item \p val in the set and unlink it
2548             if it is found and is equal to \p val (here, the equality means that
2549             \p val belongs to the set: if \p item is an item found then
2550             unlink is successful iif <tt>&val == &item</tt>)
2551
2552             The function returns \p true if success and \p false otherwise.
2553         */
2554         bool unlink( value_type& val )
2555         {
2556             hash_array arrHash;
2557             hashing( arrHash, val );
2558             position arrPos[ c_nArity ];
2559
2560             {
2561                 scoped_cell_lock guard( m_MutexPolicy, arrHash );
2562
2563                 unsigned int nTable = contains( arrPos, arrHash, val, key_predicate());
2564                 if ( nTable != c_nUndefTable && node_traits::to_value_ptr(*arrPos[nTable].itFound) == &val ) {
2565                     bucket( nTable, arrHash[nTable]).remove( arrPos[nTable].itPrev, arrPos[nTable].itFound );
2566                     --m_ItemCounter;
2567                     m_Stat.onUnlinkSuccess();
2568                     return true;
2569                 }
2570             }
2571
2572             m_Stat.onUnlinkFailed();
2573             return false;
2574         }
2575
2576         /// Deletes the item from the set
2577         /** \anchor cds_intrusive_CuckooSet_erase
2578             The function searches an item with key equal to \p val in the set,
2579             unlinks it from the set, and returns a pointer to unlinked item.
2580
2581             If the item with key equal to \p val is not found the function return \p nullptr.
2582
2583             Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type.
2584         */
2585         template <typename Q>
2586         value_type * erase( Q const& val )
2587         {
2588             return erase( val, [](value_type const&) {} );
2589         }
2590
2591         /// Deletes the item from the set using \p pred predicate for searching
2592         /**
2593             The function is an analog of \ref cds_intrusive_CuckooSet_erase "erase(Q const&)"
2594             but \p pred is used for key comparing.
2595             If cuckoo set is ordered, then \p Predicate should have the interface and semantics like \p std::less.
2596             If cuckoo set is unordered, then \p Predicate should have the interface and semantics like \p std::equal_to.
2597             \p Predicate must imply the same element order as the comparator used for building the set.
2598         */
2599         template <typename Q, typename Predicate>
2600         value_type * erase_with( Q const& val, Predicate pred )
2601         {
2602             CDS_UNUSED( pred );
2603             return erase_( val, typename predicate_wrapper<Predicate>::type(), [](value_type const&) {} );
2604         }
2605
2606         /// Delete the item from the set
2607         /** \anchor cds_intrusive_CuckooSet_erase_func
2608             The function searches an item with key equal to \p val in the set,
2609             call \p f functor with item found, unlinks it from the set, and returns a pointer to unlinked item.
2610
2611             The \p Func interface is
2612             \code
2613             struct functor {
2614                 void operator()( value_type const& item );
2615             };
2616             \endcode
2617
2618             If the item with key equal to \p val is not found the function return \p nullptr.
2619
2620             Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type.
2621         */
2622         template <typename Q, typename Func>
2623         value_type * erase( Q const& val, Func f )
2624         {
2625             return erase_( val, key_predicate(), f );
2626         }
2627
2628         /// Deletes the item from the set using \p pred predicate for searching
2629         /**
2630             The function is an analog of \ref cds_intrusive_CuckooSet_erase_func "erase(Q const&, Func)"
2631             but \p pred is used for key comparing.
2632             If you use ordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::less.
2633             If you use unordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::equal_to.
2634             \p Predicate must imply the same element order as the comparator used for building the set.
2635         */
2636         template <typename Q, typename Predicate, typename Func>
2637         value_type * erase_with( Q const& val, Predicate pred, Func f )
2638         {
2639             CDS_UNUSED( pred );
2640             return erase_( val, typename predicate_wrapper<Predicate>::type(), f );
2641         }
2642
2643         /// Find the key \p val
2644         /** \anchor cds_intrusive_CuckooSet_find_func
2645             The function searches the item with key equal to \p val and calls the functor \p f for item found.
2646             The interface of \p Func functor is:
2647             \code
2648             struct functor {
2649                 void operator()( value_type& item, Q& val );
2650             };
2651             \endcode
2652             where \p item is the item found, \p val is the <tt>find</tt> function argument.
2653
2654             The functor may change non-key fields of \p item.
2655
2656             The \p val argument is non-const since it can be used as \p f functor destination i.e., the functor
2657             may modify both arguments.
2658
2659             Note the hash functor specified for class \p Traits template parameter
2660             should accept a parameter of type \p Q that can be not the same as \p value_type.
2661
2662             The function returns \p true if \p val is found, \p false otherwise.
2663         */
2664         template <typename Q, typename Func>
2665         bool find( Q& val, Func f )
2666         {
2667             return find_( val, key_predicate(), f );
2668         }
2669         //@cond
2670         template <typename Q, typename Func>
2671         bool find( Q const& val, Func f )
2672         {
2673             return find_( val, key_predicate(), f );
2674         }
2675         //@endcond
2676
2677         /// Find the key \p val using \p pred predicate for comparing
2678         /**
2679             The function is an analog of \ref cds_intrusive_CuckooSet_find_func "find(Q&, Func)"
2680             but \p pred is used for key comparison.
2681             If you use ordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::less.
2682             If you use unordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::equal_to.
2683             \p pred must imply the same element order as the comparator used for building the set.
2684         */
2685         template <typename Q, typename Predicate, typename Func>
2686         bool find_with( Q& val, Predicate pred, Func f )
2687         {
2688             CDS_UNUSED( pred );
2689             return find_( val, typename predicate_wrapper<Predicate>::type(), f );
2690         }
2691         //@cond
2692         template <typename Q, typename Predicate, typename Func>
2693         bool find_with( Q const& val, Predicate pred, Func f )
2694         {
2695             CDS_UNUSED( pred );
2696             return find_( val, typename predicate_wrapper<Predicate>::type(), f );
2697         }
2698         //@endcond
2699
2700         /// Checks whether the set contains \p key
2701         /**
2702             The function searches the item with key equal to \p key
2703             and returns \p true if it is found, and \p false otherwise.
2704         */
2705         template <typename Q>
2706         bool contains( Q const& key )
2707         {
2708             return find( key, [](value_type&, Q const& ) {} );
2709         }
2710         //@cond
2711         template <typename Q>
2712         CDS_DEPRECATED("deprecated, use contains()")
2713         bool find( Q const& key )
2714         {
2715             return contains( key );
2716         }
2717         //@endcond
2718
2719         /// Checks whether the set contains \p key using \p pred predicate for searching
2720         /**
2721             The function is similar to <tt>contains( key )</tt> but \p pred is used for key comparing.
2722             If the set is unordered, \p Predicate has semantics like \p std::equal_to.
2723             For ordered set \p Predicate has \p std::less semantics. In that case \p pred
2724             must imply the same element order as the comparator used for building the set.
2725         */
2726         template <typename Q, typename Predicate>
2727         bool contains( Q const& key, Predicate pred )
2728         {
2729             CDS_UNUSED( pred );
2730             return find_with( key, typename predicate_wrapper<Predicate>::type(), [](value_type& , Q const& ) {} );
2731         }
2732         //@cond
2733         template <typename Q, typename Predicate>
2734         CDS_DEPRECATED("deprecated, use contains()")
2735         bool find_with( Q const& key, Predicate pred )
2736         {
2737             return contains( key, pred );
2738         }
2739         //@endcond
2740
2741         /// Clears the set
2742         /**
2743             The function unlinks all items from the set.
2744             For any item \p Traits::disposer is called
2745         */
2746         void clear()
2747         {
2748             clear_and_dispose( disposer());
2749         }
2750
2751         /// Clears the set and calls \p disposer for each item
2752         /**
2753             The function unlinks all items from the set calling \p oDisposer for each item.
2754             \p Disposer functor interface is:
2755             \code
2756             struct Disposer{
2757                 void operator()( value_type * p );
2758             };
2759             \endcode
2760
2761             The \p Traits::disposer is not called.
2762         */
2763         template <typename Disposer>
2764         void clear_and_dispose( Disposer oDisposer )
2765         {
2766             // locks entire array
2767             scoped_full_lock sl( m_MutexPolicy );
2768
2769             for ( unsigned int i = 0; i < c_nArity; ++i ) {
2770                 bucket_entry * pEntry = m_BucketTable[i];
2771                 bucket_entry * pEnd = pEntry + m_nBucketMask.load( atomics::memory_order_relaxed ) + 1;
2772                 for ( ; pEntry != pEnd ; ++pEntry ) {
2773                     pEntry->clear( [&oDisposer]( node_type * pNode ){ oDisposer( node_traits::to_value_ptr( pNode )) ; } );
2774                 }
2775             }
2776             m_ItemCounter.reset();
2777         }
2778
2779         /// Checks if the set is empty
2780         /**
2781             Emptiness is checked by item counting: if item count is zero then the set is empty.
2782         */
2783         bool empty() const
2784         {
2785             return size() == 0;
2786         }
2787
2788         /// Returns item count in the set
2789         size_t size() const
2790         {
2791             return m_ItemCounter;
2792         }
2793
2794         /// Returns the size of hash table
2795         /**
2796             The hash table size is non-constant and can be increased via resizing.
2797         */
2798         size_t bucket_count() const
2799         {
2800             return m_nBucketMask.load( atomics::memory_order_relaxed ) + 1;
2801         }
2802         //@cond
2803         size_t bucket_count( atomics::memory_order load_mo ) const
2804         {
2805             return m_nBucketMask.load( load_mo ) + 1;
2806         }
2807         //@endcond
2808
2809         /// Returns lock array size
2810         size_t lock_count() const
2811         {
2812             return m_MutexPolicy.lock_count();
2813         }
2814
2815         /// Returns const reference to internal statistics
2816         stat const& statistics() const
2817         {
2818             return m_Stat;
2819         }
2820
2821         /// Returns const reference to mutex policy internal statistics
2822         typename mutex_policy::statistics_type const& mutex_policy_statistics() const
2823         {
2824             return m_MutexPolicy.statistics();
2825         }
2826     };
2827 }} // namespace cds::intrusive
2828
2829 #endif // #ifndef CDSLIB_INTRUSIVE_CUCKOO_SET_H