Text formatting, docfix
[libcds.git] / cds / intrusive / michael_set_rcu.h
1 //$$CDS-header$$
2
3 #ifndef CDSLIB_INTRUSIVE_MICHAEL_SET_RCU_H
4 #define CDSLIB_INTRUSIVE_MICHAEL_SET_RCU_H
5
6 #include <cds/intrusive/details/michael_set_base.h>
7 #include <cds/details/allocator.h>
8
9 namespace cds { namespace intrusive {
10
11     /// Michael's hash set, \ref cds_urcu_desc "RCU" specialization
12     /** @ingroup cds_intrusive_map
13         \anchor cds_intrusive_MichaelHashSet_rcu
14
15         Source:
16             - [2002] Maged Michael "High performance dynamic lock-free hash tables and list-based sets"
17
18         Michael's hash table algorithm is based on lock-free ordered list and it is very simple.
19         The main structure is an array \p T of size \p M. Each element in \p T is basically a pointer
20         to a hash bucket, implemented as a singly linked list. The array of buckets cannot be dynamically expanded.
21         However, each bucket may contain unbounded number of items.
22
23         Template parameters are:
24         - \p RCU - one of \ref cds_urcu_gc "RCU type"
25         - \p OrderedList - ordered list implementation used as bucket for hash set, for example, MichaelList, LazyList.
26             The intrusive ordered list implementation specifies the type \p T stored in the hash-set, the reclamation
27             schema \p GC used by hash-set, the comparison functor for the type \p T and other features specific for
28             the ordered list.
29         - \p Traits - type traits, default is \p michael_set::traits.
30             Instead of defining \p Traits struct you can use option-based syntax with \p michael_set::make_traits metafunction.
31
32         \par Usage
33             Before including <tt><cds/intrusive/michael_set_rcu.h></tt> you should include appropriate RCU header file,
34             see \ref cds_urcu_gc "RCU type" for list of existing RCU class and corresponding header files.
35             For example, for \ref cds_urcu_general_buffered_gc "general-purpose buffered RCU" you should include:
36             \code
37             #include <cds/urcu/general_buffered.h>
38             #include <cds/intrusive/michael_list_rcu.h>
39             #include <cds/intrusive/michael_set_rcu.h>
40
41             struct Foo { ... };
42             // Hash functor for struct Foo
43             struct foo_hash {
44                 size_t operator()( Foo const& foo ) const { return ... }
45             };
46
47             // Now, you can declare Michael's list for type Foo and default traits:
48             typedef cds::intrusive::MichaelList<cds::urcu::gc< general_buffered<> >, Foo > rcu_michael_list;
49
50             // Declare Michael's set with MichaelList as bucket type
51             typedef cds::intrusive::MichaelSet<
52                 cds::urcu::gc< general_buffered<> >,
53                 rcu_michael_list,
54                 cds::intrusive::michael_set::make_traits<
55                     cds::opt::::hash< foo_hash >
56                 >::type
57             > rcu_michael_set;
58
59             // Declares hash set for 1000000 items with load factor 2
60             rcu_michael_set theSet( 1000000, 2 );
61
62             // Now you can use theSet object in many threads without any synchronization.
63             \endcode
64     */
65     template <
66         class RCU,
67         class OrderedList,
68 #ifdef CDS_DOXYGEN_INVOKED
69         class Traits = michael_set::traits
70 #else
71         class Traits
72 #endif
73     >
74     class MichaelHashSet< cds::urcu::gc< RCU >, OrderedList, Traits >
75     {
76     public:
77         typedef cds::urcu::gc< RCU > gc;   ///< RCU schema
78         typedef OrderedList bucket_type;   ///< type of ordered list used as a bucket implementation
79         typedef Traits traits;             ///< Set traits
80
81         typedef typename bucket_type::value_type        value_type      ;   ///< type of value stored in the list
82         typedef typename bucket_type::key_comparator    key_comparator  ;   ///< key comparing functor
83         typedef typename bucket_type::disposer          disposer        ;   ///< Node disposer functor
84
85         /// Hash functor for \ref value_type and all its derivatives that you use
86         typedef typename cds::opt::v::hash_selector< typename traits::hash >::type hash;
87         typedef typename traits::item_counter item_counter;   ///< Item counter type
88
89         /// Bucket table allocator
90         typedef cds::details::Allocator< bucket_type, typename traits::allocator >  bucket_table_allocator;
91
92         typedef typename bucket_type::rcu_lock    rcu_lock;   ///< RCU scoped lock
93         typedef typename bucket_type::exempt_ptr  exempt_ptr; ///< pointer to extracted node
94         typedef typename bucket_type::raw_ptr     raw_ptr;    ///< Return type of \p get() member function and its derivatives
95         /// Group of \p extract_xxx functions require external locking if underlying ordered list requires that
96         static CDS_CONSTEXPR const bool c_bExtractLockExternal = bucket_type::c_bExtractLockExternal;
97
98     protected:
99         item_counter    m_ItemCounter;   ///< Item counter
100         hash            m_HashFunctor;   ///< Hash functor
101         bucket_type *   m_Buckets;       ///< bucket table
102
103     private:
104         //@cond
105         const size_t m_nHashBitmask;
106         //@endcond
107
108     protected:
109         //@cond
110         /// Calculates hash value of \p key
111         template <typename Q>
112         size_t hash_value( Q const& key ) const
113         {
114             return m_HashFunctor( key ) & m_nHashBitmask;
115         }
116
117         /// Returns the bucket (ordered list) for \p key
118         template <typename Q>
119         bucket_type& bucket( Q const& key )
120         {
121             return m_Buckets[ hash_value( key ) ];
122         }
123         template <typename Q>
124         bucket_type const& bucket( Q const& key ) const
125         {
126             return m_Buckets[ hash_value( key ) ];
127         }
128         //@endcond
129
130     public:
131         /// Forward iterator
132         /**
133             The forward iterator for Michael's set is based on \p OrderedList forward iterator and has some features:
134             - it has no post-increment operator
135             - it iterates items in unordered fashion
136         */
137         typedef michael_set::details::iterator< bucket_type, false >    iterator;
138
139         /// Const forward iterator
140         /**
141             For iterator's features and requirements see \ref iterator
142         */
143         typedef michael_set::details::iterator< bucket_type, true >     const_iterator;
144
145         /// Returns a forward iterator addressing the first element in a set
146         /**
147             For empty set \code begin() == end() \endcode
148         */
149         iterator begin()
150         {
151             return iterator( m_Buckets[0].begin(), m_Buckets, m_Buckets + bucket_count() );
152         }
153
154         /// Returns an iterator that addresses the location succeeding the last element in a set
155         /**
156             Do not use the value returned by <tt>end</tt> function to access any item.
157             The returned value can be used only to control reaching the end of the set.
158             For empty set \code begin() == end() \endcode
159         */
160         iterator end()
161         {
162             return iterator( m_Buckets[bucket_count() - 1].end(), m_Buckets + bucket_count() - 1, m_Buckets + bucket_count() );
163         }
164
165         /// Returns a forward const iterator addressing the first element in a set
166         //@{
167         const_iterator begin() const
168         {
169             return cbegin();
170         }
171         const_iterator cbegin() const
172         {
173             return const_iterator( m_Buckets[0].cbegin(), m_Buckets, m_Buckets + bucket_count() );
174         }
175         //@}
176
177         /// Returns an const iterator that addresses the location succeeding the last element in a set
178         //@{
179         const_iterator end() const
180         {
181             return cend();
182         }
183         const_iterator cend() const
184         {
185             return const_iterator( m_Buckets[bucket_count() - 1].cend(), m_Buckets + bucket_count() - 1, m_Buckets + bucket_count() );
186         }
187         //@}
188
189     public:
190         /// Initialize hash set
191         /**
192             The Michael's hash set is an unbounded container, but its hash table is non-expandable.
193             At construction time you should pass estimated maximum item count and a load factor.
194             The load factor is average size of one bucket - a small number between 1 and 10.
195             The bucket is an ordered single-linked list, the complexity of searching in the bucket is linear <tt>O(nLoadFactor)</tt>.
196             The constructor defines hash table size as rounding <tt>nMaxItemCount / nLoadFactor</tt> up to nearest power of two.
197         */
198         MichaelHashSet(
199             size_t nMaxItemCount,   ///< estimation of max item count in the hash set
200             size_t nLoadFactor      ///< load factor: average size of the bucket
201         ) : m_nHashBitmask( michael_set::details::init_hash_bitmask( nMaxItemCount, nLoadFactor ))
202         {
203             // GC and OrderedList::gc must be the same
204             static_assert( std::is_same<gc, typename bucket_type::gc>::value, "GC and OrderedList::gc must be the same");
205
206             // atomicity::empty_item_counter is not allowed as a item counter
207             static_assert( !std::is_same<item_counter, atomicity::empty_item_counter>::value,
208                 "atomicity::empty_item_counter is not allowed as a item counter");
209
210             m_Buckets = bucket_table_allocator().NewArray( bucket_count() );
211         }
212
213         /// Clear hash set and destroy it
214         ~MichaelHashSet()
215         {
216             clear();
217             bucket_table_allocator().Delete( m_Buckets, bucket_count() );
218         }
219
220         /// Inserts new node
221         /**
222             The function inserts \p val in the set if it does not contain
223             an item with key equal to \p val.
224
225             Returns \p true if \p val is placed into the set, \p false otherwise.
226         */
227         bool insert( value_type& val )
228         {
229             bool bRet = bucket( val ).insert( val );
230             if ( bRet )
231                 ++m_ItemCounter;
232             return bRet;
233         }
234
235         /// Inserts new node
236         /**
237             This function is intended for derived non-intrusive containers.
238
239             The function allows to split creating of new item into two part:
240             - create item with key only
241             - insert new item into the set
242             - if inserting is success, calls  \p f functor to initialize value-field of \p val.
243
244             The functor signature is:
245             \code
246                 void func( value_type& val );
247             \endcode
248             where \p val is the item inserted.
249             The user-defined functor is called only if the inserting is success.
250
251             @warning For \ref cds_intrusive_MichaelList_rcu "MichaelList" as the bucket see \ref cds_intrusive_item_creating "insert item troubleshooting".
252             \ref cds_intrusive_LazyList_rcu "LazyList" provides exclusive access to inserted item and does not require any node-level
253             synchronization.
254         */
255         template <typename Func>
256         bool insert( value_type& val, Func f )
257         {
258             bool bRet = bucket( val ).insert( val, f );
259             if ( bRet )
260                 ++m_ItemCounter;
261             return bRet;
262         }
263
264         /// Updates the element
265         /**
266             The operation performs inserting or changing data with lock-free manner.
267
268             If the item \p val not found in the set, then \p val is inserted iff \p bAllowInsert is \p true.
269             Otherwise, the functor \p func is called with item found.
270             The functor signature is:
271             \code
272                 struct functor {
273                     void operator()( bool bNew, value_type& item, value_type& val );
274                 };
275             \endcode
276             with arguments:
277             - \p bNew - \p true if the item has been inserted, \p false otherwise
278             - \p item - item of the set
279             - \p val - argument \p val passed into the \p %update() function
280             If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
281             refers to the same thing.
282
283             The functor may change non-key fields of the \p item.
284
285             Returns <tt> std::pair<bool, bool> </tt> where \p first is \p true if operation is successfull,
286             \p second is \p true if new item has been added or \p false if the item with \p key
287             already is in the set.
288
289             @warning For \ref cds_intrusive_MichaelList_hp "MichaelList" as the bucket see \ref cds_intrusive_item_creating "insert item troubleshooting".
290             \ref cds_intrusive_LazyList_hp "LazyList" provides exclusive access to inserted item and does not require any node-level
291             synchronization.
292         */
293         template <typename Func>
294         std::pair<bool, bool> update( value_type& val, Func func, bool bAllowInsert = true )
295         {
296             std::pair<bool, bool> bRet = bucket( val ).update( val, func, bAllowInsert );
297             if ( bRet.second )
298                 ++m_ItemCounter;
299             return bRet;
300         }
301         //@cond
302         template <typename Func>
303         CDS_DEPRECATED("ensure() is deprecated, use update()")
304         std::pair<bool, bool> ensure( value_type& val, Func func )
305         {
306             return update( val, func, true );
307         }
308         //@endcond
309
310         /// Unlinks the item \p val from the set
311         /**
312             The function searches the item \p val in the set and unlink it from the set
313             if it is found and is equal to \p val.
314
315             The function returns \p true if success and \p false otherwise.
316         */
317         bool unlink( value_type& val )
318         {
319             bool bRet = bucket( val ).unlink( val );
320             if ( bRet )
321                 --m_ItemCounter;
322             return bRet;
323         }
324
325         /// Deletes the item from the set
326         /** \anchor cds_intrusive_MichaelHashSet_rcu_erase
327             The function searches an item with key equal to \p key in the set,
328             unlinks it from the set, and returns \p true.
329             If the item with key equal to \p key is not found the function return \p false.
330
331             Note the hash functor should accept a parameter of type \p Q that may be not the same as \p value_type.
332         */
333         template <typename Q>
334         bool erase( Q const& key )
335         {
336             if ( bucket( key ).erase( key )) {
337                 --m_ItemCounter;
338                 return true;
339             }
340             return false;
341         }
342
343         /// Deletes the item from the set using \p pred predicate for searching
344         /**
345             The function is an analog of \ref cds_intrusive_MichaelHashSet_rcu_erase "erase(Q const&)"
346             but \p pred is used for key comparing.
347             \p Less functor has the interface like \p std::less.
348             \p pred must imply the same element order as the comparator used for building the set.
349         */
350         template <typename Q, typename Less>
351         bool erase_with( Q const& key, Less pred )
352         {
353             if ( bucket( key ).erase_with( key, pred )) {
354                 --m_ItemCounter;
355                 return true;
356             }
357             return false;
358         }
359
360         /// Deletes the item from the set
361         /** \anchor cds_intrusive_MichaelHashSet_rcu_erase_func
362             The function searches an item with key equal to \p key in the set,
363             call \p f functor with item found, and unlinks it from the set.
364             The \ref disposer specified in \p OrderedList class template parameter is called
365             by garbage collector \p GC asynchronously.
366
367             The \p Func interface is
368             \code
369             struct functor {
370                 void operator()( value_type const& item );
371             };
372             \endcode
373
374             If the item with key equal to \p key is not found the function return \p false.
375
376             Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type.
377         */
378         template <typename Q, typename Func>
379         bool erase( const Q& key, Func f )
380         {
381             if ( bucket( key ).erase( key, f )) {
382                 --m_ItemCounter;
383                 return true;
384             }
385             return false;
386         }
387
388         /// Deletes the item from the set using \p pred predicate for searching
389         /**
390             The function is an analog of \ref cds_intrusive_MichaelHashSet_rcu_erase_func "erase(Q const&)"
391             but \p pred is used for key comparing.
392             \p Less functor has the interface like \p std::less.
393             \p pred must imply the same element order as the comparator used for building the set.
394         */
395         template <typename Q, typename Less, typename Func>
396         bool erase_with( const Q& key, Less pred, Func f )
397         {
398             if ( bucket( key ).erase_with( key, pred, f )) {
399                 --m_ItemCounter;
400                 return true;
401             }
402             return false;
403         }
404
405         /// Extracts an item from the set
406         /** \anchor cds_intrusive_MichaelHashSet_rcu_extract
407             The function searches an item with key equal to \p key in the set,
408             unlinks it from the set, and returns \ref cds::urcu::exempt_ptr "exempt_ptr" pointer to the item found.
409             If the item with the key equal to \p key is not found the function returns an empty \p exempt_ptr.
410
411             Depends on \p bucket_type you should or should not lock RCU before calling of this function:
412             - for the set based on \ref cds_intrusive_MichaelList_rcu "MichaelList" RCU should not be locked
413             - for the set based on \ref cds_intrusive_LazyList_rcu "LazyList" RCU should be locked
414             See ordered list implementation for details.
415
416             \code
417             #include <cds/urcu/general_buffered.h>
418             #include <cds/intrusive/michael_list_rcu.h>
419             #include <cds/intrusive/michael_set_rcu.h>
420
421             typedef cds::urcu::gc< general_buffered<> > rcu;
422             typedef cds::intrusive::MichaelList< rcu, Foo > rcu_michael_list;
423             typedef cds::intrusive::MichaelHashSet< rcu, rcu_michael_list, foo_traits > rcu_michael_set;
424
425             rcu_michael_set theSet;
426             // ...
427
428             typename rcu_michael_set::exempt_ptr p;
429
430             // For MichaelList we should not lock RCU
431
432             // Now, you can apply extract function
433             // Note that you must not delete the item found inside the RCU lock
434             p = theSet.extract( 10 )
435             if ( p ) {
436                 // do something with p
437                 ...
438             }
439
440             // We may safely release p here
441             // release() passes the pointer to RCU reclamation cycle:
442             // it invokes RCU retire_ptr function with the disposer you provided for rcu_michael_list.
443             p.release();
444             \endcode
445         */
446         template <typename Q>
447         exempt_ptr extract( Q const& key )
448         {
449             exempt_ptr p( bucket( key ).extract( key ) );
450             if ( p )
451                 --m_ItemCounter;
452             return p;
453         }
454
455         /// Extracts an item from the set using \p pred predicate for searching
456         /**
457             The function is an analog of \p extract(Q const&) but \p pred is used for key comparing.
458             \p Less functor has the interface like \p std::less.
459             \p pred must imply the same element order as the comparator used for building the set.
460         */
461         template <typename Q, typename Less>
462         exempt_ptr extract_with( Q const& key, Less pred )
463         {
464             exempt_ptr p( bucket( key ).extract_with( key, pred ) );
465             if ( p )
466                 --m_ItemCounter;
467             return p;
468         }
469
470         /// Checks whether the set contains \p key
471         /**
472
473             The function searches the item with key equal to \p key
474             and returns \p true if the key is found, and \p false otherwise.
475
476             Note the hash functor specified for class \p Traits template parameter
477             should accept a parameter of type \p Q that can be not the same as \p value_type.
478         */
479         template <typename Q>
480         bool contains( Q const& key )
481         {
482             return bucket( key ).contains( key );
483         }
484         //@cond
485         template <typename Q>
486         CDS_DEPRECATED("use contains()")
487         bool find( Q const& key )
488         {
489             return contains( key );
490         }
491         //@endcond
492
493         /// Checks whether the set contains \p key using \p pred predicate for searching
494         /**
495             The function is an analog of <tt>contains( key )</tt> but \p pred is used for key comparing.
496             \p Less functor has the interface like \p std::less.
497             \p Less must imply the same element order as the comparator used for building the set.
498         */
499         template <typename Q, typename Less>
500         bool contains( Q const& key, Less pred )
501         {
502             return bucket( key ).contains( key, pred );
503         }
504         //@cond
505         template <typename Q, typename Less>
506         CDS_DEPRECATED("use contains()")
507         bool find_with( Q const& key, Less pred )
508         {
509             return contains( key, pred );
510         }
511         //@endcond
512
513         /// Find the key \p key
514         /** \anchor cds_intrusive_MichaelHashSet_rcu_find_func
515             The function searches the item with key equal to \p key 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, Q& key );
520             };
521             \endcode
522             where \p item is the item found, \p key is the <tt>find</tt> function argument.
523
524             The functor can change non-key fields of \p item.
525             The functor does not serialize simultaneous access to the set \p item. If such access is
526             possible you must provide your own synchronization schema on item level to exclude unsafe item modifications.
527
528             The \p key argument is non-const since it can be used as \p f functor destination i.e., the functor
529             can modify both arguments.
530
531             Note the hash functor specified for class \p Traits template parameter
532             should accept a parameter of type \p Q that can be not the same as \p value_type.
533
534             The function returns \p true if \p key is found, \p false otherwise.
535         */
536         template <typename Q, typename Func>
537         bool find( Q& key, Func f )
538         {
539             return bucket( key ).find( key, f );
540         }
541         //@cond
542         template <typename Q, typename Func>
543         bool find( Q const& key, Func f )
544         {
545             return bucket( key ).find( key, f );
546         }
547         //@endcond
548
549         /// Finds the key \p key using \p pred predicate for searching
550         /**
551             The function is an analog of \ref cds_intrusive_MichaelHashSet_rcu_find_func "find(Q&, Func)"
552             but \p pred is used for key comparing.
553             \p Less functor has the interface like \p std::less.
554             \p pred must imply the same element order as the comparator used for building the set.
555         */
556         template <typename Q, typename Less, typename Func>
557         bool find_with( Q& key, Less pred, Func f )
558         {
559             return bucket( key ).find_with( key, pred, f );
560         }
561         //@cond
562         template <typename Q, typename Less, typename Func>
563         bool find_with( Q const& key, Less pred, Func f )
564         {
565             return bucket( key ).find_with( key, pred, f );
566         }
567         //@endcond
568
569         /// Finds the key \p key and return the item found
570         /** \anchor cds_intrusive_MichaelHashSet_rcu_get
571             The function searches the item with key equal to \p key and returns the pointer to item found.
572             If \p key is not found it returns \p nullptr.
573             Note the type of returned value depends on underlying \p bucket_type.
574             For details, see documentation of ordered list you use.
575
576             Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
577
578             RCU should be locked before call of this function.
579             Returned item is valid only while RCU is locked:
580             \code
581             typedef cds::intrusive::MichaelHashSet< your_template_parameters > hash_set;
582             hash_set theSet;
583             // ...
584             // Result of get() call
585             typename hash_set::raw_ptr ptr;
586             {
587                 // Lock RCU
588                 hash_set::rcu_lock lock;
589
590                 ptr = theSet.get( 5 );
591                 if ( ptr ) {
592                     // Deal with ptr
593                     //...
594                 }
595                 // Unlock RCU by rcu_lock destructor
596                 // ptr can be reclaimed by disposer at any time after RCU has been unlocked
597             }
598             \endcode
599         */
600         template <typename Q>
601         raw_ptr get( Q const& key )
602         {
603             return bucket( key ).get( key );
604         }
605
606         /// Finds the key \p key and return the item found
607         /**
608             The function is an analog of \ref cds_intrusive_MichaelHashSet_rcu_get "get(Q const&)"
609             but \p pred is used for comparing the keys.
610
611             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
612             in any order.
613             \p pred must imply the same element order as the comparator used for building the set.
614         */
615         template <typename Q, typename Less>
616         raw_ptr get_with( Q const& key, Less pred )
617         {
618             return bucket( key ).get_with( key, pred );
619         }
620
621         /// Clears the set (non-atomic)
622         /**
623             The function unlink all items from the set.
624             The function is not atomic. It cleans up each bucket and then resets the item counter to zero.
625             If there are a thread that performs insertion while \p clear is working the result is undefined in general case:
626             <tt> empty() </tt> may return \p true but the set may contain item(s).
627             Therefore, \p clear may be used only for debugging purposes.
628
629             For each item the \p disposer is called after unlinking.
630         */
631         void clear()
632         {
633             for ( size_t i = 0; i < bucket_count(); ++i )
634                 m_Buckets[i].clear();
635             m_ItemCounter.reset();
636         }
637
638
639         /// Checks if the set is empty
640         /**
641             Emptiness is checked by item counting: if item count is zero then the set is empty.
642             Thus, the correct item counting feature is an important part of Michael's set implementation.
643         */
644         bool empty() const
645         {
646             return size() == 0;
647         }
648
649         /// Returns item count in the set
650         size_t size() const
651         {
652             return m_ItemCounter;
653         }
654
655         /// Returns the size of hash table
656         /**
657             Since %MichaelHashSet cannot dynamically extend the hash table size,
658             the value returned is an constant depending on object initialization parameters;
659             see \ref cds_intrusive_MichaelHashSet_hp "MichaelHashSet" for explanation.
660         */
661         size_t bucket_count() const
662         {
663             return m_nHashBitmask + 1;
664         }
665
666     };
667
668 }} // namespace cds::intrusive
669
670 #endif // #ifndef CDSLIB_INTRUSIVE_MICHAEL_SET_NOGC_H
671