From: khizmax Date: Sat, 15 Aug 2015 15:54:22 +0000 (+0300) Subject: [draft] implemented container::MultiLevelHashSet X-Git-Tag: v2.1.0~157 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=de0bed3b8e1d7c677317fcbdcb41aedfb0110046;p=libcds.git [draft] implemented container::MultiLevelHashSet --- diff --git a/cds/container/details/ellen_bintree_base.h b/cds/container/details/ellen_bintree_base.h index 3be1c584..86402ced 100644 --- a/cds/container/details/ellen_bintree_base.h +++ b/cds/container/details/ellen_bintree_base.h @@ -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 index 00000000..be1b994e --- /dev/null +++ b/cds/container/details/multilevel_hashset_base.h @@ -0,0 +1,170 @@ +//$$CDS-header$$ + +#ifndef CDSLIB_CONTAINER_DETAILS_MULTILEVEL_HASHSET_BASE_H +#define CDSLIB_CONTAINER_DETAILS_MULTILEVEL_HASHSET_BASE_H + +#include +#include + +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 + using hash_accessor = cds::intrusive::hash_accessor< Accessor >; + + /// \p MultiLevelHashSet internal statistics, see cds::intrusive::multilevel_hashset::stat + template + 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 + 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 + 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 + 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 index 00000000..d0b2a94b --- /dev/null +++ b/cds/container/impl/multilevel_hashset.h @@ -0,0 +1,548 @@ +//$$CDS-header$$ + +#ifndef CDSLIB_CONTAINER_IMPL_MULTILEVEL_HASHSET_H +#define CDSLIB_CONTAINER_IMPL_MULTILEVEL_HASHSET_H + +#include +#include + +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 perfect hashing, 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 + with that key; it is a simple struct holding two variables. A \p dataNode in the multi-level array could be marked. + A \p markedDataNode refers to a pointer to a \p dataNode that has been bitmarked at the least significant bit (LSB) + of the pointer to the node. This signifies that this \p dataNode is contended. An expansion must occur at this node; + any thread that sees this \p markedDataNode will try to replace it with an \p arrayNode; which is a position that holds + an array of nodes. The pointer to an \p arrayNode is differentiated from that of a pointer to a \p dataNode by a bitmark + on the second-least significant bit. + + \p %MultiLevelHashSet multi-level array is similar to a tree in that we keep a pointer to the root, which is a memory array + called \p head. The length of the \p head memory array is unique, whereas every other \p arrayNode has a uniform length; + a normal \p arrayNode has a fixed power-of-two length equal to the binary logarithm of a variable called \p arrayLength. + The maximum depth of the tree, \p maxDepth, is the maximum number of pointers that must be followed to reach any node. + We define \p currentDepth as the number of memory arrays that we need to traverse to reach the \p arrayNode on which + we need to operate; this is initially one, because of \p head. + + That approach to the structure of the hash set uses an extensible hashing scheme; the hash value is treated as a bit + string and rehash incrementally. + + @note Two important things you should keep in mind when you're using \p %MultiLevelHashSet: + - all keys must be fixed-size. It means that you cannot use \p std::string as a key for \p %MultiLevelHashSet. + Instead, for the strings you should use well-known hashing algorithms like SHA1, SHA2, + MurmurHash, CityHash + or its successor FarmHash and so on, which + converts variable-length strings to fixed-length bit-strings, and use that hash as a key in \p %MultiLevelHashSet. + - \p %MultiLevelHashSet uses a perfect hashing. It means that if two different keys, for example, of type \p std::string, + have identical hash then you cannot insert both that keys in the set. \p %MultiLevelHashSet does not maintain the key, + it maintains its fixed-size hash value. + + The set supports @ref cds_container_MultilevelHashSet_iterators "bidirectional thread-safe iterators". + + Template parameters: + - \p GC - safe memory reclamation schema. Can be \p gc::HP, \p gc::DHP or one of \ref cds_urcu_type "RCU type" + - \p T - a value type to be stored in the set + - \p Traits - type traits, the structure based on \p multilevel_hashset::traits or result of \p multilevel_hashset::make_traits metafunction. + \p Traits is the mandatory argument because it has one mandatory type - an @ref multilevel_hashset::traits::hash_accessor "accessor" + to hash value of \p T. The set algorithm does not calculate that hash value. + + There are several specializations of \p %MultiLevelHashSet for each \p GC. You should include: + - for \p gc::HP garbage collector + - for \p gc::DHP garbage collector + - 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: 2head_bits specifies the size of head array, minimum is 4. + @param array_bits: 2array_bits 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 + 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 + 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 std::pair 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 + std::pair update( const Q& val, Func func, bool bInsert = true ) + { + scoped_node_ptr sp( cxx_node_allocator().New( val )); + std::pair 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 std::forward(args)... + /** + Returns \p true if inserting successful, \p false otherwise. + */ + template + bool emplace( Args&&... args ) + { + scoped_node_ptr sp( cxx_node_allocator().New( std::forward(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 + 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 + 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. + It is guaranteed that the iterators will remain valid even if another thread deletes the node the iterator points to: + Hazard Pointer embedded into the iterator object protects the node from physical reclamation. + + @note Since the iterator object contains hazard pointer that is a thread-local resource, + the iterator should not be passed to another thread. + + 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 == and !=. + 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 it1 == it2 + does not entail &(*it1) == &(*it2) : welcome to concurrent containers + - helper member function \p release() that clears internal hazard pointer. + 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 index 00000000..66b62b85 --- /dev/null +++ b/cds/container/multilevel_hashset_dhp.h @@ -0,0 +1,9 @@ +//$$CDS-header$$ + +#ifndef CDSLIB_CONTAINER_MULTILEVEL_HASHSET_DHP_H +#define CDSLIB_CONTAINER_MULTILEVEL_HASHSET_DHP_H + +#include +#include + +#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 index 00000000..34dd032c --- /dev/null +++ b/cds/container/multilevel_hashset_hp.h @@ -0,0 +1,9 @@ +//$$CDS-header$$ + +#ifndef CDSLIB_CONTAINER_MULTILEVEL_HASHSET_HP_H +#define CDSLIB_CONTAINER_MULTILEVEL_HASHSET_HP_H + +#include +#include + +#endif // #ifndef CDSLIB_CONTAINER_MULTILEVEL_HASHSET_HP_H diff --git a/cds/intrusive/details/multilevel_hashset_base.h b/cds/intrusive/details/multilevel_hashset_base.h index 3988ac15..f8f9e36c 100644 --- a/cds/intrusive/details/multilevel_hashset_base.h +++ b/cds/intrusive/details/multilevel_hashset_base.h @@ -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; diff --git a/cds/intrusive/impl/multilevel_hashset.h b/cds/intrusive/impl/multilevel_hashset.h index 0ea1ab60..a6e13f5e 100644 --- a/cds/intrusive/impl/multilevel_hashset.h +++ b/cds/intrusive/impl/multilevel_hashset.h @@ -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 @@ -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 we need to operate; this is initially one, because of \p head. - That approach to the structure of the hash map uses an extensible hashing scheme; the hash value is treated as a bit + That approach to the structure of the hash set uses an extensible hashing scheme; the hash value is treated as a bit string and rehash incrementally. @note Two important things you should keep in mind when you're using \p %MultiLevelHashSet: @@ -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()) ) @@ -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 @@ -1370,7 +1368,6 @@ namespace std { }; } // namespace std -//@endcond */ #endif // #ifndef CDSLIB_INTRUSIVE_IMPL_MULTILEVEL_HASHSET_H diff --git a/projects/Win/vc12/cds.vcxproj b/projects/Win/vc12/cds.vcxproj index 1a24a6b6..b720b44e 100644 --- a/projects/Win/vc12/cds.vcxproj +++ b/projects/Win/vc12/cds.vcxproj @@ -670,6 +670,7 @@ + @@ -685,6 +686,7 @@ + @@ -698,6 +700,8 @@ + + diff --git a/projects/Win/vc12/cds.vcxproj.filters b/projects/Win/vc12/cds.vcxproj.filters index 1e2b3db7..a5803140 100644 --- a/projects/Win/vc12/cds.vcxproj.filters +++ b/projects/Win/vc12/cds.vcxproj.filters @@ -1199,5 +1199,17 @@ Header Files\cds\algo + + Header Files\cds\container\details + + + Header Files\cds\container\impl + + + Header Files\cds\container + + + Header Files\cds\container + \ No newline at end of file