7875dea5942fcffd160c06cd7c738c83483c6bb1
[libcds.git] / cds / container / impl / multilevel_hashmap.h
1 //$$CDS-header$$
2
3 #ifndef CDSLIB_CONTAINER_IMPL_MULTILEVEL_HASHMAP_H
4 #define CDSLIB_CONTAINER_IMPL_MULTILEVEL_HASHMAP_H
5
6 #include <cds/intrusive/impl/multilevel_hashset.h>
7 #include <cds/container/details/multilevel_hashmap_base.h>
8 #include <cds/container/details/guarded_ptr_cast.h>
9
10 namespace cds { namespace container {
11
12     /// Hash map based on multi-level array
13     /** @ingroup cds_nonintrusive_map
14         @anchor cds_container_MultilevelHashMap_hp
15
16         Source:
17         - [2013] Steven Feldman, Pierre LaBorde, Damian Dechev "Concurrent Multi-level Arrays:
18                  Wait-free Extensible Hash Maps"
19
20         [From the paper] The hardest problem encountered while developing a parallel hash map is how to perform
21         a global resize, the process of redistributing the elements in a hash map that occurs when adding new
22         buckets. The negative impact of blocking synchronization is multiplied during a global resize, because all
23         threads will be forced to wait on the thread that is performing the involved process of resizing the hash map
24         and redistributing the elements. \p %MultilevelHashSet implementation avoids global resizes through new array
25         allocation. By allowing concurrent expansion this structure is free from the overhead of an explicit resize,
26         which facilitates concurrent operations.
27
28         The presented design includes dynamic hashing, the use of sub-arrays within the hash map data structure;
29         which, in combination with <b>perfect hashing</b>, means that each element has a unique final, as well as current, position.
30         It is important to note that the perfect hash function required by our hash map is trivial to realize as
31         any hash function that permutes the bits of the key is suitable. This is possible because of our approach
32         to the hash function; we require that it produces hash values that are equal in size to that of the key.
33         We know that if we expand the hash map a fixed number of times there can be no collision as duplicate keys
34         are not provided for in the standard semantics of a hash map.
35
36         \p %MultiLevelHashMap is a multi-level array which has an internal structure similar to a tree:
37         @image html multilevel_hashset.png
38         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.
39         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
40         with that key; it is a simple struct holding two variables. A \p dataNode in the multi-level array could be marked.\r
41         A \p markedDataNode refers to a pointer to a \p dataNode that has been bitmarked at the least significant bit (LSB)\r
42         of the pointer to the node. This signifies that this \p dataNode is contended. An expansion must occur at this node;\r
43         any thread that sees this \p markedDataNode will try to replace it with an \p arrayNode; which is a position that holds\r
44         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
45         on the second-least significant bit.\r
46 \r
47         \p %MultiLevelHashMap multi-level array is similar to a tree in that we keep a pointer to the root, which is a memory array\r
48         called \p head. The length of the \p head memory array is unique, whereas every other \p arrayNode has a uniform length;\r
49         a normal \p arrayNode has a fixed power-of-two length equal to the binary logarithm of a variable called \p arrayLength.\r
50         The maximum depth of the tree, \p maxDepth, is the maximum number of pointers that must be followed to reach any node.\r
51         We define \p currentDepth as the number of memory arrays that we need to traverse to reach the \p arrayNode on which\r
52         we need to operate; this is initially one, because of \p head.\r
53 \r
54         That approach to the structure of the hash map uses an extensible hashing scheme; <b> the hash value is treated as a bit\r
55         string</b> and rehash incrementally.\r
56 \r
57         @note Two important things you should keep in mind when you're using \p %MultiLevelHashMap:\r
58         - all keys is converted to fixed-size bit-string by hash functor provided. \r
59           You can use variable-length keys, for example, \p std::string as a key for \p %MultiLevelHashMap,\r
60           but real key in the map will be fixed-ize hash values of your keys.\r
61           For the strings you may use well-known hashing algorithms like <a href="https://en.wikipedia.org/wiki/Secure_Hash_Algorithm">SHA1, SHA2</a>,\r
62           <a href="https://en.wikipedia.org/wiki/MurmurHash">MurmurHash</a>, <a href="https://en.wikipedia.org/wiki/CityHash">CityHash</a> \r
63           or its successor <a href="https://code.google.com/p/farmhash/">FarmHash</a> and so on, which\r
64           converts variable-length strings to fixed-length bit-strings, and such hash values will be the keys in \p %MultiLevelHashMap.\r
65         - \p %MultiLevelHashMap uses a perfect hashing. It means that if two different keys, for example, of type \p std::string,\r
66           have identical hash then you cannot insert both that keys in the map. \p %MultiLevelHashMap does not maintain the key,\r
67           it maintains its fixed-size hash value.\r
68 \r
69         The map supports @ref cds_container_MultilevelHashMap_iterators "bidirectional thread-safe iterators".\r
70 \r
71         Template parameters:\r
72         - \p GC - safe memory reclamation schema. Can be \p gc::HP, \p gc::DHP or one of \ref cds_urcu_type "RCU type"\r
73         - \p Key - a key type to be stored in the map\r
74         - \p T - a value type to be stored in the map\r
75         - \p Traits - type traits, the structure based on \p multilevel_hashmap::traits or result of \p multilevel_hashmap::make_traits metafunction.\r
76 \r
77         There are several specializations of \p %MultiLevelHashMap for each \p GC. You should include:
78         - <tt><cds/container/multilevel_hashmap_hp.h></tt> for \p gc::HP garbage collector
79         - <tt><cds/container/multilevel_hashmap_dhp.h></tt> for \p gc::DHP garbage collector
80         - <tt><cds/container/multilevel_hashmap_rcu.h></tt> for \ref cds_intrusive_MultiLevelHashSet_rcu "RCU type". RCU specialization
81             has a slightly different interface.
82     */
83     template < 
84         class GC
85         ,typename Key
86         ,typename T
87 #ifdef CDS_DOXYGEN_INVOKED
88         ,class Traits = multilevel_hashmap::traits 
89 #else
90         ,class Traits
91 #endif
92     >
93     class MultiLevelHashMap
94 #ifdef CDS_DOXYGEN_INVOKED
95         : protected cds::intrusive::MultiLevelHashSet< GC, std::pair<Key const, T>, Traits >
96 #else
97         : protected cds::container::details::make_multilevel_hashmap< GC, Key, T, Hasher, Traits >::type
98 #endif
99     {
100         //@cond
101         typedef cds::container::details::make_multilevel_hashmap< GC, Key, T, Hasher, Traits > maker;
102         typedef typename maker::type base_class;
103         //@endcond
104     public:
105         typedef GC      gc;          ///< Garbage collector
106         typedef Key     key_type;    ///< Key type
107         typedef T       mapped_type; ///< Mapped type
108         typedef std::pair< key_type const, mapped_type> value_type;   ///< Key-value pair to be stored in the map
109         typedef Traits  traits;      ///< Map traits
110 #ifdef CDS_DOXYGEN_INVOKED
111         typedef typename traits::hash hasher; ///< Hash functor, see \p multilevel_hashmap::traits::hash
112 #else
113         typedef typename maker::hasher hasher;
114 #endif
115
116         typedef typename maker::hash_type hash_type; ///< Hash type deduced from \p hasher return type
117         typedef typename base_class::hash_comparator hash_comparator; ///< hash compare functor based on \p Traits::compare and \p Traits::less
118
119         typedef typename traits::item_counter   item_counter;   ///< Item counter type
120         typedef typename traits::allocator      allocator;      ///< Element allocator
121         typedef typename traits::node_allocator node_allocator; ///< Array node allocator
122         typedef typename traits::memory_model   memory_model;   ///< Memory model
123         typedef typename traits::back_off       back_off;       ///< Backoff strategy
124         typedef typename traits::stat           stat;           ///< Internal statistics type
125
126         /// Count of hazard pointers required
127         static CDS_CONSTEXPR size_t const c_nHazardPtrCount = base_class::c_nHazardPtrCount;
128
129     protected:
130         //@cond
131         typedef typename maker::node_type node_type;
132         typedef typename maker::cxx_node_allocator cxx_node_allocator;
133         typedef std::unique_ptr< node_type, typename maker::node_disposer > scoped_node_ptr;
134
135         template <class Iterator>
136         class bidirectional_iterator : protected Iterator
137         {
138             friend class MultiLevelHashMap;
139             typedef Iterator base_class;
140         public:
141             typedef typename std::conditional< base_class::c_bConstantIterator, value_type const*, value_type*>::type value_ptr; ///< Value pointer
142             typedef typename std::conditional< base_class::c_bConstantIterator, value_type const&, value_type&>::type value_ref; ///< Value reference
143
144         public:
145             bidirectional_iterator() CDS_NOEXCEPT
146                 : base_class()
147             {}
148
149             bidirectional_iterator( bidirectional_iterator const& rhs ) CDS_NOEXCEPT
150                 : base_class( rhs )
151             {}
152
153             bidirectional_iterator& operator=(bidirectional_iterator const& rhs) CDS_NOEXCEPT
154             {
155                 base_class::operator=( rhs );
156                 return *this;
157             }
158
159             bidirectional_iterator& operator++()
160             {
161                 base_class::operator++();
162                 return *this;
163             }
164
165             bidirectional_iterator& operator--()
166             {
167                 base_class::operator--();
168                 return *this;
169             }
170
171             value_ptr operator ->() const CDS_NOEXCEPT
172             {
173                 return pointer();
174             }
175
176             value_ref operator *() const CDS_NOEXCEPT
177             {
178                 value_ptr p = pointer();
179                 assert( p );
180                 return *p;
181             }
182
183             void release()
184             {
185                 base_class::release();
186             }
187
188             template <class It>
189             bool operator ==(It const& rhs) const CDS_NOEXCEPT
190             {
191                 return base_class::operator==( rhs );
192             }
193
194             template <class It>
195             bool operator !=(It const& rhs) const CDS_NOEXCEPT
196             {
197                 return !( *this == rhs );
198             }
199
200         protected:
201             bidirectional_iterator( base_class&& it ) CDS_NOEXCEPT
202                 : base_class( it )
203             {}
204
205             value_ptr pointer() const CDS_NOEXCEPT
206             {
207                 typename base_class::value_ptr p = base_class::pointer();
208                 return p ? &p->m_Value : nullptr;
209             }
210            
211             node_type * node_pointer() const CDS_NOEXCEPT
212             {
213                 return base_class::pointer();
214             }
215         };
216         //@endcond
217
218     public:
219 #ifdef CDS_DOXYGEN_INVOKED
220         /// Guarded pointer
221         typedef typename gc::template guarded_ptr< value_type > guarded_ptr;
222 #else
223         typedef typename gc::template guarded_ptr< node_type, value_type, cds::details::guarded_ptr_cast_set<node_type, value_type> > guarded_ptr;
224 #endif
225
226 #ifdef CDS_DOXYGEN_INVOKED
227         typedef implementation_defined iterator;            ///< @ref cds_container_MultilevelHashMap_iterators "bidirectional iterator" type
228         typedef implementation_defined const_iterator;      ///< @ref cds_container_MultilevelHashMap_iterators "bidirectional const iterator" type
229         typedef implementation_defined reverse_iterator;    ///< @ref cds_container_MultilevelHashMap_iterators "bidirectional reverse iterator" type
230         typedef implementation_defined const_reverse_iterator; ///< @ref cds_container_MultilevelHashMap_iterators "bidirectional reverse const iterator" type
231 #else
232         typedef bidirectional_iterator<typename base_class::iterator>               iterator;
233         typedef bidirectional_iterator<typename base_class::const_iterator>         const_iterator;
234         typedef bidirectional_iterator<typename base_class::reverse_iterator>       reverse_iterator;
235         typedef bidirectional_iterator<typename base_class::const_reverse_iterator> const_reverse_iterator;
236 #endif
237
238     protected:
239         //@cond
240         hasher  m_Hasher;
241         //@endcond
242
243     public:
244         /// Creates empty map
245         /**
246             @param head_bits: 2<sup>head_bits</sup> specifies the size of head array, minimum is 4.
247             @param array_bits: 2<sup>array_bits</sup> specifies the size of array node, minimum is 2.
248
249             Equation for \p head_bits and \p array_bits:
250             \code
251             sizeof(hash_type) * 8 == head_bits + N * array_bits
252             \endcode
253             where \p N is multi-level array depth.
254         */
255         MultiLevelHashMap( size_t head_bits = 8, size_t array_bits = 4 )
256             : base_class( head_bits, array_bits )
257         {}
258
259         /// Destructs the map and frees all data
260         ~MultiLevelHashMap()
261         {}
262
263         /// Inserts new element with key and default value
264         /**
265             The function creates an element with \p key and default value, and then inserts the node created into the map.
266
267             Preconditions:
268             - The \p key_type should be constructible from a value of type \p K.
269                 In trivial case, \p K is equal to \p key_type.
270             - The \p mapped_type should be default-constructible.
271
272             Returns \p true if inserting successful, \p false otherwise.
273         */
274         template <typename K>
275         bool insert( K const& key )
276         {
277             scoped_node_ptr sp( cxx_node_allocator().New( m_Hasher, key ));
278             if ( base_class::insert( *sp )) {
279                 sp.release();
280                 return true;
281             }
282             return false;
283         }
284
285         /// Inserts new element
286         /**
287             The function creates a node with copy of \p val value
288             and then inserts the node created into the map.
289
290             Preconditions:
291             - The \p key_type should be constructible from \p key of type \p K.
292             - The \p value_type should be constructible from \p val of type \p V.
293
294             Returns \p true if \p val is inserted into the map, \p false otherwise.
295         */
296         template <typename K, typename V>
297         bool insert( K const& key, V const& val )
298         {
299             scoped_node_ptr sp( cxx_node_allocator().New( m_Hasher, key, val ));
300             if ( base_class::insert( *sp )) {
301                 sp.release();
302                 return true;
303             }
304             return false;
305         }
306
307         /// Inserts new element and initialize it by a functor
308         /**
309             This function inserts new element with key \p key and if inserting is successful then it calls
310             \p func functor with signature
311             \code
312                 struct functor {
313                     void operator()( value_type& item );
314                 };
315             \endcode
316
317             The argument \p item of user-defined functor \p func is the reference
318             to the map's item inserted:
319                 - <tt>item.first</tt> is a const reference to item's key that cannot be changed.
320                 - <tt>item.second</tt> is a reference to item's value that may be changed.
321
322             \p key_type should be constructible from value of type \p K.
323
324             The function allows to split creating of new item into two part:
325             - create item from \p key;
326             - insert new item into the map;
327             - if inserting is successful, initialize the value of item by calling \p func functor
328
329             This can be useful if complete initialization of object of \p value_type is heavyweight and
330             it is preferable that the initialization should be completed only if inserting is successful.
331         */
332         template <typename K, typename Func>
333         bool insert_with( K const& key, Func func )
334         {
335             scoped_node_ptr sp( cxx_node_allocator().New( m_Hasher, key ));
336             if ( base_class::insert( *sp, [&func]( node_type& item ) { func( item.m_Value ); } )) {
337                 sp.release();
338                 return true;
339             }
340             return false;
341         }
342
343         /// For key \p key inserts data of type \p value_type created in-place from <tt>std::forward<Args>(args)...</tt>
344         /**
345             Returns \p true if inserting successful, \p false otherwise.
346         */
347         template <typename K, typename... Args>
348         bool emplace( K&& key, Args&&... args )
349         {
350             scoped_node_ptr sp( cxx_node_allocator().New( m_Hasher, std::forward<K>(key), std::forward<Args>(args)... ));
351             if ( base_class::insert( *sp )) {
352                 sp.release();
353                 return true;
354             }
355             return false;
356         }
357
358         /// Updates data by \p key
359         /**
360             The operation performs inserting or changing data with lock-free manner.
361
362             If the \p key not found in the map, then the new item created from \p key
363             will be inserted into the map iff \p bInsert is \p true
364             (note that in this case the \ref key_type should be constructible from type \p K).
365             Otherwise, if \p key is found, the functor \p func is called with item found.
366             The functor \p Func signature:
367             \code
368                 struct my_functor {
369                     void operator()( bool bNew, value_type& item );
370                 };
371             \endcode
372             where:
373             - \p bNew - \p true if the item has been inserted, \p false otherwise
374             - \p item - item of the map
375
376             The functor may change any fields of the \p item.second that is \ref value_type.
377
378             Returns <tt> std::pair<bool, bool> </tt> where \p first is \p true if operation is successfull,
379             \p second is \p true if new item has been added or \p false if \p key already exists.
380
381             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
382         */
383         template <typename K, typename Func>
384         std::pair<bool, bool> update( K const& key, Func func, bool bInsert = true )
385         {
386             scoped_node_ptr sp( cxx_node_allocator().New( m_Hasher, key ));
387             std::pair<bool, bool> result = base_class::do_update( *sp, [&func]( bool bNew, node_type& node ) { func( bNew, node.m_Value );}, bInsert );
388             if ( !result.first )
389                 sp.release();
390             return result;
391         }
392
393         /// Delete \p key from the map
394         /**
395             \p key_type must be constructible from value of type \p K.
396             The function deeltes the element with hash value equal to <tt>hash( key_type( key ))</tt>
397
398             Return \p true if \p key is found and deleted, \p false otherwise.
399         */
400         template <typename K>
401         bool erase( K const& key )
402         {
403             hash_type h = m_Hasher( key_type( key ));
404             return base_class::erase( h );
405         }
406
407         /// Delete \p key from the map
408         /**
409             The function searches an item with hash value equal to <tt>hash( key_type( key ))</tt>,
410             calls \p f functor and deletes the item. If \p key is not found, the functor is not called.
411
412             The functor \p Func interface:
413             \code
414             struct extractor {
415                 void operator()(value_type& item) { ... }
416             };
417             \endcode
418             where \p item is the element found.
419
420             \p key_type must be constructible from value of type \p K.
421
422             Return \p true if key is found and deleted, \p false otherwise
423         */
424         template <typename K, typename Func>
425         bool erase( K const& key, Func f )
426         {
427             hash_type h = m_Hasher( key_type( key ));
428             return base_class::erase( h, [&f]( node_type& node) { f( node.m_Value ); } );
429         }
430
431         /// Deletes the element pointed by iterator \p iter
432         /**
433             Returns \p true if the operation is successful, \p false otherwise.
434
435             The function does not invalidate the iterator, it remains valid and can be used for further traversing.
436         */
437         bool erase_at( iterator const& iter )
438         {
439             return base_class::erase_at( iter );
440         }
441
442         /// Extracts the item from the map with specified \p key
443         /** 
444             The function searches an item with key equal to <tt>hash( key_type( key ))</tt> in the map,
445             unlinks it from the map, and returns a guarded pointer to the item found.
446             If \p key is not found the function returns an empty guarded pointer.
447
448             The item extracted is freed automatically by garbage collector \p GC
449             when returned \p guarded_ptr object will be destroyed or released.
450             @note Each \p guarded_ptr object uses the GC's guard that can be limited resource.
451
452             Usage:
453             \code
454             typedef cds::container::MultiLevelHashMap< cds::gc::HP, int, foo, my_traits > map_type;
455             map_type theMap;
456             // ...
457             {
458                 map_type::guarded_ptr gp( theMap.extract( 5 ));
459                 if ( gp ) {
460                     // Deal with gp
461                     // ...
462                 }
463                 // Destructor of gp releases internal HP guard and frees the pointer
464             }
465             \endcode
466         */
467         template <typename K>
468         guarded_ptr extract( K const& key )
469         {
470             guarded_ptr gp;
471             typename gc::Guard guard;
472             node_type * p = base_class::do_erase( m_Hasher( key_type( key )), guard, []( node_type const&) -> bool {return true;} );
473
474             // p is guarded by HP
475             if ( p )
476                 gp.reset( p );
477             return gp;
478         }
479
480         /// Extracts the item pointed by the iterator \p iter
481         /**
482             The item extracted is freed automatically by garbage collector \p GC
483             when returned \p guarded_ptr object will be destroyed or released.
484
485             @note Each \p guarded_ptr object uses the GC's guard that can be limited resource.
486
487             Due to concurrent nature of the map the returned guarded pointer may be empty.
488             Check it before dereferencing.
489         */
490         guarded_ptr extract_at( iterator const& iter )
491         {
492             guarded_ptr gp;
493             if ( base_class::erase_at( iter )) {
494                 // The element erased is guarded by iter so it is still alive
495                 gp.reset( iter.node_pointer());
496             }
497             return gp;
498         }
499
500         /// Checks whether the map contains \p key
501         /**
502             The function searches the item by its hash that is equal to <tt>hash( key_type( key ))</tt>
503             and returns \p true if it is found, or \p false otherwise.
504         */
505         template <typename K>
506         bool contains( K const& key )
507         {
508             return base_class::contains( m_Hasher( key_type( key )) );
509         }
510
511         /// Find the key \p key
512         /**
513
514             The function searches the item by its hash that is equal to <tt>hash( key_type( key ))</tt>
515             and calls the functor \p f for item found.
516             The interface of \p Func functor is:
517             \code
518             struct functor {
519                 void operator()( value_type& item );
520             };
521             \endcode
522             where \p item is the item found.
523
524             The functor may change \p item.second.
525
526             The function returns \p true if \p key is found, \p false otherwise.
527         */
528         template <typename K, typename Func>
529         bool find( K const& key, Func f )
530         {
531             return base_class::find( m_Hasher( key_type( key )), [&f](node_type& node) { f( node.m_Value );});
532         }
533
534         /// Finds the key \p key and return the item found
535         /**
536             The function searches the item with a hash equal to <tt>hash( key_type( key ))</tt>
537             and returns a guarded pointer to the item found.
538             If \p key is not found the function returns an empty guarded pointer.
539
540             It is safe when a concurrent thread erases the item returned as \p guarded_ptr.
541             In this case the item will be freed later by garbage collector \p GC automatically
542             when \p guarded_ptr object will be destroyed or released.
543             @note Each \p guarded_ptr object uses one GC's guard which can be limited resource.
544
545             Usage:
546             \code
547             typedef cds::container::MultiLevelHashMap< cds::gc::HP, int, foo, my_traits >  map_type;
548             map_type theMap;
549             // ...
550             {
551                 map_type::guarded_ptr gp( theMap.get( 5 ));
552                 if ( gp ) {
553                     // Deal with gp
554                     //...
555                 }
556                 // Destructor of guarded_ptr releases internal HP guard
557             }
558             \endcode
559         */
560         template <typename K>
561         guarded_ptr get( K const& key )
562         {
563             guarded_ptr gp;
564             {
565                 typename gc::Guard guard;
566                 gp.reset( base_class::search( m_Hasher( key_type( key )), guard ));
567             }
568             return gp;
569         }
570
571         /// Clears the map (non-atomic)
572         /**
573             The function unlink all data node from the map.
574             The function is not atomic but is thread-safe.
575             After \p %clear() the map may not be empty because another threads may insert items.
576         */
577         void clear()
578         {
579             base_class::clear();
580         }
581
582         /// Checks if the map is empty
583         /**
584             Emptiness is checked by item counting: if item count is zero then the map is empty.
585             Thus, the correct item counting feature is an important part of the map implementation.
586         */
587         bool empty() const
588         {
589             return base_class::empty();
590         }
591
592         /// Returns item count in the map
593         size_t size() const
594         {
595             return base_class::size();
596         }
597
598         /// Returns const reference to internal statistics
599         stat const& statistics() const
600         {
601             return base_class::statistics();
602         }
603
604         /// Returns the size of head node
605         size_t head_size() const
606         {
607             return base_class::head_size();
608         }
609
610         /// Returns the size of the array node
611         size_t array_node_size() const
612         {
613             return base_class::array_node_size();
614         }
615
616     public:
617     ///@name Thread-safe iterators
618         /** @anchor cds_container_MultilevelHashMap_iterators
619             The map supports thread-safe iterators: you may iterate over the map in multi-threaded environment.\r
620             It is guaranteed that the iterators will remain valid even if another thread deletes the node the iterator points to:\r
621             Hazard Pointer embedded into the iterator object protects the node from physical reclamation.\r
622 \r
623             @note Since the iterator object contains hazard pointer that is a thread-local resource,\r
624             the iterator should not be passed to another thread.\r
625 \r
626             Each iterator object supports the common interface:\r
627             - dereference operators:\r
628                 @code\r
629                 value_type [const] * operator ->() noexcept\r
630                 value_type [const] & operator *() noexcept\r
631                 @endcode\r
632             - pre-increment and pre-decrement. Post-operators is not supported\r
633             - equality operators <tt>==</tt> and <tt>!=</tt>.\r
634                 Iterators are equal iff they point to the same cell of the same array node.
635                 Note that for two iterators \p it1 and \p it2, the conditon <tt> it1 == it2 </tt> 
636                 does not entail <tt> &(*it1) == &(*it2) </tt>
637             - helper member function \p release() that clears internal hazard pointer.\r
638                 After \p release() call the iterator points to \p nullptr but it still remain valid: further iterating is possible.
639         */
640     ///@{
641         /// Returns an iterator to the beginning of the map
642         iterator begin()
643         {
644             return iterator( base_class::begin() );
645         }
646
647         /// Returns an const iterator to the beginning of the map
648         const_iterator begin() const
649         {
650             return const_iterator( base_class::begin());
651         }
652
653         /// Returns an const iterator to the beginning of the map
654         const_iterator cbegin()
655         {
656             return const_iterator( base_class::cbegin());
657         }
658
659         /// Returns an iterator to the element following the last element of the map. This element acts as a placeholder; attempting to access it results in undefined behavior. 
660         iterator end()
661         {
662             return iterator( base_class::end());
663         }
664
665         /// Returns a const iterator to the element following the last element of the map. This element acts as a placeholder; attempting to access it results in undefined behavior. 
666         const_iterator end() const
667         {
668             return const_iterator( base_class::end());
669         }
670
671         /// Returns a const iterator to the element following the last element of the map. This element acts as a placeholder; attempting to access it results in undefined behavior. 
672         const_iterator cend()
673         {
674             return const_iterator( base_class::cend());
675         }
676
677         /// Returns a reverse iterator to the first element of the reversed map
678         reverse_iterator rbegin()
679         {
680             return reverse_iterator( base_class::rbegin());
681         }
682
683         /// Returns a const reverse iterator to the first element of the reversed map
684         const_reverse_iterator rbegin() const
685         {
686             return const_reverse_iterator( base_class::rbegin());
687         }
688
689         /// Returns a const reverse iterator to the first element of the reversed map
690         const_reverse_iterator crbegin()
691         {
692             return const_reverse_iterator( base_class::crbegin());
693         }
694
695         /// Returns a reverse iterator to the element following the last element of the reversed map
696         /**
697             It corresponds to the element preceding the first element of the non-reversed container.
698             This element acts as a placeholder, attempting to access it results in undefined behavior.
699         */
700         reverse_iterator rend()
701         {
702             return reverse_iterator( base_class::rend());
703         }
704
705         /// Returns a const reverse iterator to the element following the last element of the reversed map
706         /**
707             It corresponds to the element preceding the first element of the non-reversed container.
708             This element acts as a placeholder, attempting to access it results in undefined behavior.
709         */
710         const_reverse_iterator rend() const
711         {
712             return const_reverse_iterator( base_class::rend());
713         }
714
715         /// Returns a const reverse iterator to the element following the last element of the reversed map
716         /**
717             It corresponds to the element preceding the first element of the non-reversed container.
718             This element acts as a placeholder, attempting to access it results in undefined behavior.
719         */
720         const_reverse_iterator crend()
721         {
722             return const_reverse_iterator( base_class::crend());
723         }
724     ///@}
725     };
726
727 }} // namespace cds::container
728
729 #endif // #ifndef CDSLIB_CONTAINER_IMPL_MULTILEVEL_HASHMAP_H