Added container::MultiLevelHashMap<RCU> specialization
authorkhizmax <libcds.dev@gmail.com>
Sun, 4 Oct 2015 19:46:50 +0000 (22:46 +0300)
committerkhizmax <libcds.dev@gmail.com>
Sun, 4 Oct 2015 19:46:50 +0000 (22:46 +0300)
19 files changed:
cds/container/impl/multilevel_hashmap.h
cds/container/multilevel_hashmap_rcu.h [new file with mode: 0644]
cds/container/multilevel_hashset_rcu.h
projects/Win/vc12/cds.vcxproj
projects/Win/vc12/cds.vcxproj.filters
projects/Win/vc12/hdr-test-map.vcxproj
projects/Win/vc12/hdr-test-map.vcxproj.filters
projects/Win/vc14/cds.vcxproj
projects/Win/vc14/cds.vcxproj.filters
projects/Win/vc14/hdr-test-map.vcxproj
projects/Win/vc14/hdr-test-map.vcxproj.filters
projects/source.test-hdr.mk
tests/test-hdr/CMakeLists.txt
tests/test-hdr/map/hdr_multilevel_hashmap.h
tests/test-hdr/map/hdr_multilevel_hashmap_rcu_gpb.cpp [new file with mode: 0644]
tests/test-hdr/map/hdr_multilevel_hashmap_rcu_gpi.cpp [new file with mode: 0644]
tests/test-hdr/map/hdr_multilevel_hashmap_rcu_gpt.cpp [new file with mode: 0644]
tests/test-hdr/map/hdr_multilevel_hashmap_rcu_shb.cpp [new file with mode: 0644]
tests/test-hdr/map/hdr_multilevel_hashmap_rcu_sht.cpp [new file with mode: 0644]

index 09fadc16991b5727887d158d80bae4b63c623fc8..1ed1b4eed564e9a796e8cd3968b3ecbfa799b43e 100644 (file)
@@ -57,7 +57,7 @@ namespace cds { namespace container {
         @note Two important things you should keep in mind when you're using \p %MultiLevelHashMap:
         - all keys is converted to fixed-size bit-string by hash functor provided.
           You can use variable-length keys, for example, \p std::string as a key for \p %MultiLevelHashMap,
         @note Two important things you should keep in mind when you're using \p %MultiLevelHashMap:
         - all keys is converted to fixed-size bit-string by hash functor provided.
           You can use variable-length keys, for example, \p std::string as a key for \p %MultiLevelHashMap,
-          but real key in the map will be fixed-ize hash values of your keys.
+          but real key in the map will be fixed-size hash values of your keys.
           For the strings you may 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
           For the strings you may 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
diff --git a/cds/container/multilevel_hashmap_rcu.h b/cds/container/multilevel_hashmap_rcu.h
new file mode 100644 (file)
index 0000000..c120b34
--- /dev/null
@@ -0,0 +1,780 @@
+//$$CDS-header$$
+
+#ifndef CDSLIB_CONTAINER_MULTILEVEL_HASHMAP_RCU_H
+#define CDSLIB_CONTAINER_MULTILEVEL_HASHMAP_RCU_H
+
+#include <cds/intrusive/multilevel_hashset_rcu.h>
+#include <cds/container/details/multilevel_hashmap_base.h>
+
+namespace cds { namespace container {
+
+    /// Hash map based on multi-level array
+    /** @ingroup cds_nonintrusive_map
+        @anchor cds_container_MultilevelHashMap_rcu
+
+        Source:
+        - [2013] Steven Feldman, Pierre LaBorde, Damian Dechev "Concurrent Multi-level Arrays:
+                 Wait-free Extensible Hash Maps"
+
+        See algorithm short description @ref cds_container_MultilevelHashMap_hp "here"
+
+        @note Two important things you should keep in mind when you're using \p %MultiLevelHashMap:
+        - all keys is converted to fixed-size bit-string by hash functor provided.
+          You can use variable-length keys, for example, \p std::string as a key for \p %MultiLevelHashMap,
+          but real key in the map will be fixed-size hash values of your keys.
+          For the strings you may 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 such hash values will be the keys in \p %MultiLevelHashMap.
+        - \p %MultiLevelHashMap 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 map. \p %MultiLevelHashMap does not maintain the key,
+          it maintains its fixed-size hash value.
+
+        The map supports @ref cds_container_MultilevelHashMap_rcu_iterators "bidirectional thread-safe iterators".
+
+        Template parameters:
+        - \p RCU - one of \ref cds_urcu_gc "RCU type"
+        - \p Key - a key type to be stored in the map
+        - \p T - a value type to be stored in the map
+        - \p Traits - type traits, the structure based on \p multilevel_hashmap::traits or result of \p multilevel_hashmap::make_traits metafunction.
+
+        @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.
+    */
+    template <
+        class RCU
+        ,typename Key
+        ,typename T
+#ifdef CDS_DOXYGEN_INVOKED
+        ,class Traits = multilevel_hashmap::traits
+#else
+        ,class Traits
+#endif
+    >
+    class MultiLevelHashMap< cds::urcu::gc< RCU >, Key, T, Traits >
+#ifdef CDS_DOXYGEN_INVOKED
+        : protected cds::intrusive::MultiLevelHashSet< cds::urcu::gc< RCU >, std::pair<Key const, T>, Traits >
+#else
+        : protected cds::container::details::make_multilevel_hashmap< cds::urcu::gc< RCU >, Key, T, Traits >::type
+#endif
+    {
+        //@cond
+        typedef cds::container::details::make_multilevel_hashmap< cds::urcu::gc< RCU >, Key, T, Traits > maker;
+        typedef typename maker::type base_class;
+        //@endcond
+    public:
+        typedef cds::urcu::gc< RCU > gc; ///< RCU garbage collector
+        typedef Key     key_type;    ///< Key type
+        typedef T       mapped_type; ///< Mapped type
+        typedef std::pair< key_type const, mapped_type> value_type;   ///< Key-value pair to be stored in the map
+        typedef Traits  traits;      ///< Map traits
+#ifdef CDS_DOXYGEN_INVOKED
+        typedef typename traits::hash hasher; ///< Hash functor, see \p multilevel_hashmap::traits::hash
+#else
+        typedef typename maker::hasher hasher;
+#endif
+
+        typedef typename maker::hash_type hash_type; ///< Hash type deduced from \p hasher return type
+        typedef typename base_class::hash_comparator hash_comparator; ///< hash compare functor based on \p Traits::compare and \p Traits::less
+        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
+
+    protected:
+        //@cond
+        typedef typename maker::node_type node_type;
+        typedef typename maker::cxx_node_allocator cxx_node_allocator;
+        typedef std::unique_ptr< node_type, typename maker::node_disposer > scoped_node_ptr;
+
+        struct node_cast
+        {
+            value_type * operator()(node_type * p) const
+            {
+                return p ? &p->m_Value : nullptr;
+            }
+        };
+
+    public:
+        /// pointer to extracted node
+        using exempt_ptr = cds::urcu::exempt_ptr< gc, node_type, value_type, typename base_class::disposer, node_cast >;
+
+    protected:
+        template <bool IsConst>
+        class bidirectional_iterator: public base_class::iterator_base
+        {
+            friend class MultiLevelHashMap;
+            typedef typename base_class::iterator_base iterator_base;
+
+        protected:
+            static CDS_CONSTEXPR bool const c_bConstantIterator = IsConst;
+
+        public:
+            typedef typename std::conditional< IsConst, value_type const*, value_type*>::type value_ptr; ///< Value pointer
+            typedef typename std::conditional< IsConst, value_type const&, value_type&>::type value_ref; ///< Value reference
+
+        public:
+            bidirectional_iterator() CDS_NOEXCEPT
+            {}
+
+            bidirectional_iterator( bidirectional_iterator const& rhs ) CDS_NOEXCEPT
+                : iterator_base( rhs )
+            {}
+
+            bidirectional_iterator& operator=(bidirectional_iterator const& rhs) CDS_NOEXCEPT
+            {
+                iterator_base::operator=( rhs );
+                return *this;
+            }
+
+            bidirectional_iterator& operator++()
+            {
+                iterator_base::operator++();
+                return *this;
+            }
+
+            bidirectional_iterator& operator--()
+            {
+                iterator_base::operator--();
+                return *this;
+            }
+
+            value_ptr operator ->() const CDS_NOEXCEPT
+            {
+                node_type * p = iterator_base::pointer();
+                return p ? &p->m_Value : nullptr;
+            }
+
+            value_ref operator *() const CDS_NOEXCEPT
+            {
+                node_type * p = iterator_base::pointer();
+                assert( p );
+                return p->m_Value;
+            }
+
+            void release()
+            {
+                iterator_base::release();
+            }
+
+            template <bool IsConst2>
+            bool operator ==(bidirectional_iterator<IsConst2> const& rhs) const CDS_NOEXCEPT
+            {
+                return iterator_base::operator==( rhs );
+            }
+
+            template <bool IsConst2>
+            bool operator !=(bidirectional_iterator<IsConst2> const& rhs) const CDS_NOEXCEPT
+            {
+                return !( *this == rhs );
+            }
+
+        public: // for internal use only!
+            bidirectional_iterator( base_class const& set, typename base_class::array_node * pNode, size_t idx, bool )
+                : iterator_base( set, pNode, idx, false )
+            {}
+
+            bidirectional_iterator( base_class const& set, typename base_class::array_node * pNode, size_t idx )
+                : iterator_base( set, pNode, idx )
+            {}
+        };
+
+        /// Reverse bidirectional iterator
+        template <bool IsConst>
+        class reverse_bidirectional_iterator : public base_class::iterator_base
+        {
+            friend class MultiLevelHashMap;
+            typedef typename base_class::iterator_base iterator_base;
+
+        public:
+            typedef typename std::conditional< IsConst, value_type const*, value_type*>::type value_ptr; ///< Value pointer
+            typedef typename std::conditional< IsConst, value_type const&, value_type&>::type value_ref; ///< Value reference
+
+        public:
+            reverse_bidirectional_iterator() CDS_NOEXCEPT
+                : iterator_base()
+            {}
+
+            reverse_bidirectional_iterator( reverse_bidirectional_iterator const& rhs ) CDS_NOEXCEPT
+                : iterator_base( rhs )
+            {}
+
+            reverse_bidirectional_iterator& operator=( reverse_bidirectional_iterator const& rhs) CDS_NOEXCEPT
+            {
+                iterator_base::operator=( rhs );
+                return *this;
+            }
+
+            reverse_bidirectional_iterator& operator++()
+            {
+                iterator_base::operator--();
+                return *this;
+            }
+
+            reverse_bidirectional_iterator& operator--()
+            {
+                iterator_base::operator++();
+                return *this;
+            }
+
+            value_ptr operator ->() const CDS_NOEXCEPT
+            {
+                node_type * p = iterator_base::pointer();
+                return p ? &p->m_Value : nullptr;
+            }
+
+            value_ref operator *() const CDS_NOEXCEPT
+            {
+                node_type * p = iterator_base::pointer();
+                assert( p );
+                return p->m_Value;
+            }
+
+            void release()
+            {
+                iterator_base::release();
+            }
+
+            template <bool IsConst2>
+            bool operator ==(reverse_bidirectional_iterator<IsConst2> const& rhs) const
+            {
+                return iterator_base::operator==( rhs );
+            }
+
+            template <bool IsConst2>
+            bool operator !=(reverse_bidirectional_iterator<IsConst2> const& rhs)
+            {
+                return !( *this == rhs );
+            }
+
+        public: // for internal use only!
+            reverse_bidirectional_iterator( base_class const& set, typename base_class::array_node * pNode, size_t idx, bool )
+                : iterator_base( set, pNode, idx, false )
+            {}
+
+            reverse_bidirectional_iterator( base_class const& set, typename base_class::array_node * pNode, size_t idx )
+                : iterator_base( set, pNode, idx, false )
+            {
+                iterator_base::backward();
+            }
+        };
+        //@endcond
+
+    public:
+#ifdef CDS_DOXYGEN_INVOKED
+        typedef implementation_defined iterator;            ///< @ref cds_container_MultilevelHashMap_rcu_iterators "bidirectional iterator" type
+        typedef implementation_defined const_iterator;      ///< @ref cds_container_MultilevelHashMap_rcu_iterators "bidirectional const iterator" type
+        typedef implementation_defined reverse_iterator;    ///< @ref cds_container_MultilevelHashMap_rcu_iterators "bidirectional reverse iterator" type
+        typedef implementation_defined const_reverse_iterator; ///< @ref cds_container_MultilevelHashMap_rcu_iterators "bidirectional reverse const iterator" type
+#else
+        typedef bidirectional_iterator<false> iterator;
+        typedef bidirectional_iterator<true>  const_iterator;
+        typedef reverse_bidirectional_iterator<false> reverse_iterator;
+        typedef reverse_bidirectional_iterator<true>  const_reverse_iterator;
+#endif
+
+    protected:
+        //@cond
+        hasher  m_Hasher;
+        //@endcond
+
+    public:
+        /// Creates empty map
+        /**
+            @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.
+        */
+        MultiLevelHashMap( size_t head_bits = 8, size_t array_bits = 4 )
+            : base_class( head_bits, array_bits )
+        {}
+
+        /// Destructs the map and frees all data
+        ~MultiLevelHashMap()
+        {}
+
+        /// Inserts new element with key and default value
+        /**
+            The function creates an element with \p key and default value, and then inserts the node created into the map.
+
+            Preconditions:
+            - The \p key_type should be constructible from a value of type \p K.
+                In trivial case, \p K is equal to \p key_type.
+            - The \p mapped_type should be default-constructible.
+
+            Returns \p true if inserting successful, \p false otherwise.
+
+            The function locks RCU internally.
+        */
+        template <typename K>
+        bool insert( K&& key )
+        {
+            scoped_node_ptr sp( cxx_node_allocator().MoveNew( m_Hasher, std::forward<K>(key) ));
+            if ( base_class::insert( *sp )) {
+                sp.release();
+                return true;
+            }
+            return false;
+        }
+
+        /// Inserts new element
+        /**
+            The function creates a node with copy of \p val value
+            and then inserts the node created into the map.
+
+            Preconditions:
+            - The \p key_type should be constructible from \p key of type \p K.
+            - The \p value_type should be constructible from \p val of type \p V.
+
+            Returns \p true if \p val is inserted into the map, \p false otherwise.
+
+            The function locks RCU internally.
+        */
+        template <typename K, typename V>
+        bool insert( K&& key, V&& val )
+        {
+            scoped_node_ptr sp( cxx_node_allocator().MoveNew( m_Hasher, std::forward<K>(key), std::forward<V>(val)));
+            if ( base_class::insert( *sp )) {
+                sp.release();
+                return true;
+            }
+            return false;
+        }
+
+        /// Inserts new element and initialize it by a functor
+        /**
+            This function inserts new element with key \p key and if inserting is successful then it calls
+            \p func functor with signature
+            \code
+                struct functor {
+                    void operator()( value_type& item );
+                };
+            \endcode
+
+            The argument \p item of user-defined functor \p func is the reference
+            to the map's item inserted:
+                - <tt>item.first</tt> is a const reference to item's key that cannot be changed.
+                - <tt>item.second</tt> is a reference to item's value that may be changed.
+
+            \p key_type should be constructible from value of type \p K.
+
+            The function allows to split creating of new item into two part:
+            - create item from \p key;
+            - insert new item into the map;
+            - if inserting is successful, initialize the value of item by calling \p func functor
+
+            This can be useful if complete initialization of object of \p value_type is heavyweight and
+            it is preferable that the initialization should be completed only if inserting is successful.
+
+            The function locks RCU internally.
+        */
+        template <typename K, typename Func>
+        bool insert_with( K&& key, Func func )
+        {
+            scoped_node_ptr sp( cxx_node_allocator().MoveNew( m_Hasher, std::forward<K>(key)));
+            if ( base_class::insert( *sp, [&func]( node_type& item ) { func( item.m_Value ); } )) {
+                sp.release();
+                return true;
+            }
+            return false;
+        }
+
+        /// For key \p key 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.
+
+            The function locks RCU internally.
+        */
+        template <typename K, typename... Args>
+        bool emplace( K&& key, Args&&... args )
+        {
+            scoped_node_ptr sp( cxx_node_allocator().MoveNew( m_Hasher, std::forward<K>(key), std::forward<Args>(args)... ));
+            if ( base_class::insert( *sp )) {
+                sp.release();
+                return true;
+            }
+            return false;
+        }
+
+        /// Updates data by \p key
+        /**
+            The operation performs inserting or replacing the element with lock-free manner.
+
+            If the \p key not found in the map, then the new item created from \p key
+            will be inserted into the map iff \p bInsert is \p true
+            (note that in this case the \ref key_type should be constructible from type \p K).
+            Otherwise, if \p key is found, it is replaced with a new item created from
+            \p key.
+            The functor \p Func signature:
+            \code
+                struct my_functor {
+                    void operator()( value_type& item, value_type * old );
+                };
+            \endcode
+            where:
+            - \p item - item of the map
+            - \p old - old item of the map, if \p nullptr - the new item was inserted
+
+            The functor may change any fields of the \p item.second.
+
+            Returns <tt> std::pair<bool, bool> </tt> where \p first is \p true if operation is successfull,
+            \p second is \p true if new item has been added or \p false if \p key already exists.
+
+            The function locks RCU internally.
+
+            @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
+        */
+        template <typename K, typename Func>
+        std::pair<bool, bool> update( K&& key, Func func, bool bInsert = true )
+        {
+            scoped_node_ptr sp( cxx_node_allocator().MoveNew( m_Hasher, std::forward<K>(key)));
+            std::pair<bool, bool> result = base_class::do_update( *sp,
+                [&func]( node_type& node, node_type * old ) { func( node.m_Value, old ? &old->m_Value : nullptr );},
+                bInsert );
+            if ( result.first )
+                sp.release();
+            return result;
+        }
+
+        /// Delete \p key from the map
+        /**
+            \p key_type must be constructible from value of type \p K.
+            The function deeltes the element with hash value equal to <tt>hash( key_type( key ))</tt>
+
+            Return \p true if \p key is found and deleted, \p false otherwise.
+
+            RCU should not be locked. The function locks RCU internally.
+        */
+        template <typename K>
+        bool erase( K const& key )
+        {
+            hash_type h = m_Hasher( key_type( key ));
+            return base_class::erase( h );
+        }
+
+        /// Delete \p key from the map
+        /**
+            The function searches an item with hash value equal to <tt>hash( key_type( key ))</tt>,
+            calls \p f functor and deletes the item. If \p key is not found, the functor is not called.
+
+            The functor \p Func interface:
+            \code
+            struct extractor {
+                void operator()(value_type& item) { ... }
+            };
+            \endcode
+            where \p item is the element found.
+
+            \p key_type must be constructible from value of type \p K.
+
+            Return \p true if key is found and deleted, \p false otherwise
+
+            RCU should not be locked. The function locks RCU internally.
+        */
+        template <typename K, typename Func>
+        bool erase( K const& key, Func f )
+        {
+            hash_type h = m_Hasher( key_type( key ));
+            return base_class::erase( h, [&f]( node_type& node) { f( node.m_Value ); } );
+        }
+
+        /// Extracts the item from the map with specified \p key
+        /**
+            The function searches an item with key equal to <tt>hash( key_type( key ))</tt> in the map,
+            unlinks it from the map, and returns a guarded pointer to the item found.
+            If \p key is not found the function returns an empty guarded pointer.
+
+            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::MultiLevelHashMap< cds::urcu::gc< cds::urcu::general_buffered<>>, int, foo, my_traits > map_type;
+            map_type theMap;
+            // ...
+
+            typename map_type::exempt_ptr ep( theMap.extract( 5 ));
+            if ( ep ) {
+                // Deal with ep
+                //...
+
+                // Dispose returned item.
+                ep.release();
+            }
+            \endcode
+        */
+        template <typename K>
+        exempt_ptr extract( K const& key )
+        {
+            check_deadlock_policy::check();
+
+            node_type * p;
+            {
+                rcu_lock rcuLock;
+                p = base_class::do_erase( m_Hasher( key_type(key)), [](node_type const&) -> bool {return true;});
+            }
+            return exempt_ptr(p);
+        }
+
+        /// Checks whether the map contains \p key
+        /**
+            The function searches the item by its hash that is equal to <tt>hash( key_type( key ))</tt>
+            and returns \p true if it is found, or \p false otherwise.
+        */
+        template <typename K>
+        bool contains( K const& key )
+        {
+            return base_class::contains( m_Hasher( key_type( key )) );
+        }
+
+        /// Find the key \p key
+        /**
+
+            The function searches the item by its hash that is equal to <tt>hash( key_type( key ))</tt>
+            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 \p item.second.
+
+            The function returns \p true if \p key is found, \p false otherwise.
+        */
+        template <typename K, typename Func>
+        bool find( K const& key, Func f )
+        {
+            return base_class::find( m_Hasher( key_type( key )), [&f](node_type& node) { f( node.m_Value );});
+        }
+
+        /// Finds the key \p key and return 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::MultiLevelHashMap< your_template_params >  my_map;
+            my_map theMap;
+            // ...
+            {
+                // lock RCU
+                my_map::rcu_lock;
+
+                foo * p = theMap.get( 5 );
+                if ( p ) {
+                    // Deal with p
+                    //...
+                }
+            }
+            \endcode
+        */
+        template <typename K>
+        value_type * get( K const& key )
+        {
+            node_type * p = base_class::get( m_Hasher( key_type( key )));
+            return p ? &p->m_Value : nullptr;
+        }
+
+        /// Clears the map (non-atomic)
+        /**
+            The function unlink all data node from the map.
+            The function is not atomic but is thread-safe.
+            After \p %clear() the map may not be empty because another threads may insert items.
+        */
+        void clear()
+        {
+            base_class::clear();
+        }
+
+        /// Checks if the map is empty
+        /**
+            Emptiness is checked by item counting: if item count is zero then the map is empty.
+            Thus, the correct item counting feature is an important part of the map implementation.
+        */
+        bool empty() const
+        {
+            return base_class::empty();
+        }
+
+        /// Returns item count in the map
+        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_MultilevelHashMap_rcu_iterators
+            The map supports thread-safe iterators: you may iterate over the map 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 map
+            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 {
+                // ... other fields
+                uint32_t    payload; // only for example
+            };
+            typedef cds::urcu::gc< cds::urcu::general_buffered<>> rcu;
+            typedef cds::container::MultiLevelHashMap< rcu, std::string, foo> map_type;
+
+            map_type m;
+
+            // ...
+
+            // iterate over the map
+            {
+                // lock the RCU.
+                typename set_type::rcu_lock l; // scoped RCU lock
+
+                // traverse the map
+                for ( auto i = m.begin(); i != s.end(); ++i ) {
+                    // deal with i. Remember, erasing is prohibited here!
+                    i->second.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 map
+        iterator begin()
+        {
+            return base_class::template init_begin<iterator>();
+        }
+
+        /// Returns an const iterator to the beginning of the map
+        const_iterator begin() const
+        {
+            return base_class::template init_begin<const_iterator>();
+        }
+
+        /// Returns an const iterator to the beginning of the map
+        const_iterator cbegin()
+        {
+            return base_class::template init_begin<const_iterator>();
+        }
+
+        /// Returns an iterator to the element following the last element of the map. This element acts as a placeholder; attempting to access it results in undefined behavior.
+        iterator end()
+        {
+            return base_class::template init_end<iterator>();
+        }
+
+        /// Returns a const iterator to the element following the last element of the map. This element acts as a placeholder; attempting to access it results in undefined behavior.
+        const_iterator end() const
+        {
+            return base_class::template init_end<const_iterator>();
+        }
+
+        /// Returns a const iterator to the element following the last element of the map. This element acts as a placeholder; attempting to access it results in undefined behavior.
+        const_iterator cend()
+        {
+            return base_class::template init_end<const_iterator>();
+        }
+
+        /// Returns a reverse iterator to the first element of the reversed map
+        reverse_iterator rbegin()
+        {
+            return base_class::template init_rbegin<reverse_iterator>();
+        }
+
+        /// Returns a const reverse iterator to the first element of the reversed map
+        const_reverse_iterator rbegin() const
+        {
+            return base_class::template init_rbegin<const_reverse_iterator>();
+        }
+
+        /// Returns a const reverse iterator to the first element of the reversed map
+        const_reverse_iterator crbegin()
+        {
+            return base_class::template init_rbegin<const_reverse_iterator>();
+        }
+
+        /// Returns a reverse iterator to the element following the last element of the reversed map
+        /**
+            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::template init_rend<reverse_iterator>();
+        }
+
+        /// Returns a const reverse iterator to the element following the last element of the reversed map
+        /**
+            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::template init_rend<const_reverse_iterator>();
+        }
+
+        /// Returns a const reverse iterator to the element following the last element of the reversed map
+        /**
+            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::template init_rend<const_reverse_iterator>();
+        }
+    ///@}
+    };
+}} // namespace cds::container
+
+#endif // #ifndef CDSLIB_CONTAINER_MULTILEVEL_HASHMAP_RCU_H
index b84e344a53fda444421e4c695acfca87fa16c464..fdd1f5ed4a7fd02b27d6577c9d724e61f502a10f 100644 (file)
@@ -16,7 +16,7 @@ namespace cds { namespace container {
         - [2013] Steven Feldman, Pierre LaBorde, Damian Dechev "Concurrent Multi-level Arrays:
                  Wait-free Extensible Hash Maps"
 
         - [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"
+        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.
 
         @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.
@@ -225,6 +225,8 @@ namespace cds { namespace container {
             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.
             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.
+
+            RCU should not be locked. The function locks RCU internally.
         */
         bool erase( hash_type const& hash )
         {
         */
         bool erase( hash_type const& hash )
         {
@@ -244,6 +246,8 @@ namespace cds { namespace container {
             \endcode
 
             If \p hash is not found the function returns \p false.
             \endcode
 
             If \p hash is not found the function returns \p false.
+
+            RCU should not be locked. The function locks RCU internally.
         */
         template <typename Func>
         bool erase( hash_type const& hash, Func f )
         */
         template <typename Func>
         bool erase( hash_type const& hash, Func f )
index f83e634dff046c66c664ec145c0c5db579dd6c81..0ec0c2958014b356e9437aff38d4b1d3d7b998c0 100644 (file)
     <ClInclude Include="..\..\..\cds\container\mspriority_queue.h" />\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashmap_dhp.h" />\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashmap_hp.h" />\r
     <ClInclude Include="..\..\..\cds\container\mspriority_queue.h" />\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashmap_dhp.h" />\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashmap_hp.h" />\r
+    <ClInclude Include="..\..\..\cds\container\multilevel_hashmap_rcu.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\multilevel_hashset_dhp.h" />\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashset_hp.h" />\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashset_rcu.h" />\r
index dff39dfadca8d97e0ecf078b7bf22c2e86ec8904..7ecba805a4ef9f850adbd9b9fc36986c6a1f0b4c 100644 (file)
     <ClInclude Include="..\..\..\cds\container\multilevel_hashset_rcu.h">\r
       <Filter>Header Files\cds\container</Filter>\r
     </ClInclude>\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashset_rcu.h">\r
       <Filter>Header Files\cds\container</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\..\..\cds\container\multilevel_hashmap_rcu.h">\r
+      <Filter>Header Files\cds\container</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
   </ItemGroup>\r
 </Project>
\ No newline at end of file
index a298e9a40cdb6c23618588742dcf06b1f3e47667..674216bcd39297f6f1e9ad5b10e902960d3a7a0e 100644 (file)
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_michael_map_rcu_sht.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_dhp.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_hp.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_michael_map_rcu_sht.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_dhp.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_hp.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_gpb.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_gpi.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_gpt.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_shb.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_sht.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_refinable_hashmap_boost_flat_map.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_refinable_hashmap_boost_list.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_refinable_hashmap_boost_map.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_refinable_hashmap_boost_flat_map.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_refinable_hashmap_boost_list.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_refinable_hashmap_boost_map.cpp" />\r
index 22112b2fed885c88de6ebce43659f92e5a7d46dc..a8e47dce421f52bfadbba1fdd42adb02601e56ed 100644 (file)
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_dhp.cpp">\r
       <Filter>multilvel_hashmap</Filter>\r
     </ClCompile>\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_dhp.cpp">\r
       <Filter>multilvel_hashmap</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_gpb.cpp">\r
+      <Filter>multilvel_hashmap</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_gpi.cpp">\r
+      <Filter>multilvel_hashmap</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_gpt.cpp">\r
+      <Filter>multilvel_hashmap</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_shb.cpp">\r
+      <Filter>multilvel_hashmap</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_sht.cpp">\r
+      <Filter>multilvel_hashmap</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="..\..\..\tests\test-hdr\map\hdr_map.h" />\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="..\..\..\tests\test-hdr\map\hdr_map.h" />\r
index b194cbc4526f033f5e1c38f20c75c59f5114012b..997771d8985c9d3862d54b574b12f9408928b9fe 100644 (file)
     <ClInclude Include="..\..\..\cds\container\mspriority_queue.h" />\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashmap_dhp.h" />\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashmap_hp.h" />\r
     <ClInclude Include="..\..\..\cds\container\mspriority_queue.h" />\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashmap_dhp.h" />\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashmap_hp.h" />\r
+    <ClInclude Include="..\..\..\cds\container\multilevel_hashmap_rcu.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\multilevel_hashset_dhp.h" />\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashset_hp.h" />\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashset_rcu.h" />\r
index dff39dfadca8d97e0ecf078b7bf22c2e86ec8904..7ecba805a4ef9f850adbd9b9fc36986c6a1f0b4c 100644 (file)
     <ClInclude Include="..\..\..\cds\container\multilevel_hashset_rcu.h">\r
       <Filter>Header Files\cds\container</Filter>\r
     </ClInclude>\r
     <ClInclude Include="..\..\..\cds\container\multilevel_hashset_rcu.h">\r
       <Filter>Header Files\cds\container</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\..\..\cds\container\multilevel_hashmap_rcu.h">\r
+      <Filter>Header Files\cds\container</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
   </ItemGroup>\r
 </Project>
\ No newline at end of file
index 280939d00baac634bde5f3945e0e67f094f4b672..fc21891d44b2c4364338e9d98775c220907de6c0 100644 (file)
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_michael_map_rcu_sht.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_dhp.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_hp.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_michael_map_rcu_sht.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_dhp.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_hp.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_gpb.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_gpi.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_gpt.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_shb.cpp" />\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_sht.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_refinable_hashmap_boost_flat_map.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_refinable_hashmap_boost_list.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_refinable_hashmap_boost_map.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_refinable_hashmap_boost_flat_map.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_refinable_hashmap_boost_list.cpp" />\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_refinable_hashmap_boost_map.cpp" />\r
index 22112b2fed885c88de6ebce43659f92e5a7d46dc..a8e47dce421f52bfadbba1fdd42adb02601e56ed 100644 (file)
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_dhp.cpp">\r
       <Filter>multilvel_hashmap</Filter>\r
     </ClCompile>\r
     <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_dhp.cpp">\r
       <Filter>multilvel_hashmap</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_gpb.cpp">\r
+      <Filter>multilvel_hashmap</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_gpi.cpp">\r
+      <Filter>multilvel_hashmap</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_gpt.cpp">\r
+      <Filter>multilvel_hashmap</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_shb.cpp">\r
+      <Filter>multilvel_hashmap</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\..\tests\test-hdr\map\hdr_multilevel_hashmap_rcu_sht.cpp">\r
+      <Filter>multilvel_hashmap</Filter>\r
+    </ClCompile>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="..\..\..\tests\test-hdr\map\hdr_map.h" />\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="..\..\..\tests\test-hdr\map\hdr_map.h" />\r
index 696dfadc97f5bd04792e3ac17ef5620f5dfedf0e..3e202ed435af8e42d2009111d927cae5e98e50b9 100644 (file)
@@ -17,6 +17,11 @@ CDS_TESTHDR_MAP := \
     tests/test-hdr/map/hdr_michael_map_lazy_nogc.cpp \
     tests/test-hdr/map/hdr_multilevel_hashmap_hp.cpp \
     tests/test-hdr/map/hdr_multilevel_hashmap_dhp.cpp \
     tests/test-hdr/map/hdr_michael_map_lazy_nogc.cpp \
     tests/test-hdr/map/hdr_multilevel_hashmap_hp.cpp \
     tests/test-hdr/map/hdr_multilevel_hashmap_dhp.cpp \
+    tests/test-hdr/map/hdr_multilevel_hashmap_rcu_gpb.cpp \
+    tests/test-hdr/map/hdr_multilevel_hashmap_rcu_gpi.cpp \
+    tests/test-hdr/map/hdr_multilevel_hashmap_rcu_gpt.cpp \
+    tests/test-hdr/map/hdr_multilevel_hashmap_rcu_shb.cpp \
+    tests/test-hdr/map/hdr_multilevel_hashmap_rcu_sht.cpp \
     tests/test-hdr/map/hdr_refinable_hashmap_hashmap_std.cpp \
     tests/test-hdr/map/hdr_refinable_hashmap_boost_list.cpp \
     tests/test-hdr/map/hdr_refinable_hashmap_list.cpp \
     tests/test-hdr/map/hdr_refinable_hashmap_hashmap_std.cpp \
     tests/test-hdr/map/hdr_refinable_hashmap_boost_list.cpp \
     tests/test-hdr/map/hdr_refinable_hashmap_list.cpp \
index f6a4ccd162fbbe043b8e4daaaa962d883dda47df..e90e94624137504e5881b49ff7a650174d128fae 100644 (file)
@@ -19,6 +19,11 @@ set(CDS_TESTHDR_MAP
     map/hdr_michael_map_lazy_nogc.cpp\r
     map/hdr_multilevel_hashmap_hp.cpp\r
     map/hdr_multilevel_hashmap_dhp.cpp\r
     map/hdr_michael_map_lazy_nogc.cpp\r
     map/hdr_multilevel_hashmap_hp.cpp\r
     map/hdr_multilevel_hashmap_dhp.cpp\r
+    map/hdr_multilevel_hashmap_rcu_gpb.cpp\r
+    map/hdr_multilevel_hashmap_rcu_gpi.cpp\r
+    map/hdr_multilevel_hashmap_rcu_gpt.cpp\r
+    map/hdr_multilevel_hashmap_rcu_shb.cpp\r
+    map/hdr_multilevel_hashmap_rcu_sht.cpp\r
     map/hdr_refinable_hashmap_hashmap_std.cpp\r
     map/hdr_refinable_hashmap_boost_list.cpp\r
     map/hdr_refinable_hashmap_list.cpp\r
     map/hdr_refinable_hashmap_hashmap_std.cpp\r
     map/hdr_refinable_hashmap_boost_list.cpp\r
     map/hdr_refinable_hashmap_list.cpp\r
index d8f0a05cdf6f00e40a5326ba725b0aaaa7b1b4a1..559c87d8f1f91b2b12697c7e81a21a2640fd3d6d 100644 (file)
@@ -359,6 +359,255 @@ namespace map {
             CPPUNIT_MSG( m.statistics() );
         }
 
             CPPUNIT_MSG( m.statistics() );
         }
 
+        template <typename Map>
+        void test_rcu(size_t nHeadBits, size_t nArrayBits)
+        {
+            typedef typename Map::hash_type hash_type;
+            typedef typename Map::key_type key_type;
+            typedef typename Map::mapped_type mapped_type;
+            typedef typename Map::value_type value_type;
+            typedef typename Map::exempt_ptr exempt_ptr;
+            typedef typename Map::rcu_lock rcu_lock;
+
+            size_t const capacity = 1000;
+
+            Map m(nHeadBits, nArrayBits);
+            CPPUNIT_MSG("Array size: head=" << m.head_size() << ", array_node=" << m.array_node_size());
+            CPPUNIT_ASSERT(m.head_size() >= (size_t(1) << nHeadBits));
+            CPPUNIT_ASSERT(m.array_node_size() == (size_t(1) << nArrayBits));
+
+            CPPUNIT_ASSERT(m.empty());
+            CPPUNIT_ASSERT(m.size() == 0);
+
+            // insert( key )/update()/get()/find()
+            for (size_t i = 0; i < capacity; ++i) {
+                size_t key = i * 57;
+                CPPUNIT_ASSERT(!m.contains(key))
+                CPPUNIT_ASSERT(m.insert(key));
+                CPPUNIT_ASSERT(m.contains(key));
+                CPPUNIT_ASSERT(m.size() == i + 1);
+
+                auto ret = m.update(key, [](value_type& v, value_type * old) {
+                    CPPUNIT_ASSERT_CURRENT(old != nullptr);
+                    ++v.second.nInsertCall;
+                }, false);
+                CPPUNIT_ASSERT(ret.first);
+                CPPUNIT_ASSERT(!ret.second);
+
+                CPPUNIT_ASSERT(m.find(key, [](value_type& v) { ++v.second.nFindCall;}));
+
+                {
+                    rcu_lock l;
+                    value_type* p{ m.get(key) };
+                    CPPUNIT_ASSERT(p);
+                    CPPUNIT_ASSERT(p->first == key);
+                    CPPUNIT_ASSERT(p->second.nInsertCall == 1);
+                    CPPUNIT_ASSERT(p->second.nFindCall == 1);
+                }
+            }
+            CPPUNIT_ASSERT(!m.empty());
+            CPPUNIT_ASSERT(m.size() == capacity);
+
+            // iterator test
+            size_t nCount = 0;
+            {
+                rcu_lock l;
+                for (auto it = m.begin(), itEnd = m.end(); it != itEnd; ++it) {
+                    CPPUNIT_ASSERT(it->second.nIteratorCall == 0);
+                    CPPUNIT_ASSERT(it->second.nInsertCall == 1);
+                    CPPUNIT_ASSERT((*it).second.nFindCall == 1);
+                    it->second.nIteratorCall += 1;
+                    ++nCount;
+                }
+            }
+            CPPUNIT_ASSERT(nCount == capacity);
+
+            nCount = 0;
+            {
+                rcu_lock l;
+                for (auto it = m.rbegin(), itEnd = m.rend(); it != itEnd; ++it) {
+                    CPPUNIT_ASSERT(it->second.nInsertCall == 1);
+                    CPPUNIT_ASSERT((*it).second.nFindCall == 1);
+                    CPPUNIT_ASSERT(it->second.nIteratorCall == 1);
+                    (*it).second.nIteratorCall += 1;
+                    ++nCount;
+                }
+            }
+            CPPUNIT_ASSERT(nCount == capacity);
+
+            nCount = 0;
+            {
+                rcu_lock l;
+                for (auto it = m.cbegin(), itEnd = m.cend(); it != itEnd; ++it) {
+                    CPPUNIT_ASSERT(it->second.nInsertCall == 1);
+                    CPPUNIT_ASSERT((*it).second.nFindCall == 1);
+                    CPPUNIT_ASSERT(it->second.nIteratorCall == 2);
+                    (*it).second.nIteratorCall += 1;
+                    ++nCount;
+                }
+            }
+            CPPUNIT_ASSERT(nCount == capacity);
+
+            nCount = 0;
+            {
+                rcu_lock l;
+                for (auto it = m.crbegin(), itEnd = m.crend(); it != itEnd; ++it) {
+                    CPPUNIT_ASSERT(it->second.nInsertCall == 1);
+                    CPPUNIT_ASSERT((*it).second.nFindCall == 1);
+                    CPPUNIT_ASSERT(it->second.nIteratorCall == 3);
+                    (*it).second.nIteratorCall += 1;
+                    ++nCount;
+                }
+            }
+            CPPUNIT_ASSERT(nCount == capacity);
+
+            // find
+            for (size_t i = 0; i < capacity; i++) {
+                size_t key = i * 57;
+                CPPUNIT_ASSERT(m.find(key, [key](value_type& v) {
+                    CPPUNIT_ASSERT_CURRENT(v.first == key);
+                    CPPUNIT_ASSERT_CURRENT(v.second.nInsertCall == 1);
+                    CPPUNIT_ASSERT_CURRENT(v.second.nFindCall == 1);
+                    CPPUNIT_ASSERT_CURRENT(v.second.nIteratorCall == 4);
+                }));
+            }
+
+            // erase
+            for (size_t i = capacity; i > 0; --i) {
+                size_t key = (i - 1) * 57;
+                {
+                    rcu_lock l;
+                    value_type* p = m.get(key);
+                    CPPUNIT_ASSERT(p);
+                    CPPUNIT_ASSERT(p->first == key);
+                    CPPUNIT_ASSERT(p->second.nInsertCall == 1);
+                    CPPUNIT_ASSERT(p->second.nFindCall == 1);
+                    CPPUNIT_ASSERT(p->second.nIteratorCall == 4);
+                }
+
+                CPPUNIT_ASSERT(m.erase(key));
+
+                {
+                    rcu_lock l;
+                    value_type* p = m.get(key);
+                    CPPUNIT_ASSERT(!p);
+                }
+                CPPUNIT_ASSERT(!m.contains(key));
+            }
+            CPPUNIT_ASSERT(m.empty());
+            CPPUNIT_ASSERT(m.size() == 0);
+
+            // Iterators on empty map
+            {
+                rcu_lock l;
+                CPPUNIT_ASSERT(m.begin() == m.end());
+                CPPUNIT_ASSERT(m.cbegin() == m.cend());
+                CPPUNIT_ASSERT(m.rbegin() == m.rend());
+                CPPUNIT_ASSERT(m.crbegin() == m.crend());
+            }
+
+            // insert( key, val )
+            for (size_t i = 0; i < capacity; ++i) {
+                CPPUNIT_ASSERT(!m.contains(i));
+                CPPUNIT_ASSERT(m.insert(i, (unsigned int)i * 100));
+                CPPUNIT_ASSERT(m.contains(i));
+                CPPUNIT_ASSERT(m.find(i, [i](value_type& v) {
+                    CPPUNIT_ASSERT_CURRENT(v.first == i);
+                    CPPUNIT_ASSERT_CURRENT(v.second.nInsertCall == i * 100);
+                }));
+            }
+            CPPUNIT_ASSERT(!m.empty());
+            CPPUNIT_ASSERT(m.size() == capacity);
+
+            // erase( key, func )
+            for (size_t i = 0; i < capacity; ++i) {
+                CPPUNIT_ASSERT(m.contains(i));
+                CPPUNIT_ASSERT(m.erase(i, [i](value_type& v) {
+                    CPPUNIT_ASSERT_CURRENT(v.first == i);
+                    CPPUNIT_ASSERT_CURRENT(v.second.nInsertCall == i * 100);
+                    v.second.nInsertCall = 0;
+                }));
+            }
+            CPPUNIT_ASSERT(m.empty());
+            CPPUNIT_ASSERT(m.size() == 0);
+
+            // insert_with
+            for (size_t i = 0; i < capacity; ++i) {
+                size_t key = i * 121;
+                CPPUNIT_ASSERT(!m.contains(key));
+                CPPUNIT_ASSERT(m.insert_with(key, [key](value_type& v) {
+                    CPPUNIT_ASSERT_CURRENT(v.first == key);
+                    CPPUNIT_ASSERT_CURRENT(v.second.nInsertCall == 0);
+                    v.second.nInsertCall = decltype(v.second.nInsertCall)(key);
+                }));
+                CPPUNIT_ASSERT(m.find(key, [key](value_type& v) {
+                    CPPUNIT_ASSERT_CURRENT(v.first == key);
+                    CPPUNIT_ASSERT_CURRENT(v.second.nInsertCall == key);
+                }));
+                CPPUNIT_ASSERT(m.size() == i + 1);
+            }
+            CPPUNIT_ASSERT(!m.empty());
+            CPPUNIT_ASSERT(m.size() == capacity);
+
+            nCount = 0;
+            {
+                rcu_lock l;
+                for (auto it = m.begin(), itEnd = m.end(); it != itEnd; ++it) {
+                    CPPUNIT_ASSERT(it->first == it->second.nInsertCall);
+                    CPPUNIT_ASSERT(it->second.nIteratorCall == 0);
+                    it->second.nIteratorCall += 1;
+                    ++nCount;
+                }
+            }
+            CPPUNIT_ASSERT(nCount == capacity);
+
+            nCount = 0;
+            {
+                rcu_lock l;
+                for (auto it = m.rbegin(), itEnd = m.rend(); it != itEnd; ++it) {
+                    CPPUNIT_ASSERT(it->first == it->second.nInsertCall);
+                    CPPUNIT_ASSERT(it->second.nIteratorCall == 1);
+                    it->second.nIteratorCall += 1;
+                    ++nCount;
+                }
+            }
+            CPPUNIT_ASSERT(nCount == capacity);
+
+            // clear()
+            m.clear();
+            CPPUNIT_ASSERT(m.empty());
+            CPPUNIT_ASSERT(m.size() == 0);
+
+            // emplace
+            for (size_t i = 0; i < capacity; ++i) {
+                size_t key = i * 1023;
+                CPPUNIT_ASSERT(!m.contains(key));
+                CPPUNIT_ASSERT(m.emplace(key, static_cast<unsigned int>(i)));
+                CPPUNIT_ASSERT(m.find(key, [key](value_type& v) {
+                    CPPUNIT_ASSERT_CURRENT(v.first == key);
+                    CPPUNIT_ASSERT_CURRENT(v.second.nInsertCall * 1023 == key);
+                }));
+                CPPUNIT_ASSERT(m.size() == i + 1);
+            }
+            CPPUNIT_ASSERT(!m.empty());
+            CPPUNIT_ASSERT(m.size() == capacity);
+
+            // extract
+            for (size_t i = capacity; i > 0; --i) {
+                size_t key = (i - 1) * 1023;
+                exempt_ptr xp{ m.extract(key) };
+                CPPUNIT_ASSERT(xp);
+                CPPUNIT_ASSERT(xp->first == key);
+                CPPUNIT_ASSERT((*xp).second.nInsertCall == static_cast<unsigned int>(i - 1));
+                xp = m.extract(key);
+                CPPUNIT_ASSERT(!xp);
+            }
+            CPPUNIT_ASSERT(m.empty());
+            CPPUNIT_ASSERT(m.size() == 0);
+
+            CPPUNIT_MSG(m.statistics());
+        }
+
         void hp_stdhash();
         void hp_stdhash_stat();
         void hp_stdhash_5_3();
         void hp_stdhash();
         void hp_stdhash_stat();
         void hp_stdhash_5_3();
@@ -377,6 +626,51 @@ namespace map {
         void dhp_hash128_4_3();
         void dhp_hash128_4_3_stat();
 
         void dhp_hash128_4_3();
         void dhp_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_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_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(MultiLevelHashMapHdrTest)
             CPPUNIT_TEST(hp_stdhash)
             CPPUNIT_TEST(hp_stdhash_stat)
         CPPUNIT_TEST_SUITE(MultiLevelHashMapHdrTest)
             CPPUNIT_TEST(hp_stdhash)
             CPPUNIT_TEST(hp_stdhash_stat)
@@ -395,6 +689,51 @@ namespace map {
             CPPUNIT_TEST(dhp_hash128_stat)
             CPPUNIT_TEST(dhp_hash128_4_3)
             CPPUNIT_TEST(dhp_hash128_4_3_stat)
             CPPUNIT_TEST(dhp_hash128_stat)
             CPPUNIT_TEST(dhp_hash128_4_3)
             CPPUNIT_TEST(dhp_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_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_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()
 
     };
         CPPUNIT_TEST_SUITE_END()
 
     };
diff --git a/tests/test-hdr/map/hdr_multilevel_hashmap_rcu_gpb.cpp b/tests/test-hdr/map/hdr_multilevel_hashmap_rcu_gpb.cpp
new file mode 100644 (file)
index 0000000..51c9095
--- /dev/null
@@ -0,0 +1,139 @@
+//$$CDS-header$$
+
+#include "map/hdr_multilevel_hashmap.h"
+#include <cds/urcu/general_buffered.h>
+#include <cds/container/multilevel_hashmap_rcu.h>
+#include "unit/print_multilevel_hashset_stat.h"
+
+namespace map {
+    namespace {
+        typedef cds::urcu::gc< cds::urcu::general_buffered<>> rcu_type;
+    } // namespace
+
+    void MultiLevelHashMapHdrTest::rcu_gpb_stdhash()
+    {
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item > map_type;
+
+        test_rcu<map_type>(4, 2);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpb_hash128()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef hash128::make hash;
+            typedef hash128::less less;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 2);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::hash< hash128::make >
+                , co::less< hash128::less >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 2);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpb_stdhash_stat()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef cc::multilevel_hashmap::stat<> stat;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 2);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::stat< cc::multilevel_hashmap::stat<>>
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 2);
+    }
+
+        void MultiLevelHashMapHdrTest::rcu_gpb_hash128_stat()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef cc::multilevel_hashmap::stat<> stat;
+            typedef hash128::make hash;
+            typedef hash128::cmp compare;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 2);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::stat< cc::multilevel_hashmap::stat<>>
+                , co::hash< hash128::make >
+                , co::compare< hash128::cmp >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 2);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpb_stdhash_5_3()
+    {
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item > map_type;
+
+        test_rcu<map_type>(5, 3);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpb_stdhash_5_3_stat()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef cc::multilevel_hashmap::stat<> stat;
+            typedef cds::backoff::empty back_off;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(5, 3);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::stat< cc::multilevel_hashmap::stat<>>
+                ,co::back_off< cds::backoff::empty >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(5, 3);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpb_hash128_4_3()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef hash128::make hash;
+            typedef hash128::less less;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 3);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::hash< hash128::make >
+                , co::less< hash128::less >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 3);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpb_hash128_4_3_stat()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef hash128::make hash;
+            typedef hash128::less less;
+            typedef cc::multilevel_hashmap::stat<> stat;
+            typedef co::v::sequential_consistent memory_model;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 3);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::hash< hash128::make >
+                , co::less< hash128::less >
+                , co::stat< cc::multilevel_hashmap::stat<>>
+                , co::memory_model< co::v::sequential_consistent >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 3);
+    }
+
+} // namespace map
diff --git a/tests/test-hdr/map/hdr_multilevel_hashmap_rcu_gpi.cpp b/tests/test-hdr/map/hdr_multilevel_hashmap_rcu_gpi.cpp
new file mode 100644 (file)
index 0000000..134eae2
--- /dev/null
@@ -0,0 +1,139 @@
+//$$CDS-header$$
+
+#include "map/hdr_multilevel_hashmap.h"
+#include <cds/urcu/general_instant.h>
+#include <cds/container/multilevel_hashmap_rcu.h>
+#include "unit/print_multilevel_hashset_stat.h"
+
+namespace map {
+    namespace {
+        typedef cds::urcu::gc< cds::urcu::general_instant<>> rcu_type;
+    } // namespace
+
+    void MultiLevelHashMapHdrTest::rcu_gpi_stdhash()
+    {
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item > map_type;
+
+        test_rcu<map_type>(4, 2);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpi_hash128()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef hash128::make hash;
+            typedef hash128::less less;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 2);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::hash< hash128::make >
+                , co::less< hash128::less >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 2);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpi_stdhash_stat()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef cc::multilevel_hashmap::stat<> stat;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 2);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::stat< cc::multilevel_hashmap::stat<>>
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 2);
+    }
+
+        void MultiLevelHashMapHdrTest::rcu_gpi_hash128_stat()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef cc::multilevel_hashmap::stat<> stat;
+            typedef hash128::make hash;
+            typedef hash128::cmp compare;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 2);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::stat< cc::multilevel_hashmap::stat<>>
+                , co::hash< hash128::make >
+                , co::compare< hash128::cmp >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 2);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpi_stdhash_5_3()
+    {
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item > map_type;
+
+        test_rcu<map_type>(5, 3);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpi_stdhash_5_3_stat()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef cc::multilevel_hashmap::stat<> stat;
+            typedef cds::backoff::empty back_off;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(5, 3);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::stat< cc::multilevel_hashmap::stat<>>
+                ,co::back_off< cds::backoff::empty >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(5, 3);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpi_hash128_4_3()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef hash128::make hash;
+            typedef hash128::less less;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 3);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::hash< hash128::make >
+                , co::less< hash128::less >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 3);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpi_hash128_4_3_stat()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef hash128::make hash;
+            typedef hash128::less less;
+            typedef cc::multilevel_hashmap::stat<> stat;
+            typedef co::v::sequential_consistent memory_model;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 3);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::hash< hash128::make >
+                , co::less< hash128::less >
+                , co::stat< cc::multilevel_hashmap::stat<>>
+                , co::memory_model< co::v::sequential_consistent >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 3);
+    }
+
+} // namespace map
diff --git a/tests/test-hdr/map/hdr_multilevel_hashmap_rcu_gpt.cpp b/tests/test-hdr/map/hdr_multilevel_hashmap_rcu_gpt.cpp
new file mode 100644 (file)
index 0000000..32d85b3
--- /dev/null
@@ -0,0 +1,139 @@
+//$$CDS-header$$
+
+#include "map/hdr_multilevel_hashmap.h"
+#include <cds/urcu/general_threaded.h>
+#include <cds/container/multilevel_hashmap_rcu.h>
+#include "unit/print_multilevel_hashset_stat.h"
+
+namespace map {
+    namespace {
+        typedef cds::urcu::gc< cds::urcu::general_threaded<>> rcu_type;
+    } // namespace
+
+    void MultiLevelHashMapHdrTest::rcu_gpt_stdhash()
+    {
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item > map_type;
+
+        test_rcu<map_type>(4, 2);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpt_hash128()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef hash128::make hash;
+            typedef hash128::less less;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 2);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::hash< hash128::make >
+                , co::less< hash128::less >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 2);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpt_stdhash_stat()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef cc::multilevel_hashmap::stat<> stat;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 2);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::stat< cc::multilevel_hashmap::stat<>>
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 2);
+    }
+
+        void MultiLevelHashMapHdrTest::rcu_gpt_hash128_stat()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef cc::multilevel_hashmap::stat<> stat;
+            typedef hash128::make hash;
+            typedef hash128::cmp compare;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 2);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::stat< cc::multilevel_hashmap::stat<>>
+                , co::hash< hash128::make >
+                , co::compare< hash128::cmp >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 2);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpt_stdhash_5_3()
+    {
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item > map_type;
+
+        test_rcu<map_type>(5, 3);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpt_stdhash_5_3_stat()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef cc::multilevel_hashmap::stat<> stat;
+            typedef cds::backoff::empty back_off;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(5, 3);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::stat< cc::multilevel_hashmap::stat<>>
+                ,co::back_off< cds::backoff::empty >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(5, 3);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpt_hash128_4_3()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef hash128::make hash;
+            typedef hash128::less less;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 3);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::hash< hash128::make >
+                , co::less< hash128::less >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 3);
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_gpt_hash128_4_3_stat()
+    {
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef hash128::make hash;
+            typedef hash128::less less;
+            typedef cc::multilevel_hashmap::stat<> stat;
+            typedef co::v::sequential_consistent memory_model;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 3);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::hash< hash128::make >
+                , co::less< hash128::less >
+                , co::stat< cc::multilevel_hashmap::stat<>>
+                , co::memory_model< co::v::sequential_consistent >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 3);
+    }
+
+} // namespace map
diff --git a/tests/test-hdr/map/hdr_multilevel_hashmap_rcu_shb.cpp b/tests/test-hdr/map/hdr_multilevel_hashmap_rcu_shb.cpp
new file mode 100644 (file)
index 0000000..b553661
--- /dev/null
@@ -0,0 +1,157 @@
+//$$CDS-header$$
+
+#include "map/hdr_multilevel_hashmap.h"
+#include <cds/urcu/signal_buffered.h>
+#include <cds/container/multilevel_hashmap_rcu.h>
+#include "unit/print_multilevel_hashset_stat.h"
+
+namespace map {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+    namespace {
+        typedef cds::urcu::gc< cds::urcu::signal_buffered<>> rcu_type;
+    } // namespace
+#endif
+
+    void MultiLevelHashMapHdrTest::rcu_shb_stdhash()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item > map_type;
+
+        test_rcu<map_type>(4, 2);
+#endif
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_shb_hash128()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef hash128::make hash;
+            typedef hash128::less less;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 2);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::hash< hash128::make >
+                , co::less< hash128::less >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 2);
+#endif
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_shb_stdhash_stat()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef cc::multilevel_hashmap::stat<> stat;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 2);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::stat< cc::multilevel_hashmap::stat<>>
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 2);
+#endif
+    }
+
+        void MultiLevelHashMapHdrTest::rcu_shb_hash128_stat()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef cc::multilevel_hashmap::stat<> stat;
+            typedef hash128::make hash;
+            typedef hash128::cmp compare;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 2);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::stat< cc::multilevel_hashmap::stat<>>
+                , co::hash< hash128::make >
+                , co::compare< hash128::cmp >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 2);
+#endif
+        }
+
+    void MultiLevelHashMapHdrTest::rcu_shb_stdhash_5_3()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item > map_type;
+
+        test_rcu<map_type>(5, 3);
+#endif
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_shb_stdhash_5_3_stat()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef cc::multilevel_hashmap::stat<> stat;
+            typedef cds::backoff::empty back_off;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(5, 3);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::stat< cc::multilevel_hashmap::stat<>>
+                ,co::back_off< cds::backoff::empty >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(5, 3);
+#endif
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_shb_hash128_4_3()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef hash128::make hash;
+            typedef hash128::less less;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 3);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::hash< hash128::make >
+                , co::less< hash128::less >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 3);
+#endif
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_shb_hash128_4_3_stat()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef hash128::make hash;
+            typedef hash128::less less;
+            typedef cc::multilevel_hashmap::stat<> stat;
+            typedef co::v::sequential_consistent memory_model;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 3);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::hash< hash128::make >
+                , co::less< hash128::less >
+                , co::stat< cc::multilevel_hashmap::stat<>>
+                , co::memory_model< co::v::sequential_consistent >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 3);
+#endif
+    }
+
+} // namespace map
diff --git a/tests/test-hdr/map/hdr_multilevel_hashmap_rcu_sht.cpp b/tests/test-hdr/map/hdr_multilevel_hashmap_rcu_sht.cpp
new file mode 100644 (file)
index 0000000..0fc205f
--- /dev/null
@@ -0,0 +1,157 @@
+//$$CDS-header$$
+
+#include "map/hdr_multilevel_hashmap.h"
+#include <cds/urcu/signal_threaded.h>
+#include <cds/container/multilevel_hashmap_rcu.h>
+#include "unit/print_multilevel_hashset_stat.h"
+
+namespace map {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+    namespace {
+        typedef cds::urcu::gc< cds::urcu::signal_threaded<>> rcu_type;
+    } // namespace
+#endif
+
+    void MultiLevelHashMapHdrTest::rcu_sht_stdhash()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item > map_type;
+
+        test_rcu<map_type>(4, 2);
+#endif
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_sht_hash128()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef hash128::make hash;
+            typedef hash128::less less;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 2);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::hash< hash128::make >
+                , co::less< hash128::less >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 2);
+#endif
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_sht_stdhash_stat()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef cc::multilevel_hashmap::stat<> stat;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 2);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::stat< cc::multilevel_hashmap::stat<>>
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 2);
+#endif
+    }
+
+        void MultiLevelHashMapHdrTest::rcu_sht_hash128_stat()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef cc::multilevel_hashmap::stat<> stat;
+            typedef hash128::make hash;
+            typedef hash128::cmp compare;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 2);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::stat< cc::multilevel_hashmap::stat<>>
+                , co::hash< hash128::make >
+                , co::compare< hash128::cmp >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 2);
+#endif
+        }
+
+    void MultiLevelHashMapHdrTest::rcu_sht_stdhash_5_3()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item > map_type;
+
+        test_rcu<map_type>(5, 3);
+#endif
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_sht_stdhash_5_3_stat()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef cc::multilevel_hashmap::stat<> stat;
+            typedef cds::backoff::empty back_off;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(5, 3);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::stat< cc::multilevel_hashmap::stat<>>
+                ,co::back_off< cds::backoff::empty >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(5, 3);
+#endif
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_sht_hash128_4_3()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef hash128::make hash;
+            typedef hash128::less less;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 3);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::hash< hash128::make >
+                , co::less< hash128::less >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 3);
+#endif
+    }
+
+    void MultiLevelHashMapHdrTest::rcu_sht_hash128_4_3_stat()
+    {
+#ifdef CDS_URCU_SIGNAL_HANDLING_ENABLED
+        struct traits : public cc::multilevel_hashmap::traits {
+            typedef hash128::make hash;
+            typedef hash128::less less;
+            typedef cc::multilevel_hashmap::stat<> stat;
+            typedef co::v::sequential_consistent memory_model;
+        };
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item, traits > map_type;
+        test_rcu<map_type>(4, 3);
+
+        typedef cc::MultiLevelHashMap< rcu_type, size_t, Item,
+            typename cc::multilevel_hashmap::make_traits<
+                co::hash< hash128::make >
+                , co::less< hash128::less >
+                , co::stat< cc::multilevel_hashmap::stat<>>
+                , co::memory_model< co::v::sequential_consistent >
+            >::type
+        > map_type2;
+        test_rcu<map_type2>(4, 3);
+#endif
+    }
+
+} // namespace map