2 This file is a part of libcds - Concurrent Data Structures library
4 (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017
6 Source code repo: http://github.com/khizmax/libcds/
7 Download: http://sourceforge.net/projects/libcds/files/
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
12 * Redistributions of source code must retain the above copyright notice, this
13 list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright notice,
16 this list of conditions and the following disclaimer in the documentation
17 and/or other materials provided with the distribution.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 #ifndef CDSLIB_CONTAINER_MICHAEL_SET_RCU_H
32 #define CDSLIB_CONTAINER_MICHAEL_SET_RCU_H
34 #include <cds/container/details/michael_set_base.h>
35 #include <cds/details/allocator.h>
37 namespace cds { namespace container {
39 /// Michael's hash set (template specialization for \ref cds_urcu_desc "RCU")
40 /** @ingroup cds_nonintrusive_set
41 \anchor cds_nonintrusive_MichaelHashSet_rcu
44 - [2002] Maged Michael "High performance dynamic lock-free hash tables and list-based sets"
46 Michael's hash table algorithm is based on lock-free ordered list and it is very simple.
47 The main structure is an array \p T of size \p M. Each element in \p T is basically a pointer
48 to a hash bucket, implemented as a singly linked list. The array of buckets cannot be dynamically expanded.
49 However, each bucket may contain unbounded number of items.
51 Template parameters are:
52 - \p RCU - one of \ref cds_urcu_gc "RCU type"
53 - \p OrderedList - ordered list implementation used as the bucket for hash set, for example,
54 \ref cds_nonintrusive_MichaelList_rcu "MichaelList".
55 The ordered list implementation specifies the type \p T stored in the hash-set,
56 the comparison functor for the type \p T and other features specific for
58 - \p Traits - set traits, default is michael_set::traits.
59 Instead of defining \p Traits struct you may use option-based syntax with michael_set::make_traits metafunction.
61 About hash functor see \ref cds_nonintrusive_MichaelHashSet_hash_functor "MichaelSet hash functor".
65 Suppose, we have the following type \p Foo that we want to store in your \p %MichaelHashSet:
68 int nKey ; // key field
69 int nVal ; // value field
73 To use \p %MichaelHashSet for \p Foo values, you should first choose suitable ordered list class
74 that will be used as a bucket for the set. We will cds::urcu::general_buffered<> RCU type and
75 MichaelList as a bucket type.
76 You should include RCU-related header file (<tt>cds/urcu/general_buffered.h</tt> in this example)
77 before including <tt>cds/container/michael_set_rcu.h</tt>.
78 Also, for ordered list we should develop a comparator for our \p Foo struct.
80 #include <cds/urcu/general_buffered.h>
81 #include <cds/container/michael_list_rcu.h>
82 #include <cds/container/michael_set_rcu.h>
84 namespace cc = cds::container;
88 int operator ()(Foo const& v1, Foo const& v2 ) const
90 if ( std::less( v1.nKey, v2.nKey ))
92 return std::less(v2.nKey, v1.nKey) ? 1 : 0;
97 typedef cc::MichaelList< cds::urcu::gc< cds::urcu::general_buffered<> >, Foo,
98 typename cc::michael_list::make_traits<
99 cc::opt::compare< Foo_cmp > // item comparator option
103 // Hash functor for Foo
105 size_t operator ()( int i ) const
107 return std::hash( i );
109 size_t operator()( Foo const& i ) const
111 return std::hash( i.nKey );
116 // Note that \p RCU template parameter of ordered list must be equal \p RCU for the set.
117 typedef cc::MichaelHashSet< cds::urcu::gc< cds::urcu::general_buffered<> >, bucket_list,
118 cc::michael_set::make_traits<
119 cc::opt::hash< foo_hash >
129 #ifdef CDS_DOXYGEN_INVOKED
130 class Traits = michael_set::traits
135 class MichaelHashSet< cds::urcu::gc< RCU >, OrderedList, Traits >
138 typedef cds::urcu::gc< RCU > gc; ///< RCU used as garbage collector
139 typedef OrderedList ordered_list; ///< type of ordered list to be used as a bucket implementation
140 typedef Traits traits; ///< Set traits
142 typedef typename ordered_list::value_type value_type; ///< type of value to be stored in the list
143 typedef typename ordered_list::key_comparator key_comparator; ///< key comparing functor
144 #ifdef CDS_DOXYGEN_INVOKED
145 typedef typename ordered_list::stat stat; ///< Internal statistics
146 typedef typename ordered_list::exempt_ptr exempt_ptr; ///< pointer to extracted node
147 typedef typename ordered_list::raw_ptr raw_ptr; ///< Return type of \p get() member function and its derivatives
150 /// Hash functor for \ref value_type and all its derivatives that you use
151 typedef typename cds::opt::v::hash_selector< typename traits::hash >::type hash;
152 typedef typename traits::item_counter item_counter; ///< Item counter type
153 typedef typename traits::allocator allocator; ///< Bucket table allocator
155 typedef typename ordered_list::rcu_lock rcu_lock; ///< RCU scoped lock
156 /// Group of \p extract_xxx functions require external locking if underlying ordered list requires that
157 static CDS_CONSTEXPR const bool c_bExtractLockExternal = ordered_list::c_bExtractLockExternal;
159 // GC and OrderedList::gc must be the same
160 static_assert(std::is_same<gc, typename ordered_list::gc>::value, "GC and OrderedList::gc must be the same");
163 typedef typename ordered_list::template select_stat_wrapper< typename ordered_list::stat > bucket_stat;
165 typedef typename ordered_list::template rebind_traits<
166 cds::opt::item_counter< cds::atomicity::empty_item_counter >
167 , cds::opt::stat< typename bucket_stat::wrapped_stat >
168 >::type internal_bucket_type_;
170 class internal_bucket_type: public internal_bucket_type_
172 typedef internal_bucket_type_ base_class;
174 using base_class::base_class;
175 using typename base_class::node_type;
176 using base_class::alloc_node;
177 using base_class::insert_node;
178 using base_class::node_to_value;
181 typedef typename internal_bucket_type::exempt_ptr exempt_ptr;
182 typedef typename internal_bucket_type::raw_ptr raw_ptr;
183 typedef typename bucket_stat::stat stat;
188 /// Bucket table allocator
189 typedef typename allocator::template rebind< internal_bucket_type >::other bucket_table_allocator;
191 const size_t m_nHashBitmask;
192 hash m_HashFunctor; ///< Hash functor
193 internal_bucket_type* m_Buckets; ///< bucket table
194 item_counter m_ItemCounter; ///< Item counter
195 stat m_Stat; ///< Internal statistics
199 ///@name Forward iterators (thread-safe under RCU lock)
203 The forward iterator for Michael's set is based on \p OrderedList forward iterator and has some features:
204 - it has no post-increment operator
205 - it iterates items in unordered fashion
207 You may safely use iterators in multi-threaded environment only under RCU lock.
208 Otherwise, a crash is possible if another thread deletes the element the iterator points to.
210 The iterator interface:
214 // Default constructor
218 iterator( iterator const& src );
220 // Dereference operator
221 value_type * operator ->() const;
223 // Dereference operator
224 value_type& operator *() const;
226 // Preincrement operator
227 iterator& operator ++();
229 // Assignment operator
230 iterator& operator = (iterator const& src);
232 // Equality operators
233 bool operator ==(iterator const& i ) const;
234 bool operator !=(iterator const& i ) const;
238 typedef michael_set::details::iterator< internal_bucket_type, false > iterator;
240 /// Const forward iterator
241 typedef michael_set::details::iterator< internal_bucket_type, true > const_iterator;
243 /// Returns a forward iterator addressing the first element in a set
245 For empty set \code begin() == end() \endcode
249 return iterator( m_Buckets[0].begin(), m_Buckets, m_Buckets + bucket_count());
252 /// Returns an iterator that addresses the location succeeding the last element in a set
254 Do not use the value returned by <tt>end</tt> function to access any item.
255 The returned value can be used only to control reaching the end of the set.
256 For empty set \code begin() == end() \endcode
260 return iterator( m_Buckets[bucket_count() - 1].end(), m_Buckets + bucket_count() - 1, m_Buckets + bucket_count());
263 /// Returns a forward const iterator addressing the first element in a set
264 const_iterator begin() const
266 return get_const_begin();
269 /// Returns a forward const iterator addressing the first element in a set
270 const_iterator cbegin() const
272 return get_const_begin();
275 /// Returns an const iterator that addresses the location succeeding the last element in a set
276 const_iterator end() const
278 return get_const_end();
281 /// Returns an const iterator that addresses the location succeeding the last element in a set
282 const_iterator cend() const
284 return get_const_end();
289 /// Initialize hash set
291 The Michael's hash set is non-expandable container. You should point the average count of items \p nMaxItemCount
292 when you create an object.
293 \p nLoadFactor parameter defines average count of items per bucket and it should be small number between 1 and 10.
294 Remember, since the bucket implementation is an ordered list, searching in the bucket is linear [<tt>O(nLoadFactor)</tt>].
296 The ctor defines hash table size as rounding <tt>nMaxItemCount / nLoadFactor</tt> up to nearest power of two.
299 size_t nMaxItemCount, ///< estimation of max item count in the hash set
300 size_t nLoadFactor ///< load factor: estimation of max number of items in the bucket
301 ) : m_nHashBitmask( michael_set::details::init_hash_bitmask( nMaxItemCount, nLoadFactor ))
302 , m_Buckets( bucket_table_allocator().allocate( bucket_count()))
304 for ( auto it = m_Buckets, itEnd = m_Buckets + bucket_count(); it != itEnd; ++it )
305 construct_bucket<bucket_stat>( it );
308 /// Clears hash set and destroys it
313 for ( auto it = m_Buckets, itEnd = m_Buckets + bucket_count(); it != itEnd; ++it )
314 it->~internal_bucket_type();
315 bucket_table_allocator().deallocate( m_Buckets, bucket_count());
321 The function creates a node with copy of \p val value
322 and then inserts the node created into the set.
324 The type \p Q should contain as minimum the complete key for the node.
325 The object of \ref value_type should be constructible from a value of type \p Q.
326 In trivial case, \p Q is equal to \ref value_type.
328 The function applies RCU lock internally.
330 Returns \p true if \p val is inserted into the set, \p false otherwise.
332 template <typename Q>
333 bool insert( Q&& val )
335 const bool bRet = bucket( val ).insert( std::forward<Q>( val ));
343 The function allows to split creating of new item into two part:
344 - create item with key only
345 - insert new item into the set
346 - if inserting is success, calls \p f functor to initialize value-fields of \p val.
348 The functor signature is:
350 void func( value_type& val );
352 where \p val is the item inserted.
353 The user-defined functor is called only if the inserting is success.
355 The function applies RCU lock internally.
357 @warning For \ref cds_nonintrusive_MichaelList_rcu "MichaelList" as the bucket see \ref cds_intrusive_item_creating "insert item troubleshooting".
358 \ref cds_nonintrusive_LazyList_rcu "LazyList" provides exclusive access to inserted item and does not require any node-level
361 template <typename Q, typename Func>
362 bool insert( Q&& val, Func f )
364 const bool bRet = bucket( val ).insert( std::forward<Q>( val ), f );
370 /// Updates the element
372 The operation performs inserting or changing data with lock-free manner.
374 If the item \p val not found in the set, then \p val is inserted iff \p bAllowInsert is \p true.
375 Otherwise, the functor \p func is called with item found.
376 The functor signature is:
379 void operator()( bool bNew, value_type& item, Q const& val );
383 - \p bNew - \p true if the item has been inserted, \p false otherwise
384 - \p item - item of the set
385 - \p val - argument \p val passed into the \p %update() function
387 The functor may change non-key fields of the \p item.
389 The function applies RCU lock internally.
391 Returns <tt> std::pair<bool, bool> </tt> where \p first is \p true if operation is successful,
392 \p second is \p true if new item has been added or \p false if the item with \p key
393 already is in the set.
395 @warning For \ref cds_intrusive_MichaelList_hp "MichaelList" as the bucket see \ref cds_intrusive_item_creating "insert item troubleshooting".
396 \ref cds_intrusive_LazyList_hp "LazyList" provides exclusive access to inserted item and does not require any node-level
399 template <typename Q, typename Func>
400 std::pair<bool, bool> update( Q const& val, Func func, bool bAllowInsert = true )
402 std::pair<bool, bool> bRet = bucket( val ).update( val, func, bAllowInsert );
407 template <typename Q, typename Func>
408 CDS_DEPRECATED("ensure() is deprecated, use update()")
409 std::pair<bool, bool> ensure( const Q& val, Func func )
411 return update( val, func, true );
415 /// Inserts data of type \p value_type created from \p args
417 Returns \p true if inserting successful, \p false otherwise.
419 The function applies RCU lock internally.
421 template <typename... Args>
422 bool emplace( Args&&... args )
424 typename internal_bucket_type::node_type * pNode = internal_bucket_type::alloc_node( std::forward<Args>( args )... );
425 bool bRet = bucket( internal_bucket_type::node_to_value( *pNode )).insert_node( pNode );
431 /// Deletes \p key from the set
432 /** \anchor cds_nonintrusive_MichealSet_rcu_erase_val
434 Since the key of MichaelHashSet's item type \p value_type is not explicitly specified,
435 template parameter \p Q defines the key type searching in the list.
436 The set item comparator should be able to compare the type \p value_type
439 RCU \p synchronize method can be called. RCU should not be locked.
441 Return \p true if key is found and deleted, \p false otherwise
443 template <typename Q>
444 bool erase( Q const& key )
446 const bool bRet = bucket( key ).erase( key );
452 /// Deletes the item from the set using \p pred predicate for searching
454 The function is an analog of \ref cds_nonintrusive_MichealSet_rcu_erase_val "erase(Q const&)"
455 but \p pred is used for key comparing.
456 \p Less functor has the interface like \p std::less.
457 \p Less must imply the same element order as the comparator used for building the set.
459 template <typename Q, typename Less>
460 bool erase_with( Q const& key, Less pred )
462 const bool bRet = bucket( key ).erase_with( key, pred );
468 /// Deletes \p key from the set
469 /** \anchor cds_nonintrusive_MichealSet_rcu_erase_func
471 The function searches an item with key \p key, calls \p f functor
472 and deletes the item. If \p key is not found, the functor is not called.
474 The functor \p Func interface:
477 void operator()(value_type const& val);
481 Since the key of %MichaelHashSet's \p value_type is not explicitly specified,
482 template parameter \p Q defines the key type searching in the list.
483 The list item comparator should be able to compare the type \p T of list item
486 RCU \p synchronize method can be called. RCU should not be locked.
488 Return \p true if key is found and deleted, \p false otherwise
490 template <typename Q, typename Func>
491 bool erase( Q const& key, Func f )
493 const bool bRet = bucket( key ).erase( key, f );
499 /// Deletes the item from the set using \p pred predicate for searching
501 The function is an analog of \ref cds_nonintrusive_MichealSet_rcu_erase_func "erase(Q const&, Func)"
502 but \p pred is used for key comparing.
503 \p Less functor has the interface like \p std::less.
504 \p Less must imply the same element order as the comparator used for building the set.
506 template <typename Q, typename Less, typename Func>
507 bool erase_with( Q const& key, Less pred, Func f )
509 const bool bRet = bucket( key ).erase_with( key, pred, f );
515 /// Extracts an item from the set
516 /** \anchor cds_nonintrusive_MichaelHashSet_rcu_extract
517 The function searches an item with key equal to \p key in the set,
518 unlinks it from the set, and returns \ref cds::urcu::exempt_ptr "exempt_ptr" pointer to the item found.
519 If the item with the key equal to \p key is not found the function return an empty \p exempt_ptr.
521 The function just excludes the item from the set and returns a pointer to item found.
522 Depends on \p ordered_list you should or should not lock RCU before calling of this function:
523 - for the set based on \ref cds_nonintrusive_MichaelList_rcu "MichaelList" RCU should not be locked
524 - for the set based on \ref cds_nonintrusive_LazyList_rcu "LazyList" RCU should be locked
525 See ordered list implementation for details.
528 #include <cds/urcu/general_buffered.h>
529 #include <cds/container/michael_list_rcu.h>
530 #include <cds/container/michael_set_rcu.h>
532 typedef cds::urcu::gc< general_buffered<> > rcu;
533 typedef cds::container::MichaelList< rcu, Foo > rcu_michael_list;
534 typedef cds::container::MichaelHashSet< rcu, rcu_michael_list, foo_traits > rcu_michael_set;
536 rcu_michael_set theSet;
539 typename rcu_michael_set::exempt_ptr p;
541 // For MichaelList we should not lock RCU
543 // Note that you must not delete the item found inside the RCU lock
544 p = theSet.extract( 10 );
546 // do something with p
550 // We may safely release p here
551 // release() passes the pointer to RCU reclamation cycle
555 template <typename Q>
556 exempt_ptr extract( Q const& key )
558 exempt_ptr p = bucket( key ).extract( key );
564 /// Extracts an item from the set using \p pred predicate for searching
566 The function is an analog of \p extract(Q const&) but \p pred is used for key comparing.
567 \p Less functor has the interface like \p std::less.
568 \p pred must imply the same element order as the comparator used for building the set.
570 template <typename Q, typename Less>
571 exempt_ptr extract_with( Q const& key, Less pred )
573 exempt_ptr p = bucket( key ).extract_with( key, pred );
579 /// Finds the key \p key
580 /** \anchor cds_nonintrusive_MichealSet_rcu_find_func
582 The function searches the item with key equal to \p key and calls the functor \p f for item found.
583 The interface of \p Func functor is:
586 void operator()( value_type& item, Q& key );
589 where \p item is the item found, \p key is the <tt>find</tt> function argument.
591 The functor may change non-key fields of \p item. Note that the functor is only guarantee
592 that \p item cannot be disposed during functor is executing.
593 The functor does not serialize simultaneous access to the set's \p item. If such access is
594 possible you must provide your own synchronization schema on item level to exclude unsafe item modifications.
596 The \p key argument is non-const since it can be used as \p f functor destination i.e., the functor
597 can modify both arguments.
599 Note the hash functor specified for class \p Traits template parameter
600 should accept a parameter of type \p Q that may be not the same as \p value_type.
602 The function applies RCU lock internally.
604 The function returns \p true if \p key is found, \p false otherwise.
606 template <typename Q, typename Func>
607 bool find( Q& key, Func f )
609 return bucket( key ).find( key, f );
612 template <typename Q, typename Func>
613 bool find( Q const& key, Func f )
615 return bucket( key ).find( key, f );
619 /// Finds the key \p key using \p pred predicate for searching
621 The function is an analog of \ref cds_nonintrusive_MichealSet_rcu_find_func "find(Q&, Func)"
622 but \p pred is used for key comparing.
623 \p Less functor has the interface like \p std::less.
624 \p Less must imply the same element order as the comparator used for building the set.
626 template <typename Q, typename Less, typename Func>
627 bool find_with( Q& key, Less pred, Func f )
629 return bucket( key ).find_with( key, pred, f );
632 template <typename Q, typename Less, typename Func>
633 bool find_with( Q const& key, Less pred, Func f )
635 return bucket( key ).find_with( key, pred, f );
639 /// Checks whether the set contains \p key
642 The function searches the item with key equal to \p key
643 and returns \p true if the key is found, and \p false otherwise.
645 Note the hash functor specified for class \p Traits template parameter
646 should accept a parameter of type \p Q that can be not the same as \p value_type.
648 template <typename Q>
649 bool contains( Q const& key )
651 return bucket( key ).contains( key );
654 template <typename Q>
655 CDS_DEPRECATED("use contains()")
656 bool find( Q const& key )
658 return contains( key );
662 /// Checks whether the set contains \p key using \p pred predicate for searching
664 The function is an analog of <tt>contains( key )</tt> but \p pred is used for key comparing.
665 \p Less functor has the interface like \p std::less.
666 \p Less must imply the same element order as the comparator used for building the set.
668 template <typename Q, typename Less>
669 bool contains( Q const& key, Less pred )
671 return bucket( key ).contains( key, pred );
674 template <typename Q, typename Less>
675 CDS_DEPRECATED("use contains()")
676 bool find_with( Q const& key, Less pred )
678 return contains( key, pred );
682 /// Finds the key \p key and return the item found
683 /** \anchor cds_nonintrusive_MichaelHashSet_rcu_get
684 The function searches the item with key equal to \p key and returns the pointer to item found.
685 If \p key is not found it returns \p nullptr.
686 Note the type of returned value depends on underlying \p ordered_list.
687 For details, see documentation of ordered list you use.
689 Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
691 RCU should be locked before call of this function.
692 Returned item is valid only while RCU is locked:
694 typedef cds::container::MichaelHashSet< your_template_parameters > hash_set;
696 typename hash_set::raw_ptr gp;
700 hash_set::rcu_lock lock;
702 gp = theSet.get( 5 );
707 // Unlock RCU by rcu_lock destructor
708 // gp can be reclaimed at any time after RCU has been unlocked
712 template <typename Q>
713 raw_ptr get( Q const& key )
715 return bucket( key ).get( key );
718 /// Finds the key \p key and return the item found
720 The function is an analog of \ref cds_nonintrusive_MichaelHashSet_rcu_get "get(Q const&)"
721 but \p pred is used for comparing the keys.
723 \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
725 \p pred must imply the same element order as the comparator used for building the set.
727 template <typename Q, typename Less>
728 raw_ptr get_with( Q const& key, Less pred )
730 return bucket( key ).get_with( key, pred );
733 /// Clears the set (not atomic)
736 for ( size_t i = 0; i < bucket_count(); ++i )
737 m_Buckets[i].clear();
738 m_ItemCounter.reset();
741 /// Checks if the set is empty
743 @warning If you use \p atomicity::empty_item_counter in \p traits::item_counter,
744 the function always returns \p true.
751 /// Returns item count in the set
753 @warning If you use \p atomicity::empty_item_counter in \p traits::item_counter,
754 the function always returns 0.
758 return m_ItemCounter;
761 /// Returns const reference to internal statistics
762 stat const& statistics() const
767 /// Returns the size of hash table
769 Since \p %MichaelHashSet cannot dynamically extend the hash table size,
770 the value returned is an constant depending on object initialization parameters;
771 see MichaelHashSet::MichaelHashSet for explanation.
773 size_t bucket_count() const
775 return m_nHashBitmask + 1;
780 /// Calculates hash value of \p key
781 template <typename Q>
782 size_t hash_value( Q const& key ) const
784 return m_HashFunctor( key ) & m_nHashBitmask;
787 /// Returns the bucket (ordered list) for \p key
788 template <typename Q>
789 internal_bucket_type& bucket( Q const& key )
791 return m_Buckets[hash_value( key )];
793 template <typename Q>
794 internal_bucket_type const& bucket( Q const& key ) const
796 return m_Buckets[hash_value( key )];
799 template <typename Stat>
800 typename std::enable_if< Stat::empty >::type construct_bucket( internal_bucket_type* bkt )
802 new (bkt) internal_bucket_type;
805 template <typename Stat>
806 typename std::enable_if< !Stat::empty >::type construct_bucket( internal_bucket_type* bkt )
808 new (bkt) internal_bucket_type( m_Stat );
811 const_iterator get_const_begin() const
813 return const_iterator( const_cast<internal_bucket_type const&>(m_Buckets[0]).begin(), m_Buckets, m_Buckets + bucket_count());
815 const_iterator get_const_end() const
817 return const_iterator( const_cast<internal_bucket_type const&>(m_Buckets[bucket_count() - 1]).end(), m_Buckets + bucket_count() - 1, m_Buckets + bucket_count());
822 }} // namespace cds::container
824 #endif // ifndef CDSLIB_CONTAINER_MICHAEL_SET_RCU_H