Start implementing MultiLevelHashSet
authorkhizmax <libcds.dev@gmail.com>
Mon, 20 Jul 2015 20:43:18 +0000 (23:43 +0300)
committerkhizmax <libcds.dev@gmail.com>
Mon, 20 Jul 2015 20:43:18 +0000 (23:43 +0300)
cds/intrusive/details/multilevel_hashset_base.h [new file with mode: 0644]
cds/intrusive/impl/multilevel_hashset.h [new file with mode: 0644]
cds/intrusive/michael_set.h
cds/intrusive/multilevel_hashset_dhp.h [new file with mode: 0644]
cds/intrusive/multilevel_hashset_hp.h [new file with mode: 0644]
cds/opt/compare.h
projects/Win/vc12/cds.vcxproj
projects/Win/vc12/cds.vcxproj.filters
readme.md

diff --git a/cds/intrusive/details/multilevel_hashset_base.h b/cds/intrusive/details/multilevel_hashset_base.h
new file mode 100644 (file)
index 0000000..2d99478
--- /dev/null
@@ -0,0 +1,229 @@
+//$$CDS-header$$
+
+#ifndef CDSLIB_INTRUSIVE_DETAILS_MULTILEVEL_HASHSET_BASE_H
+#define CDSLIB_INTRUSIVE_DETAILS_MULTILEVEL_HASHSET_BASE_H
+
+#include <memory.h> // memcmp
+#include <type_traits>
+#include <cds/intrusive/details/base.h>
+#include <cds/opt/compare.h>
+#include <cds/algo/atomic.h>
+#include <cds/details/marked_ptr.h>
+#include <cds/urcu/options.h>
+
+namespace cds { namespace intrusive {
+
+    /// MultiLevelHashSet ordered list related definitions
+    /** @ingroup cds_intrusive_helper
+    */
+    namespace multilevel_hashset {
+
+        /// Hash accessor option
+        /**
+            @copydetails traits::hash_accessor
+        */
+        template <typename Accessor>
+        struct hash_accessor {
+            //@cond
+            template <typename Base> struct pack: public Base
+            {
+                typedef Accessor hash_accessor;
+            };
+            //@endcond
+        };
+
+        /// Head node allocator option
+        /**
+            @copydetails traits::head_node_allocator
+        */
+        template <typename Accessor>
+        struct head_node_allocator {
+            //@cond
+            template <typename Base> struct pack: public Base
+            {
+                typedef Accessor head_node_allocator;
+            };
+            //@endcond
+        };
+
+        /// Array node allocator option
+        /**
+            @copydetails traits::array_node_allocator
+        */
+        template <typename Accessor>
+        struct array_node_allocator {
+            //@cond
+            template <typename Base> struct pack: public Base
+            {
+                typedef Accessor array_node_allocator;
+            };
+            //@endcond
+        };
+
+        /// \p MultiLevelHashSet internal statistics
+        template <typename EventCounter = cds::atomicity::event_counter>
+        struct stat {
+            typedef EventCounter event_counter ; ///< Event counter type
+        };
+
+        /// \p MultiLevelHashSet empty internal statistics
+        struct empty_stat {
+        };
+
+        /// MultiLevelHashSet traits
+        struct traits 
+        {
+            /// Mandatory functor to get hash value from data node
+            /**
+                It is most-important feature of \p MultiLevelHashSet.
+                That functor must return a reference to fixed-sized hash value of data node.
+                The return value of that functor specifies the type of hash value.
+
+                Example:
+                \code
+                typedef uint8_t hash_type[32]; // 256-bit hash type
+                struct foo {
+                    hash_type  hash; // 256-bit hash value
+                    // ... other fields
+                };
+
+                struct foo_hash_functor {
+                    hash_type const& operator()( foo const& d ) const
+                    {
+                        return d.hash;
+                    }
+                };
+                \endcode
+            */
+            typedef opt::none hash_accessor;
+
+            /// Disposer for removing data nodes
+            typedef opt::v::empty_disposer disposer;
+
+            /// Hash comparing functor
+            /**
+                No default functor is provided.
+                If the option is not specified, the \p less option is used.
+            */
+            typedef opt::none compare;
+
+            /// Specifies binary predicate used for hash compare.
+            /**
+                If the option is not specified, \p memcmp() -like bit-wise hash comparator is used
+                because the hash value is treated as fixed-sized bit-string.
+            */
+            typedef opt::none less;
+
+            /// Item counter
+            /**
+                The item counting is an important part of \p MultiLevelHashSet algorithm:
+                the \p empty() member function depends on correct item counting.
+                Therefore, \p atomicity::empty_item_counter is not allowed as a type of the option.
+
+                Default is \p atomicity::item_counter.
+            */
+            typedef cds::atomicity::item_counter item_counter;
+
+            /// Head node allocator
+            /**
+                Allocator for head node. That allocator uses only in the set's constructor for allocating
+                main head array and in the destructor for destroying the head array.
+                Default is \ref CDS_DEFAULT_ALLOCATOR
+            */
+            typedef CDS_DEFAULT_ALLOCATOR head_node_allocator;
+
+            /// Array node allocator
+            /**
+                Allocator for array nodes. That allocator is used for creating \p arrayNode when the set grows.
+                The size of each array node is fixed.
+                Default is \ref CDS_DEFAULT_ALLOCATOR
+            */
+            typedef CDS_DEFAULT_ALLOCATOR array_node_allocator;
+
+            /// 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).
+            */
+            typedef opt::v::relaxed_ordering memory_model;
+
+            /// Back-off strategy
+            typedef cds::backoff::Default back_off;
+
+            /// Internal statistics
+            /**
+                By default, internal statistics is disabled (\p multilevel_hashset::empty_stat).
+                Use \p multilevel_hashset::stat to enable it.
+            */
+            typedef empty_stat stat;
+
+            /// RCU deadlock checking policy (only for \ref cds_intrusive_MultilevelHashSet_rcu "RCU-based MultilevelHashSet")
+            /**
+                List of available policy see \p opt::rcu_check_deadlock
+            */
+            typedef 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 multilevel_hashset::head_node_allocator - head node allocator.
+                @copydetails traits::head_node_allocator
+            - \p multilevel_hashset::array_node_allocator - array node allocator.
+                @copydetails traits::array_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. 
+                If the option is not specified, \p memcmp() -like bit-wise hash comparator is used
+                because the hash value is treated as fixed-sized bit-string.
+            - \p opt::back_off - back-off strategy used. If the option is not specified, the \p cds::backoff::Default is used.
+            - \p opt::disposer - the functor used for disposing removed data node. Default is \p opt::v::empty_disposer. Due the nature
+                of GC schema the disposer may be called asynchronously.
+            - \p opt::item_counter - the type of item counting feature.
+                 The item counting is an important part of \p MultiLevelHashSet algorithm:
+                 the \p empty() member function depends on correct item counting.
+                 Therefore, \p atomicity::empty_item_counter is not allowed as a type of the option.
+                 Default is \p atomicity::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
+        };
+
+        /// Bit-wise memcmp-based comparator for hash value \p T
+        template <typename T>
+        struct bitwise_compare
+        {
+            int operator()( T const& lhs, T const& rhs ) const
+            {
+                return memcmp( &lhs, &rhs, sizeof(T));
+            }
+        };
+
+    } // namespace multilevel_hashset
+
+    //@cond
+    // Forward declaration
+    template < class GC, typename T, class Traits = multilevel_hashset::traits >
+    class MultiLevelHashSet;
+    //@endcond
+
+}} // namespace cds::intrusive
+
+#endif // #ifndef CDSLIB_INTRUSIVE_DETAILS_MULTILEVEL_HASHSET_BASE_H
diff --git a/cds/intrusive/impl/multilevel_hashset.h b/cds/intrusive/impl/multilevel_hashset.h
new file mode 100644 (file)
index 0000000..ecdf13e
--- /dev/null
@@ -0,0 +1,473 @@
+//$$CDS-header$$
+
+#ifndef CDSLIB_INTRUSIVE_IMPL_MULTILEVEL_HASHSET_H
+#define CDSLIB_INTRUSIVE_IMPL_MULTILEVEL_HASHSET_H
+
+#include <cds/intrusive/details/multilevel_hashset_base.h>
+#include <cds/details/allocator.h>
+
+namespace cds { namespace intrusive {
+    /// Intrusive hash set based on multi-level array
+    /** @ingroup cds_intrusive_map
+        @anchor cds_intrusive_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 whitch 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
+        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 map 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 fized-size hash value.\r
+\r
+\r
+        There are several specializations of \p %MultiLevelHashSet for each \p GC. You should include:
+        - <tt><cds/intrusive/multilevel_hashset_hp.h></tt> for \p gc::HP garbage collector
+        - <tt><cds/intrusive/multilevel_hashset_dhp.h></tt> for \p gc::DHP garbage collector
+        - <tt><cds/intrusive/multilevel_hashset_nogc.h></tt> for \ref cds_intrusive_MultiLevelHashSet_nogc for append-only set
+        - <tt><cds/intrusive/multilevel_hashset_rcu.h></tt> for \ref cds_intrusive_MultiLevelHashSet_rcu "RCU type"
+    */
+    template <
+        class GC
+        ,typename T
+#ifdef CDS_DOXYGEN_INVOKED
+       ,typename Traits = multilevel_hashset::traits
+#else
+       ,typename Traits
+#endif
+    >
+    class MultiLevelHashSet
+    {
+    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 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
+        typedef typename std::decay< 
+            typename std::remove_reference<
+                decltype( hash_accessor()( std::declval<T>()) )
+            >::type
+        >::type hash_type;
+        static_assert( !std::is_pointer<hash_type>, "hash_accessor should return a reference to hash value" );
+
+        typedef typename traits::disposer disposer; ///< data node disposer
+
+#   ifdef CDS_DOXYGEN_INVOKED
+        typedef implementation_defined hash_comparator  ;    ///< hash compare functor based on opt::compare and opt::less option setter
+#   else
+        typedef typename cds::opt::details::make_comparator_from< 
+            hash_type,
+            traits,
+            multilevel_hashset::bitwise_compare< hash_type >
+        >::type hash_comparator;
+#   endif
+
+        typedef typename traits::item_counter         item_counter;         ///< Item counter type
+        typedef typename traits::head_node_allocator  head_node_allocator;  ///< Head node allocator
+        typedef typename traits::array_node_allocator array_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
+
+    protected:
+        //@cond
+        enum cell_flags {
+            logically_deleted = 1,  ///< the cell (data node) is logically deleted
+            array_converting = 2,   ///< the cell is converting from data node to an array node
+            array_node = 4          ///< the cell is a pointer to an array node
+        };
+        typedef cds::details::marked_ptr< value_type, 7 > node_ptr;
+        typedef atomics::atomic< node_ptr * >  atomic_node_ptr;
+
+        typedef cds::details::Allocator< atomic_node_ptr, head_node_allocator > cxx_head_node_allocator;
+        typedef cds::details::Allocator< atomic_node_ptr, array_node_allocator > cxx_array_node_allocator;
+
+        struct metrics {
+            size_t  head_node_size;     // power-of-two
+            size_t  head_node_size_log; // log2( head_node_size )
+            size_t  array_node_size;    // power-of-two
+            size_t  array_node_size_log;// log2( array_node_size )
+        };
+        //@endcond
+
+    private:
+        //@cond
+        metrics const     m_Metrics;     ///< Metrics
+        atomic_node_ptr * m_Head;        ///< Head array
+        item_counter      m_ItemCounter; ///< Item counter
+        stat              m_Stat;        ///< Internal statistics
+        //@endcond
+
+    public:
+        /// Creates empty set
+        /**
+            @param head_bits: 2<sup>head_bits</sup> specifies the size of head array, minimum is 8.
+            @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 )
+            : m_Metrics(make_metrics( head_bits, array_bits ))
+            , m_Head( cxx_head_node_allocator().NewArray( m_Metrics.head_node_size, nullptr ))
+        {}
+
+        /// Destructs the set and frees all data
+        ~MultiLevelHashSet()
+        {
+            destroy_tree();
+            cxx_head_node_allocator().Delete( m_Head, m_Metrics.head_node_size );
+        }
+
+        /// Inserts new node
+        /**
+            The function inserts \p val in the set if it does not contain 
+            an item with that hash.
+
+            Returns \p true if \p val is placed into the set, \p false otherwise.
+        */
+        bool insert( value_type& val )
+        {
+        }
+
+        /// Inserts new node
+        /**
+            This function is intended for derived non-intrusive containers.
+
+            The function allows to split creating of new item into two part:
+            - create item with key only
+            - insert new item into the set
+            - if inserting is success, calls  \p f functor to initialize value-field of \p val.
+
+            The functor signature is:
+            \code
+                void func( value_type& val );
+            \endcode
+            where \p val is the item inserted.
+
+            The user-defined functor is called only if the inserting is success.
+
+            @warning See \ref cds_intrusive_item_creating "insert item troubleshooting".
+        */
+        template <typename Func>
+        bool insert( value_type& val, Func f )
+        {
+        }
+
+        /// Updates the node
+        /**
+            The operation performs inserting or changing data with lock-free manner.
+
+            If the item \p val not found in the set, then \p val is inserted into the set iff \p bInsert is \p true.
+            Otherwise, the functor \p func is called with item found, \p bInsert argument is ignored.
+
+            The functor signature is:
+            \code
+                void func( bool bNew, value_type& item, value_type& val );
+            \endcode
+            with arguments:
+            - \p bNew - \p true if the item has been inserted, \p false otherwise
+            - \p item - the item in the set (new or existing)
+            - \p val - argument \p val passed into the \p update function
+            If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
+            refers to the same thing.
+
+            The functor may change non-key fields of the \p item.
+
+            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 the set contains that hash.
+
+            @warning See \ref cds_intrusive_item_creating "insert item troubleshooting".
+        */
+        template <typename Func>
+        std::pair<bool, bool> update( value_type& val, Func func, bool bInsert = true )
+        {
+        }
+
+        /// Unlinks the item \p val from the set
+        /**
+            The function searches the item \p val in the set and unlink it
+            if it is found and its address is equal to <tt>&val</tt>.
+
+            The function returns \p true if success and \p false otherwise.
+        */
+        bool unlink( value_type& val )
+        {
+        }
+
+        /// Deletes the item from the set
+        /** 
+            The function searches \p hash in the set,
+            unlinks the item found, and returns \p true.
+            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 )
+        {
+            if ( bucket( key ).erase( key )) {
+                --m_ItemCounter;
+                return true;
+            }
+            return false;
+        }
+
+        /// Deletes the item from the set
+        /**
+            The function searches \p hash in the set,
+            call \p f functor with item found, and unlinks it from the set.
+            The \ref disposer specified in \p Traits is called
+            by garbage collector \p GC asynchronously.
+
+            The \p Func interface is
+            \code
+            struct functor {
+                void operator()( value_type const& item );
+            };
+            \endcode
+
+            If \p hash is not found the function returns \p false.
+        */
+        template <typename Func>
+        bool erase( hash_type const& hash, Func f )
+        {
+        }
+
+        /// 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 \p disposer specified in \p Traits class' template parameter is called automatically
+            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::intrusive::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 )
+        {
+        }
+
+        /// 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& hash, Func f )
+        {
+        }
+        //@cond
+        template <typename Func>
+        bool find( hash_type const& hash, Func f )
+        {
+        }
+        //@endcond
+
+        /// Finds an item by it's \p hash
+        /**
+            The function searches the item by its \p hash
+            and returns \p true if it is found, or \p false otherwise.
+        */
+        bool find( hash_type const& 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::intrusive::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 )
+        {
+        }
+
+        /// Clears the set (non-atomic)
+        /**
+            The function unlink all items from the set.
+            The function is not atomic. It removes all data nodes and then resets the item counter to zero.
+            If there are a thread that performs insertion while \p %clear() is working the result is undefined in general case:
+            \p empty() may return \p true but the set may contain item(s).
+            Therefore, \p %clear() may be used only for debugging purposes.
+
+            For each item the \p disposer is called after unlinking.
+        */
+        void 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 size() == 0;
+        }
+
+        /// Returns item count in the set
+        size_t size() const
+        {
+            return m_ItemCounter;
+        }
+
+        /// Returns const reference to internal statistics
+        stat const& statistics() const
+        {
+            return m_Stat;
+        }
+
+        /// Returns the size of head node
+        size_t head_size() const
+        {
+            return m_Metrics.head_node_size;
+        }
+
+        /// Returns the size of the array node
+        size_t array_node_size() const
+        {
+            return m_Metrics.array_node_size;
+        }
+
+    private:
+        //@cond
+        static metrics make_metrics( size_t head_bits, size_t array_bits )
+        {
+            size_t const hash_bits = sizeof( hash_type ) * 8;
+
+            if ( array_bits < 2 )
+                array_bits = 2;
+            if ( head_bits < 8 )
+                head_bits = 8;
+            if ( head_bits > hash_bits )
+                head_bits = hash_bits;
+            if ( (hash_bits - head_bits) % array_bits != 0 )
+                head_bits += (hash_bits - head_bits) % array_bits;
+
+            assert( (hash_bits - head_bits) % array_bits == 0 );
+
+            metrics m;
+            m.head_node_size_log = head_bits;
+            m.head_node_size = 1 << head_bits;
+            m.array_node_size_log = array_bits;
+            m.array_node_size = 1 << array_bits;
+            return m;
+        }
+
+        template <typename T>
+        static bool check_node_alignment( T * p )
+        {
+            return (reinterpret_cast<uintptr_t>(p) & node_ptr::bitmask) == 0;
+        }
+
+        void destroy_tree()
+        {
+            // The function is not thread-safe. For use in dtor only
+            // Remove data node
+            clear();
+
+            //TODO: free all array nodes
+        }
+        //@endcond
+    };
+}} // namespace cds::intrusive
+
+#endif // #ifndef CDSLIB_INTRUSIVE_IMPL_MULTILEVEL_HASHSET_H
index 3f7944fcef00ac069d5216ac344245db02460676..87feff4d8baa5d6ad8259e42d81393a49e8d3a56 100644 (file)
@@ -699,7 +699,7 @@ namespace cds { namespace intrusive {
 
             Usage:
             \code
-            typedef cds::intrusive::MichaeHashSet< your_template_params >  michael_set;
+            typedef cds::intrusive::MichaelHashSet< your_template_params >  michael_set;
             michael_set theSet;
             // ...
             {
@@ -740,9 +740,9 @@ namespace cds { namespace intrusive {
         /**
             The function unlink all items from the set.
             The function is not atomic. It cleans up each bucket and then resets the item counter to zero.
-            If there are a thread that performs insertion while \p clear is working the result is undefined in general case:
-            <tt> empty() </tt> may return \p true but the set may contain item(s).
-            Therefore, \p clear may be used only for debugging purposes.
+            If there are a thread that performs insertion while \p %clear() is working the result is undefined in general case:
+            \p empty() may return \p true but the set may contain item(s).
+            Therefore, \p %clear() may be used only for debugging purposes.
 
             For each item the \p disposer is called after unlinking.
         */
@@ -753,7 +753,6 @@ namespace cds { namespace intrusive {
             m_ItemCounter.reset();
         }
 
-
         /// Checks if the set is empty
         /**
             Emptiness is checked by item counting: if item count is zero then the set is empty.
diff --git a/cds/intrusive/multilevel_hashset_dhp.h b/cds/intrusive/multilevel_hashset_dhp.h
new file mode 100644 (file)
index 0000000..e0c9511
--- /dev/null
@@ -0,0 +1,9 @@
+//$$CDS-header$$
+
+#ifndef CDSLIB_INTRUSIVE_MULTILEVEL_HASHSET_DHP_H
+#define CDSLIB_INTRUSIVE_MULTILEVEL_HASHSET_DHP_H
+
+#include <cds/intrusive/impl/multilevel_hashset.h>
+#include <cds/gc/dhp.h>
+
+#endif // #ifndef CDSLIB_INTRUSIVE_MULTILEVEL_HASHSET_DHP_H
diff --git a/cds/intrusive/multilevel_hashset_hp.h b/cds/intrusive/multilevel_hashset_hp.h
new file mode 100644 (file)
index 0000000..97dc8a2
--- /dev/null
@@ -0,0 +1,9 @@
+//$$CDS-header$$
+
+#ifndef CDSLIB_INTRUSIVE_MULTILEVEL_HASHSET_HP_H
+#define CDSLIB_INTRUSIVE_MULTILEVEL_HASHSET_HP_H
+
+#include <cds/intrusive/impl/multilevel_hashset.h>
+#include <cds/gc/hp.h>
+
+#endif // #ifndef CDSLIB_INTRUSIVE_MULTILEVEL_HASHSET_HP_H
index 4e98955689ef38aec83a5c2b77e2f3d2066ed270..4be39579a9413ea3a60b95916796c0d8ecb53a22 100644 (file)
@@ -166,8 +166,8 @@ namespace cds { namespace opt {
             }
         };
 
-        template <typename T, typename Traits, bool Forced = true >
-        struct make_comparator
+        template <typename T, typename Traits, typename DefaultCmp = make_comparator_from_less< std::less<T>>::type >
+        struct make_comparator_from
         {
             typedef typename Traits::compare compare;
             typedef typename Traits::less less;
@@ -176,13 +176,17 @@ namespace cds { namespace opt {
                 std::is_same< compare, opt::none >::value,
                 typename std::conditional<
                     std::is_same< less, opt::none >::value,
-                    typename std::conditional< Forced, make_comparator_from_less< std::less<T> >, opt::none >::type,
+                    DefaultCmp,
                     make_comparator_from_less< less >
                 >::type,
                 compare
             >::type type;
         };
 
+
+        template <typename T, typename Traits, bool Forced = true >
+        using make_comparator = make_comparator_from< T, Traits, typename std::conditional< Forced, make_comparator_from_less< std::less<T>>, opt::none >::type >;
+
         template <typename T, typename... Options>
         struct make_comparator_from_option_list
         {
index 514be40c8f9c2ca1dfe9ecebf44be049cd8e5811..bb711f02a94ae7bc7187a0598054877775ed8414 100644 (file)
     <ClInclude Include="..\..\..\cds\intrusive\bronson_avltree_rcu.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\cuckoo_set.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\details\base.h" />\r
-    <ClInclude Include="..\..\..\cds\intrusive\details\bronson_avltree_base.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\details\ellen_bintree_base.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\details\lazy_list_base.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\details\michael_list_base.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\details\michael_set_base.h" />\r
+    <ClInclude Include="..\..\..\cds\intrusive\details\multilevel_hashset_base.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\details\node_traits.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\details\raw_ptr_disposer.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\details\single_link_struct.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\impl\ellen_bintree.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\impl\lazy_list.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\impl\michael_list.h" />\r
+    <ClInclude Include="..\..\..\cds\intrusive\impl\multilevel_hashset.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\impl\skip_list.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\lazy_list_dhp.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\lazy_list_rcu.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\michael_list_rcu.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\michael_set_rcu.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\mspriority_queue.h" />\r
+    <ClInclude Include="..\..\..\cds\intrusive\multilevel_hashset_dhp.h" />\r
+    <ClInclude Include="..\..\..\cds\intrusive\multilevel_hashset_hp.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\options.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\skip_list_dhp.h" />\r
     <ClInclude Include="..\..\..\cds\intrusive\skip_list_hp.h" />\r
index b2487acc653cc9a3618a94662d4ca318cf2e85cc..2548ad5489d64d1161d391b20ab29b6b5efa93b1 100644 (file)
     <ClInclude Include="..\..\..\cds\algo\atomic.h">\r
       <Filter>Header Files\cds\algo</Filter>\r
     </ClInclude>\r
-    <ClInclude Include="..\..\..\cds\intrusive\details\bronson_avltree_base.h">\r
-      <Filter>Header Files\cds\intrusive\details</Filter>\r
-    </ClInclude>\r
     <ClInclude Include="..\..\..\cds\intrusive\bronson_avltree_rcu.h">\r
       <Filter>Header Files\cds\intrusive</Filter>\r
     </ClInclude>\r
     <ClInclude Include="..\..\..\cds\intrusive\details\raw_ptr_disposer.h">\r
       <Filter>Header Files\cds\intrusive\details</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\..\..\cds\intrusive\impl\multilevel_hashset.h">\r
+      <Filter>Header Files\cds\intrusive\impl</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\..\cds\intrusive\details\multilevel_hashset_base.h">\r
+      <Filter>Header Files\cds\intrusive\details</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\..\cds\intrusive\multilevel_hashset_hp.h">\r
+      <Filter>Header Files\cds\intrusive</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\..\cds\intrusive\multilevel_hashset_dhp.h">\r
+      <Filter>Header Files\cds\intrusive</Filter>\r
+    </ClInclude>\r
   </ItemGroup>\r
 </Project>
\ No newline at end of file
index 612a30870b348a6282b376b8f2502ec47cf4dce4..8c73956714e61af13e60336e0d8cd9d097abc4eb 100644 (file)
--- a/readme.md
+++ b/readme.md
@@ -82,6 +82,8 @@ References
   - *StripedMap*, *StripedSet*: [2008] Maurice Herlihy, Nir Shavit "The Art of Multiprocessor Programming"\r
   - *CuckooMap*, *CuckooSet*: [2008] Maurice Herlihy, Nir Shavit "The Art of Multiprocessor Programming"\r
   - *SkipListMap*, *SkipListSet*: [2008] Maurice Herlihy, Nir Shavit "The Art of Multiprocessor Programming"\r
+  - *BronsonAVLTreeMap* - lock-based fine-grained AVL-tree implementation: \r
+        [2010] Nathan Bronson, Jared Casper, Hassan Chafi, Kunle Olukotun "A Practical Concurrent Binary Search Tree"\r
         \r
 *Ordered single-linked list*\r
   - *LazyList*: [2005] Steve Heller, Maurice Herlihy, Victor Luchangco, Mark Moir, William N. Scherer III, and Nir Shavit "A Lazy Concurrent List-Based Set Algorithm"\r