fixed adding file problem
[c11concurrency-benchmarks.git] / gdax-orderbook-hpp / demo / dependencies / libcds-2.3.2 / cds / intrusive / cuckoo_set.h
diff --git a/gdax-orderbook-hpp/demo/dependencies/libcds-2.3.2/cds/intrusive/cuckoo_set.h b/gdax-orderbook-hpp/demo/dependencies/libcds-2.3.2/cds/intrusive/cuckoo_set.h
new file mode 100644 (file)
index 0000000..6ad0ad7
--- /dev/null
@@ -0,0 +1,2829 @@
+/*
+    This file is a part of libcds - Concurrent Data Structures library
+
+    (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017
+
+    Source code repo: http://github.com/khizmax/libcds/
+    Download: http://sourceforge.net/projects/libcds/files/
+
+    Redistribution and use in source and binary forms, with or without
+    modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this
+      list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+    SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+    OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef CDSLIB_INTRUSIVE_CUCKOO_SET_H
+#define CDSLIB_INTRUSIVE_CUCKOO_SET_H
+
+#include <memory>
+#include <type_traits>
+#include <mutex>
+#include <functional>   // ref
+#include <cds/intrusive/details/base.h>
+#include <cds/opt/compare.h>
+#include <cds/opt/hash.h>
+#include <cds/sync/lock_array.h>
+#include <cds/os/thread.h>
+#include <cds/sync/spinlock.h>
+
+
+namespace cds { namespace intrusive {
+
+    /// CuckooSet-related definitions
+    namespace cuckoo {
+        /// Option to define probeset type
+        /**
+            The option specifies probeset type for the CuckooSet.
+            Available values:
+            - \p cds::intrusive::cuckoo::list - the probeset is a single-linked list.
+                The node contains pointer to next node in probeset.
+            - \p cds::intrusive::cuckoo::vector<Capacity> - the probeset is a vector
+                with constant-size \p Capacity where \p Capacity is an <tt>unsigned int</tt> constant.
+                The node does not contain any auxiliary data.
+        */
+        template <typename Type>
+        struct probeset_type
+        {
+            //@cond
+            template <typename Base>
+            struct pack: public Base {
+                typedef Type    probeset_type;
+            };
+            //@endcond
+        };
+
+        /// Option specifying whether to store hash values in the node
+        /**
+             This option reserves additional space in the hook to store the hash value of the object once it's introduced in the container.
+             When this option is used, the unordered container will store the calculated hash value in the hook and rehashing operations won't need
+             to recalculate the hash of the value. This option will improve the performance of unordered containers
+             when rehashing is frequent or hashing the value is a slow operation
+
+             The \p Count template parameter defines the size of hash array. Remember that cuckoo hashing implies at least two
+             hash values per item.
+
+             Possible values of \p Count:
+             - 0 - no hash storing in the node
+             - greater that 1 - store hash values.
+
+             Value 1 is deprecated.
+        */
+        template <unsigned int Count>
+        struct store_hash
+        {
+            //@cond
+            template <typename Base>
+            struct pack: public Base {
+                static unsigned int const store_hash = Count;
+            };
+            //@endcond
+        };
+
+
+        //@cond
+        // Probeset type placeholders
+        struct list_probeset_class;
+        struct vector_probeset_class;
+        //@endcond
+
+        //@cond
+        /// List probeset type
+        struct list;
+        //@endcond
+
+        /// Vector probeset type
+        template <unsigned int Capacity>
+        struct vector
+        {
+            /// Vector capacity
+            static unsigned int const c_nCapacity = Capacity;
+        };
+
+        /// CuckooSet node
+        /**
+            Template arguments:
+            - \p ProbesetType - type of probeset. Can be \p cds::intrusive::cuckoo::list
+                or \p cds::intrusive::cuckoo::vector<Capacity>.
+            - \p StoreHashCount - constant that defines whether to store node hash values.
+                See cuckoo::store_hash option for explanation
+            - \p Tag - a \ref cds_intrusive_hook_tag "tag"
+        */
+        template <typename ProbesetType = cuckoo::list, unsigned int StoreHashCount = 0, typename Tag = opt::none>
+        struct node
+#ifdef CDS_DOXYGEN_INVOKED
+        {
+            typedef ProbesetType    probeset_type   ;   ///< Probeset type
+            typedef Tag             tag             ;   ///< Tag
+            static unsigned int const hash_array_size = StoreHashCount ;    ///< The size of hash array
+        }
+#endif
+;
+
+        //@cond
+        template <typename Tag>
+        struct node< cuckoo::list, 0, Tag>
+        {
+            typedef list_probeset_class probeset_class;
+            typedef cuckoo::list        probeset_type;
+            typedef Tag                 tag;
+            static unsigned int const hash_array_size = 0;
+            static unsigned int const probeset_size = 0;
+
+            node *  m_pNext;
+
+            constexpr node() noexcept
+                : m_pNext( nullptr )
+            {}
+
+            void store_hash( size_t const* )
+            {}
+
+            size_t * get_hash() const
+            {
+                // This node type does not store hash values!!!
+                assert(false);
+                return nullptr;
+            }
+
+            void clear()
+            {
+                m_pNext = nullptr;
+            }
+        };
+
+        template <unsigned int StoreHashCount, typename Tag>
+        struct node< cuckoo::list, StoreHashCount, Tag>
+        {
+            typedef list_probeset_class probeset_class;
+            typedef cuckoo::list        probeset_type;
+            typedef Tag                 tag;
+            static unsigned int const hash_array_size = StoreHashCount;
+            static unsigned int const probeset_size = 0;
+
+            node *  m_pNext;
+            size_t  m_arrHash[ hash_array_size ];
+
+            node() noexcept
+                : m_pNext( nullptr )
+            {
+                memset( m_arrHash, 0, sizeof(m_arrHash));
+            }
+
+            void store_hash( size_t const* pHashes )
+            {
+                memcpy( m_arrHash, pHashes, sizeof( m_arrHash ));
+            }
+
+            size_t * get_hash() const
+            {
+                return const_cast<size_t *>( m_arrHash );
+            }
+
+            void clear()
+            {
+                m_pNext = nullptr;
+            }
+        };
+
+        template <unsigned int VectorSize, typename Tag>
+        struct node< cuckoo::vector<VectorSize>, 0, Tag>
+        {
+            typedef vector_probeset_class       probeset_class;
+            typedef cuckoo::vector<VectorSize>  probeset_type;
+            typedef Tag                         tag;
+            static unsigned int const hash_array_size = 0;
+            static unsigned int const probeset_size = probeset_type::c_nCapacity;
+
+            node() noexcept
+            {}
+
+            void store_hash( size_t const* )
+            {}
+
+            size_t * get_hash() const
+            {
+                // This node type does not store hash values!!!
+                assert(false);
+                return nullptr;
+            }
+
+            void clear()
+            {}
+        };
+
+        template <unsigned int VectorSize, unsigned int StoreHashCount, typename Tag>
+        struct node< cuckoo::vector<VectorSize>, StoreHashCount, Tag>
+        {
+            typedef vector_probeset_class       probeset_class;
+            typedef cuckoo::vector<VectorSize>  probeset_type;
+            typedef Tag                         tag;
+            static unsigned int const hash_array_size = StoreHashCount;
+            static unsigned int const probeset_size = probeset_type::c_nCapacity;
+
+            size_t  m_arrHash[ hash_array_size ];
+
+            node() noexcept
+            {
+                memset( m_arrHash, 0, sizeof(m_arrHash));
+            }
+
+            void store_hash( size_t const* pHashes )
+            {
+                memcpy( m_arrHash, pHashes, sizeof( m_arrHash ));
+            }
+
+            size_t * get_hash() const
+            {
+                return const_cast<size_t *>( m_arrHash );
+            }
+
+            void clear()
+            {}
+        };
+        //@endcond
+
+
+        //@cond
+        struct default_hook {
+            typedef cuckoo::list probeset_type;
+            static unsigned int const store_hash = 0;
+            typedef opt::none   tag;
+        };
+
+        template < typename HookType, typename... Options>
+        struct hook
+        {
+            typedef typename opt::make_options< default_hook, Options...>::type  traits;
+
+            typedef typename traits::probeset_type probeset_type;
+            typedef typename traits::tag tag;
+            static unsigned int const store_hash = traits::store_hash;
+
+            typedef node<probeset_type, store_hash, tag>   node_type;
+
+            typedef HookType        hook_type;
+        };
+        //@endcond
+
+        /// Base hook
+        /**
+            \p Options are:
+            - \p cuckoo::probeset_type - probeset type. Defaul is \p cuckoo::list
+            - \p cuckoo::store_hash - store hash values in the node or not. Default is 0 (no storing)
+            - \p opt::tag - a \ref cds_intrusive_hook_tag "tag"
+        */
+        template < typename... Options >
+        struct base_hook: public hook< opt::base_hook_tag, Options... >
+        {};
+
+        /// Member hook
+        /**
+            \p MemberOffset defines offset in bytes of \ref node member into your structure.
+            Use \p offsetof macro to define \p MemberOffset
+
+            \p Options are:
+            - \p cuckoo::probeset_type - probeset type. Defaul is \p cuckoo::list
+            - \p cuckoo::store_hash - store hash values in the node or not. Default is 0 (no storing)
+            - \p opt::tag - a \ref cds_intrusive_hook_tag "tag"
+        */
+        template < size_t MemberOffset, typename... Options >
+        struct member_hook: public hook< opt::member_hook_tag, Options... >
+        {
+            //@cond
+            static const size_t c_nMemberOffset = MemberOffset;
+            //@endcond
+        };
+
+        /// Traits hook
+        /**
+            \p NodeTraits defines type traits for node.
+            See \ref node_traits for \p NodeTraits interface description
+
+            \p Options are:
+            - \p cuckoo::probeset_type - probeset type. Defaul is \p cuckoo::list
+            - \p cuckoo::store_hash - store hash values in the node or not. Default is 0 (no storing)
+            - \p opt::tag - a \ref cds_intrusive_hook_tag "tag"
+        */
+        template <typename NodeTraits, typename... Options >
+        struct traits_hook: public hook< opt::traits_hook_tag, Options... >
+        {
+            //@cond
+            typedef NodeTraits node_traits;
+            //@endcond
+        };
+
+        /// Internal statistics for \ref striping mutex policy
+        struct striping_stat {
+            typedef cds::atomicity::event_counter   counter_type;   ///< Counter type
+
+            counter_type   m_nCellLockCount    ;  ///< Count of obtaining cell lock
+            counter_type   m_nCellTryLockCount ;  ///< Count of cell \p try_lock attempts
+            counter_type   m_nFullLockCount    ;  ///< Count of obtaining full lock
+            counter_type   m_nResizeLockCount  ;  ///< Count of obtaining resize lock
+            counter_type   m_nResizeCount      ;  ///< Count of resize event
+
+            //@cond
+            void    onCellLock()    { ++m_nCellLockCount; }
+            void    onCellTryLock() { ++m_nCellTryLockCount; }
+            void    onFullLock()    { ++m_nFullLockCount; }
+            void    onResizeLock()  { ++m_nResizeLockCount;   }
+            void    onResize()      { ++m_nResizeCount; }
+            //@endcond
+        };
+
+        /// Dummy internal statistics for \ref striping mutex policy
+        struct empty_striping_stat {
+            //@cond
+            void    onCellLock()    const {}
+            void    onCellTryLock() const {}
+            void    onFullLock()    const {}
+            void    onResizeLock()  const {}
+            void    onResize()      const {}
+            //@endcond
+        };
+
+        /// Lock striping concurrent access policy
+        /**
+            This is one of available opt::mutex_policy option type for CuckooSet
+
+            Lock striping is very simple technique.
+            The cuckoo set consists of the bucket tables and the array of locks.
+            There is single lock array for each bucket table, at least, the count of bucket table is 2.
+            Initially, the capacity of lock array and each bucket table is the same.
+            When set is resized, bucket table capacity will be doubled but lock array will not.
+            The lock \p i protects each bucket \p j, where <tt> j = i mod L </tt>,
+            where \p L - the size of lock array.
+
+            The policy contains an internal array of \p RecursiveLock locks.
+
+            Template arguments:
+            - \p RecursiveLock - the type of recursive mutex. The default is \p std::recursive_mutex. The mutex type should be default-constructible.
+                Note that a recursive spin-lock is not suitable for lock striping for performance reason.
+            - \p Arity - unsigned int constant that specifies an arity. The arity is the count of hash functors, i.e., the
+                count of lock arrays. Default value is 2.
+            - \p Alloc - allocator type used for lock array memory allocation. Default is \p CDS_DEFAULT_ALLOCATOR.
+            - \p Stat - internal statistics type. Note that this template argument is automatically selected by \ref CuckooSet
+                class according to its \p opt::stat option.
+        */
+        template <
+            class RecursiveLock = std::recursive_mutex,
+            unsigned int Arity = 2,
+            class Alloc = CDS_DEFAULT_ALLOCATOR,
+            class Stat = empty_striping_stat
+        >
+        class striping
+        {
+        public:
+            typedef RecursiveLock   lock_type       ;   ///< lock type
+            typedef Alloc           allocator_type  ;   ///< allocator type
+            static unsigned int const c_nArity = Arity ;    ///< the arity
+            typedef Stat            statistics_type ;   ///< Internal statistics type (\ref striping_stat or \ref empty_striping_stat)
+
+            //@cond
+            typedef striping_stat       real_stat;
+            typedef empty_striping_stat empty_stat;
+
+            template <typename Stat2>
+            struct rebind_statistics {
+                typedef striping<lock_type, c_nArity, allocator_type, Stat2> other;
+            };
+            //@endcond
+
+            typedef cds::sync::lock_array< lock_type, cds::sync::pow2_select_policy, allocator_type >    lock_array_type ;   ///< lock array type
+
+        protected:
+            //@cond
+            class lock_array: public lock_array_type
+            {
+            public:
+                // placeholder ctor
+                lock_array(): lock_array_type( typename lock_array_type::select_cell_policy(2)) {}
+
+                // real ctor
+                lock_array( size_t nCapacity ): lock_array_type( nCapacity, typename lock_array_type::select_cell_policy(nCapacity)) {}
+            };
+
+            class scoped_lock: public std::unique_lock< lock_array_type >
+            {
+                typedef std::unique_lock< lock_array_type > base_class;
+            public:
+                scoped_lock( lock_array& arrLock, size_t nHash ): base_class( arrLock, nHash ) {}
+            };
+            //@endcond
+
+        protected:
+            //@cond
+            lock_array      m_Locks[c_nArity] ;   ///< array of \p lock_array_type
+            statistics_type m_Stat              ; ///< internal statistics
+            //@endcond
+
+        public:
+            //@cond
+            class scoped_cell_lock {
+                lock_type * m_guard[c_nArity];
+
+            public:
+                scoped_cell_lock( striping& policy, size_t const* arrHash )
+                {
+                    for ( unsigned int i = 0; i < c_nArity; ++i ) {
+                        m_guard[i] = &( policy.m_Locks[i].at( policy.m_Locks[i].lock( arrHash[i] )));
+                    }
+                    policy.m_Stat.onCellLock();
+                }
+
+                ~scoped_cell_lock()
+                {
+                    for ( unsigned int i = 0; i < c_nArity; ++i )
+                        m_guard[i]->unlock();
+                }
+            };
+
+            class scoped_cell_trylock
+            {
+                typedef typename lock_array_type::lock_type lock_type;
+
+                lock_type * m_guard[c_nArity];
+                bool        m_bLocked;
+
+            public:
+                scoped_cell_trylock( striping& policy, size_t const* arrHash )
+                {
+                    size_t nCell = policy.m_Locks[0].try_lock( arrHash[0] );
+                    m_bLocked = nCell != lock_array_type::c_nUnspecifiedCell;
+                    if ( m_bLocked ) {
+                        m_guard[0] = &(policy.m_Locks[0].at(nCell));
+                        for ( unsigned int i = 1; i < c_nArity; ++i ) {
+                            m_guard[i] = &( policy.m_Locks[i].at( policy.m_Locks[i].lock( arrHash[i] )));
+                        }
+                    }
+                    else {
+                        std::fill( m_guard, m_guard + c_nArity, nullptr );
+                    }
+                    policy.m_Stat.onCellTryLock();
+                }
+                ~scoped_cell_trylock()
+                {
+                    if ( m_bLocked ) {
+                        for ( unsigned int i = 0; i < c_nArity; ++i )
+                            m_guard[i]->unlock();
+                    }
+                }
+
+                bool locked() const
+                {
+                    return m_bLocked;
+                }
+            };
+
+            class scoped_full_lock {
+                std::unique_lock< lock_array_type >   m_guard;
+            public:
+                scoped_full_lock( striping& policy )
+                    : m_guard( policy.m_Locks[0] )
+                {
+                    policy.m_Stat.onFullLock();
+                }
+
+                /// Ctor for scoped_resize_lock - no statistics is incremented
+                scoped_full_lock( striping& policy, bool )
+                    : m_guard( policy.m_Locks[0] )
+                {}
+            };
+
+            class scoped_resize_lock: public scoped_full_lock {
+            public:
+                scoped_resize_lock( striping& policy )
+                    : scoped_full_lock( policy, false )
+                {
+                    policy.m_Stat.onResizeLock();
+                }
+            };
+            //@endcond
+
+        public:
+            /// Constructor
+            striping(
+                size_t nLockCount          ///< The size of lock array. Must be power of two.
+            )
+            {
+                // Trick: initialize the array of locks
+                for ( unsigned int i = 0; i < c_nArity; ++i ) {
+                    lock_array * pArr = m_Locks + i;
+                    pArr->lock_array::~lock_array();
+                    new ( pArr ) lock_array( nLockCount );
+                }
+            }
+
+            /// Returns lock array size
+            /**
+                Lock array size is unchanged during \p striping object lifetime
+            */
+            size_t lock_count() const
+            {
+                return m_Locks[0].size();
+            }
+
+            //@cond
+            void resize( size_t )
+            {
+                m_Stat.onResize();
+            }
+            //@endcond
+
+            /// Returns the arity of striping mutex policy
+            constexpr unsigned int arity() const noexcept
+            {
+                return c_nArity;
+            }
+
+            /// Returns internal statistics
+            statistics_type const& statistics() const
+            {
+                return m_Stat;
+            }
+        };
+
+        /// Internal statistics for \ref refinable mutex policy
+        struct refinable_stat {
+            typedef cds::atomicity::event_counter   counter_type    ;   ///< Counter type
+
+            counter_type   m_nCellLockCount         ;   ///< Count of obtaining cell lock
+            counter_type   m_nCellLockWaitResizing  ;   ///< Count of loop iteration to wait for resizing
+            counter_type   m_nCellLockArrayChanged  ;   ///< Count of event "Lock array has been changed when obtaining cell lock"
+            counter_type   m_nCellLockFailed        ;   ///< Count of event "Cell lock failed because of the array is owned by other thread"
+
+            counter_type   m_nSecondCellLockCount   ;   ///< Count of obtaining cell lock when another cell is already locked
+            counter_type   m_nSecondCellLockFailed  ;   ///< Count of unsuccess obtaining cell lock when another cell is already locked
+
+            counter_type   m_nFullLockCount         ;   ///< Count of obtaining full lock
+            counter_type   m_nFullLockIter          ;   ///< Count of unsuccessfull iteration to obtain full lock
+
+            counter_type   m_nResizeLockCount       ;   ///< Count of obtaining resize lock
+            counter_type   m_nResizeLockIter        ;   ///< Count of unsuccessfull iteration to obtain resize lock
+            counter_type   m_nResizeLockArrayChanged;   ///< Count of event "Lock array has been changed when obtaining resize lock"
+            counter_type   m_nResizeCount           ;   ///< Count of resize event
+
+            //@cond
+            void    onCellLock()    { ++m_nCellLockCount; }
+            void    onCellWaitResizing() { ++m_nCellLockWaitResizing; }
+            void    onCellArrayChanged() { ++m_nCellLockArrayChanged; }
+            void    onCellLockFailed()   { ++m_nCellLockFailed; }
+            void    onSecondCellLock()   { ++m_nSecondCellLockCount; }
+            void    onSecondCellLockFailed() { ++m_nSecondCellLockFailed; }
+            void    onFullLock()         { ++m_nFullLockCount; }
+            void    onFullLockIter()     { ++m_nFullLockIter; }
+            void    onResizeLock()       { ++m_nResizeLockCount;   }
+            void    onResizeLockIter()   { ++m_nResizeLockIter; }
+            void    onResizeLockArrayChanged() { ++m_nResizeLockArrayChanged; }
+            void    onResize()           { ++m_nResizeCount; }
+            //@endcond
+        };
+
+        /// Dummy internal statistics for \ref refinable mutex policy
+        struct empty_refinable_stat {
+            //@cond
+            void    onCellLock()            const {}
+            void    onCellWaitResizing()    const {}
+            void    onCellArrayChanged()    const {}
+            void    onCellLockFailed()      const {}
+            void    onSecondCellLock()      const {}
+            void    onSecondCellLockFailed() const {}
+            void    onFullLock()            const {}
+            void    onFullLockIter()        const {}
+            void    onResizeLock()          const {}
+            void    onResizeLockIter()      const {}
+            void    onResizeLockArrayChanged() const {}
+            void    onResize()              const {}
+            //@endcond
+        };
+
+        /// Refinable concurrent access policy
+        /**
+            This is one of available \p opt::mutex_policy option type for \p CuckooSet
+
+            Refining is like a striping technique (see \p cuckoo::striping)
+            but it allows growing the size of lock array when resizing the hash table.
+            So, the sizes of hash table and lock array are equal.
+
+            Template arguments:
+            - \p RecursiveLock - the type of mutex. Reentrant (recursive) mutex is required.
+                The default is \p std::recursive_mutex. The mutex type should be default-constructible.
+            - \p Arity - unsigned int constant that specifies an arity. The arity is the count of hash functors, i.e., the
+                count of lock arrays. Default value is 2.
+            - \p BackOff - back-off strategy. Default is \p cds::backoff::Default
+            - \p Alloc - allocator type used for lock array memory allocation. Default is \p CDS_DEFAULT_ALLOCATOR.
+            - \p Stat - internal statistics type. Note that this template argument is automatically selected by \ref CuckooSet
+                class according to its \p opt::stat option.
+        */
+        template <
+            class RecursiveLock = std::recursive_mutex,
+            unsigned int Arity = 2,
+            typename BackOff = cds::backoff::Default,
+            class Alloc = CDS_DEFAULT_ALLOCATOR,
+            class Stat = empty_refinable_stat
+        >
+        class refinable
+        {
+        public:
+            typedef RecursiveLock   lock_type       ;   ///< lock type
+            typedef Alloc           allocator_type  ;   ///< allocator type
+            typedef BackOff         back_off        ;   ///< back-off strategy
+            typedef Stat            statistics_type ;   ///< internal statistics type
+            static unsigned int const c_nArity = Arity; ///< the arity
+
+            //@cond
+            typedef refinable_stat          real_stat;
+            typedef empty_refinable_stat    empty_stat;
+
+            template <typename Stat2>
+            struct rebind_statistics {
+                typedef refinable< lock_type, c_nArity, back_off, allocator_type, Stat2>    other;
+            };
+            //@endcond
+
+        protected:
+            //@cond
+            typedef cds::sync::trivial_select_policy  lock_selection_policy;
+
+            class lock_array_type
+                : public cds::sync::lock_array< lock_type, lock_selection_policy, allocator_type >
+                , public std::enable_shared_from_this< lock_array_type >
+            {
+                typedef cds::sync::lock_array< lock_type, lock_selection_policy, allocator_type >    lock_array_base;
+            public:
+                lock_array_type( size_t nCapacity )
+                    : lock_array_base( nCapacity )
+                {}
+            };
+            typedef std::shared_ptr< lock_array_type >  lock_array_ptr;
+            typedef cds::details::Allocator< lock_array_type, allocator_type >  lock_array_allocator;
+
+            typedef unsigned long long  owner_t;
+            typedef cds::OS::ThreadId   threadId_t;
+
+            typedef cds::sync::spin     spinlock_type;
+            typedef std::unique_lock< spinlock_type > scoped_spinlock;
+            //@endcond
+
+        protected:
+            //@cond
+            static owner_t const c_nOwnerMask = (((owner_t) 1) << (sizeof(owner_t) * 8 - 1)) - 1;
+
+            atomics::atomic< owner_t >   m_Owner     ;   ///< owner mark (thread id + boolean flag)
+            atomics::atomic<size_t>      m_nCapacity ;   ///< lock array capacity
+            lock_array_ptr               m_arrLocks[ c_nArity ]  ; ///< Lock array. The capacity of array is specified in constructor.
+            spinlock_type                m_access    ;   ///< access to m_arrLocks
+            statistics_type              m_Stat      ;   ///< internal statistics
+            //@endcond
+
+        protected:
+            //@cond
+            struct lock_array_disposer {
+                void operator()( lock_array_type * pArr )
+                {
+                    // Seems, there is a false positive in std::shared_ptr deallocation in uninstrumented libc++
+                    // see, for example, https://groups.google.com/forum/#!topic/thread-sanitizer/eHu4dE_z7Cc
+                    // https://reviews.llvm.org/D21609
+                    CDS_TSAN_ANNOTATE_IGNORE_WRITES_BEGIN;
+                    lock_array_allocator().Delete( pArr );
+                    CDS_TSAN_ANNOTATE_IGNORE_WRITES_END;
+                }
+            };
+
+            lock_array_ptr create_lock_array( size_t nCapacity )
+            {
+                return lock_array_ptr( lock_array_allocator().New( nCapacity ), lock_array_disposer());
+            }
+
+            void acquire( size_t const * arrHash, lock_array_ptr * pLockArr, lock_type ** parrLock )
+            {
+                owner_t me = (owner_t) cds::OS::get_current_thread_id();
+                owner_t who;
+                size_t cur_capacity;
+
+                back_off bkoff;
+                while ( true ) {
+
+                    {
+                        scoped_spinlock sl(m_access);
+                        for ( unsigned int i = 0; i < c_nArity; ++i )
+                            pLockArr[i] = m_arrLocks[i];
+                        cur_capacity = m_nCapacity.load( atomics::memory_order_acquire );
+                    }
+
+                    // wait while resizing
+                    while ( true ) {
+                        who = m_Owner.load( atomics::memory_order_acquire );
+                        if ( !( who & 1 ) || (who >> 1) == (me & c_nOwnerMask))
+                            break;
+                        bkoff();
+                        m_Stat.onCellWaitResizing();
+                    }
+
+                    if ( cur_capacity == m_nCapacity.load( atomics::memory_order_acquire )) {
+
+                        size_t const nMask = pLockArr[0]->size() - 1;
+                        assert( cds::beans::is_power2( nMask + 1 ));
+
+                        for ( unsigned int i = 0; i < c_nArity; ++i ) {
+                            parrLock[i] = &( pLockArr[i]->at( arrHash[i] & nMask ));
+                            parrLock[i]->lock();
+                        }
+
+                        who = m_Owner.load( atomics::memory_order_acquire );
+                        if ( ( !(who & 1) || (who >> 1) == (me & c_nOwnerMask)) && cur_capacity == m_nCapacity.load( atomics::memory_order_acquire )) {
+                            m_Stat.onCellLock();
+                            return;
+                        }
+
+                        for ( unsigned int i = 0; i < c_nArity; ++i )
+                            parrLock[i]->unlock();
+
+                        m_Stat.onCellLockFailed();
+                    }
+                    else
+                        m_Stat.onCellArrayChanged();
+
+                    // clears pLockArr can lead to calling dtor for each item of pLockArr[i] that may be a heavy-weighted operation
+                    // (each pLockArr[i] is a shared pointer to array of a ton of mutexes)
+                    // It is better to do this before the next loop iteration where we will use spin-locked assignment to pLockArr
+                    // However, destructing a lot of mutexes under spin-lock is a bad solution
+                    for ( unsigned int i = 0; i < c_nArity; ++i )
+                        pLockArr[i].reset();
+                }
+            }
+
+            bool try_second_acquire( size_t const * arrHash, lock_type ** parrLock )
+            {
+                // It is assumed that the current thread already has a lock
+                // and requires a second lock for other hash
+
+                size_t const nMask = m_nCapacity.load(atomics::memory_order_acquire) - 1;
+                size_t nCell = m_arrLocks[0]->try_lock( arrHash[0] & nMask);
+                if ( nCell == lock_array_type::c_nUnspecifiedCell ) {
+                    m_Stat.onSecondCellLockFailed();
+                    return false;
+                }
+                parrLock[0] = &(m_arrLocks[0]->at(nCell));
+
+                for ( unsigned int i = 1; i < c_nArity; ++i ) {
+                    parrLock[i] = &( m_arrLocks[i]->at( m_arrLocks[i]->lock( arrHash[i] & nMask)));
+                }
+
+                m_Stat.onSecondCellLock();
+                return true;
+            }
+
+            void acquire_all()
+            {
+                owner_t me = (owner_t) cds::OS::get_current_thread_id();
+
+                back_off bkoff;
+                while ( true ) {
+                    owner_t ownNull = 0;
+                    if ( m_Owner.compare_exchange_strong( ownNull, (me << 1) | 1, atomics::memory_order_acq_rel, atomics::memory_order_relaxed )) {
+                        m_arrLocks[0]->lock_all();
+
+                        m_Stat.onFullLock();
+                        return;
+                    }
+                    bkoff();
+                    m_Stat.onFullLockIter();
+                }
+            }
+
+            void release_all()
+            {
+                m_arrLocks[0]->unlock_all();
+                m_Owner.store( 0, atomics::memory_order_release );
+            }
+
+            void acquire_resize( lock_array_ptr * pOldLocks )
+            {
+                owner_t me = (owner_t) cds::OS::get_current_thread_id();
+                size_t cur_capacity;
+
+                while ( true ) {
+                    {
+                        scoped_spinlock sl(m_access);
+                        for ( unsigned int i = 0; i < c_nArity; ++i )
+                            pOldLocks[i] = m_arrLocks[i];
+                        cur_capacity = m_nCapacity.load( atomics::memory_order_acquire );
+                    }
+
+                    // global lock
+                    owner_t ownNull = 0;
+                    if ( m_Owner.compare_exchange_strong( ownNull, (me << 1) | 1, atomics::memory_order_acq_rel, atomics::memory_order_relaxed )) {
+                        if ( cur_capacity == m_nCapacity.load( atomics::memory_order_acquire )) {
+                            pOldLocks[0]->lock_all();
+                            m_Stat.onResizeLock();
+                            return;
+                        }
+
+                        m_Owner.store( 0, atomics::memory_order_release );
+                        m_Stat.onResizeLockArrayChanged();
+                    }
+                    else
+                        m_Stat.onResizeLockIter();
+
+                    // clears pOldLocks can lead to calling dtor for each item of pOldLocks[i] that may be a heavy-weighted operation
+                    // (each pOldLocks[i] is a shared pointer to array of a ton of mutexes)
+                    // It is better to do this before the next loop iteration where we will use spin-locked assignment to pOldLocks
+                    // However, destructing a lot of mutexes under spin-lock is a bad solution
+                    for ( unsigned int i = 0; i < c_nArity; ++i )
+                        pOldLocks[i].reset();
+                }
+            }
+
+            void release_resize( lock_array_ptr * pOldLocks )
+            {
+                m_Owner.store( 0, atomics::memory_order_release );
+                pOldLocks[0]->unlock_all();
+            }
+            //@endcond
+
+        public:
+            //@cond
+            class scoped_cell_lock {
+                lock_type * m_arrLock[ c_nArity ];
+                lock_array_ptr  m_arrLockArr[ c_nArity ];
+
+            public:
+                scoped_cell_lock( refinable& policy, size_t const* arrHash )
+                {
+                    policy.acquire( arrHash, m_arrLockArr, m_arrLock );
+                }
+
+                ~scoped_cell_lock()
+                {
+                    for ( unsigned int i = 0; i < c_nArity; ++i )
+                        m_arrLock[i]->unlock();
+                }
+            };
+
+            class scoped_cell_trylock {
+                lock_type * m_arrLock[ c_nArity ];
+                bool        m_bLocked;
+
+            public:
+                scoped_cell_trylock( refinable& policy, size_t const* arrHash )
+                {
+                    m_bLocked = policy.try_second_acquire( arrHash, m_arrLock );
+                }
+
+                ~scoped_cell_trylock()
+                {
+                    if ( m_bLocked ) {
+                        for ( unsigned int i = 0; i < c_nArity; ++i )
+                            m_arrLock[i]->unlock();
+                    }
+                }
+
+                bool locked() const
+                {
+                    return m_bLocked;
+                }
+            };
+
+            class scoped_full_lock {
+                refinable& m_policy;
+            public:
+                scoped_full_lock( refinable& policy )
+                    : m_policy( policy )
+                {
+                    policy.acquire_all();
+                }
+                ~scoped_full_lock()
+                {
+                    m_policy.release_all();
+                }
+            };
+
+            class scoped_resize_lock
+            {
+                refinable&      m_policy;
+                lock_array_ptr  m_arrLocks[ c_nArity ];
+            public:
+                scoped_resize_lock( refinable& policy )
+                    : m_policy(policy)
+                {
+                    policy.acquire_resize( m_arrLocks );
+                }
+                ~scoped_resize_lock()
+                {
+                    m_policy.release_resize( m_arrLocks );
+                }
+            };
+            //@endcond
+
+        public:
+            /// Constructor
+            refinable(
+                size_t nLockCount   ///< The size of lock array. Must be power of two.
+            )   : m_Owner(0)
+                , m_nCapacity( nLockCount )
+            {
+                assert( cds::beans::is_power2( nLockCount ));
+                for ( unsigned int i = 0; i < c_nArity; ++i )
+                    m_arrLocks[i] = create_lock_array( nLockCount );
+            }
+
+            //@cond
+            void resize( size_t nCapacity )
+            {
+                lock_array_ptr pNew[ c_nArity ];
+                for ( unsigned int i = 0; i < c_nArity; ++i )
+                    pNew[i] = create_lock_array( nCapacity );
+
+                {
+                    scoped_spinlock sl(m_access);
+                    m_nCapacity.store( nCapacity, atomics::memory_order_release );
+                    for ( unsigned int i = 0; i < c_nArity; ++i )
+                        m_arrLocks[i] = pNew[i];
+                }
+
+                m_Stat.onResize();
+            }
+            //@endcond
+
+            /// Returns lock array size
+            /**
+                Lock array size is not a constant for \p refinable policy and can be changed when the set is resized.
+            */
+            size_t lock_count() const
+            {
+                return m_nCapacity.load(atomics::memory_order_relaxed);
+            }
+
+            /// Returns the arity of \p refinable mutex policy
+            constexpr unsigned int arity() const noexcept
+            {
+                return c_nArity;
+            }
+
+            /// Returns internal statistics
+            statistics_type const& statistics() const
+            {
+                return m_Stat;
+            }
+        };
+
+        /// \p CuckooSet internal statistics
+        struct stat {
+            typedef cds::atomicity::event_counter   counter_type ;  ///< Counter type
+
+            counter_type    m_nRelocateCallCount    ; ///< Count of \p relocate() function call
+            counter_type    m_nRelocateRoundCount   ; ///< Count of attempts to relocate items
+            counter_type    m_nFalseRelocateCount   ; ///< Count of unneeded attempts of \p relocate call
+            counter_type    m_nSuccessRelocateCount ; ///< Count of successful item relocating
+            counter_type    m_nRelocateAboveThresholdCount; ///< Count of item relocating above probeset threshold
+            counter_type    m_nFailedRelocateCount  ;   ///< Count of failed relocation attemp (when all probeset is full)
+
+            counter_type    m_nResizeCallCount      ;   ///< Count of \p resize() function call
+            counter_type    m_nFalseResizeCount     ;   ///< Count of false \p resize() function call (when other thread has been resized the set)
+            counter_type    m_nResizeSuccessNodeMove;   ///< Count of successful node moving when resizing
+            counter_type    m_nResizeRelocateCall   ;   ///< Count of \p relocate() function call from \p resize function
+
+            counter_type    m_nInsertSuccess        ;   ///< Count of successful \p insert() function call
+            counter_type    m_nInsertFailed         ;   ///< Count of failed \p insert() function call
+            counter_type    m_nInsertResizeCount    ;   ///< Count of \p resize() function call from \p insert()
+            counter_type    m_nInsertRelocateCount  ;   ///< Count of \p relocate() function call from \p insert()
+            counter_type    m_nInsertRelocateFault  ;   ///< Count of failed \p relocate() function call from \p insert()
+
+            counter_type    m_nUpdateExistCount     ;   ///< Count of call \p update() function for existing node
+            counter_type    m_nUpdateSuccessCount   ;   ///< Count of successful \p insert() function call for new node
+            counter_type    m_nUpdateResizeCount    ;   ///< Count of \p resize() function call from \p update()
+            counter_type    m_nUpdateRelocateCount  ;   ///< Count of \p relocate() function call from \p update()
+            counter_type    m_nUpdateRelocateFault  ;   ///< Count of failed \p relocate() function call from \p update()
+
+            counter_type    m_nUnlinkSuccess        ;   ///< Count of success \p unlink() function call
+            counter_type    m_nUnlinkFailed         ;   ///< Count of failed \p unlink() function call
+
+            counter_type    m_nEraseSuccess         ;   ///< Count of success \p erase() function call
+            counter_type    m_nEraseFailed          ;   ///< Count of failed \p erase() function call
+
+            counter_type    m_nFindSuccess         ;   ///< Count of success \p find() function call
+            counter_type    m_nFindFailed          ;   ///< Count of failed \p find() function call
+
+            counter_type    m_nFindEqualSuccess         ;   ///< Count of success \p find_equal() function call
+            counter_type    m_nFindEqualFailed          ;   ///< Count of failed \p find_equal() function call
+
+            counter_type    m_nFindWithSuccess         ;   ///< Count of success \p find_with() function call
+            counter_type    m_nFindWithFailed          ;   ///< Count of failed \p find_with() function call
+
+            //@cond
+            void    onRelocateCall()        { ++m_nRelocateCallCount; }
+            void    onRelocateRound()       { ++m_nRelocateRoundCount; }
+            void    onFalseRelocateRound()  { ++m_nFalseRelocateCount; }
+            void    onSuccessRelocateRound(){ ++m_nSuccessRelocateCount; }
+            void    onRelocateAboveThresholdRound() { ++m_nRelocateAboveThresholdCount; }
+            void    onFailedRelocate()      { ++m_nFailedRelocateCount; }
+
+            void    onResizeCall()          { ++m_nResizeCallCount; }
+            void    onFalseResizeCall()     { ++m_nFalseResizeCount; }
+            void    onResizeSuccessMove()   { ++m_nResizeSuccessNodeMove; }
+            void    onResizeRelocateCall()  { ++m_nResizeRelocateCall; }
+
+            void    onInsertSuccess()       { ++m_nInsertSuccess; }
+            void    onInsertFailed()        { ++m_nInsertFailed; }
+            void    onInsertResize()        { ++m_nInsertResizeCount; }
+            void    onInsertRelocate()      { ++m_nInsertRelocateCount; }
+            void    onInsertRelocateFault() { ++m_nInsertRelocateFault; }
+
+            void    onUpdateExist()         { ++m_nUpdateExistCount; }
+            void    onUpdateSuccess()       { ++m_nUpdateSuccessCount; }
+            void    onUpdateResize()        { ++m_nUpdateResizeCount; }
+            void    onUpdateRelocate()      { ++m_nUpdateRelocateCount; }
+            void    onUpdateRelocateFault() { ++m_nUpdateRelocateFault; }
+
+            void    onUnlinkSuccess()       { ++m_nUnlinkSuccess; }
+            void    onUnlinkFailed()        { ++m_nUnlinkFailed; }
+
+            void    onEraseSuccess()        { ++m_nEraseSuccess; }
+            void    onEraseFailed()         { ++m_nEraseFailed; }
+
+            void    onFindSuccess()         { ++m_nFindSuccess; }
+            void    onFindFailed()          { ++m_nFindFailed; }
+
+            void    onFindWithSuccess()     { ++m_nFindWithSuccess; }
+            void    onFindWithFailed()      { ++m_nFindWithFailed; }
+            //@endcond
+        };
+
+        /// CuckooSet empty internal statistics
+        struct empty_stat {
+            //@cond
+            void    onRelocateCall()        const {}
+            void    onRelocateRound()       const {}
+            void    onFalseRelocateRound()  const {}
+            void    onSuccessRelocateRound()const {}
+            void    onRelocateAboveThresholdRound() const {}
+            void    onFailedRelocate()      const {}
+
+            void    onResizeCall()          const {}
+            void    onFalseResizeCall()     const {}
+            void    onResizeSuccessMove()   const {}
+            void    onResizeRelocateCall()  const {}
+
+            void    onInsertSuccess()       const {}
+            void    onInsertFailed()        const {}
+            void    onInsertResize()        const {}
+            void    onInsertRelocate()      const {}
+            void    onInsertRelocateFault() const {}
+
+            void    onUpdateExist()         const {}
+            void    onUpdateSuccess()       const {}
+            void    onUpdateResize()        const {}
+            void    onUpdateRelocate()      const {}
+            void    onUpdateRelocateFault() const {}
+
+            void    onUnlinkSuccess()       const {}
+            void    onUnlinkFailed()        const {}
+
+            void    onEraseSuccess()        const {}
+            void    onEraseFailed()         const {}
+
+            void    onFindSuccess()         const {}
+            void    onFindFailed()          const {}
+
+            void    onFindWithSuccess()     const {}
+            void    onFindWithFailed()      const {}
+            //@endcond
+        };
+
+        /// Type traits for CuckooSet class
+        struct traits
+        {
+            /// Hook used
+            /**
+                Possible values are: cuckoo::base_hook, cuckoo::member_hook, cuckoo::traits_hook.
+            */
+            typedef base_hook<>         hook;
+
+            /// Hash functors tuple
+            /**
+                This is mandatory type and has no predefined one.
+
+                At least, two hash functors should be provided. All hash functor
+                should be orthogonal (different): for each <tt> i,j: i != j => h[i](x) != h[j](x) </tt>.
+                The hash functors are defined as <tt> std::tuple< H1, H2, ... Hn > </tt>:
+                \@code cds::opt::hash< std::tuple< h1, h2 > > \@endcode
+                The number of hash functors specifies the number \p k - the count of hash tables in cuckoo hashing.
+
+                To specify hash tuple in traits you should use \p cds::opt::hash_tuple:
+                \code
+                struct my_traits: public cds::intrusive::cuckoo::traits {
+                    typedef cds::opt::hash_tuple< hash1, hash2 > hash;
+                };
+                \endcode
+            */
+            typedef cds::opt::none      hash;
+
+            /// Concurrent access policy
+            /**
+                Available opt::mutex_policy types:
+                - \p cuckoo::striping - simple, but the lock array is not resizable
+                - \p cuckoo::refinable - resizable lock array, but more complex access to set data.
+
+                Default is \p cuckoo::striping.
+            */
+            typedef cuckoo::striping<>               mutex_policy;
+
+            /// Key equality functor
+            /**
+                Default is <tt>std::equal_to<T></tt>
+            */
+            typedef opt::none                       equal_to;
+
+            /// Key comparing functor
+            /**
+                No default functor is provided. If the option is not specified, the \p less is used.
+            */
+            typedef opt::none                       compare;
+
+            /// specifies binary predicate used for key comparison.
+            /**
+                Default is \p std::less<T>.
+            */
+            typedef opt::none                       less;
+
+            /// Item counter
+            /**
+                The type for item counting feature.
+                Default is \p cds::atomicity::item_counter
+
+                Only atomic item counter type is allowed.
+            */
+            typedef atomicity::item_counter             item_counter;
+
+            /// Allocator type
+            /**
+                The allocator type for allocating bucket tables.
+            */
+            typedef CDS_DEFAULT_ALLOCATOR       allocator;
+
+            /// Disposer
+            /**
+                The disposer functor is used in \p CuckooSet::clear() member function
+                to free set's node.
+            */
+            typedef intrusive::opt::v::empty_disposer   disposer;
+
+            /// Internal statistics. Available statistics: \p cuckoo::stat, \p cuckoo::empty_stat
+            typedef empty_stat                  stat;
+        };
+
+        /// Metafunction converting option list to \p CuckooSet traits
+        /**
+            Template argument list \p Options... are:
+            - \p intrusive::opt::hook - hook used. Possible values are: \p cuckoo::base_hook, \p cuckoo::member_hook,
+                \p cuckoo::traits_hook.
+                If the option is not specified, <tt>%cuckoo::base_hook<></tt> is used.
+            - \p opt::hash - hash functor tuple, mandatory option. At least, two hash functors should be provided. All hash functor
+                should be orthogonal (different): for each <tt> i,j: i != j => h[i](x) != h[j](x) </tt>.
+                The hash functors are passed as <tt> std::tuple< H1, H2, ... Hn > </tt>. The number of hash functors specifies
+                the number \p k - the count of hash tables in cuckoo hashing.
+            - \p opt::mutex_policy - concurrent access policy.
+                Available policies: \p cuckoo::striping, \p cuckoo::refinable.
+                Default is \p %cuckoo::striping.
+            - \p opt::equal_to - key equality functor like \p std::equal_to.
+                If this functor is defined then the probe-set will be unordered.
+                If \p %opt::compare or \p %opt::less option is specified too, then the probe-set will be ordered
+                and \p %opt::equal_to will be ignored.
+            - \p opt::compare - key comparison functor. No default functor is provided.
+                If the option is not specified, the \p %opt::less is used.
+                If \p %opt::compare or \p %opt::less option is specified, then the probe-set will be ordered.
+            - \p opt::less - specifies binary predicate used for key comparison. Default is \p std::less<T>.
+                If \p %opt::compare or \p %opt::less option is specified, then the probe-set will be ordered.
+            - \p opt::item_counter - the type of item counting feature. Default is \p atomicity::item_counter
+                The item counter should be atomic.
+            - \p opt::allocator - the allocator type using for allocating bucket tables.
+                Default is \ref CDS_DEFAULT_ALLOCATOR
+            - \p intrusive::opt::disposer - the disposer type used in \p clear() member function for
+                freeing nodes. Default is \p intrusive::opt::v::empty_disposer
+            - \p opt::stat - internal statistics. Possibly types: \p cuckoo::stat, \p cuckoo::empty_stat.
+                Default is \p %cuckoo::empty_stat
+
+            The probe set traits \p cuckoo::probeset_type and \p cuckoo::store_hash are taken from \p node type
+            specified by \p opt::hook option.
+        */
+        template <typename... Options>
+        struct make_traits {
+            typedef typename cds::opt::make_options<
+                typename cds::opt::find_type_traits< cuckoo::traits, Options... >::type
+                ,Options...
+            >::type   type ;    ///< Result of metafunction
+        };
+
+        //@cond
+        namespace details {
+            template <typename Node, typename Probeset>
+            class bucket_entry;
+
+            template <typename Node>
+            class bucket_entry<Node, cuckoo::list>
+            {
+            public:
+                typedef Node                        node_type;
+                typedef cuckoo::list_probeset_class probeset_class;
+                typedef cuckoo::list                probeset_type;
+
+            protected:
+                node_type *     pHead;
+                unsigned int    nSize;
+
+            public:
+                class iterator
+                {
+                    node_type * pNode;
+                    friend class bucket_entry;
+
+                public:
+                    iterator()
+                        : pNode( nullptr )
+                    {}
+                    iterator( node_type * p )
+                        : pNode( p )
+                    {}
+                    iterator( iterator const& it)
+                        : pNode( it.pNode )
+                    {}
+
+                    iterator& operator=( iterator const& it )
+                    {
+                        pNode = it.pNode;
+                        return *this;
+                    }
+
+                    iterator& operator=( node_type * p )
+                    {
+                        pNode = p;
+                        return *this;
+                    }
+
+                    node_type * operator->()
+                    {
+                        return pNode;
+                    }
+                    node_type& operator*()
+                    {
+                        assert( pNode != nullptr );
+                        return *pNode;
+                    }
+
+                    // preinc
+                    iterator& operator ++()
+                    {
+                        if ( pNode )
+                            pNode = pNode->m_pNext;
+                        return *this;
+                    }
+
+                    bool operator==(iterator const& it ) const
+                    {
+                        return pNode == it.pNode;
+                    }
+                    bool operator!=(iterator const& it ) const
+                    {
+                        return !( *this == it );
+                    }
+                };
+
+            public:
+                bucket_entry()
+                    : pHead( nullptr )
+                    , nSize(0)
+                {
+                    static_assert(( std::is_same<typename node_type::probeset_type, probeset_type>::value ), "Incompatible node type" );
+                }
+
+                iterator begin()
+                {
+                    return iterator(pHead);
+                }
+                iterator end()
+                {
+                    return iterator();
+                }
+
+                void insert_after( iterator it, node_type * p )
+                {
+                    node_type * pPrev = it.pNode;
+                    if ( pPrev ) {
+                        p->m_pNext = pPrev->m_pNext;
+                        pPrev->m_pNext = p;
+                    }
+                    else {
+                        // insert as head
+                        p->m_pNext = pHead;
+                        pHead = p;
+                    }
+                    ++nSize;
+                }
+
+                void remove( iterator itPrev, iterator itWhat )
+                {
+                    node_type * pPrev = itPrev.pNode;
+                    node_type * pWhat = itWhat.pNode;
+                    assert( (!pPrev && pWhat == pHead) || (pPrev && pPrev->m_pNext == pWhat));
+
+                    if ( pPrev )
+                        pPrev->m_pNext = pWhat->m_pNext;
+                    else {
+                        assert( pWhat == pHead );
+                        pHead = pHead->m_pNext;
+                    }
+                    pWhat->clear();
+                    --nSize;
+                }
+
+                void clear()
+                {
+                    node_type * pNext;
+                    for ( node_type * pNode = pHead; pNode; pNode = pNext ) {
+                        pNext = pNode->m_pNext;
+                        pNode->clear();
+                    }
+
+                    nSize = 0;
+                    pHead = nullptr;
+                }
+
+                template <typename Disposer>
+                void clear( Disposer disp )
+                {
+                    node_type * pNext;
+                    for ( node_type * pNode = pHead; pNode; pNode = pNext ) {
+                        pNext = pNode->m_pNext;
+                        pNode->clear();
+                        disp( pNode );
+                    }
+
+                    nSize = 0;
+                    pHead = nullptr;
+                }
+
+                unsigned int size() const
+                {
+                    return nSize;
+                }
+            };
+
+            template <typename Node, unsigned int Capacity>
+            class bucket_entry<Node, cuckoo::vector<Capacity>>
+            {
+            public:
+                typedef Node                            node_type;
+                typedef cuckoo::vector_probeset_class   probeset_class;
+                typedef cuckoo::vector<Capacity>        probeset_type;
+
+                static unsigned int const c_nCapacity = probeset_type::c_nCapacity;
+
+            protected:
+                node_type *     m_arrNode[c_nCapacity];
+                unsigned int    m_nSize;
+
+                void shift_up( unsigned int nFrom )
+                {
+                    assert( m_nSize < c_nCapacity );
+
+                    if ( nFrom < m_nSize )
+                        std::copy_backward( m_arrNode + nFrom, m_arrNode + m_nSize, m_arrNode + m_nSize + 1 );
+                }
+
+                void shift_down( node_type ** pFrom )
+                {
+                    assert( m_arrNode <= pFrom && pFrom < m_arrNode + m_nSize);
+                    std::copy( pFrom + 1, m_arrNode + m_nSize, pFrom );
+                }
+            public:
+                class iterator
+                {
+                    node_type **    pArr;
+                    friend class bucket_entry;
+
+                public:
+                    iterator()
+                        : pArr( nullptr )
+                    {}
+                    iterator( node_type ** p )
+                        : pArr(p)
+                    {}
+                    iterator( iterator const& it)
+                        : pArr( it.pArr )
+                    {}
+
+                    iterator& operator=( iterator const& it )
+                    {
+                        pArr = it.pArr;
+                        return *this;
+                    }
+
+                    node_type * operator->()
+                    {
+                        assert( pArr != nullptr );
+                        return *pArr;
+                    }
+                    node_type& operator*()
+                    {
+                        assert( pArr != nullptr );
+                        assert( *pArr != nullptr );
+                        return *(*pArr);
+                    }
+
+                    // preinc
+                    iterator& operator ++()
+                    {
+                        ++pArr;
+                        return *this;
+                    }
+
+                    bool operator==(iterator const& it ) const
+                    {
+                        return pArr == it.pArr;
+                    }
+                    bool operator!=(iterator const& it ) const
+                    {
+                        return !( *this == it );
+                    }
+                };
+
+            public:
+                bucket_entry()
+                    : m_nSize(0)
+                {
+                    memset( m_arrNode, 0, sizeof(m_arrNode));
+                    static_assert(( std::is_same<typename node_type::probeset_type, probeset_type>::value ), "Incompatible node type" );
+                }
+
+                iterator begin()
+                {
+                    return iterator(m_arrNode);
+                }
+                iterator end()
+                {
+                    return iterator(m_arrNode + size());
+                }
+
+                void insert_after( iterator it, node_type * p )
+                {
+                    assert( m_nSize < c_nCapacity );
+                    assert( !it.pArr || (m_arrNode <= it.pArr && it.pArr <= m_arrNode + m_nSize));
+
+                    if ( it.pArr ) {
+                        shift_up( static_cast<unsigned int>(it.pArr - m_arrNode) + 1 );
+                        it.pArr[1] = p;
+                    }
+                    else {
+                        shift_up(0);
+                        m_arrNode[0] = p;
+                    }
+                    ++m_nSize;
+                }
+
+                void remove( iterator /*itPrev*/, iterator itWhat )
+                {
+                    itWhat->clear();
+                    shift_down( itWhat.pArr );
+                    --m_nSize;
+                }
+
+                void clear()
+                {
+                    m_nSize = 0;
+                }
+
+                template <typename Disposer>
+                void clear( Disposer disp )
+                {
+                    for ( unsigned int i = 0; i < m_nSize; ++i ) {
+                        disp( m_arrNode[i] );
+                    }
+                    m_nSize = 0;
+                }
+
+                unsigned int size() const
+                {
+                    return m_nSize;
+                }
+            };
+
+            template <typename Node, unsigned int ArraySize>
+            struct hash_ops {
+                static void store( Node * pNode, size_t const* pHashes )
+                {
+                    memcpy( pNode->m_arrHash, pHashes, sizeof(pHashes[0]) * ArraySize );
+                }
+                static bool equal_to( Node& node, unsigned int nTable, size_t nHash )
+                {
+                    return node.m_arrHash[nTable] == nHash;
+                }
+            };
+            template <typename Node>
+            struct hash_ops<Node, 0>
+            {
+                static void store( Node * /*pNode*/, size_t * /*pHashes*/ )
+                {}
+                static bool equal_to( Node& /*node*/, unsigned int /*nTable*/, size_t /*nHash*/ )
+                {
+                    return true;
+                }
+            };
+
+            template <typename NodeTraits, bool Ordered>
+            struct contains;
+
+            template <typename NodeTraits>
+            struct contains<NodeTraits, true>
+            {
+                template <typename BucketEntry, typename Position, typename Q, typename Compare>
+                static bool find( BucketEntry& probeset, Position& pos, unsigned int /*nTable*/, size_t /*nHash*/, Q const& val, Compare cmp )
+                {
+                    // Ordered version
+                    typedef typename BucketEntry::iterator bucket_iterator;
+
+                    bucket_iterator itPrev;
+
+                    for ( bucket_iterator it = probeset.begin(), itEnd = probeset.end(); it != itEnd; ++it ) {
+                        int cmpRes = cmp( *NodeTraits::to_value_ptr(*it), val );
+                        if ( cmpRes >= 0 ) {
+                            pos.itFound = it;
+                            pos.itPrev = itPrev;
+                            return cmpRes == 0;
+                        }
+
+                        itPrev = it;
+                    }
+
+                    pos.itPrev = itPrev;
+                    pos.itFound = probeset.end();
+                    return false;
+                }
+            };
+
+            template <typename NodeTraits>
+            struct contains<NodeTraits, false>
+            {
+                template <typename BucketEntry, typename Position, typename Q, typename EqualTo>
+                static bool find( BucketEntry& probeset, Position& pos, unsigned int nTable, size_t nHash, Q const& val, EqualTo eq )
+                {
+                    // Unordered version
+                    typedef typename BucketEntry::iterator  bucket_iterator;
+                    typedef typename BucketEntry::node_type node_type;
+
+                    bucket_iterator itPrev;
+
+                    for ( bucket_iterator it = probeset.begin(), itEnd = probeset.end(); it != itEnd; ++it ) {
+                        if ( hash_ops<node_type, node_type::hash_array_size>::equal_to( *it, nTable, nHash ) && eq( *NodeTraits::to_value_ptr(*it), val )) {
+                            pos.itFound = it;
+                            pos.itPrev = itPrev;
+                            return true;
+                        }
+                        itPrev = it;
+                    }
+
+                    pos.itPrev = itPrev;
+                    pos.itFound = probeset.end();
+                    return false;
+                }
+            };
+
+        }   // namespace details
+        //@endcond
+
+    } // namespace cuckoo
+
+    /// Cuckoo hash set
+    /** @ingroup cds_intrusive_map
+
+        Source
+            - [2007] M.Herlihy, N.Shavit, M.Tzafrir "Concurrent Cuckoo Hashing. Technical report"
+            - [2008] Maurice Herlihy, Nir Shavit "The Art of Multiprocessor Programming"
+
+        <b>About Cuckoo hashing</b>
+
+            [From <i>"The Art of Multiprocessor Programming"</i>]
+            <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
+            occupying the same slot. For brevity, a table is a k-entry array of items. For a hash set of size
+            N = 2k we use a two-entry array of tables, and two independent hash functions,
+            <tt> h0, h1: KeyRange -> 0,...,k-1</tt>
+            mapping the set of possible keys to entries in he array. To test whether a value \p x is in the set,
+            <tt>find(x)</tt> tests whether either <tt>table[0][h0(x)]</tt> or <tt>table[1][h1(x)]</tt> is
+            equal to \p x. Similarly, <tt>erase(x)</tt>checks whether \p x is in either <tt>table[0][h0(x)]</tt>
+            or <tt>table[1][h1(x)]</tt>, ad removes it if found.
+
+            The <tt>insert(x)</tt> successively "kicks out" conflicting items until every key has a slot.
+            To add \p x, the method swaps \p x with \p y, the current occupant of <tt>table[0][h0(x)]</tt>.
+            If the prior value was \p nullptr, it is done. Otherwise, it swaps the newly nest-less value \p y
+            for the current occupant of <tt>table[1][h1(y)]</tt> in the same way. As before, if the prior value
+            was \p nullptr, it is done. Otherwise, the method continues swapping entries (alternating tables)
+            until it finds an empty slot. We might not find an empty slot, either because the table is full,
+            or because the sequence of displacement forms a cycle. We therefore need an upper limit on the
+            number of successive displacements we are willing to undertake. When this limit is exceeded,
+            we resize the hash table, choose new hash functions and start over.
+
+            For concurrent cuckoo hashing, rather than organizing the set as a two-dimensional table of
+            items, we use two-dimensional table of probe sets, where a probe set is a constant-sized set
+            of items with the same hash code. Each probe set holds at most \p PROBE_SIZE items, but the algorithm
+            tries to ensure that when the set is quiescent (i.e no method call in progress) each probe set
+            holds no more than <tt>THRESHOLD < PROBE_SET</tt> items. While method calls are in-flight, a probe
+            set may temporarily hold more than \p THRESHOLD but never more than \p PROBE_SET items.
+
+            In current implementation, a probe set can be defined either as a (single-linked) list
+            or as a fixed-sized vector, optionally ordered.
+
+            In description above two-table cuckoo hashing (<tt>k = 2</tt>) has been considered.
+            We can generalize this approach for <tt>k >= 2</tt> when we have \p k hash functions
+            <tt>h[0], ... h[k-1]</tt> and \p k tables <tt>table[0], ... table[k-1]</tt>.
+
+            The search in probe set is linear, the complexity is <tt> O(PROBE_SET) </tt>.
+            The probe set may be ordered or not. Ordered probe set can be more efficient since
+            the average search complexity is <tt>O(PROBE_SET/2)</tt>.
+            However, the overhead of sorting can eliminate a gain of ordered search.
+
+            The probe set is ordered if \p compare or \p less is specified in \p Traits template
+            parameter. Otherwise, the probe set is unordered and \p Traits should provide
+            \p equal_to predicate.
+
+            The \p cds::intrusive::cuckoo namespace contains \p %CuckooSet-related declarations.
+
+        Template arguments:
+        - \p T - the type stored in the set. The type must be based on \p cuckoo::node (for \p cuckoo::base_hook)
+            or it must have a member of type %cuckoo::node (for \p cuckoo::member_hook),
+            or it must be convertible to \p %cuckoo::node (for \p cuckoo::traits_hook)
+        - \p Traits - type traits, default is \p cuckoo::traits. It is possible to declare option-based
+            set with \p cuckoo::make_traits metafunction result as \p Traits template argument.
+
+        <b>How to use</b>
+
+        You should incorporate \p cuckoo::node into your struct \p T and provide
+        appropriate \p cuckoo::traits::hook in your \p Traits template parameters.
+        Usually, for \p Traits you define a struct based on \p cuckoo::traits.
+
+        Example for base hook and list-based probe-set:
+        \code
+        #include <cds/intrusive/cuckoo_set.h>
+
+        // Data stored in cuckoo set
+        // We use list as probe-set container and store hash values in the node
+        // (since we use two hash functions we should store 2 hash values per node)
+        struct my_data: public cds::intrusive::cuckoo::node< cds::intrusive::cuckoo::list, 2 >
+        {
+            // key field
+            std::string     strKey;
+
+            // other data
+            // ...
+        };
+
+        // Provide equal_to functor for my_data since we will use unordered probe-set
+        struct my_data_equal_to {
+            bool operator()( const my_data& d1, const my_data& d2 ) const
+            {
+                return d1.strKey.compare( d2.strKey ) == 0;
+            }
+
+            bool operator()( const my_data& d, const std::string& s ) const
+            {
+                return d.strKey.compare(s) == 0;
+            }
+
+            bool operator()( const std::string& s, const my_data& d ) const
+            {
+                return s.compare( d.strKey ) == 0;
+            }
+        };
+
+        // Provide two hash functor for my_data
+        struct hash1 {
+            size_t operator()(std::string const& s) const
+            {
+                return cds::opt::v::hash<std::string>( s );
+            }
+            size_t operator()( my_data const& d ) const
+            {
+                return (*this)( d.strKey );
+            }
+        };
+
+        struct hash2: private hash1 {
+            size_t operator()(std::string const& s) const
+            {
+                size_t h = ~( hash1::operator()(s));
+                return ~h + 0x9e3779b9 + (h << 6) + (h >> 2);
+            }
+            size_t operator()( my_data const& d ) const
+            {
+                return (*this)( d.strKey );
+            }
+        };
+
+        // Declare type traits
+        struct my_traits: public cds::intrusive::cuckoo::traits
+        {
+            typedef cds::intrusive::cuckoo::base_hook<
+                cds::intrusive::cuckoo::probeset_type< my_data::probeset_type >
+                ,cds::intrusive::cuckoo::store_hash< my_data::hash_array_size >
+            >   hook;
+            typedef my_data_equa_to equal_to;
+            typedef cds::opt::hash_tuple< hash1, hash2 > hash;
+        };
+
+        // Declare CuckooSet type
+        typedef cds::intrusive::CuckooSet< my_data, my_traits > my_cuckoo_set;
+
+        // Equal option-based declaration
+        typedef cds::intrusive::CuckooSet< my_data,
+            cds::intrusive::cuckoo::make_traits<
+                cds::intrusive::opt::hook< cds::intrusive::cuckoo::base_hook<
+                    cds::intrusive::cuckoo::probeset_type< my_data::probeset_type >
+                    ,cds::intrusive::cuckoo::store_hash< my_data::hash_array_size >
+                > >
+                ,cds::opt::hash< std::tuple< hash1, hash2 > >
+                ,cds::opt::equal_to< my_data_equal_to >
+            >::type
+        > opt_cuckoo_set;
+        \endcode
+
+        If we provide \p compare function instead of \p equal_to for \p my_data
+        we get as a result a cuckoo set with ordered probe set that may improve
+        performance.
+        Example for base hook and ordered vector-based probe-set:
+
+        \code
+        #include <cds/intrusive/cuckoo_set.h>
+
+        // Data stored in cuckoo set
+        // We use a vector of capacity 4 as probe-set container and store hash values in the node
+        // (since we use two hash functions we should store 2 hash values per node)
+        struct my_data: public cds::intrusive::cuckoo::node< cds::intrusive::cuckoo::vector<4>, 2 >
+        {
+            // key field
+            std::string     strKey;
+
+            // other data
+            // ...
+        };
+
+        // Provide compare functor for my_data since we want to use ordered probe-set
+        struct my_data_compare {
+            int operator()( const my_data& d1, const my_data& d2 ) const
+            {
+                return d1.strKey.compare( d2.strKey );
+            }
+
+            int operator()( const my_data& d, const std::string& s ) const
+            {
+                return d.strKey.compare(s);
+            }
+
+            int operator()( const std::string& s, const my_data& d ) const
+            {
+                return s.compare( d.strKey );
+            }
+        };
+
+        // Provide two hash functor for my_data
+        struct hash1 {
+            size_t operator()(std::string const& s) const
+            {
+                return cds::opt::v::hash<std::string>( s );
+            }
+            size_t operator()( my_data const& d ) const
+            {
+                return (*this)( d.strKey );
+            }
+        };
+
+        struct hash2: private hash1 {
+            size_t operator()(std::string const& s) const
+            {
+                size_t h = ~( hash1::operator()(s));
+                return ~h + 0x9e3779b9 + (h << 6) + (h >> 2);
+            }
+            size_t operator()( my_data const& d ) const
+            {
+                return (*this)( d.strKey );
+            }
+        };
+
+        // Declare type traits
+        struct my_traits: public cds::intrusive::cuckoo::traits
+        {
+            typedef cds::intrusive::cuckoo::base_hook<
+                cds::intrusive::cuckoo::probeset_type< my_data::probeset_type >
+                ,cds::intrusive::cuckoo::store_hash< my_data::hash_array_size >
+            >   hook;
+            typedef my_data_compare compare;
+            typedef cds::opt::hash_tuple< hash1, hash2 > hash;
+        };
+
+        // Declare CuckooSet type
+        typedef cds::intrusive::CuckooSet< my_data, my_traits > my_cuckoo_set;
+
+        // Equal option-based declaration
+        typedef cds::intrusive::CuckooSet< my_data,
+            cds::intrusive::cuckoo::make_traits<
+                cds::intrusive::opt::hook< cds::intrusive::cuckoo::base_hook<
+                    cds::intrusive::cuckoo::probeset_type< my_data::probeset_type >
+                    ,cds::intrusive::cuckoo::store_hash< my_data::hash_array_size >
+                > >
+                ,cds::opt::hash< std::tuple< hash1, hash2 > >
+                ,cds::opt::compare< my_data_compare >
+            >::type
+        > opt_cuckoo_set;
+        \endcode
+
+    */
+    template <typename T, typename Traits = cuckoo::traits>
+    class CuckooSet
+    {
+    public:
+        typedef T   value_type;   ///< The value type stored in the set
+        typedef Traits traits;    ///< Set traits
+
+        typedef typename traits::hook       hook;        ///< hook type
+        typedef typename hook::node_type    node_type;   ///< node type
+        typedef typename get_node_traits< value_type, node_type, hook>::type node_traits;    ///< node traits
+
+        typedef typename traits::hash          hash;   ///< hash functor tuple wrapped for internal use
+        typedef typename hash::hash_tuple_type hash_tuple_type; ///< Type of hash tuple
+
+        typedef typename traits::stat          stat;   ///< internal statistics type
+
+        typedef typename traits::mutex_policy  original_mutex_policy; ///< Concurrent access policy, see \p cuckoo::traits::mutex_policy
+
+        //@cond
+        typedef typename original_mutex_policy::template rebind_statistics<
+            typename std::conditional<
+                std::is_same< stat, cuckoo::empty_stat >::value
+                ,typename original_mutex_policy::empty_stat
+                ,typename original_mutex_policy::real_stat
+            >::type
+        >::other    mutex_policy;
+        //@endcond
+
+        /// Probe set should be ordered or not
+        /**
+            If \p Traits specifies \p cmpare or \p less functor then the set is ordered.
+            Otherwise, it is unordered and \p Traits should provide \p equal_to functor.
+        */
+        static bool const c_isSorted = !( std::is_same< typename traits::compare, opt::none >::value
+                && std::is_same< typename traits::less, opt::none >::value );
+        static size_t const c_nArity = hash::size ; ///< the arity of cuckoo hashing: the number of hash functors provided; minimum 2.
+
+        /// Key equality functor; used only for unordered probe-set
+        typedef typename opt::details::make_equal_to< value_type, traits, !c_isSorted>::type key_equal_to;
+
+        /// key comparing functor based on \p opt::compare and \p opt::less option setter. Used only for ordered probe set
+        typedef typename opt::details::make_comparator< value_type, traits >::type key_comparator;
+
+        /// allocator type
+        typedef typename traits::allocator     allocator;
+
+        /// item counter type
+        typedef typename traits::item_counter  item_counter;
+
+        /// node disposer
+        typedef typename traits::disposer      disposer;
+
+    protected:
+        //@cond
+        typedef typename node_type::probeset_class  probeset_class;
+        typedef typename node_type::probeset_type   probeset_type;
+        static unsigned int const c_nNodeHashArraySize = node_type::hash_array_size;
+
+        typedef typename mutex_policy::scoped_cell_lock     scoped_cell_lock;
+        typedef typename mutex_policy::scoped_cell_trylock  scoped_cell_trylock;
+        typedef typename mutex_policy::scoped_full_lock     scoped_full_lock;
+        typedef typename mutex_policy::scoped_resize_lock   scoped_resize_lock;
+
+        typedef cuckoo::details::bucket_entry< node_type, probeset_type >   bucket_entry;
+        typedef typename bucket_entry::iterator                     bucket_iterator;
+        typedef cds::details::Allocator< bucket_entry, allocator >  bucket_table_allocator;
+
+        typedef size_t  hash_array[c_nArity]    ;   ///< hash array
+
+        struct position {
+            bucket_iterator     itPrev;
+            bucket_iterator     itFound;
+        };
+
+        typedef cuckoo::details::contains< node_traits, c_isSorted > contains_action;
+
+        template <typename Predicate>
+        struct predicate_wrapper {
+            typedef typename std::conditional< c_isSorted, cds::opt::details::make_comparator_from_less<Predicate>, Predicate>::type   type;
+        };
+
+        typedef typename std::conditional< c_isSorted, key_comparator, key_equal_to >::type key_predicate;
+        //@endcond
+
+    public:
+        static unsigned int const   c_nDefaultProbesetSize = 4;   ///< default probeset size
+        static size_t const         c_nDefaultInitialSize = 16;   ///< default initial size
+        static unsigned int const   c_nRelocateLimit = c_nArity * 2 - 1; ///< Count of attempts to relocate before giving up
+
+    protected:
+        bucket_entry *      m_BucketTable[ c_nArity ] ; ///< Bucket tables
+
+        atomics::atomic<size_t> m_nBucketMask           ;   ///< Hash bitmask; bucket table size minus 1.
+        unsigned int const      m_nProbesetSize         ;   ///< Probe set size
+        unsigned int const      m_nProbesetThreshold    ;   ///< Probe set threshold
+
+        hash            m_Hash              ;   ///< Hash functor tuple
+        mutex_policy    m_MutexPolicy       ;   ///< concurrent access policy
+        item_counter    m_ItemCounter       ;   ///< item counter
+        mutable stat    m_Stat              ;   ///< internal statistics
+
+    protected:
+        //@cond
+        static void check_common_constraints()
+        {
+            static_assert( (c_nArity == mutex_policy::c_nArity), "The count of hash functors must be equal to mutex_policy arity" );
+        }
+
+        void check_probeset_properties() const
+        {
+            assert( m_nProbesetThreshold < m_nProbesetSize );
+
+            // if probe set type is cuckoo::vector<N> then m_nProbesetSize == N
+            assert( node_type::probeset_size == 0 || node_type::probeset_size == m_nProbesetSize );
+        }
+
+        template <typename Q>
+        void hashing( size_t * pHashes, Q const& v ) const
+        {
+            m_Hash( pHashes, v );
+        }
+
+        void copy_hash( size_t * pHashes, value_type const& v ) const
+        {
+            constexpr_if ( c_nNodeHashArraySize != 0 )
+                memcpy( pHashes, node_traits::to_node_ptr( v )->get_hash(), sizeof( pHashes[0] ) * c_nNodeHashArraySize );
+            else
+                hashing( pHashes, v );
+        }
+
+        bucket_entry& bucket( unsigned int nTable, size_t nHash )
+        {
+            assert( nTable < c_nArity );
+            return m_BucketTable[nTable][nHash & m_nBucketMask.load( atomics::memory_order_relaxed ) ];
+        }
+
+        static void store_hash( node_type * pNode, size_t * pHashes )
+        {
+            cuckoo::details::hash_ops< node_type, c_nNodeHashArraySize >::store( pNode, pHashes );
+        }
+
+        static bool equal_hash( node_type& node, unsigned int nTable, size_t nHash )
+        {
+            return cuckoo::details::hash_ops< node_type, c_nNodeHashArraySize >::equal_to( node, nTable, nHash );
+        }
+
+        void allocate_bucket_tables( size_t nSize )
+        {
+            assert( cds::beans::is_power2( nSize ));
+
+            m_nBucketMask.store( nSize - 1, atomics::memory_order_release );
+            bucket_table_allocator alloc;
+            for ( unsigned int i = 0; i < c_nArity; ++i )
+                m_BucketTable[i] = alloc.NewArray( nSize );
+        }
+
+        static void free_bucket_tables( bucket_entry ** pTable, size_t nCapacity )
+        {
+            bucket_table_allocator alloc;
+            for ( unsigned int i = 0; i < c_nArity; ++i ) {
+                alloc.Delete( pTable[i], nCapacity );
+                pTable[i] = nullptr;
+            }
+        }
+        void free_bucket_tables()
+        {
+            free_bucket_tables( m_BucketTable, m_nBucketMask.load( atomics::memory_order_relaxed ) + 1 );
+        }
+
+        static constexpr unsigned int const c_nUndefTable = (unsigned int) -1;
+        template <typename Q, typename Predicate >
+        unsigned int contains( position * arrPos, size_t * arrHash, Q const& val, Predicate pred )
+        {
+            // Buckets must be locked
+
+            for ( unsigned int i = 0; i < c_nArity; ++i ) {
+                bucket_entry& probeset = bucket( i, arrHash[i] );
+                if ( contains_action::find( probeset, arrPos[i], i, arrHash[i], val, pred ))
+                    return i;
+            }
+            return c_nUndefTable;
+        }
+
+        template <typename Q, typename Predicate, typename Func>
+        value_type * erase_( Q const& val, Predicate pred, Func f )
+        {
+            hash_array arrHash;
+            hashing( arrHash, val );
+            position arrPos[ c_nArity ];
+
+            {
+                scoped_cell_lock guard( m_MutexPolicy, arrHash );
+
+                unsigned int nTable = contains( arrPos, arrHash, val, pred );
+                if ( nTable != c_nUndefTable ) {
+                    node_type& node = *arrPos[nTable].itFound;
+                    f( *node_traits::to_value_ptr(node));
+                    bucket( nTable, arrHash[nTable]).remove( arrPos[nTable].itPrev, arrPos[nTable].itFound );
+                    --m_ItemCounter;
+                    m_Stat.onEraseSuccess();
+                    return node_traits::to_value_ptr( node );
+                }
+            }
+
+            m_Stat.onEraseFailed();
+            return nullptr;
+        }
+
+        template <typename Q, typename Predicate, typename Func>
+        bool find_( Q& val, Predicate pred, Func f )
+        {
+            hash_array arrHash;
+            position arrPos[ c_nArity ];
+            hashing( arrHash, val );
+            scoped_cell_lock sl( m_MutexPolicy, arrHash );
+
+            unsigned int nTable = contains( arrPos, arrHash, val, pred );
+            if ( nTable != c_nUndefTable ) {
+                f( *node_traits::to_value_ptr( *arrPos[nTable].itFound ), val );
+                m_Stat.onFindSuccess();
+                return true;
+            }
+
+            m_Stat.onFindFailed();
+            return false;
+        }
+
+        bool relocate( unsigned int nTable, size_t * arrGoalHash )
+        {
+            // arrGoalHash contains hash values for relocating element
+            // Relocating element is first one from bucket( nTable, arrGoalHash[nTable] ) probeset
+
+            m_Stat.onRelocateCall();
+
+            hash_array arrHash;
+            value_type * pVal;
+            for ( unsigned int nRound = 0; nRound < c_nRelocateLimit; ++nRound ) {
+                m_Stat.onRelocateRound();
+
+                while ( true ) {
+                    scoped_cell_lock guard( m_MutexPolicy, arrGoalHash );
+
+                    bucket_entry& refBucket = bucket( nTable, arrGoalHash[nTable] );
+                    if ( refBucket.size() < m_nProbesetThreshold ) {
+                        // probeset is not above the threshold
+                        m_Stat.onFalseRelocateRound();
+                        return true;
+                    }
+
+                    pVal = node_traits::to_value_ptr( *refBucket.begin());
+                    copy_hash( arrHash, *pVal );
+
+                    scoped_cell_trylock guard2( m_MutexPolicy, arrHash );
+                    if ( !guard2.locked())
+                        continue ;  // try one more time
+
+                    refBucket.remove( typename bucket_entry::iterator(), refBucket.begin());
+
+                    unsigned int i = (nTable + 1) % c_nArity;
+
+                    // try insert into free probeset
+                    while ( i != nTable ) {
+                        bucket_entry& bkt = bucket( i, arrHash[i] );
+                        if ( bkt.size() < m_nProbesetThreshold ) {
+                            position pos;
+                            contains_action::find( bkt, pos, i, arrHash[i], *pVal, key_predicate()) ; // must return false!
+                            bkt.insert_after( pos.itPrev, node_traits::to_node_ptr( pVal ));
+                            m_Stat.onSuccessRelocateRound();
+                            return true;
+                        }
+                        i = ( i + 1 ) % c_nArity;
+                    }
+
+                    // try insert into partial probeset
+                    i = (nTable + 1) % c_nArity;
+                    while ( i != nTable ) {
+                        bucket_entry& bkt = bucket( i, arrHash[i] );
+                        if ( bkt.size() < m_nProbesetSize ) {
+                            position pos;
+                            contains_action::find( bkt, pos, i, arrHash[i], *pVal, key_predicate()) ; // must return false!
+                            bkt.insert_after( pos.itPrev, node_traits::to_node_ptr( pVal ));
+                            nTable = i;
+                            memcpy( arrGoalHash, arrHash, sizeof(arrHash));
+                            m_Stat.onRelocateAboveThresholdRound();
+                            goto next_iteration;
+                        }
+                        i = (i + 1) % c_nArity;
+                    }
+
+                    // all probeset is full, relocating fault
+                    refBucket.insert_after( typename bucket_entry::iterator(), node_traits::to_node_ptr( pVal ));
+                    m_Stat.onFailedRelocate();
+                    return false;
+                }
+
+            next_iteration:;
+            }
+            return false;
+        }
+
+        void resize()
+        {
+            m_Stat.onResizeCall();
+
+            size_t nOldCapacity = bucket_count( atomics::memory_order_acquire );
+            bucket_entry* pOldTable[ c_nArity ];
+            {
+                scoped_resize_lock guard( m_MutexPolicy );
+
+                if ( nOldCapacity != bucket_count()) {
+                    m_Stat.onFalseResizeCall();
+                    return;
+                }
+
+                size_t nCapacity = nOldCapacity * 2;
+
+                m_MutexPolicy.resize( nCapacity );
+                memcpy( pOldTable, m_BucketTable, sizeof(pOldTable));
+                allocate_bucket_tables( nCapacity );
+
+                hash_array arrHash;
+                position arrPos[ c_nArity ];
+
+                for ( unsigned int nTable = 0; nTable < c_nArity; ++nTable ) {
+                    bucket_entry * pTable = pOldTable[nTable];
+                    for ( size_t k = 0; k < nOldCapacity; ++k ) {
+                        bucket_iterator itNext;
+                        for ( bucket_iterator it = pTable[k].begin(), itEnd = pTable[k].end(); it != itEnd; it = itNext ) {
+                            itNext = it;
+                            ++itNext;
+
+                            value_type& val = *node_traits::to_value_ptr( *it );
+                            copy_hash( arrHash, val );
+                            CDS_VERIFY_EQ( contains( arrPos, arrHash, val, key_predicate()), c_nUndefTable );
+
+                            for ( unsigned int i = 0; i < c_nArity; ++i ) {
+                                bucket_entry& refBucket = bucket( i, arrHash[i] );
+                                if ( refBucket.size() < m_nProbesetThreshold ) {
+                                    refBucket.insert_after( arrPos[i].itPrev, &*it );
+                                    m_Stat.onResizeSuccessMove();
+                                    goto do_next;
+                                }
+                            }
+
+                            for ( unsigned int i = 0; i < c_nArity; ++i ) {
+                                bucket_entry& refBucket = bucket( i, arrHash[i] );
+                                if ( refBucket.size() < m_nProbesetSize ) {
+                                    refBucket.insert_after( arrPos[i].itPrev, &*it );
+                                    assert( refBucket.size() > 1 );
+                                    copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()));
+                                    m_Stat.onResizeRelocateCall();
+                                    relocate( i, arrHash );
+                                    break;
+                                }
+                            }
+                        do_next:;
+                        }
+                    }
+                }
+            }
+            free_bucket_tables( pOldTable, nOldCapacity );
+        }
+
+        constexpr static unsigned int calc_probeset_size( unsigned int nProbesetSize ) noexcept
+        {
+            return std::is_same< probeset_class, cuckoo::vector_probeset_class >::value
+                ? node_type::probeset_size
+                : (nProbesetSize
+                    ? nProbesetSize
+                    : ( node_type::probeset_size ? node_type::probeset_size : c_nDefaultProbesetSize ));
+        }
+        //@endcond
+
+    public:
+        /// Default constructor
+        /**
+            Initial size = \ref c_nDefaultInitialSize
+
+            Probe set size:
+            - \p c_nDefaultProbesetSize if \p probeset_type is \p cuckoo::list
+            - \p Capacity if \p probeset_type is <tt> cuckoo::vector<Capacity> </tt>
+
+            Probe set threshold = probe set size - 1
+        */
+        CuckooSet()
+            : m_nProbesetSize( calc_probeset_size(0))
+            , m_nProbesetThreshold( m_nProbesetSize - 1 )
+            , m_MutexPolicy( c_nDefaultInitialSize )
+        {
+            check_common_constraints();
+            check_probeset_properties();
+
+            allocate_bucket_tables( c_nDefaultInitialSize );
+        }
+
+        /// Constructs the set object with given probe set size and threshold
+        /**
+            If probe set type is <tt> cuckoo::vector<Capacity> </tt> vector
+            then \p nProbesetSize is ignored since it should be equal to vector's \p Capacity.
+        */
+        CuckooSet(
+            size_t nInitialSize                 ///< Initial set size; if 0 - use default initial size \p c_nDefaultInitialSize
+            , unsigned int nProbesetSize        ///< probe set size
+            , unsigned int nProbesetThreshold = 0   ///< probe set threshold, <tt>nProbesetThreshold < nProbesetSize</tt>. If 0, <tt>nProbesetThreshold = nProbesetSize - 1</tt>
+        )
+            : m_nProbesetSize( calc_probeset_size(nProbesetSize))
+            , m_nProbesetThreshold( nProbesetThreshold ? nProbesetThreshold : m_nProbesetSize - 1 )
+            , m_MutexPolicy( cds::beans::ceil2(nInitialSize ? nInitialSize : c_nDefaultInitialSize ))
+        {
+            check_common_constraints();
+            check_probeset_properties();
+
+            allocate_bucket_tables( nInitialSize ? cds::beans::ceil2( nInitialSize ) : c_nDefaultInitialSize );
+        }
+
+        /// Constructs the set object with given hash functor tuple
+        /**
+            The probe set size and threshold are set as default, see \p CuckooSet()
+        */
+        CuckooSet(
+            hash_tuple_type const& h    ///< hash functor tuple of type <tt>std::tuple<H1, H2, ... Hn></tt> where <tt> n == \ref c_nArity </tt>
+        )
+            : m_nProbesetSize( calc_probeset_size(0))
+            , m_nProbesetThreshold( m_nProbesetSize -1 )
+            , m_Hash( h )
+            , m_MutexPolicy( c_nDefaultInitialSize )
+        {
+            check_common_constraints();
+            check_probeset_properties();
+
+            allocate_bucket_tables( c_nDefaultInitialSize );
+        }
+
+        /// Constructs the set object with given probe set properties and hash functor tuple
+        /**
+            If probe set type is <tt> cuckoo::vector<Capacity> </tt> vector
+            then \p nProbesetSize should be equal to vector's \p Capacity.
+        */
+        CuckooSet(
+            size_t nInitialSize                 ///< Initial set size; if 0 - use default initial size \p c_nDefaultInitialSize
+            , unsigned int nProbesetSize        ///< probe set size, positive integer
+            , unsigned int nProbesetThreshold   ///< probe set threshold, <tt>nProbesetThreshold < nProbesetSize</tt>. If 0, <tt>nProbesetThreshold = nProbesetSize - 1</tt>
+            , hash_tuple_type const& h    ///< hash functor tuple of type <tt>std::tuple<H1, H2, ... Hn></tt> where <tt> n == \ref c_nArity </tt>
+        )
+            : m_nProbesetSize( calc_probeset_size(nProbesetSize))
+            , m_nProbesetThreshold( nProbesetThreshold ? nProbesetThreshold : m_nProbesetSize - 1)
+            , m_Hash( h )
+            , m_MutexPolicy( cds::beans::ceil2(nInitialSize ? nInitialSize : c_nDefaultInitialSize ))
+        {
+            check_common_constraints();
+            check_probeset_properties();
+
+            allocate_bucket_tables( nInitialSize ? cds::beans::ceil2( nInitialSize ) : c_nDefaultInitialSize );
+        }
+
+        /// Constructs the set object with given hash functor tuple (move semantics)
+        /**
+            The probe set size and threshold are set as default, see \p CuckooSet()
+        */
+        CuckooSet(
+            hash_tuple_type&& h     ///< hash functor tuple of type <tt>std::tuple<H1, H2, ... Hn></tt> where <tt> n == \ref c_nArity </tt>
+        )
+            : m_nProbesetSize( calc_probeset_size(0))
+            , m_nProbesetThreshold( m_nProbesetSize / 2 )
+            , m_Hash( std::forward<hash_tuple_type>(h))
+            , m_MutexPolicy( c_nDefaultInitialSize )
+        {
+            check_common_constraints();
+            check_probeset_properties();
+
+            allocate_bucket_tables( c_nDefaultInitialSize );
+        }
+
+        /// Constructs the set object with given probe set properties and hash functor tuple (move semantics)
+        /**
+            If probe set type is <tt> cuckoo::vector<Capacity> </tt> vector
+            then \p nProbesetSize should be equal to vector's \p Capacity.
+        */
+        CuckooSet(
+            size_t nInitialSize                 ///< Initial set size; if 0 - use default initial size \p c_nDefaultInitialSize
+            , unsigned int nProbesetSize        ///< probe set size, positive integer
+            , unsigned int nProbesetThreshold   ///< probe set threshold, <tt>nProbesetThreshold < nProbesetSize</tt>. If 0, <tt>nProbesetThreshold = nProbesetSize - 1</tt>
+            , hash_tuple_type&& h    ///< hash functor tuple of type <tt>std::tuple<H1, H2, ... Hn></tt> where <tt> n == \ref c_nArity </tt>
+        )
+            : m_nProbesetSize( calc_probeset_size(nProbesetSize))
+            , m_nProbesetThreshold( nProbesetThreshold ? nProbesetThreshold : m_nProbesetSize - 1)
+            , m_Hash( std::forward<hash_tuple_type>(h))
+            , m_MutexPolicy( cds::beans::ceil2(nInitialSize ? nInitialSize : c_nDefaultInitialSize ))
+        {
+            check_common_constraints();
+            check_probeset_properties();
+
+            allocate_bucket_tables( nInitialSize ? cds::beans::ceil2( nInitialSize ) : c_nDefaultInitialSize );
+        }
+
+        /// Destructor
+        ~CuckooSet()
+        {
+            free_bucket_tables();
+        }
+
+    public:
+        /// Inserts new node
+        /**
+            The function inserts \p val in the set if it does not contain an item with key equal to \p val.
+
+            Returns \p true if \p val is inserted into the set, \p false otherwise.
+        */
+        bool insert( value_type& val )
+        {
+            return insert( val, []( value_type& ) {} );
+        }
+
+        /// Inserts new node
+        /**
+            The function allows to split creating of new item into two part:
+            - create item with key only
+            - insert new item into the set
+            - if inserting is success, calls  \p f functor to initialize value-field of \p val.
+
+            The functor signature is:
+            \code
+                void func( value_type& val );
+            \endcode
+            where \p val is the item inserted.
+
+            The user-defined functor is called only if the inserting is success.
+        */
+        template <typename Func>
+        bool insert( value_type& val, Func f )
+        {
+            hash_array arrHash;
+            position arrPos[ c_nArity ];
+            unsigned int nGoalTable;
+
+            hashing( arrHash, val );
+            node_type * pNode = node_traits::to_node_ptr( val );
+            store_hash( pNode, arrHash );
+
+            while (true) {
+                {
+                    scoped_cell_lock guard( m_MutexPolicy, arrHash );
+
+                    if ( contains( arrPos, arrHash, val, key_predicate()) != c_nUndefTable ) {
+                        m_Stat.onInsertFailed();
+                        return false;
+                    }
+
+                    for ( unsigned int i = 0; i < c_nArity; ++i ) {
+                        bucket_entry& refBucket = bucket( i, arrHash[i] );
+                        if ( refBucket.size() < m_nProbesetThreshold ) {
+                            refBucket.insert_after( arrPos[i].itPrev, pNode );
+                            f( val );
+                            ++m_ItemCounter;
+                            m_Stat.onInsertSuccess();
+                            return true;
+                        }
+                    }
+
+                    for ( unsigned int i = 0; i < c_nArity; ++i ) {
+                        bucket_entry& refBucket = bucket( i, arrHash[i] );
+                        if ( refBucket.size() < m_nProbesetSize ) {
+                            refBucket.insert_after( arrPos[i].itPrev, pNode );
+                            f( val );
+                            ++m_ItemCounter;
+                            nGoalTable = i;
+                            assert( refBucket.size() > 1 );
+                            copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()));
+                            goto do_relocate;
+                        }
+                    }
+                }
+
+                m_Stat.onInsertResize();
+                resize();
+            }
+
+        do_relocate:
+            m_Stat.onInsertRelocate();
+            if ( !relocate( nGoalTable, arrHash )) {
+                m_Stat.onInsertRelocateFault();
+                m_Stat.onInsertResize();
+                resize();
+            }
+
+            m_Stat.onInsertSuccess();
+            return true;
+        }
+
+        /// Updates the node
+        /**
+            The operation performs inserting or changing data with lock-free manner.
+
+            If the item \p val is not found in the set, then \p val is inserted into the set
+            iff \p bAllowInsert is \p true.
+            Otherwise, the functor \p func is called with item found.
+            The functor \p func signature is:
+            \code
+                void func( bool bNew, value_type& item, value_type& val );
+            \endcode
+            with arguments:
+            - \p bNew - \p true if the item has been inserted, \p false otherwise
+            - \p item - item of the set
+            - \p val - argument \p val passed into the \p %update() function
+            If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
+            refer to the same thing.
+
+            The functor may change non-key fields of the \p item.
+
+            Returns std::pair<bool, bool> where \p first is \p true if operation is successful,
+            i.e. the node has been inserted or updated,
+            \p second is \p true if new item has been added or \p false if the item with \p key
+            already exists.
+        */
+        template <typename Func>
+        std::pair<bool, bool> update( value_type& val, Func func, bool bAllowInsert = true )
+        {
+            hash_array arrHash;
+            position arrPos[ c_nArity ];
+            unsigned int nGoalTable;
+
+            hashing( arrHash, val );
+            node_type * pNode = node_traits::to_node_ptr( val );
+            store_hash( pNode, arrHash );
+
+            while (true) {
+                {
+                    scoped_cell_lock guard( m_MutexPolicy, arrHash );
+
+                    unsigned int nTable = contains( arrPos, arrHash, val, key_predicate());
+                    if ( nTable != c_nUndefTable ) {
+                        func( false, *node_traits::to_value_ptr( *arrPos[nTable].itFound ), val );
+                        m_Stat.onUpdateExist();
+                        return std::make_pair( true, false );
+                    }
+
+                    if ( !bAllowInsert )
+                        return std::make_pair( false, false );
+
+                    //node_type * pNode = node_traits::to_node_ptr( val );
+                    //store_hash( pNode, arrHash );
+
+                    for ( unsigned int i = 0; i < c_nArity; ++i ) {
+                        bucket_entry& refBucket = bucket( i, arrHash[i] );
+                        if ( refBucket.size() < m_nProbesetThreshold ) {
+                            refBucket.insert_after( arrPos[i].itPrev, pNode );
+                            func( true, val, val );
+                            ++m_ItemCounter;
+                            m_Stat.onUpdateSuccess();
+                            return std::make_pair( true, true );
+                        }
+                    }
+
+                    for ( unsigned int i = 0; i < c_nArity; ++i ) {
+                        bucket_entry& refBucket = bucket( i, arrHash[i] );
+                        if ( refBucket.size() < m_nProbesetSize ) {
+                            refBucket.insert_after( arrPos[i].itPrev, pNode );
+                            func( true, val, val );
+                            ++m_ItemCounter;
+                            nGoalTable = i;
+                            assert( refBucket.size() > 1 );
+                            copy_hash( arrHash, *node_traits::to_value_ptr( *refBucket.begin()));
+                            goto do_relocate;
+                        }
+                    }
+                }
+
+                m_Stat.onUpdateResize();
+                resize();
+            }
+
+        do_relocate:
+            m_Stat.onUpdateRelocate();
+            if ( !relocate( nGoalTable, arrHash )) {
+                m_Stat.onUpdateRelocateFault();
+                m_Stat.onUpdateResize();
+                resize();
+            }
+
+            m_Stat.onUpdateSuccess();
+            return std::make_pair( true, true );
+        }
+        //@cond
+        template <typename Func>
+        CDS_DEPRECATED("ensure() is deprecated, use update()")
+        std::pair<bool, bool> ensure( value_type& val, Func func )
+        {
+            return update( val, func, true );
+        }
+        //@endcond
+
+        /// Unlink the item \p val from the set
+        /**
+            The function searches the item \p val in the set and unlink it
+            if it is found and is equal to \p val (here, the equality means that
+            \p val belongs to the set: if \p item is an item found then
+            unlink is successful iif <tt>&val == &item</tt>)
+
+            The function returns \p true if success and \p false otherwise.
+        */
+        bool unlink( value_type& val )
+        {
+            hash_array arrHash;
+            hashing( arrHash, val );
+            position arrPos[ c_nArity ];
+
+            {
+                scoped_cell_lock guard( m_MutexPolicy, arrHash );
+
+                unsigned int nTable = contains( arrPos, arrHash, val, key_predicate());
+                if ( nTable != c_nUndefTable && node_traits::to_value_ptr(*arrPos[nTable].itFound) == &val ) {
+                    bucket( nTable, arrHash[nTable]).remove( arrPos[nTable].itPrev, arrPos[nTable].itFound );
+                    --m_ItemCounter;
+                    m_Stat.onUnlinkSuccess();
+                    return true;
+                }
+            }
+
+            m_Stat.onUnlinkFailed();
+            return false;
+        }
+
+        /// Deletes the item from the set
+        /** \anchor cds_intrusive_CuckooSet_erase
+            The function searches an item with key equal to \p val in the set,
+            unlinks it from the set, and returns a pointer to unlinked item.
+
+            If the item with key equal to \p val is not found the function return \p nullptr.
+
+            Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type.
+        */
+        template <typename Q>
+        value_type * erase( Q const& val )
+        {
+            return erase( val, [](value_type const&) {} );
+        }
+
+        /// Deletes the item from the set using \p pred predicate for searching
+        /**
+            The function is an analog of \ref cds_intrusive_CuckooSet_erase "erase(Q const&)"
+            but \p pred is used for key comparing.
+            If cuckoo set is ordered, then \p Predicate should have the interface and semantics like \p std::less.
+            If cuckoo set is unordered, then \p Predicate should have the interface and semantics like \p std::equal_to.
+            \p Predicate must imply the same element order as the comparator used for building the set.
+        */
+        template <typename Q, typename Predicate>
+        value_type * erase_with( Q const& val, Predicate pred )
+        {
+            CDS_UNUSED( pred );
+            return erase_( val, typename predicate_wrapper<Predicate>::type(), [](value_type const&) {} );
+        }
+
+        /// Delete the item from the set
+        /** \anchor cds_intrusive_CuckooSet_erase_func
+            The function searches an item with key equal to \p val in the set,
+            call \p f functor with item found, unlinks it from the set, and returns a pointer to unlinked item.
+
+            The \p Func interface is
+            \code
+            struct functor {
+                void operator()( value_type const& item );
+            };
+            \endcode
+
+            If the item with key equal to \p val is not found the function return \p nullptr.
+
+            Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type.
+        */
+        template <typename Q, typename Func>
+        value_type * erase( Q const& val, Func f )
+        {
+            return erase_( val, key_predicate(), f );
+        }
+
+        /// Deletes the item from the set using \p pred predicate for searching
+        /**
+            The function is an analog of \ref cds_intrusive_CuckooSet_erase_func "erase(Q const&, Func)"
+            but \p pred is used for key comparing.
+            If you use ordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::less.
+            If you use unordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::equal_to.
+            \p Predicate must imply the same element order as the comparator used for building the set.
+        */
+        template <typename Q, typename Predicate, typename Func>
+        value_type * erase_with( Q const& val, Predicate pred, Func f )
+        {
+            CDS_UNUSED( pred );
+            return erase_( val, typename predicate_wrapper<Predicate>::type(), f );
+        }
+
+        /// Find the key \p val
+        /** \anchor cds_intrusive_CuckooSet_find_func
+            The function searches the item with key equal to \p val and calls the functor \p f for item found.
+            The interface of \p Func functor is:
+            \code
+            struct functor {
+                void operator()( value_type& item, Q& val );
+            };
+            \endcode
+            where \p item is the item found, \p val is the <tt>find</tt> function argument.
+
+            The functor may change non-key fields of \p item.
+
+            The \p val argument is non-const since it can be used as \p f functor destination i.e., the functor
+            may modify both arguments.
+
+            Note the hash functor specified for class \p Traits template parameter
+            should accept a parameter of type \p Q that can be not the same as \p value_type.
+
+            The function returns \p true if \p val is found, \p false otherwise.
+        */
+        template <typename Q, typename Func>
+        bool find( Q& val, Func f )
+        {
+            return find_( val, key_predicate(), f );
+        }
+        //@cond
+        template <typename Q, typename Func>
+        bool find( Q const& val, Func f )
+        {
+            return find_( val, key_predicate(), f );
+        }
+        //@endcond
+
+        /// Find the key \p val using \p pred predicate for comparing
+        /**
+            The function is an analog of \ref cds_intrusive_CuckooSet_find_func "find(Q&, Func)"
+            but \p pred is used for key comparison.
+            If you use ordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::less.
+            If you use unordered cuckoo set, then \p Predicate should have the interface and semantics like \p std::equal_to.
+            \p pred must imply the same element order as the comparator used for building the set.
+        */
+        template <typename Q, typename Predicate, typename Func>
+        bool find_with( Q& val, Predicate pred, Func f )
+        {
+            CDS_UNUSED( pred );
+            return find_( val, typename predicate_wrapper<Predicate>::type(), f );
+        }
+        //@cond
+        template <typename Q, typename Predicate, typename Func>
+        bool find_with( Q const& val, Predicate pred, Func f )
+        {
+            CDS_UNUSED( pred );
+            return find_( val, typename predicate_wrapper<Predicate>::type(), f );
+        }
+        //@endcond
+
+        /// Checks whether the set contains \p key
+        /**
+            The function searches the item with key equal to \p key
+            and returns \p true if it is found, and \p false otherwise.
+        */
+        template <typename Q>
+        bool contains( Q const& key )
+        {
+            return find( key, [](value_type&, Q const& ) {} );
+        }
+        //@cond
+        template <typename Q>
+        CDS_DEPRECATED("deprecated, use contains()")
+        bool find( Q const& key )
+        {
+            return contains( key );
+        }
+        //@endcond
+
+        /// Checks whether the set contains \p key using \p pred predicate for searching
+        /**
+            The function is similar to <tt>contains( key )</tt> but \p pred is used for key comparing.
+            If the set is unordered, \p Predicate has semantics like \p std::equal_to.
+            For ordered set \p Predicate has \p std::less semantics. In that case \p pred
+            must imply the same element order as the comparator used for building the set.
+        */
+        template <typename Q, typename Predicate>
+        bool contains( Q const& key, Predicate pred )
+        {
+            CDS_UNUSED( pred );
+            return find_with( key, typename predicate_wrapper<Predicate>::type(), [](value_type& , Q const& ) {} );
+        }
+        //@cond
+        template <typename Q, typename Predicate>
+        CDS_DEPRECATED("deprecated, use contains()")
+        bool find_with( Q const& key, Predicate pred )
+        {
+            return contains( key, pred );
+        }
+        //@endcond
+
+        /// Clears the set
+        /**
+            The function unlinks all items from the set.
+            For any item \p Traits::disposer is called
+        */
+        void clear()
+        {
+            clear_and_dispose( disposer());
+        }
+
+        /// Clears the set and calls \p disposer for each item
+        /**
+            The function unlinks all items from the set calling \p oDisposer for each item.
+            \p Disposer functor interface is:
+            \code
+            struct Disposer{
+                void operator()( value_type * p );
+            };
+            \endcode
+
+            The \p Traits::disposer is not called.
+        */
+        template <typename Disposer>
+        void clear_and_dispose( Disposer oDisposer )
+        {
+            // locks entire array
+            scoped_full_lock sl( m_MutexPolicy );
+
+            for ( unsigned int i = 0; i < c_nArity; ++i ) {
+                bucket_entry * pEntry = m_BucketTable[i];
+                bucket_entry * pEnd = pEntry + m_nBucketMask.load( atomics::memory_order_relaxed ) + 1;
+                for ( ; pEntry != pEnd ; ++pEntry ) {
+                    pEntry->clear( [&oDisposer]( node_type * pNode ){ oDisposer( node_traits::to_value_ptr( pNode )) ; } );
+                }
+            }
+            m_ItemCounter.reset();
+        }
+
+        /// Checks if the set is empty
+        /**
+            Emptiness is checked by item counting: if item count is zero then the set is empty.
+        */
+        bool empty() const
+        {
+            return size() == 0;
+        }
+
+        /// Returns item count in the set
+        size_t size() const
+        {
+            return m_ItemCounter;
+        }
+
+        /// Returns the size of hash table
+        /**
+            The hash table size is non-constant and can be increased via resizing.
+        */
+        size_t bucket_count() const
+        {
+            return m_nBucketMask.load( atomics::memory_order_relaxed ) + 1;
+        }
+        //@cond
+        size_t bucket_count( atomics::memory_order load_mo ) const
+        {
+            return m_nBucketMask.load( load_mo ) + 1;
+        }
+        //@endcond
+
+        /// Returns lock array size
+        size_t lock_count() const
+        {
+            return m_MutexPolicy.lock_count();
+        }
+
+        /// Returns const reference to internal statistics
+        stat const& statistics() const
+        {
+            return m_Stat;
+        }
+
+        /// Returns const reference to mutex policy internal statistics
+        typename mutex_policy::statistics_type const& mutex_policy_statistics() const
+        {
+            return m_MutexPolicy.statistics();
+        }
+    };
+}} // namespace cds::intrusive
+
+#endif // #ifndef CDSLIB_INTRUSIVE_CUCKOO_SET_H