[draft] implemented container::MultiLevelHashSet
authorkhizmax <libcds.dev@gmail.com>
Sat, 15 Aug 2015 15:54:22 +0000 (18:54 +0300)
committerkhizmax <libcds.dev@gmail.com>
Sat, 15 Aug 2015 15:54:22 +0000 (18:54 +0300)
cds/container/details/ellen_bintree_base.h
cds/container/details/multilevel_hashset_base.h [new file with mode: 0644]
cds/container/impl/multilevel_hashset.h [new file with mode: 0644]
cds/container/multilevel_hashset_dhp.h [new file with mode: 0644]
cds/container/multilevel_hashset_hp.h [new file with mode: 0644]
cds/intrusive/details/multilevel_hashset_base.h
cds/intrusive/impl/multilevel_hashset.h
projects/Win/vc12/cds.vcxproj
projects/Win/vc12/cds.vcxproj.filters

index 3be1c584c41635c525c480985671d23c2910d418..86402cedf0d9f22b9993508efbc7a1c87f054614 100644 (file)
@@ -97,7 +97,7 @@ namespace cds { namespace container {
             {}
         };
 
-        /// Type traits for EllenBinTreeSet and EllenBinTreeMap
+        /// Type traits for \p EllenBinTreeSet and \p EllenBinTreeMap
         struct traits
         {
             /// Key extracting functor (only for \p EllenBinTreeSet)
diff --git a/cds/container/details/multilevel_hashset_base.h b/cds/container/details/multilevel_hashset_base.h
new file mode 100644 (file)
index 0000000..be1b994
--- /dev/null
@@ -0,0 +1,170 @@
+//$$CDS-header$$
+
+#ifndef CDSLIB_CONTAINER_DETAILS_MULTILEVEL_HASHSET_BASE_H
+#define CDSLIB_CONTAINER_DETAILS_MULTILEVEL_HASHSET_BASE_H
+
+#include <cds/intrusive/details/multilevel_hashset_base.h>
+#include <cds/container/details/base.h>
+
+namespace cds { namespace container {
+    /// MultiLevelHashSet related definitions
+    /** @ingroup cds_nonintrusive_helper
+    */
+    namespace multilevel_hashset {
+
+        /// Hash accessor option
+        /**
+            @copydetails cds::intrusive::multilevel_hashset::traits::hash_accessor
+        */
+        template <typename Accessor>
+        using hash_accessor = cds::intrusive::hash_accessor< Accessor >;
+
+        /// \p MultiLevelHashSet internal statistics, see cds::intrusive::multilevel_hashset::stat
+        template <typename EventCounter = cds::atomicity::event_counter>
+        using stat = cds::intrusive::multilevel_hashset::stat< EventCounter >;
+
+        /// \p MultiLevelHashSet empty internal statistics
+        typedef cds::intrusive::multilevel_hashset::empty_stat empty_stat;
+
+        /// Bit-wise memcmp-based comparator for hash value \p T
+        template <typename T>
+        using bitwise_compare = cds::intrusive::multilevel_hashset::bitwise_compare< T >;
+
+        /// \p MultiLevelHashSet traits
+        struct traits
+        {
+            /// Mandatory functor to get hash value from data node
+            /**
+                @copydetails cds::intrusive::multilevel_hashset::traits::hash_accessor
+            */
+            typedef cds::opt::none hash_accessor;
+
+            /// Hash comparing functor
+            /**
+                @copydetails cds::intrusive::multilevel_hashset::traits::compare
+            */
+            typedef cds::opt::none compare;
+
+            /// Specifies binary predicate used for hash compare.
+            /**
+                @copydetails cds::intrusive::multilevel_hashset::traits::less
+            */
+            typedef cds::opt::none less;
+
+            /// Item counter
+            /**
+                @copydetails cds::intrusive::multilevel_hashset::traits::item_counter
+            */
+            typedef cds::atomicity::item_counter item_counter;
+
+            /// Item allocator
+            /**
+                Default is \ref CDS_DEFAULT_ALLOCATOR
+            */
+            typedef CDS_DEFAULT_ALLOCATOR allocator;
+
+            /// Array node allocator
+            /**
+                @copydetails cds::intrusive::multilevel_hashset::traits::node_allocator
+            */
+            typedef CDS_DEFAULT_ALLOCATOR node_allocator;
+
+            /// C++ memory ordering model
+            /**
+                @copydetails cds::intrusive::multilevel_hashset::traits::memory_model
+            */
+            typedef cds::opt::v::relaxed_ordering memory_model;
+
+            /// Back-off strategy
+            typedef cds::backoff::Default back_off;
+
+            /// Internal statistics
+            /**
+                @copydetails cds::intrusive::multilevel_hashset::traits::stat
+            */
+            typedef empty_stat stat;
+
+            /// RCU deadlock checking policy (only for \ref cds_container_MultilevelHashSet_rcu "RCU-based MultilevelHashSet")
+            /**
+                @copydetails cds::intrusive::multilevel_hashset::traits::rcu_check_deadlock
+            */
+            typedef cds::opt::v::rcu_throw_deadlock rcu_check_deadlock;
+        };
+
+        /// Metafunction converting option list to \p multilevel_hashset::traits
+        /**
+            Supported \p Options are:
+            - \p multilevel_hashset::hash_accessor - mandatory option, hash accessor functor.
+                @copydetails traits::hash_accessor
+            - \p opt::allocator - item allocator
+                @copydetails traits::allocator
+            - \p opt::node_allocator - array node allocator.
+                @copydetails traits::node_allocator
+            - \p opt::compare - hash comparison functor. No default functor is provided.
+                If the option is not specified, the \p opt::less is used.
+            - \p opt::less - specifies binary predicate used for hash comparison. 
+                @copydetails cds::container::multilevel_hashset::traits::less
+            - \p opt::back_off - back-off strategy used. If the option is not specified, the \p cds::backoff::Default is used.
+            - \p opt::item_counter - the type of item counting feature.
+                @copydetails cds::intrusive::multilevel_hashset::traits::item_counter
+            - \p opt::memory_model - C++ memory ordering model. Can be \p opt::v::relaxed_ordering (relaxed memory model, the default)                
+                or \p opt::v::sequential_consistent (sequentially consisnent memory model).
+            - \p opt::stat - internal statistics. By default, it is disabled (\p multilevel_hashset::empty_stat).
+                To enable it use \p multilevel_hashset::stat
+            - \p opt::rcu_check_deadlock - a deadlock checking policy for \ref cds_intrusive_MultilevelHashSet_rcu "RCU-based MultilevelHashSet"
+                Default is \p opt::v::rcu_throw_deadlock
+        */
+        template <typename... Options>
+        struct make_traits
+        {
+#   ifdef CDS_DOXYGEN_INVOKED
+            typedef implementation_defined type ;   ///< Metafunction result
+#   else
+            typedef typename cds::opt::make_options<
+                typename cds::opt::find_type_traits< traits, Options... >::type
+                ,Options...
+            >::type   type;
+#   endif
+        };
+    } // namespace multilevel_hashset
+
+    //@cond
+    // Forward declaration
+    template < class GC, typename T, class Traits = multilevel_hashset::traits >
+    class MultiLevelHashSet;
+    //@endcond
+
+    //@cond
+    namespace details {
+
+        template <typename GC, typename T, typename Traits>
+        struct make_multilevel_hashset
+        {
+            typedef GC      gc;
+            typedef T       value_type;
+            typedef Traits  original_traits;
+
+            typedef cds::details::Allocator< value_type, typename original_traits::allocator > cxx_node_allocator;
+
+            struct node_disposer
+            {
+                void operator()( value_type * p ) const
+                {
+                    cxx_node_allocator().Delete( p );
+                }
+            };
+
+            struct intrusive_traits: public original_traits
+            {
+                typedef node_disposer disposer;
+            };
+
+            // Metafunction result
+            typedef cds::intrusive::MultiLevelHashSet< GC, T, intrusive_traits > type;
+        };
+    } // namespace details
+    //@endcond
+
+}} // namespace cds::container
+
+#endif // #ifndef CDSLIB_CONTAINER_DETAILS_MULTILEVEL_HASHSET_BASE_H
diff --git a/cds/container/impl/multilevel_hashset.h b/cds/container/impl/multilevel_hashset.h
new file mode 100644 (file)
index 0000000..d0b2a94
--- /dev/null
@@ -0,0 +1,548 @@
+//$$CDS-header$$
+
+#ifndef CDSLIB_CONTAINER_IMPL_MULTILEVEL_HASHSET_H
+#define CDSLIB_CONTAINER_IMPL_MULTILEVEL_HASHSET_H
+
+#include <cds/intrusive/impl/multilevel_hashset.h>
+#include <cds/container/details/multilevel_hashset_base.h>
+
+namespace cds { namespace container {
+
+    /// Hash set based on multi-level array
+    /** @ingroup cds_nonintrusive_set
+        @anchor cds_container_MultilevelHashSet_hp
+
+        Source:
+        - [2013] Steven Feldman, Pierre LaBorde, Damian Dechev "Concurrent Multi-level Arrays:
+                 Wait-free Extensible Hash Maps"
+
+        [From the paper] The hardest problem encountered while developing a parallel hash map is how to perform
+        a global resize, the process of redistributing the elements in a hash map that occurs when adding new
+        buckets. The negative impact of blocking synchronization is multiplied during a global resize, because all
+        threads will be forced to wait on the thread that is performing the involved process of resizing the hash map
+        and redistributing the elements. \p %MultilevelHashSet implementation avoids global resizes through new array
+        allocation. By allowing concurrent expansion this structure is free from the overhead of an explicit resize,
+        which facilitates concurrent operations.
+
+        The presented design includes dynamic hashing, the use of sub-arrays within the hash map data structure;
+        which, in combination with <b>perfect hashing</b>, means that each element has a unique final, as well as current, position.
+        It is important to note that the perfect hash function required by our hash map is trivial to realize as
+        any hash function that permutes the bits of the key is suitable. This is possible because of our approach
+        to the hash function; we require that it produces hash values that are equal in size to that of the key.
+        We know that if we expand the hash map a fixed number of times there can be no collision as duplicate keys
+        are not provided for in the standard semantics of a hash map.
+
+        \p %MultiLevelHashSet is a multi-level array which has an internal structure similar to a tree:
+        @image html multilevel_hashset.png
+        The multi-level array differs from a tree in that each position on the tree could hold an array of nodes or a single node.
+        A position that holds a single node is a \p dataNode which holds the hash value of a key and the value that is associated\r
+        with that key; it is a simple struct holding two variables. A \p dataNode in the multi-level array could be marked.\r
+        A \p markedDataNode refers to a pointer to a \p dataNode that has been bitmarked at the least significant bit (LSB)\r
+        of the pointer to the node. This signifies that this \p dataNode is contended. An expansion must occur at this node;\r
+        any thread that sees this \p markedDataNode will try to replace it with an \p arrayNode; which is a position that holds\r
+        an array of nodes. The pointer to an \p arrayNode is differentiated from that of a pointer to a \p dataNode by a bitmark\r
+        on the second-least significant bit.\r
+\r
+        \p %MultiLevelHashSet multi-level array is similar to a tree in that we keep a pointer to the root, which is a memory array\r
+        called \p head. The length of the \p head memory array is unique, whereas every other \p arrayNode has a uniform length;\r
+        a normal \p arrayNode has a fixed power-of-two length equal to the binary logarithm of a variable called \p arrayLength.\r
+        The maximum depth of the tree, \p maxDepth, is the maximum number of pointers that must be followed to reach any node.\r
+        We define \p currentDepth as the number of memory arrays that we need to traverse to reach the \p arrayNode on which\r
+        we need to operate; this is initially one, because of \p head.\r
+\r
+        That approach to the structure of the hash set uses an extensible hashing scheme; <b> the hash value is treated as a bit\r
+        string</b> and rehash incrementally.\r
+\r
+        @note Two important things you should keep in mind when you're using \p %MultiLevelHashSet:\r
+        - all keys must be fixed-size. It means that you cannot use \p std::string as a key for \p %MultiLevelHashSet.\r
+          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>,\r
+          <a href="https://en.wikipedia.org/wiki/MurmurHash">MurmurHash</a>, <a href="https://en.wikipedia.org/wiki/CityHash">CityHash</a> \r
+          or its successor <a href="https://code.google.com/p/farmhash/">FarmHash</a> and so on, which\r
+          converts variable-length strings to fixed-length bit-strings, and use that hash as a key in \p %MultiLevelHashSet.\r
+        - \p %MultiLevelHashSet uses a perfect hashing. It means that if two different keys, for example, of type \p std::string,\r
+          have identical hash then you cannot insert both that keys in the set. \p %MultiLevelHashSet does not maintain the key,\r
+          it maintains its fixed-size hash value.\r
+\r
+        The set supports @ref cds_container_MultilevelHashSet_iterators "bidirectional thread-safe iterators".\r
+\r
+        Template parameters:\r
+        - \p GC - safe memory reclamation schema. Can be \p gc::HP, \p gc::DHP or one of \ref cds_urcu_type "RCU type"\r
+        - \p T - a value type to be stored in the set\r
+        - \p Traits - type traits, the structure based on \p multilevel_hashset::traits or result of \p multilevel_hashset::make_traits metafunction.\r
+            \p Traits is the mandatory argument because it has one mandatory type - an @ref multilevel_hashset::traits::hash_accessor "accessor" \r
+            to hash value of \p T. The set algorithm does not calculate that hash value.\r
+\r
+        There are several specializations of \p %MultiLevelHashSet for each \p GC. You should include:
+        - <tt><cds/container/multilevel_hashset_hp.h></tt> for \p gc::HP garbage collector
+        - <tt><cds/container/multilevel_hashset_dhp.h></tt> for \p gc::DHP garbage collector
+        - <tt><cds/container/multilevel_hashset_rcu.h></tt> for \ref cds_intrusive_MultiLevelHashSet_rcu "RCU type". RCU specialization
+            has a slightly different interface.
+    */
+    template <
+        class GC
+        , typename T
+#ifdef CDS_DOXYGEN_INVOKED
+        , class Traits = multilevel_hashset::traits
+#else
+        , class Traits
+#endif
+    >
+    class MultiLevelHashSet
+#ifdef CDS_DOXYGEN_INVOKED
+        : protected cds::intrusive::MultiLevelHashSet< GC, T, Traits >
+#else
+        : protected cds::container::details::make_multilevel_hashset< GC, T, Traits >::type
+#endif
+    {
+        //@cond
+        typedef cds::container::details::make_multilevel_hashset< GC, T, Traits > maker;
+        typedef typename maker::type base_class;
+        //@endcond
+
+    public:
+        typedef GC      gc;         ///< 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 gc::template guarded_ptr< value_type > guarded_ptr; ///< Guarded pointer
+
+        /// Count of hazard pointers required
+        static CDS_CONSTEXPR size_t const c_nHazardPtrCount = base_class::c_nHazardPtrCount;
+
+        typedef typename base_class::iterator               iterator;       ///< @ref cds_container_MultilevelHashSet_iterators "bidirectional iterator" type
+        typedef typename base_class::const_iterator         const_iterator; ///< @ref cds_container_MultilevelHashSet_iterators "bidirectional const iterator" type
+        typedef typename base_class::reverse_iterator       reverse_iterator;       ///< @ref cds_container_MultilevelHashSet_iterators "bidirectional reverse iterator" type
+        typedef typename base_class::const_reverse_iterator const_reverse_iterator; ///< @ref cds_container_MultilevelHashSet_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 key 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.
+        */
+        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.
+        */
+        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 changing data 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, the functor \p func will be called with the item found.
+
+            The functor \p Func signature:
+            \code
+                struct my_functor {
+                    void operator()( bool bNew, value_type& item, const Q& val );
+                };
+            \endcode
+            where:
+            - \p bNew - \p true if the item has been inserted, \p false otherwise
+            - \p item - item of the set
+            - \p val - argument \p key passed into the \p %update() function
+
+            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.
+
+            @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 )
+        {
+            scoped_node_ptr sp( cxx_node_allocator().New( val ));
+            std::pair<bool, bool> bRes = base_class::update( *sp, func, bInsert );
+            if ( bRes.first && bRes.second )
+                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 );
+        }
+
+        /// Deletes the item pointed by iterator \p iter
+        /**
+            Returns \p true if the operation is successful, \p false otherwise.
+
+            The function does not invalidate the iterator, it remains valid and can be used for further traversing.
+        */
+        bool erase_at( iterator const& iter )
+        {
+            return base_class::erase_at( iter );
+        }
+
+        /// 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.
+            If \p hash is not found the function returns an empty guarded pointer.
+
+            The item returned is reclaimed by garbage collector \p GC 
+            when returned \ref guarded_ptr object to be destroyed or released.
+            @note Each \p guarded_ptr object uses the GC's guard that can be limited resource.
+
+            Usage:
+            \code
+            typedef cds::container::MultiLevelHashSet< your_template_args > my_set;
+            my_set theSet;
+            // ...
+            {
+                my_set::guarded_ptr gp( theSet.extract( 5 ));
+                if ( gp ) {
+                    // Deal with gp
+                    // ...
+                }
+                // Destructor of gp releases internal HP guard
+            }
+            \endcode
+        */
+        guarded_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 guarded pointer to the item found.
+            If \p hash is not found the function returns an empty \p guarded_ptr.
+
+            @note Each \p guarded_ptr object uses one GC's guard which can be limited resource.
+
+            Usage:
+            \code
+            typedef cds::container::MultiLevelHashSet< your_template_params >  my_set;
+            my_set theSet;
+            // ...
+            {
+                my_set::guarded_ptr gp( theSet.get( 5 ));
+                if ( theSet.get( 5 )) {
+                    // Deal with gp
+                    //...
+                }
+                // Destructor of guarded_ptr releases internal HP guard
+            }
+            \endcode
+        */
+        guarded_ptr 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_iterators
+            The set supports thread-safe iterators: you may iterate over the set in multi-threaded environment.\r
+            It is guaranteed that the iterators will remain valid even if another thread deletes the node the iterator points to:\r
+            Hazard Pointer embedded into the iterator object protects the node from physical reclamation.\r
+\r
+            @note Since the iterator object contains hazard pointer that is a thread-local resource,\r
+            the iterator should not be passed to another thread.\r
+\r
+            Each iterator object supports the common interface:\r
+            - dereference operators:\r
+                @code\r
+                value_type [const] * operator ->() noexcept\r
+                value_type [const] & operator *() noexcept\r
+                @endcode\r
+            - pre-increment and pre-decrement. Post-operators is not supported\r
+            - equality operators <tt>==</tt> and <tt>!=</tt>.\r
+                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 conditon <tt> it1 == it2 </tt> 
+                does not entail <tt> &(*it1) == &(*it2) </tt>: welcome to concurrent containers
+            - helper member function \p release() that clears internal hazard pointer.\r
+                After \p release() call the iterator points to \p nullptr but it still remain valid: further iterating is possible.
+        */
+    ///@{
+
+        /// 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_IMPL_MULTILEVEL_HASHSET_H
diff --git a/cds/container/multilevel_hashset_dhp.h b/cds/container/multilevel_hashset_dhp.h
new file mode 100644 (file)
index 0000000..66b62b8
--- /dev/null
@@ -0,0 +1,9 @@
+//$$CDS-header$$
+
+#ifndef CDSLIB_CONTAINER_MULTILEVEL_HASHSET_DHP_H
+#define CDSLIB_CONTAINER_MULTILEVEL_HASHSET_DHP_H
+
+#include <cds/container/impl/multilevel_hashset.h>
+#include <cds/gc/dhp.h>
+
+#endif // #ifndef CDSLIB_CONTAINER_MULTILEVEL_HASHSET_DHP_H
diff --git a/cds/container/multilevel_hashset_hp.h b/cds/container/multilevel_hashset_hp.h
new file mode 100644 (file)
index 0000000..34dd032
--- /dev/null
@@ -0,0 +1,9 @@
+//$$CDS-header$$
+
+#ifndef CDSLIB_CONTAINER_MULTILEVEL_HASHSET_HP_H
+#define CDSLIB_CONTAINER_MULTILEVEL_HASHSET_HP_H
+
+#include <cds/container/impl/multilevel_hashset.h>
+#include <cds/gc/hp.h>
+
+#endif // #ifndef CDSLIB_CONTAINER_MULTILEVEL_HASHSET_HP_H
index 3988ac15055e4c49e30a9816f7e0628feb3b83ce..f8f9e36c5a321cf7bc191b2ed2f18d2bace74836 100644 (file)
@@ -15,7 +15,7 @@
 
 namespace cds { namespace intrusive {
 
-    /// MultiLevelHashSet ordered list related definitions
+    /// MultiLevelHashSet related definitions
     /** @ingroup cds_intrusive_helper
     */
     namespace multilevel_hashset {
@@ -105,7 +105,7 @@ namespace cds { namespace intrusive {
             //@endcond
         };
 
-        /// MultiLevelHashSet traits
+        /// \p MultiLevelHashSet traits
         struct traits 
         {
             /// Mandatory functor to get hash value from data node
@@ -145,7 +145,7 @@ namespace cds { namespace intrusive {
 
             /// Specifies binary predicate used for hash compare.
             /**
-                If the option is not specified, \p memcmp() -like bit-wise hash comparator is used
+                If \p %less and \p %compare are not specified, \p memcmp() -like @ref bitwise_compare "bit-wise hash comparator" is used
                 because the hash value is treated as fixed-sized bit-string.
             */
             typedef cds::opt::none less;
index 0ea1ab605771ca04eff627b8c6e9cdeaf9c3d570..a6e13f5e2c109849639d01cdd5edccbbdf06181c 100644 (file)
@@ -34,7 +34,7 @@ namespace cds { namespace intrusive {
         We know that if we expand the hash map a fixed number of times there can be no collision as duplicate keys
         are not provided for in the standard semantics of a hash map.
 
-        \p %MultiLevelHashSet is a multi-level array whitch has a structure similar to a tree:
+        \p %MultiLevelHashSet is a multi-level array which has a structure similar to a tree:
         @image html multilevel_hashset.png
         The multi-level array differs from a tree in that each position on the tree could hold an array of nodes or a single node.
         A position that holds a single node is a \p dataNode which holds the hash value of a key and the value that is associated\r
@@ -52,7 +52,7 @@ namespace cds { namespace intrusive {
         We define \p currentDepth as the number of memory arrays that we need to traverse to reach the \p arrayNode on which\r
         we need to operate; this is initially one, because of \p head.\r
 \r
-        That approach to the structure of the hash map uses an extensible hashing scheme; <b> the hash value is treated as a bit\r
+        That approach to the structure of the hash set uses an extensible hashing scheme; <b> the hash value is treated as a bit\r
         string</b> and rehash incrementally.\r
 \r
         @note Two important things you should keep in mind when you're using \p %MultiLevelHashSet:\r
@@ -99,7 +99,7 @@ namespace cds { namespace intrusive {
         typedef typename traits::hash_accessor hash_accessor; ///< Hash accessor functor
         static_assert(!std::is_same< hash_accessor, cds::opt::none >::value, "hash_accessor functor must be specified" );
 
-        /// Hash type defined as \p hash_accessor return type
+        /// Hash type deduced from \p hash_accessor return type
         typedef typename std::decay< 
             typename std::remove_reference<
                 decltype( hash_accessor()( std::declval<T>()) )
@@ -728,7 +728,6 @@ namespace cds { namespace intrusive {
             If that item is not found the function returns \p false.
 
             The \ref disposer specified in \p Traits is called by garbage collector \p GC asynchronously.
-
         */
         bool erase( hash_type const& hash )
         {
@@ -1340,7 +1339,6 @@ namespace cds { namespace intrusive {
 }} // namespace cds::intrusive
 
 /*
-//@cond
 namespace std {
 
     template <class GC, typename T, typename Traits>
@@ -1370,7 +1368,6 @@ namespace std {
     };
 
 } // namespace std
-//@endcond
 */
 
 #endif // #ifndef CDSLIB_INTRUSIVE_IMPL_MULTILEVEL_HASHSET_H
index 1a24a6b60ab1cb6a35eb8b4378cc99eec0356025..b720b44e7db50215c2c966ca92cb288aff914f82 100644 (file)
     <ClInclude Include="..\..\..\cds\container\details\michael_list_base.h" />\r
     <ClInclude Include="..\..\..\cds\container\details\michael_map_base.h" />\r
     <ClInclude Include="..\..\..\cds\container\details\michael_set_base.h" />\r
+    <ClInclude Include="..\..\..\cds\container\details\multilevel_hashset_base.h" />\r
     <ClInclude Include="..\..\..\cds\container\details\skip_list_base.h" />\r
     <ClInclude Include="..\..\..\cds\container\details\split_list_base.h" />\r
     <ClInclude Include="..\..\..\cds\container\ellen_bintree_map_dhp.h" />\r
     <ClInclude Include="..\..\..\cds\container\impl\lazy_list.h" />\r
     <ClInclude Include="..\..\..\cds\container\impl\michael_kvlist.h" />\r
     <ClInclude Include="..\..\..\cds\container\impl\michael_list.h" />\r
+    <ClInclude Include="..\..\..\cds\container\impl\multilevel_hashset.h" />\r
     <ClInclude Include="..\..\..\cds\container\impl\skip_list_map.h" />\r
     <ClInclude Include="..\..\..\cds\container\impl\skip_list_set.h" />\r
     <ClInclude Include="..\..\..\cds\container\lazy_kvlist_dhp.h" />\r
     <ClInclude Include="..\..\..\cds\container\michael_map_rcu.h" />\r
     <ClInclude Include="..\..\..\cds\container\michael_set_rcu.h" />\r
     <ClInclude Include="..\..\..\cds\container\mspriority_queue.h" />\r
+    <ClInclude Include="..\..\..\cds\container\multilevel_hashset_dhp.h" />\r
+    <ClInclude Include="..\..\..\cds\container\multilevel_hashset_hp.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 1e2b3db79bd214eb3d424e2b55c9e1fabd161df4..a58031401abec4b88081ccec22459ffeaac77050 100644 (file)
     <ClInclude Include="..\..\..\cds\algo\split_bitstring.h">\r
       <Filter>Header Files\cds\algo</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\..\..\cds\container\details\multilevel_hashset_base.h">\r
+      <Filter>Header Files\cds\container\details</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\..\cds\container\impl\multilevel_hashset.h">\r
+      <Filter>Header Files\cds\container\impl</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\..\cds\container\multilevel_hashset_hp.h">\r
+      <Filter>Header Files\cds\container</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\..\cds\container\multilevel_hashset_dhp.h">\r
+      <Filter>Header Files\cds\container</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file