Added container::MultiLevelHashSet<RCU> specialization
authorkhizmax <libcds.dev@gmail.com>
Sun, 4 Oct 2015 09:34:51 +0000 (12:34 +0300)
committerkhizmax <libcds.dev@gmail.com>
Sun, 4 Oct 2015 09:34:51 +0000 (12:34 +0300)
19 files changed:
cds/container/impl/multilevel_hashset.h
cds/container/multilevel_hashset_rcu.h [new file with mode: 0644]
cds/intrusive/multilevel_hashset_rcu.h
projects/Win/vc12/cds.vcxproj
projects/Win/vc12/cds.vcxproj.filters
projects/Win/vc12/hdr-test-set.vcxproj
projects/Win/vc12/hdr-test-set.vcxproj.filters
projects/Win/vc14/cds.vcxproj
projects/Win/vc14/cds.vcxproj.filters
projects/Win/vc14/hdr-test-set.vcxproj
projects/Win/vc14/hdr-test-set.vcxproj.filters
projects/source.test-hdr.mk
tests/test-hdr/CMakeLists.txt
tests/test-hdr/set/hdr_multilevel_hashset.h
tests/test-hdr/set/hdr_multilevel_hashset_rcu_gpb.cpp [new file with mode: 0644]
tests/test-hdr/set/hdr_multilevel_hashset_rcu_gpi.cpp [new file with mode: 0644]
tests/test-hdr/set/hdr_multilevel_hashset_rcu_gpt.cpp [new file with mode: 0644]
tests/test-hdr/set/hdr_multilevel_hashset_rcu_shb.cpp [new file with mode: 0644]
tests/test-hdr/set/hdr_multilevel_hashset_rcu_sht.cpp [new file with mode: 0644]

index 8a353e59ba21d57a2ca0dbeea0705f68f02240cd..9d3341bab869443dd879e20637960b9ffe528ce3 100644 (file)
@@ -204,7 +204,8 @@ namespace cds { namespace container {
 
             If the \p val key not found in the set, then the new item created from \p val
             will be inserted into the set iff \p bInsert is \p true.
-            Otherwise, if \p val is found, it is replaced with new item created from \p val.
+            Otherwise, if \p val is found, it is replaced with new item created from \p val
+            and previous item is disposed.
             In both cases \p func functor is called.
 
             The functor \p Func signature:
@@ -225,11 +226,9 @@ namespace cds { namespace container {
             i.e. the item has been inserted or updated,
             \p second is \p true if the new item has been added or \p false if the item with key equal to \p val
             already exists.
-
-            @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
         */
         template <typename Q, typename Func>
-        std::pair<bool, bool> update( const Q& val, Func func, bool bInsert = true )
+        std::pair<bool, bool> update( Q const& val, Func func, bool bInsert = true )
         {
             scoped_node_ptr sp( cxx_node_allocator().New( val ));
             std::pair<bool, bool> bRes = base_class::do_update( *sp, func, bInsert );
@@ -304,7 +303,7 @@ namespace cds { namespace container {
         /// Extracts the item with specified \p hash
         /**
             The function searches \p hash in the set,
-            unlinks it from the set, and returns an guarded pointer to the item extracted.
+            unlinks it from the set, and returns a guarded pointer to the item extracted.
             If \p hash is not found the function returns an empty guarded pointer.
 
             The item returned is reclaimed by garbage collector \p GC
diff --git a/cds/container/multilevel_hashset_rcu.h b/cds/container/multilevel_hashset_rcu.h
new file mode 100644 (file)
index 0000000..b84e344
--- /dev/null
@@ -0,0 +1,546 @@
+//$$CDS-header$$
+
+#ifndef CDSLIB_CONTAINER_MULTILEVEL_HASHSET_RCU_H
+#define CDSLIB_CONTAINER_MULTILEVEL_HASHSET_RCU_H
+
+#include <cds/intrusive/multilevel_hashset_rcu.h>
+#include <cds/container/details/multilevel_hashset_base.h>
+
+namespace cds { namespace container {
+
+    /// Hash set based on multi-level array, \ref cds_urcu_desc "RCU" specialization
+    /** @ingroup cds_nonintrusive_set
+        @anchor cds_container_MultilevelHashSet_rcu
+
+        Source:
+        - [2013] Steven Feldman, Pierre LaBorde, Damian Dechev "Concurrent Multi-level Arrays:
+                 Wait-free Extensible Hash Maps"
+
+        See algorithm short description @ref cds_intrusive_MultilevelHashSet_RCU "here"
+
+        @note Two important things you should keep in mind when you're using \p %MultiLevelHashSet:
+        - all keys must be fixed-size. It means that you cannot use \p std::string as a key for \p %MultiLevelHashSet.
+          Instead, for the strings you should use well-known hashing algorithms like <a href="https://en.wikipedia.org/wiki/Secure_Hash_Algorithm">SHA1, SHA2</a>,
+          <a href="https://en.wikipedia.org/wiki/MurmurHash">MurmurHash</a>, <a href="https://en.wikipedia.org/wiki/CityHash">CityHash</a>
+          or its successor <a href="https://code.google.com/p/farmhash/">FarmHash</a> and so on, which
+          converts variable-length strings to fixed-length bit-strings, and use that hash as a key in \p %MultiLevelHashSet.
+        - \p %MultiLevelHashSet uses a perfect hashing. It means that if two different keys, for example, of type \p std::string,
+          have identical hash then you cannot insert both that keys in the set. \p %MultiLevelHashSet does not maintain the key,
+          it maintains its fixed-size hash value.
+
+        The set supports @ref cds_container_MultilevelHashSet_iterators "bidirectional thread-safe iterators".
+
+        Template parameters:
+        - \p RCU - one of \ref cds_urcu_gc "RCU type"
+        - \p T - a value type to be stored in the set
+        - \p Traits - type traits, the structure based on \p multilevel_hashset::traits or result of \p multilevel_hashset::make_traits metafunction.
+            \p Traits is the mandatory argument because it has one mandatory type - an @ref multilevel_hashset::traits::hash_accessor "accessor"
+            to hash value of \p T. The set algorithm does not calculate that hash value.
+
+            @note Before including <tt><cds/intrusive/multilevel_hashset_rcu.h></tt> you should include appropriate RCU header file,
+            see \ref cds_urcu_gc "RCU type" for list of existing RCU class and corresponding header files.
+
+            The set supports @ref cds_container_MultilevelHashSet_rcu_iterators "bidirectional thread-safe iterators"
+            with some restrictions.
+    */
+    template <
+        class RCU
+        , typename T
+#ifdef CDS_DOXYGEN_INVOKED
+        , class Traits = multilevel_hashset::traits
+#else
+        , class Traits
+#endif
+    >
+    class MultiLevelHashSet< cds::urcu::gc< RCU >, T, Traits >
+#ifdef CDS_DOXYGEN_INVOKED
+        : protected cds::intrusive::MultiLevelHashSet< cds::urcu::gc< RCU >, T, Traits >
+#else
+        : protected cds::container::details::make_multilevel_hashset< cds::urcu::gc< RCU >, T, Traits >::type
+#endif
+    {
+        //@cond
+        typedef cds::container::details::make_multilevel_hashset< cds::urcu::gc< RCU >, T, Traits > maker;
+        typedef typename maker::type base_class;
+        //@endcond
+
+    public:
+        typedef cds::urcu::gc< RCU > gc; ///< RCU garbage collector
+        typedef T       value_type; ///< type of value stored in the set
+        typedef Traits  traits;     ///< Traits template parameter, see \p multilevel_hashset::traits
+
+        typedef typename base_class::hash_accessor hash_accessor; ///< Hash accessor functor
+        typedef typename base_class::hash_type hash_type; ///< Hash type deduced from \p hash_accessor return type
+        typedef typename base_class::hash_comparator hash_comparator; ///< hash compare functor based on \p opt::compare and \p opt::less option setter
+
+        typedef typename traits::item_counter   item_counter;   ///< Item counter type
+        typedef typename traits::allocator      allocator;      ///< Element allocator
+        typedef typename traits::node_allocator node_allocator; ///< Array node allocator
+        typedef typename traits::memory_model   memory_model;   ///< Memory model
+        typedef typename traits::back_off       back_off;       ///< Backoff strategy
+        typedef typename traits::stat           stat;           ///< Internal statistics type
+        typedef typename traits::rcu_check_deadlock rcu_check_deadlock; ///< Deadlock checking policy
+        typedef typename gc::scoped_lock       rcu_lock;        ///< RCU scoped lock
+        static CDS_CONSTEXPR const bool c_bExtractLockExternal = false; ///< Group of \p extract_xxx functions does not require external locking
+        typedef typename base_class::exempt_ptr exempt_ptr; ///< pointer to extracted node
+
+        typedef typename base_class::iterator               iterator;       ///< @ref cds_container_MultilevelHashSet_rcu_iterators "bidirectional iterator" type
+        typedef typename base_class::const_iterator         const_iterator; ///< @ref cds_container_MultilevelHashSet_rcu_iterators "bidirectional const iterator" type
+        typedef typename base_class::reverse_iterator       reverse_iterator;       ///< @ref cds_container_MultilevelHashSet_rcu_iterators "bidirectional reverse iterator" type
+        typedef typename base_class::const_reverse_iterator const_reverse_iterator; ///< @ref cds_container_MultilevelHashSet_rcu_iterators "bidirectional reverse const iterator" type
+
+    protected:
+        //@cond
+        typedef typename maker::cxx_node_allocator cxx_node_allocator;
+        typedef std::unique_ptr< value_type, typename maker::node_disposer > scoped_node_ptr;
+        //@endcond
+
+    public:
+        /// Creates empty set
+        /**
+            @param head_bits: 2<sup>head_bits</sup> specifies the size of head array, minimum is 4.
+            @param array_bits: 2<sup>array_bits</sup> specifies the size of array node, minimum is 2.
+
+            Equation for \p head_bits and \p array_bits:
+            \code
+            sizeof(hash_type) * 8 == head_bits + N * array_bits
+            \endcode
+            where \p N is multi-level array depth.
+        */
+        MultiLevelHashSet( size_t head_bits = 8, size_t array_bits = 4 )
+            : base_class( head_bits, array_bits )
+        {}
+
+        /// Destructs the set and frees all data
+        ~MultiLevelHashSet()
+        {}
+
+        /// Inserts new element
+        /**
+            The function creates an element with copy of \p val value and then inserts it into the set.
+
+            The type \p Q should contain as minimum the complete hash for the element.
+            The object of \ref value_type should be constructible from a value of type \p Q.
+            In trivial case, \p Q is equal to \ref value_type.
+
+            Returns \p true if \p val is inserted into the set, \p false otherwise.
+
+            The function locks RCU internally.
+        */
+        template <typename Q>
+        bool insert( Q const& val )
+        {
+            scoped_node_ptr sp( cxx_node_allocator().New( val ));
+            if ( base_class::insert( *sp )) {
+                sp.release();
+                return true;
+            }
+            return false;
+        }
+
+        /// Inserts new element
+        /**
+            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-fields of \p val.
+
+            The functor signature is:
+            \code
+                void func( value_type& val );
+            \endcode
+            where \p val is the item inserted. User-defined functor \p f should guarantee that during changing
+            \p val no any other changes could be made on this set's item by concurrent threads.
+            The user-defined functor is called only if the inserting is success.
+
+            The function locks RCU internally.
+        */
+        template <typename Q, typename Func>
+        bool insert( Q const& val, Func f )
+        {
+            scoped_node_ptr sp( cxx_node_allocator().New( val ));
+            if ( base_class::insert( *sp, f )) {
+                sp.release();
+                return true;
+            }
+            return false;
+        }
+
+        /// Updates the element
+        /**
+            The operation performs inserting or replacing with lock-free manner.
+
+            If the \p val key not found in the set, then the new item created from \p val
+            will be inserted into the set iff \p bInsert is \p true.
+            Otherwise, if \p val is found, it is replaced with new item created from \p val
+            and previous item is disposed.
+            In both cases \p func functor is called.
+
+            The functor \p Func signature:
+            \code
+                struct my_functor {
+                    void operator()( value_type& cur, value_type * prev );
+                };
+            \endcode
+            where:
+            - \p cur - current element
+            - \p prev - pointer to previous element with such hash. \p prev is \p nullptr
+                 if \p cur was just inserted.
+
+            The functor may change non-key fields of the \p item; however, \p func must guarantee
+            that during changing no any other modifications could be made on this item by concurrent threads.
+
+            Returns <tt> std::pair<bool, bool> </tt> where \p first is \p true if operation is successfull,
+            i.e. the item has been inserted or updated,
+            \p second is \p true if the new item has been added or \p false if the item with key equal to \p val
+            already exists.
+        */
+        template <typename Q, typename Func>
+        std::pair<bool, bool> update( Q const& val, Func func, bool bInsert = true )
+        {
+            scoped_node_ptr sp( cxx_node_allocator().New( val ));
+            std::pair<bool, bool> bRes = base_class::do_update( *sp, func, bInsert );
+            if ( bRes.first )
+                sp.release();
+            return bRes;
+        }
+
+        /// Inserts data of type \p value_type created in-place from <tt>std::forward<Args>(args)...</tt>
+        /**
+            Returns \p true if inserting successful, \p false otherwise.
+        */
+        template <typename... Args>
+        bool emplace( Args&&... args )
+        {
+            scoped_node_ptr sp( cxx_node_allocator().New( std::forward<Args>(args)... ));
+            if ( base_class::insert( *sp )) {
+                sp.release();
+                return true;
+            }
+            return false;
+        }
+
+        /// Deletes the item from the set
+        /**
+            The function searches \p hash in the set,
+            deletes the item found, and returns \p true.
+            If that item is not found the function returns \p false.
+        */
+        bool erase( hash_type const& hash )
+        {
+            return base_class::erase( hash );
+        }
+
+        /// Deletes the item from the set
+        /**
+            The function searches \p hash in the set,
+            call \p f functor with item found, and deltes the element from the set.
+
+            The \p Func interface is
+            \code
+            struct functor {
+                void operator()( value_type& item );
+            };
+            \endcode
+
+            If \p hash is not found the function returns \p false.
+        */
+        template <typename Func>
+        bool erase( hash_type const& hash, Func f )
+        {
+            return base_class::erase( hash, f );
+        }
+
+        /// Extracts the item with specified \p hash
+        /**
+            The function searches \p hash in the set,
+            unlinks it from the set, and returns \ref cds::urcu::exempt_ptr "exempt_ptr" pointer to the item found.
+            If the item with key equal to \p key is not found the function returns an empty \p exempt_ptr.
+
+            RCU \p synchronize method can be called. RCU should NOT be locked.
+            The function does not call the disposer for the item found.
+            The disposer will be implicitly invoked when the returned object is destroyed or when
+            its \p release() member function is called.
+            Example:
+            \code
+            typedef cds::container::MultiLevelHashSet< cds::urcu::gc< cds::urcu::general_buffered<> >, foo, my_traits > set_type;
+            set_type theSet;
+            // ...
+
+            typename set_type::exempt_ptr ep( theSet.extract( 5 ));
+            if ( ep ) {
+                // Deal with ep
+                //...
+
+                // Dispose returned item.
+                ep.release();
+            }
+            \endcode
+        */
+        exempt_ptr extract( hash_type const& hash )
+        {
+            return base_class::extract( hash );
+        }
+
+        /// Finds an item by it's \p hash
+        /**
+            The function searches the item by \p hash and calls the functor \p f for item found.
+            The interface of \p Func functor is:
+            \code
+            struct functor {
+                void operator()( value_type& item );
+            };
+            \endcode
+            where \p item is the item found.
+
+            The functor may change non-key fields of \p item. Note that the functor is only guarantee
+            that \p item cannot be disposed during the functor is executing.
+            The functor does not serialize simultaneous access to the set's \p item. If such access is
+            possible you must provide your own synchronization schema on item level to prevent unsafe item modifications.
+
+            The function returns \p true if \p hash is found, \p false otherwise.
+        */
+        template <typename Func>
+        bool find( hash_type const& hash, Func f )
+        {
+            return base_class::find( hash, f );
+        }
+
+        /// Checks whether the set contains \p hash
+        /**
+            The function searches the item by its \p hash
+            and returns \p true if it is found, or \p false otherwise.
+        */
+        bool contains( hash_type const& hash )
+        {
+            return base_class::contains( hash );
+        }
+
+        /// Finds an item by it's \p hash and returns the item found
+        /**
+            The function searches the item by its \p hash
+            and returns the pointer to the item found.
+            If \p hash is not found the function returns \p nullptr.
+
+            RCU should be locked before the function invocation.
+            Returned pointer is valid only while RCU is locked.
+
+            Usage:
+            \code
+            typedef cds::container::MultiLevelHashSet< your_template_params >  my_set;
+            my_set theSet;
+            // ...
+            {
+                // lock RCU
+                my_set::rcu_lock;
+
+                foo * p = theSet.get( 5 );
+                if ( p ) {
+                    // Deal with p
+                    //...
+                }
+            }
+            \endcode
+        */
+        value_type * get( hash_type const& hash )
+        {
+            return base_class::get( hash );
+        }
+
+        /// Clears the set (non-atomic)
+        /**
+            The function unlink all data node from the set.
+            The function is not atomic but is thread-safe.
+            After \p %clear() the set may not be empty because another threads may insert items.
+        */
+        void clear()
+        {
+            base_class::clear();
+        }
+
+        /// Checks if the set is empty
+        /**
+            Emptiness is checked by item counting: if item count is zero then the set is empty.
+            Thus, the correct item counting feature is an important part of the set implementation.
+        */
+        bool empty() const
+        {
+            return base_class::empty();
+        }
+
+        /// Returns item count in the set
+        size_t size() const
+        {
+            return base_class::size();
+        }
+
+        /// Returns const reference to internal statistics
+        stat const& statistics() const
+        {
+            return base_class::statistics();
+        }
+
+        /// Returns the size of head node
+        size_t head_size() const
+        {
+            return base_class::head_size();
+        }
+
+        /// Returns the size of the array node
+        size_t array_node_size() const
+        {
+            return base_class::array_node_size();
+        }
+
+    public:
+        ///@name Thread-safe iterators
+        /** @anchor cds_container_MultilevelHashSet_rcu_iterators
+            The set supports thread-safe iterators: you may iterate over the set in multi-threaded environment
+            under explicit RCU lock.
+            RCU lock requirement means that inserting or searching is allowed but you must not erase the items from the set
+            since erasing under RCU lock can lead to a deadlock. However, another thread can call \p erase() safely
+            while your thread is iterating.
+
+            A typical example is:
+            \code
+            struct foo {
+                uint32_t    hash;
+                // ... other fields
+                uint32_t    payload; // only for example
+            };
+            struct set_traits: cds::container::multilevel_hashset::traits
+            {
+                struct hash_accessor {
+                    uint32_t operator()( foo const& src ) const
+                    {
+                        retur src.hash;
+                    }
+                };
+            };
+
+            typedef cds::urcu::gc< cds::urcu::general_buffered<>> rcu;
+            typedef cds::container::MultiLevelHashSet< rcu, foo, set_traits > set_type;
+
+            set_type s;
+
+            // ...
+
+            // iterate over the set
+            {
+                // lock the RCU.
+                typename set_type::rcu_lock l; // scoped RCU lock
+
+                // traverse the set
+                for ( auto i = s.begin(); i != s.end(); ++i ) {
+                    // deal with i. Remember, erasing is prohibited here!
+                    i->payload++;
+                }
+            } // at this point RCU lock is released
+            /endcode
+
+            Each iterator object supports the common interface:
+            - dereference operators:
+                @code
+                value_type [const] * operator ->() noexcept
+                value_type [const] & operator *() noexcept
+                @endcode
+            - pre-increment and pre-decrement. Post-operators is not supported
+            - equality operators <tt>==</tt> and <tt>!=</tt>.
+                Iterators are equal iff they point to the same cell of the same array node.
+                Note that for two iterators \p it1 and \p it2 the condition <tt> it1 == it2 </tt>
+                does not entail <tt> &(*it1) == &(*it2) </tt>: welcome to concurrent containers
+
+            @note It is possible the item can be iterated more that once, for example, if an iterator points to the item
+            in an array node that is being splitted.
+        */
+    ///@{
+
+        /// Returns an iterator to the beginning of the set
+        iterator begin()
+        {
+            return base_class::begin();
+        }
+
+        /// Returns an const iterator to the beginning of the set
+        const_iterator begin() const
+        {
+            return base_class::begin();
+        }
+
+        /// Returns an const iterator to the beginning of the set
+        const_iterator cbegin()
+        {
+            return base_class::cbegin();
+        }
+
+        /// Returns an iterator to the element following the last element of the set. This element acts as a placeholder; attempting to access it results in undefined behavior.
+        iterator end()
+        {
+            return base_class::end();
+        }
+
+        /// Returns a const iterator to the element following the last element of the set. This element acts as a placeholder; attempting to access it results in undefined behavior.
+        const_iterator end() const
+        {
+            return base_class::end();
+        }
+
+        /// Returns a const iterator to the element following the last element of the set. This element acts as a placeholder; attempting to access it results in undefined behavior.
+        const_iterator cend()
+        {
+            return base_class::cend();
+        }
+
+        /// Returns a reverse iterator to the first element of the reversed set
+        reverse_iterator rbegin()
+        {
+            return base_class::rbegin();
+        }
+
+        /// Returns a const reverse iterator to the first element of the reversed set
+        const_reverse_iterator rbegin() const
+        {
+            return base_class::rbegin();
+        }
+
+        /// Returns a const reverse iterator to the first element of the reversed set
+        const_reverse_iterator crbegin()
+        {
+            return base_class::crbegin();
+        }
+
+        /// Returns a reverse iterator to the element following the last element of the reversed set
+        /**
+            It corresponds to the element preceding the first element of the non-reversed container.
+            This element acts as a placeholder, attempting to access it results in undefined behavior.
+        */
+        reverse_iterator rend()
+        {
+            return base_class::rend();
+        }
+
+        /// Returns a const reverse iterator to the element following the last element of the reversed set
+        /**
+            It corresponds to the element preceding the first element of the non-reversed container.
+            This element acts as a placeholder, attempting to access it results in undefined behavior.
+        */
+        const_reverse_iterator rend() const
+        {
+            return base_class::rend();
+        }
+
+        /// Returns a const reverse iterator to the element following the last element of the reversed set
+        /**
+            It corresponds to the element preceding the first element of the non-reversed container.
+            This element acts as a placeholder, attempting to access it results in undefined behavior.
+        */
+        const_reverse_iterator crend()
+        {
+            return base_class::crend();
+        }
+    ///@}
+    };
+
+}} // namespace cds::container
+
+#endif // #ifndef CDSLIB_CONTAINER_MULTILEVEL_HASHSET_RCU_H
index 3146a6ad84cc0732965c518f097f4237e5c7908c..1c0886127aa0afcc1e59afe8e291a56e0aeaa22e 100644 (file)
@@ -24,6 +24,16 @@ namespace cds { namespace intrusive {
 
         See algorithm short description @ref cds_intrusive_MultilevelHashSet_hp "here"
 
+        @note Two important things you should keep in mind when you're using \p %MultiLevelHashSet:
+        - all keys must be fixed-size. It means that you cannot use \p std::string as a key for \p %MultiLevelHashSet.
+          Instead, for the strings you should use well-known hashing algorithms like <a href="https://en.wikipedia.org/wiki/Secure_Hash_Algorithm">SHA1, SHA2</a>,
+          <a href="https://en.wikipedia.org/wiki/MurmurHash">MurmurHash</a>, <a href="https://en.wikipedia.org/wiki/CityHash">CityHash</a>
+          or its successor <a href="https://code.google.com/p/farmhash/">FarmHash</a> and so on, which
+          converts variable-length strings to fixed-length bit-strings, and use that hash as a key in \p %MultiLevelHashSet.
+        - \p %MultiLevelHashSet uses a perfect hashing. It means that if two different keys, for example, of type \p std::string,
+          have identical hash then you cannot insert both that keys in the set. \p %MultiLevelHashSet does not maintain the key,
+          it maintains its fixed-size hash value.
+
         Template parameters:
         - \p RCU - one of \ref cds_urcu_gc "RCU type"
         - \p T - a value type to be stored in the set
@@ -458,7 +468,7 @@ namespace cds { namespace intrusive {
             // ...
             {
                 // lock RCU
-                my_set::rcu_lock
+                my_set::rcu_lock;
 
                 foo * p = theSet.get( 5 );
                 if ( p ) {
index d4769161bba1d89cdfd1aa33098d5ecae83a9da7..f83e634dff046c66c664ec145c0c5db579dd6c81 100644 (file)
     <ClInclude Include="..\..\..\cds\container\multilevel_hashmap_hp.h" />\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashset_dhp.h" />\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashset_hp.h" />\r
+    <ClInclude Include="..\..\..\cds\container\multilevel_hashset_rcu.h" />\r
     <ClInclude Include="..\..\..\cds\container\skip_list_map_dhp.h" />\r
     <ClInclude Include="..\..\..\cds\container\skip_list_map_hp.h" />\r
     <ClInclude Include="..\..\..\cds\container\skip_list_map_nogc.h" />\r
index c228d1801efe08ee52d43e6b3efc1ac863f057f6..dff39dfadca8d97e0ecf078b7bf22c2e86ec8904 100644 (file)
     <ClInclude Include="..\..\..\cds\intrusive\multilevel_hashset_rcu.h">\r
       <Filter>Header Files\cds\intrusive</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\..\..\cds\container\multilevel_hashset_rcu.h">\r
+      <Filter>Header Files\cds\container</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
index 3d30e7a33b034da0e816f4670f9b2a906b393613..098bcba113e9ff034db1bb3cba2c7a1b2802e306 100644 (file)
     <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_michael_set_rcu_sht.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_dhp.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_hp.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_gpb.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_gpi.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_gpt.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_shb.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_sht.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_skiplist_set_dhp.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_skiplist_set_hp.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_skiplist_set_nogc.cpp" />\r
index f9c2520e402283eb71cba6a5d34e58a5d063cf88..0f5788e0dbe97ad28a15b039127672184d3ee077 100644 (file)
     <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_intrusive_multilevel_hashset_rcu_sht.cpp">\r
       <Filter>intrusive\multilevel_hashset</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_gpb.cpp">\r
+      <Filter>container\multilevel_hashset</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_gpi.cpp">\r
+      <Filter>container\multilevel_hashset</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_gpt.cpp">\r
+      <Filter>container\multilevel_hashset</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_shb.cpp">\r
+      <Filter>container\multilevel_hashset</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_sht.cpp">\r
+      <Filter>container\multilevel_hashset</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
index 7be0c3f36475023f4d5898bbaf0bdf61f488c657..b194cbc4526f033f5e1c38f20c75c59f5114012b 100644 (file)
     <ClInclude Include="..\..\..\cds\container\multilevel_hashmap_hp.h" />\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashset_dhp.h" />\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashset_hp.h" />\r
+    <ClInclude Include="..\..\..\cds\container\multilevel_hashset_rcu.h" />\r
     <ClInclude Include="..\..\..\cds\container\skip_list_map_dhp.h" />\r
     <ClInclude Include="..\..\..\cds\container\skip_list_map_hp.h" />\r
     <ClInclude Include="..\..\..\cds\container\skip_list_map_nogc.h" />\r
index c228d1801efe08ee52d43e6b3efc1ac863f057f6..dff39dfadca8d97e0ecf078b7bf22c2e86ec8904 100644 (file)
     <ClInclude Include="..\..\..\cds\intrusive\multilevel_hashset_rcu.h">\r
       <Filter>Header Files\cds\intrusive</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\..\..\cds\container\multilevel_hashset_rcu.h">\r
+      <Filter>Header Files\cds\container</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
index a18d6719d364f4c01bf13e827383421e1a724a6c..afce40f90129dece08ac2b1a11f2c8d5e9182ebb 100644 (file)
     <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_michael_set_rcu_sht.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_dhp.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_hp.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_gpb.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_gpi.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_gpt.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_shb.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_sht.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_skiplist_set_dhp.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_skiplist_set_hp.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_skiplist_set_nogc.cpp" />\r
index 08d97627332aded817f854f5a3a273e35676b1e8..d9522f15405770dd6243edb6abd9a2b81bb9d309 100644 (file)
     <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_intrusive_multilevel_hashset_rcu_sht.cpp">\r
       <Filter>intrusive\multilevel_hashset</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_gpi.cpp">\r
+      <Filter>container\multilevel_hashset</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_gpb.cpp">\r
+      <Filter>container\multilevel_hashset</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_gpt.cpp">\r
+      <Filter>container\multilevel_hashset</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_shb.cpp">\r
+      <Filter>container\multilevel_hashset</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\set\hdr_multilevel_hashset_rcu_sht.cpp">\r
+      <Filter>container\multilevel_hashset</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
index 3517c56f456e933331f112617e1310490f1571c4..696dfadc97f5bd04792e3ac17ef5620f5dfedf0e 100644 (file)
@@ -181,6 +181,11 @@ CDS_TESTHDR_SET := \
     tests/test-hdr/set/hdr_michael_set_lazy_nogc.cpp \
     tests/test-hdr/set/hdr_multilevel_hashset_hp.cpp \
     tests/test-hdr/set/hdr_multilevel_hashset_dhp.cpp \
+    tests/test-hdr/set/hdr_multilevel_hashset_rcu_gpb.cpp \
+    tests/test-hdr/set/hdr_multilevel_hashset_rcu_gpi.cpp \
+    tests/test-hdr/set/hdr_multilevel_hashset_rcu_gpt.cpp \
+    tests/test-hdr/set/hdr_multilevel_hashset_rcu_shb.cpp \
+    tests/test-hdr/set/hdr_multilevel_hashset_rcu_sht.cpp \
     tests/test-hdr/set/hdr_refinable_hashset_hashset_std.cpp \
     tests/test-hdr/set/hdr_refinable_hashset_boost_flat_set.cpp \
     tests/test-hdr/set/hdr_refinable_hashset_boost_list.cpp \
index 55590de9404d3c2e777fa311df05196b39fa6772..f6a4ccd162fbbe043b8e4daaaa962d883dda47df 100644 (file)
@@ -183,6 +183,11 @@ set(CDS_TESTHDR_SET
     set/hdr_michael_set_lazy_nogc.cpp\r
     set/hdr_multilevel_hashset_hp.cpp\r
     set/hdr_multilevel_hashset_dhp.cpp\r
+    set/hdr_multilevel_hashset_rcu_gpb.cpp\r
+    set/hdr_multilevel_hashset_rcu_gpi.cpp\r
+    set/hdr_multilevel_hashset_rcu_gpt.cpp\r
+    set/hdr_multilevel_hashset_rcu_shb.cpp\r
+    set/hdr_multilevel_hashset_rcu_sht.cpp\r
     set/hdr_refinable_hashset_hashset_std.cpp\r
     set/hdr_refinable_hashset_boost_flat_set.cpp\r
     set/hdr_refinable_hashset_boost_list.cpp\r
index 24a5989c639b5f050b059139d94a54e73e121ee1..2c5f39556a8d5e30fc39573929e3a791639fc189 100644 (file)
@@ -387,6 +387,255 @@ namespace set {
             CPPUNIT_MSG( s.statistics() );
         }
 
+        template <typename Set, typename Hasher>
+        void test_rcu(size_t nHeadBits, size_t nArrayBits)
+        {
+            typedef typename Set::hash_type hash_type;
+            typedef typename Set::value_type value_type;
+            typedef Arg<hash_type> arg_type;
+            typedef typename Set::exempt_ptr exempt_ptr;
+            typedef typename Set::rcu_lock rcu_lock;
+
+            Hasher hasher;
+
+            size_t const capacity = 1000;
+
+            Set s(nHeadBits, nArrayBits);
+            CPPUNIT_MSG("Array size: head=" << s.head_size() << ", array_node=" << s.array_node_size());
+            CPPUNIT_ASSERT(s.head_size() >= (size_t(1) << nHeadBits));
+            CPPUNIT_ASSERT(s.array_node_size() == (size_t(1) << nArrayBits));
+
+            CPPUNIT_ASSERT(s.empty());
+            CPPUNIT_ASSERT(s.size() == 0);
+
+            // insert test
+            for (size_t i = 0; i < capacity; ++i) {
+                hash_type h = hasher(i);
+                CPPUNIT_ASSERT(!s.contains(h));
+                CPPUNIT_ASSERT(s.insert(value_type(i, h)));
+                CPPUNIT_ASSERT(s.contains(h));
+
+                CPPUNIT_ASSERT(!s.empty());
+                CPPUNIT_ASSERT(s.size() == i + 1);
+
+                CPPUNIT_ASSERT(!s.insert(arg_type(i, h)));
+                CPPUNIT_ASSERT(s.size() == i + 1);
+            }
+
+            // update existing test
+            for (size_t i = 0; i < capacity; ++i) {
+                hash_type h = hasher(i);
+                CPPUNIT_ASSERT(s.contains(h));
+                std::pair<bool, bool> ret = s.update(arg_type(i, h),
+                    [](value_type& i, value_type * prev) {
+                    CPPUNIT_ASSERT_CURRENT(prev != nullptr);
+                    CPPUNIT_ASSERT_CURRENT(i.key == prev->key);
+                    CPPUNIT_ASSERT_CURRENT(i.hash == prev->hash);
+                    i.nInsertCall += 1;
+                }, false);
+                CPPUNIT_ASSERT(ret.first);
+                CPPUNIT_ASSERT(!ret.second);
+                CPPUNIT_ASSERT(s.contains(h));
+                CPPUNIT_ASSERT(s.size() == capacity);
+
+                {
+                    rcu_lock l;
+                    value_type * p = s.get(h);
+                    CPPUNIT_ASSERT(p);
+                    CPPUNIT_ASSERT(p->nInsertCall == 1);
+                    CPPUNIT_ASSERT(p->key == i);
+                    CPPUNIT_ASSERT(p->hash == h);
+                }
+            }
+
+            // erase test
+            for (size_t i = 0; i < capacity; ++i) {
+                CPPUNIT_ASSERT(!s.empty());
+                CPPUNIT_ASSERT(s.size() == capacity - i);
+                CPPUNIT_ASSERT(s.find(hasher(i), [](value_type &) {}));
+                CPPUNIT_ASSERT(s.erase(hasher(i)));
+                CPPUNIT_ASSERT(!s.find(hasher(i), [](value_type &) {}));
+                CPPUNIT_ASSERT(s.size() == capacity - i - 1);
+            }
+            CPPUNIT_ASSERT(s.empty());
+
+            // Iterators on empty set
+            {
+                rcu_lock l;
+                CPPUNIT_ASSERT(s.begin() == s.end());
+                CPPUNIT_ASSERT(s.cbegin() == s.cend());
+                CPPUNIT_ASSERT(s.rbegin() == s.rend());
+                CPPUNIT_ASSERT(s.crbegin() == s.crend());
+            }
+
+            // insert with functor
+            for (size_t i = capacity; i > 0; --i) {
+                CPPUNIT_ASSERT(s.size() == capacity - i);
+                CPPUNIT_ASSERT(s.insert(arg_type(i, hasher(i)), [](value_type& val) { val.nInsertCall += 1; }));
+                CPPUNIT_ASSERT(s.size() == capacity - i + 1);
+                CPPUNIT_ASSERT(!s.empty());
+
+                CPPUNIT_ASSERT(s.find(hasher(i), [](value_type& val) {
+                    CPPUNIT_ASSERT_CURRENT(val.nInsertCall == 1);
+                    val.nFindCall += 1;
+                }));
+            }
+            CPPUNIT_ASSERT(s.size() == capacity);
+
+            // for-each iterator test
+            {
+                rcu_lock l;
+                for (auto& el : s) {
+                    CPPUNIT_ASSERT(el.nInsertCall == 1);
+                    CPPUNIT_ASSERT(el.nFindCall == 1);
+                    el.nFindCall += 1;
+                }
+            }
+
+            // iterator test
+            {
+                rcu_lock l;
+                for (auto it = s.begin(), itEnd = s.end(); it != itEnd; ++it) {
+                    CPPUNIT_ASSERT(it->nInsertCall == 1);
+                    CPPUNIT_ASSERT(it->nFindCall == 2);
+                    it->nFindCall += 1;
+                }
+            }
+
+            // reverse iterator test
+            {
+                rcu_lock l;
+                for (auto it = s.rbegin(), itEnd = s.rend(); it != itEnd; ++it) {
+                    CPPUNIT_ASSERT(it->nInsertCall == 1);
+                    CPPUNIT_ASSERT(it->nFindCall == 3);
+                    it->nFindCall += 1;
+                }
+            }
+
+            // const iterator test
+            {
+                rcu_lock l;
+                for (auto it = s.cbegin(), itEnd = s.cend(); it != itEnd; ++it) {
+                    CPPUNIT_ASSERT(it->nInsertCall == 1);
+                    CPPUNIT_ASSERT(it->nFindCall == 4);
+                    it->nIteratorCall += 1;
+                }
+            }
+
+            // const reverse iterator test
+            {
+                rcu_lock l;
+                for (auto it = s.rbegin(), itEnd = s.rend(); it != itEnd; ++it) {
+                    CPPUNIT_ASSERT(it->nInsertCall == 1);
+                    CPPUNIT_ASSERT(it->nFindCall == 4);
+                    CPPUNIT_ASSERT(it->nIteratorCall == 1);
+                    it->nIteratorCall += 1;
+                }
+            }
+
+            // check completeness
+            for (size_t i = 1; i <= capacity; ++i) {
+                CPPUNIT_ASSERT(s.find(hasher(i), [](value_type const& el) {
+                    CPPUNIT_ASSERT_CURRENT(el.nInsertCall == 1);
+                    CPPUNIT_ASSERT_CURRENT(el.nFindCall == 4);
+                    CPPUNIT_ASSERT_CURRENT(el.nIteratorCall == 2);
+                }));
+            }
+
+            // erase with functor test
+            {
+                size_t nSum = 0;
+                for (size_t i = 1; i <= capacity; ++i) {
+                    CPPUNIT_ASSERT(s.size() == capacity - i + 1);
+                    CPPUNIT_ASSERT(s.erase(hasher(i), [&nSum](value_type const& val) {
+                        CPPUNIT_ASSERT_CURRENT(val.nInsertCall == 1);
+                        CPPUNIT_ASSERT_CURRENT(val.nFindCall == 4);
+                        CPPUNIT_ASSERT_CURRENT(val.nIteratorCall == 2);
+                        nSum += val.key;
+                    }));
+                    CPPUNIT_ASSERT(s.size() == capacity - i);
+                    CPPUNIT_ASSERT(!s.erase(hasher(i), [&nSum](value_type const& val) { nSum += val.key; }))
+                }
+                CPPUNIT_ASSERT(s.empty());
+                CPPUNIT_ASSERT(nSum == (1 + capacity) * capacity / 2);
+            }
+
+            // update test with insert allowing
+            for (size_t i = 0; i < capacity; ++i) {
+                hash_type h = hasher(i);
+                CPPUNIT_ASSERT(!s.contains(h));
+
+                {
+                    rcu_lock l;
+                    value_type * p = s.get(h);
+                    CPPUNIT_ASSERT(!p);
+                }
+                std::pair<bool, bool> ret = s.update(arg_type(i, h),
+                    [](value_type& i, value_type * prev) {
+                    CPPUNIT_ASSERT_CURRENT(prev == nullptr);
+                    i.nInsertCall += 1;
+                });
+                CPPUNIT_ASSERT(ret.first);
+                CPPUNIT_ASSERT(ret.second);
+                CPPUNIT_ASSERT(s.contains(h));
+                CPPUNIT_ASSERT(s.size() == i + 1);
+
+                {
+                    rcu_lock l;
+                    value_type * p = s.get(h);
+                    CPPUNIT_ASSERT(p);
+                    CPPUNIT_ASSERT(p->nInsertCall == 1);
+                    CPPUNIT_ASSERT(p->key == i);
+                    CPPUNIT_ASSERT(p->hash == h);
+                }
+            }
+            CPPUNIT_ASSERT(!s.empty());
+            CPPUNIT_ASSERT(s.size() == capacity);
+
+            s.clear();
+            CPPUNIT_ASSERT(s.empty());
+            CPPUNIT_ASSERT(s.size() == 0);
+
+            // emplace test
+            for (size_t i = 0; i < capacity; ++i) {
+                hash_type h = hasher(i);
+                CPPUNIT_ASSERT(!s.contains(h));
+                CPPUNIT_ASSERT(s.emplace(i, hasher(i)));
+                CPPUNIT_ASSERT(s.contains(h));
+
+                CPPUNIT_ASSERT(!s.empty());
+                CPPUNIT_ASSERT(s.size() == i + 1);
+
+                CPPUNIT_ASSERT(!s.emplace(arg_type(i, h)));
+                CPPUNIT_ASSERT(s.size() == i + 1);
+            }
+            CPPUNIT_ASSERT(!s.empty());
+            CPPUNIT_ASSERT(s.size() == capacity);
+
+            // extract test
+            for (size_t i = capacity; i != 0; --i) {
+                CPPUNIT_ASSERT(!s.empty());
+                CPPUNIT_ASSERT(s.size() == i);
+
+                exempt_ptr gp{ s.extract(hasher(i - 1)) };
+                CPPUNIT_ASSERT(gp);
+                CPPUNIT_ASSERT(gp->key == i - 1);
+                CPPUNIT_ASSERT(gp->hash == hasher(i - 1));
+                CPPUNIT_ASSERT(!s.contains(hasher(i - 1)));
+
+                {
+                    rcu_lock l;
+                    value_type * p = s.get(hasher(i - 1));
+                    CPPUNIT_ASSERT( p == nullptr );
+                }
+                CPPUNIT_ASSERT(s.size() == i - 1);
+            }
+            CPPUNIT_ASSERT(s.empty());
+            CPPUNIT_ASSERT(s.size() == 0);
+
+            CPPUNIT_MSG(s.statistics());
+        }
+
         void hp_stdhash();
         void hp_stdhash_stat();
         void hp_stdhash_5_3();
@@ -405,6 +654,51 @@ namespace set {
         void dhp_hash128_4_3();
         void dhp_hash128_4_3_stat();
 
+        void rcu_gpi_stdhash();
+        void rcu_gpi_stdhash_stat();
+        void rcu_gpi_stdhash_5_3();
+        void rcu_gpi_stdhash_5_3_stat();
+        void rcu_gpi_hash128();
+        void rcu_gpi_hash128_stat();
+        void rcu_gpi_hash128_4_3();
+        void rcu_gpi_hash128_4_3_stat();
+
+        void rcu_gpb_stdhash();
+        void rcu_gpb_stdhash_stat();
+        void rcu_gpb_stdhash_5_3();
+        void rcu_gpb_stdhash_5_3_stat();
+        void rcu_gpb_hash128();
+        void rcu_gpb_hash128_stat();
+        void rcu_gpb_hash128_4_3();
+        void rcu_gpb_hash128_4_3_stat();
+
+        void rcu_gpt_stdhash();
+        void rcu_gpt_stdhash_stat();
+        void rcu_gpt_stdhash_5_3();
+        void rcu_gpt_stdhash_5_3_stat();
+        void rcu_gpt_hash128();
+        void rcu_gpt_hash128_stat();
+        void rcu_gpt_hash128_4_3();
+        void rcu_gpt_hash128_4_3_stat();
+
+        void rcu_shb_stdhash();
+        void rcu_shb_stdhash_stat();
+        void rcu_shb_stdhash_5_3();
+        void rcu_shb_stdhash_5_3_stat();
+        void rcu_shb_hash128();
+        void rcu_shb_hash128_stat();
+        void rcu_shb_hash128_4_3();
+        void rcu_shb_hash128_4_3_stat();
+
+        void rcu_sht_stdhash();
+        void rcu_sht_stdhash_stat();
+        void rcu_sht_stdhash_5_3();
+        void rcu_sht_stdhash_5_3_stat();
+        void rcu_sht_hash128();
+        void rcu_sht_hash128_stat();
+        void rcu_sht_hash128_4_3();
+        void rcu_sht_hash128_4_3_stat();
+
         CPPUNIT_TEST_SUITE(MultiLevelHashSetHdrTest)
             CPPUNIT_TEST(hp_stdhash)
             CPPUNIT_TEST(hp_stdhash_stat)
@@ -423,6 +717,51 @@ namespace set {
             CPPUNIT_TEST(dhp_hash128_stat)
             CPPUNIT_TEST(dhp_hash128_4_3)
             CPPUNIT_TEST(dhp_hash128_4_3_stat)
+
+            CPPUNIT_TEST(rcu_gpi_stdhash)
+            CPPUNIT_TEST(rcu_gpi_stdhash_stat)
+            CPPUNIT_TEST(rcu_gpi_stdhash_5_3)
+            CPPUNIT_TEST(rcu_gpi_stdhash_5_3_stat)
+            CPPUNIT_TEST(rcu_gpi_hash128)
+            CPPUNIT_TEST(rcu_gpi_hash128_stat)
+            CPPUNIT_TEST(rcu_gpi_hash128_4_3)
+            CPPUNIT_TEST(rcu_gpi_hash128_4_3_stat)
+
+            CPPUNIT_TEST(rcu_gpb_stdhash)
+            CPPUNIT_TEST(rcu_gpb_stdhash_stat)
+            CPPUNIT_TEST(rcu_gpb_stdhash_5_3)
+            CPPUNIT_TEST(rcu_gpb_stdhash_5_3_stat)
+            CPPUNIT_TEST(rcu_gpb_hash128)
+            CPPUNIT_TEST(rcu_gpb_hash128_stat)
+            CPPUNIT_TEST(rcu_gpb_hash128_4_3)
+            CPPUNIT_TEST(rcu_gpb_hash128_4_3_stat)
+
+            CPPUNIT_TEST(rcu_gpt_stdhash)
+            CPPUNIT_TEST(rcu_gpt_stdhash_stat)
+            CPPUNIT_TEST(rcu_gpt_stdhash_5_3)
+            CPPUNIT_TEST(rcu_gpt_stdhash_5_3_stat)
+            CPPUNIT_TEST(rcu_gpt_hash128)
+            CPPUNIT_TEST(rcu_gpt_hash128_stat)
+            CPPUNIT_TEST(rcu_gpt_hash128_4_3)
+            CPPUNIT_TEST(rcu_gpt_hash128_4_3_stat)
+
+            CPPUNIT_TEST(rcu_shb_stdhash)
+            CPPUNIT_TEST(rcu_shb_stdhash_stat)
+            CPPUNIT_TEST(rcu_shb_stdhash_5_3)
+            CPPUNIT_TEST(rcu_shb_stdhash_5_3_stat)
+            CPPUNIT_TEST(rcu_shb_hash128)
+            CPPUNIT_TEST(rcu_shb_hash128_stat)
+            CPPUNIT_TEST(rcu_shb_hash128_4_3)
+            CPPUNIT_TEST(rcu_shb_hash128_4_3_stat)
+
+            CPPUNIT_TEST(rcu_sht_stdhash)
+            CPPUNIT_TEST(rcu_sht_stdhash_stat)
+            CPPUNIT_TEST(rcu_sht_stdhash_5_3)
+            CPPUNIT_TEST(rcu_sht_stdhash_5_3_stat)
+            CPPUNIT_TEST(rcu_sht_hash128)
+            CPPUNIT_TEST(rcu_sht_hash128_stat)
+            CPPUNIT_TEST(rcu_sht_hash128_4_3)
+            CPPUNIT_TEST(rcu_sht_hash128_4_3_stat)
         CPPUNIT_TEST_SUITE_END()
     };
 
diff --git a/tests/test-hdr/set/hdr_multilevel_hashset_rcu_gpb.cpp b/tests/test-hdr/set/hdr_multilevel_hashset_rcu_gpb.cpp
new file mode 100644 (file)
index 0000000..57fe646
--- /dev/null
@@ -0,0 +1,206 @@
+//$$CDS-header$$
+
+#include "set/hdr_multilevel_hashset.h"
+#include <cds/urcu/general_buffered.h>
+#include <cds/container/multilevel_hashset_rcu.h>
+#include "unit/print_multilevel_hashset_stat.h"
+
+namespace set {
+    namespace {
+        typedef cds::urcu::gc<cds::urcu::general_buffered<>> rcu_type;
+    } // namespace
+
+    void MultiLevelHashSetHdrTest::rcu_gpb_stdhash()
+    {
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(4, 2);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpb_hash128()
+    {
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef hash128::less less;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash128!!!" );
+        test_rcu<set_type, hash128::make>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                , co::less< hash_type::less >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash128::make>(4, 2);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpb_stdhash_stat()
+    {
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef cc::multilevel_hashset::stat<> stat;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::stat< cc::multilevel_hashset::stat<>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(4, 2);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpb_hash128_stat()
+    {
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef hash128::cmp  compare;
+            typedef cc::multilevel_hashset::stat<> stat;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash_type!!!" );
+        test_rcu<set_type, hash_type::make>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::stat< cc::multilevel_hashset::stat<>>
+                ,co::compare< hash128::cmp >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash_type::make>(4, 2);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpb_stdhash_5_3()
+    {
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(5, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(5, 3);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpb_hash128_4_3()
+    {
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef co::v::sequential_consistent memory_model;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash_type!!!" );
+        test_rcu<set_type, hash128::make >(4, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::memory_model< co::v::sequential_consistent >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash128::make >(4, 3);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpb_stdhash_5_3_stat()
+    {
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef cc::multilevel_hashset::stat<> stat;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(5, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::stat< cc::multilevel_hashset::stat<>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(5, 3);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpb_hash128_4_3_stat()
+    {
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef cc::multilevel_hashset::stat<> stat;
+            typedef hash128::less less;
+            typedef hash128::cmp compare;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash_type!!!" );
+        test_rcu<set_type, hash_type::make>(4, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                , co::stat< cc::multilevel_hashset::stat<>>
+                , co::less< hash_type::less >
+                , co::compare< hash128::cmp >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash_type::make>(4, 3);
+    }
+} // namespace set
diff --git a/tests/test-hdr/set/hdr_multilevel_hashset_rcu_gpi.cpp b/tests/test-hdr/set/hdr_multilevel_hashset_rcu_gpi.cpp
new file mode 100644 (file)
index 0000000..8f49d77
--- /dev/null
@@ -0,0 +1,206 @@
+//$$CDS-header$$
+
+#include "set/hdr_multilevel_hashset.h"
+#include <cds/urcu/general_instant.h>
+#include <cds/container/multilevel_hashset_rcu.h>
+#include "unit/print_multilevel_hashset_stat.h"
+
+namespace set {
+    namespace {
+        typedef cds::urcu::gc<cds::urcu::general_instant<>> rcu_type;
+    } // namespace
+
+    void MultiLevelHashSetHdrTest::rcu_gpi_stdhash()
+    {
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(4, 2);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpi_hash128()
+    {
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef hash128::less less;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash128!!!" );
+        test_rcu<set_type, hash128::make>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                , co::less< hash_type::less >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash128::make>(4, 2);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpi_stdhash_stat()
+    {
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef cc::multilevel_hashset::stat<> stat;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::stat< cc::multilevel_hashset::stat<>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(4, 2);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpi_hash128_stat()
+    {
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef hash128::cmp  compare;
+            typedef cc::multilevel_hashset::stat<> stat;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash_type!!!" );
+        test_rcu<set_type, hash_type::make>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::stat< cc::multilevel_hashset::stat<>>
+                ,co::compare< hash128::cmp >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash_type::make>(4, 2);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpi_stdhash_5_3()
+    {
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(5, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(5, 3);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpi_hash128_4_3()
+    {
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef co::v::sequential_consistent memory_model;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash_type!!!" );
+        test_rcu<set_type, hash128::make >(4, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::memory_model< co::v::sequential_consistent >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash128::make >(4, 3);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpi_stdhash_5_3_stat()
+    {
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef cc::multilevel_hashset::stat<> stat;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(5, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::stat< cc::multilevel_hashset::stat<>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(5, 3);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpi_hash128_4_3_stat()
+    {
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef cc::multilevel_hashset::stat<> stat;
+            typedef hash128::less less;
+            typedef hash128::cmp compare;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash_type!!!" );
+        test_rcu<set_type, hash_type::make>(4, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                , co::stat< cc::multilevel_hashset::stat<>>
+                , co::less< hash_type::less >
+                , co::compare< hash128::cmp >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash_type::make>(4, 3);
+    }
+} // namespace set
diff --git a/tests/test-hdr/set/hdr_multilevel_hashset_rcu_gpt.cpp b/tests/test-hdr/set/hdr_multilevel_hashset_rcu_gpt.cpp
new file mode 100644 (file)
index 0000000..400ff18
--- /dev/null
@@ -0,0 +1,206 @@
+//$$CDS-header$$
+
+#include "set/hdr_multilevel_hashset.h"
+#include <cds/urcu/general_threaded.h>
+#include <cds/container/multilevel_hashset_rcu.h>
+#include "unit/print_multilevel_hashset_stat.h"
+
+namespace set {
+    namespace {
+        typedef cds::urcu::gc<cds::urcu::general_threaded<>> rcu_type;
+    } // namespace
+
+    void MultiLevelHashSetHdrTest::rcu_gpt_stdhash()
+    {
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(4, 2);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpt_hash128()
+    {
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef hash128::less less;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash128!!!" );
+        test_rcu<set_type, hash128::make>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                , co::less< hash_type::less >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash128::make>(4, 2);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpt_stdhash_stat()
+    {
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef cc::multilevel_hashset::stat<> stat;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::stat< cc::multilevel_hashset::stat<>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(4, 2);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpt_hash128_stat()
+    {
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef hash128::cmp  compare;
+            typedef cc::multilevel_hashset::stat<> stat;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash_type!!!" );
+        test_rcu<set_type, hash_type::make>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::stat< cc::multilevel_hashset::stat<>>
+                ,co::compare< hash128::cmp >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash_type::make>(4, 2);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpt_stdhash_5_3()
+    {
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(5, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(5, 3);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpt_hash128_4_3()
+    {
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef co::v::sequential_consistent memory_model;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash_type!!!" );
+        test_rcu<set_type, hash128::make >(4, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::memory_model< co::v::sequential_consistent >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash128::make >(4, 3);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpt_stdhash_5_3_stat()
+    {
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef cc::multilevel_hashset::stat<> stat;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(5, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::stat< cc::multilevel_hashset::stat<>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(5, 3);
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_gpt_hash128_4_3_stat()
+    {
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef cc::multilevel_hashset::stat<> stat;
+            typedef hash128::less less;
+            typedef hash128::cmp compare;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash_type!!!" );
+        test_rcu<set_type, hash_type::make>(4, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                , co::stat< cc::multilevel_hashset::stat<>>
+                , co::less< hash_type::less >
+                , co::compare< hash128::cmp >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash_type::make>(4, 3);
+    }
+} // namespace set
diff --git a/tests/test-hdr/set/hdr_multilevel_hashset_rcu_shb.cpp b/tests/test-hdr/set/hdr_multilevel_hashset_rcu_shb.cpp
new file mode 100644 (file)
index 0000000..319d923
--- /dev/null
@@ -0,0 +1,224 @@
+//$$CDS-header$$
+
+#include "set/hdr_multilevel_hashset.h"
+#include <cds/urcu/signal_buffered.h>
+#include <cds/container/multilevel_hashset_rcu.h>
+#include "unit/print_multilevel_hashset_stat.h"
+
+namespace set {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+    namespace {
+        typedef cds::urcu::gc<cds::urcu::signal_buffered<>> rcu_type;
+    } // namespace
+#endif
+
+    void MultiLevelHashSetHdrTest::rcu_shb_stdhash()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(4, 2);
+#endif
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_shb_hash128()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef hash128::less less;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash128!!!" );
+        test_rcu<set_type, hash128::make>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                , co::less< hash_type::less >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash128::make>(4, 2);
+#endif
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_shb_stdhash_stat()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef cc::multilevel_hashset::stat<> stat;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::stat< cc::multilevel_hashset::stat<>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(4, 2);
+#endif
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_shb_hash128_stat()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef hash128::cmp  compare;
+            typedef cc::multilevel_hashset::stat<> stat;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash_type!!!" );
+        test_rcu<set_type, hash_type::make>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::stat< cc::multilevel_hashset::stat<>>
+                ,co::compare< hash128::cmp >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash_type::make>(4, 2);
+#endif
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_shb_stdhash_5_3()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(5, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(5, 3);
+#endif
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_shb_hash128_4_3()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef co::v::sequential_consistent memory_model;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash_type!!!" );
+        test_rcu<set_type, hash128::make >(4, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::memory_model< co::v::sequential_consistent >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash128::make >(4, 3);
+#endif
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_shb_stdhash_5_3_stat()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef cc::multilevel_hashset::stat<> stat;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(5, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::stat< cc::multilevel_hashset::stat<>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(5, 3);
+#endif
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_shb_hash128_4_3_stat()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef cc::multilevel_hashset::stat<> stat;
+            typedef hash128::less less;
+            typedef hash128::cmp compare;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash_type!!!" );
+        test_rcu<set_type, hash_type::make>(4, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                , co::stat< cc::multilevel_hashset::stat<>>
+                , co::less< hash_type::less >
+                , co::compare< hash128::cmp >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash_type::make>(4, 3);
+#endif
+    }
+} // namespace set
diff --git a/tests/test-hdr/set/hdr_multilevel_hashset_rcu_sht.cpp b/tests/test-hdr/set/hdr_multilevel_hashset_rcu_sht.cpp
new file mode 100644 (file)
index 0000000..34415de
--- /dev/null
@@ -0,0 +1,224 @@
+//$$CDS-header$$
+
+#include "set/hdr_multilevel_hashset.h"
+#include <cds/urcu/signal_threaded.h>
+#include <cds/container/multilevel_hashset_rcu.h>
+#include "unit/print_multilevel_hashset_stat.h"
+
+namespace set {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+    namespace {
+        typedef cds::urcu::gc<cds::urcu::signal_threaded<>> rcu_type;
+    } // namespace
+#endif
+
+    void MultiLevelHashSetHdrTest::rcu_sht_stdhash()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(4, 2);
+#endif
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_sht_hash128()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef hash128::less less;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash128!!!" );
+        test_rcu<set_type, hash128::make>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                , co::less< hash_type::less >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash128::make>(4, 2);
+#endif
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_sht_stdhash_stat()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef cc::multilevel_hashset::stat<> stat;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::stat< cc::multilevel_hashset::stat<>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(4, 2);
+#endif
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_sht_hash128_stat()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef hash128::cmp  compare;
+            typedef cc::multilevel_hashset::stat<> stat;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash_type!!!" );
+        test_rcu<set_type, hash_type::make>(4, 2);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::stat< cc::multilevel_hashset::stat<>>
+                ,co::compare< hash128::cmp >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash_type::make>(4, 2);
+#endif
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_sht_stdhash_5_3()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(5, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(5, 3);
+#endif
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_sht_hash128_4_3()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef co::v::sequential_consistent memory_model;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash_type!!!" );
+        test_rcu<set_type, hash128::make >(4, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::memory_model< co::v::sequential_consistent >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash128::make >(4, 3);
+#endif
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_sht_stdhash_5_3_stat()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef size_t hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef cc::multilevel_hashset::stat<> stat;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, size_t>::value, "set::hash_type != size_t!!!" );
+        test_rcu<set_type, std::hash<hash_type>>(5, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                ,co::stat< cc::multilevel_hashset::stat<>>
+            >::type
+        > set_type2;
+        test_rcu<set_type2, std::hash<hash_type>>(5, 3);
+#endif
+    }
+
+    void MultiLevelHashSetHdrTest::rcu_sht_hash128_4_3_stat()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef hash128 hash_type;
+
+        struct traits: public cc::multilevel_hashset::traits
+        {
+            typedef get_hash<hash_type> hash_accessor;
+            typedef cc::multilevel_hashset::stat<> stat;
+            typedef hash128::less less;
+            typedef hash128::cmp compare;
+        };
+        typedef cc::MultiLevelHashSet< rcu_type, Item<hash_type>, traits > set_type;
+        static_assert(std::is_same< typename set_type::hash_type, hash_type>::value, "set::hash_type != hash_type!!!" );
+        test_rcu<set_type, hash_type::make>(4, 3);
+
+        typedef cc::MultiLevelHashSet<
+            rcu_type,
+            Item<hash_type>,
+            typename cc::multilevel_hashset::make_traits<
+                cc::multilevel_hashset::hash_accessor< get_hash<hash_type>>
+                , co::stat< cc::multilevel_hashset::stat<>>
+                , co::less< hash_type::less >
+                , co::compare< hash128::cmp >
+            >::type
+        > set_type2;
+        test_rcu<set_type2, hash_type::make>(4, 3);
+#endif
+    }
+} // namespace set