Removed unused implementation_tag typedef
[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             The function searches the item with key equal to \p key
473             and returns \p true if the key is found, and \p false otherwise.
474
475             Note the hash functor specified for class \p Traits template parameter
476             should accept a parameter of type \p Q that can be not the same as \p value_type.
477         */
478         template <typename Q>
479         bool contains( Q const& key )
480         {
481             return bucket( key ).contains( key );
482         }
483         //@cond
484         template <typename Q>
485         CDS_DEPRECATED("use contains()")
486         bool find( Q const& key )
487         {
488             return contains( key );
489         }
490         //@endcond
491
492         /// Checks whether the set contains \p key using \p pred predicate for searching
493         /**
494             The function is an analog of <tt>contains( key )</tt> but \p pred is used for key comparing.
495             \p Less functor has the interface like \p std::less.
496             \p Less must imply the same element order as the comparator used for building the set.
497         */
498         template <typename Q, typename Less>
499         bool contains( Q const& key, Less pred )
500         {
501             return bucket( key ).contains( key, pred );
502         }
503         //@cond
504         template <typename Q, typename Less>
505         CDS_DEPRECATED("use contains()")
506         bool find_with( Q const& key, Less pred )
507         {
508             return contains( key, pred );
509         }
510         //@endcond
511
512         /// Find the key \p key
513         /** \anchor cds_intrusive_MichaelHashSet_rcu_find_func
514             The function searches the item with key equal to \p key and calls the functor \p f for item found.
515             The interface of \p Func functor is:
516             \code
517             struct functor {
518                 void operator()( value_type& item, Q& key );
519             };
520             \endcode
521             where \p item is the item found, \p key is the <tt>find</tt> function argument.
522
523             The functor can change non-key fields of \p item.
524             The functor does not serialize simultaneous access to the set \p item. If such access is
525             possible you must provide your own synchronization schema on item level to exclude unsafe item modifications.
526
527             The \p key argument is non-const since it can be used as \p f functor destination i.e., the functor
528             can modify both arguments.
529
530             Note the hash functor specified for class \p Traits template parameter
531             should accept a parameter of type \p Q that can be not the same as \p value_type.
532
533             The function returns \p true if \p key is found, \p false otherwise.
534         */
535         template <typename Q, typename Func>
536         bool find( Q& key, Func f )
537         {
538             return bucket( key ).find( key, f );
539         }
540         //@cond
541         template <typename Q, typename Func>
542         bool find( Q const& key, Func f )
543         {
544             return bucket( key ).find( key, f );
545         }
546         //@endcond
547
548         /// Finds the key \p key using \p pred predicate for searching
549         /**
550             The function is an analog of \ref cds_intrusive_MichaelHashSet_rcu_find_func "find(Q&, Func)"
551             but \p pred is used for key comparing.
552             \p Less functor has the interface like \p std::less.
553             \p pred must imply the same element order as the comparator used for building the set.
554         */
555         template <typename Q, typename Less, typename Func>
556         bool find_with( Q& key, Less pred, Func f )
557         {
558             return bucket( key ).find_with( key, pred, f );
559         }
560         //@cond
561         template <typename Q, typename Less, typename Func>
562         bool find_with( Q const& key, Less pred, Func f )
563         {
564             return bucket( key ).find_with( key, pred, f );
565         }
566         //@endcond
567
568         /// Finds the key \p key and return the item found
569         /** \anchor cds_intrusive_MichaelHashSet_rcu_get
570             The function searches the item with key equal to \p key and returns the pointer to item found.
571             If \p key is not found it returns \p nullptr.
572             Note the type of returned value depends on underlying \p bucket_type.
573             For details, see documentation of ordered list you use.
574
575             Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
576
577             RCU should be locked before call of this function.
578             Returned item is valid only while RCU is locked:
579             \code
580             typedef cds::intrusive::MichaelHashSet< your_template_parameters > hash_set;
581             hash_set theSet;
582             // ...
583             // Result of get() call
584             typename hash_set::raw_ptr ptr;
585             {
586                 // Lock RCU
587                 hash_set::rcu_lock lock;
588
589                 ptr = theSet.get( 5 );
590                 if ( ptr ) {
591                     // Deal with ptr
592                     //...
593                 }
594                 // Unlock RCU by rcu_lock destructor
595                 // ptr can be reclaimed by disposer at any time after RCU has been unlocked
596             }
597             \endcode
598         */
599         template <typename Q>
600         raw_ptr get( Q const& key )
601         {
602             return bucket( key ).get( key );
603         }
604
605         /// Finds the key \p key and return the item found
606         /**
607             The function is an analog of \ref cds_intrusive_MichaelHashSet_rcu_get "get(Q const&)"
608             but \p pred is used for comparing the keys.
609
610             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
611             in any order.
612             \p pred must imply the same element order as the comparator used for building the set.
613         */
614         template <typename Q, typename Less>
615         raw_ptr get_with( Q const& key, Less pred )
616         {
617             return bucket( key ).get_with( key, pred );
618         }
619
620         /// Clears the set (non-atomic)
621         /**
622             The function unlink all items from the set.
623             The function is not atomic. It cleans up each bucket and then resets the item counter to zero.
624             If there are a thread that performs insertion while \p clear is working the result is undefined in general case:
625             <tt> empty() </tt> may return \p true but the set may contain item(s).
626             Therefore, \p clear may be used only for debugging purposes.
627
628             For each item the \p disposer is called after unlinking.
629         */
630         void clear()
631         {
632             for ( size_t i = 0; i < bucket_count(); ++i )
633                 m_Buckets[i].clear();
634             m_ItemCounter.reset();
635         }
636
637
638         /// Checks if the set is empty
639         /**
640             Emptiness is checked by item counting: if item count is zero then the set is empty.
641             Thus, the correct item counting feature is an important part of Michael's set implementation.
642         */
643         bool empty() const
644         {
645             return size() == 0;
646         }
647
648         /// Returns item count in the set
649         size_t size() const
650         {
651             return m_ItemCounter;
652         }
653
654         /// Returns the size of hash table
655         /**
656             Since %MichaelHashSet cannot dynamically extend the hash table size,
657             the value returned is an constant depending on object initialization parameters;
658             see \ref cds_intrusive_MichaelHashSet_hp "MichaelHashSet" for explanation.
659         */
660         size_t bucket_count() const
661         {
662             return m_nHashBitmask + 1;
663         }
664
665     };
666
667 }} // namespace cds::intrusive
668
669 #endif // #ifndef CDSLIB_INTRUSIVE_MICHAEL_SET_NOGC_H
670