Replace cds::ref/boost::ref with std::ref, remove cds::unref and cds/ref.h header
[libcds.git] / cds / intrusive / michael_set_rcu.h
1 //$$CDS-header$$
2
3 #ifndef __CDS_INTRUSIVE_MICHAEL_SET_RCU_H
4 #define __CDS_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. See michael_set::type_traits for explanation.
30             Instead of defining \p Traits struct you can use option-based syntax with 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::type_traits
70 #else
71         class Traits
72 #endif
73     >
74     class MichaelHashSet< cds::urcu::gc< RCU >, OrderedList, Traits >
75     {
76     public:
77         typedef OrderedList bucket_type     ;   ///< type of ordered list used as a bucket implementation
78         typedef Traits      options         ;   ///< Traits template parameters
79
80         typedef typename bucket_type::value_type        value_type      ;   ///< type of value stored in the list
81         typedef cds::urcu::gc< RCU >                    gc              ;   ///< RCU schema
82         typedef typename bucket_type::key_comparator    key_comparator  ;   ///< key comparison 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 options::hash >::type   hash;
87         typedef typename options::item_counter          item_counter    ;   ///< Item counter type
88
89         /// Bucket table allocator
90         typedef cds::details::Allocator< bucket_type, typename options::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         /// Group of \p extract_xxx functions require external locking if underlying ordered list requires that
95         static CDS_CONSTEXPR_CONST bool c_bExtractLockExternal = bucket_type::c_bExtractLockExternal;
96
97     protected:
98         item_counter    m_ItemCounter   ;   ///< Item counter
99         hash            m_HashFunctor   ;   ///< Hash functor
100
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 get_const_begin();
170         }
171         const_iterator cbegin()
172         {
173             return get_const_begin();
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 get_const_end();
182         }
183         const_iterator cend()
184         {
185             return get_const_end();
186         }
187         //@}
188
189     private:
190         //@cond
191         const_iterator get_const_begin() const
192         {
193             return const_iterator( m_Buckets[0].begin(), m_Buckets, m_Buckets + bucket_count() );
194         }
195         const_iterator get_const_end() const
196         {
197             return const_iterator( m_Buckets[bucket_count() - 1].end(), m_Buckets + bucket_count() - 1, m_Buckets + bucket_count() );
198         }
199         //@endcond
200
201     public:
202         /// Initialize hash set
203         /**
204             The Michael's hash set is an unbounded container, but its hash table is non-expandable.
205             At construction time you should pass estimated maximum item count and a load factor.
206             The load factor is average size of one bucket - a small number between 1 and 10.
207             The bucket is an ordered single-linked list, the complexity of searching in the bucket is linear <tt>O(nLoadFactor)</tt>.
208             The constructor defines hash table size as rounding <tt>nMaxItemCount / nLoadFactor</tt> up to nearest power of two.
209         */
210         MichaelHashSet(
211             size_t nMaxItemCount,   ///< estimation of max item count in the hash set
212             size_t nLoadFactor      ///< load factor: average size of the bucket
213         ) : m_nHashBitmask( michael_set::details::init_hash_bitmask( nMaxItemCount, nLoadFactor ))
214         {
215             // GC and OrderedList::gc must be the same
216             static_assert(( std::is_same<gc, typename bucket_type::gc>::value ), "GC and OrderedList::gc must be the same");
217
218             // atomicity::empty_item_counter is not allowed as a item counter
219             static_assert(( !std::is_same<item_counter, atomicity::empty_item_counter>::value ), "atomicity::empty_item_counter is not allowed as a item counter");
220
221             m_Buckets = bucket_table_allocator().NewArray( bucket_count() );
222         }
223
224         /// Clear hash set and destroy it
225         ~MichaelHashSet()
226         {
227             clear();
228             bucket_table_allocator().Delete( m_Buckets, bucket_count() );
229         }
230
231         /// Inserts new node
232         /**
233             The function inserts \p val in the set if it does not contain
234             an item with key equal to \p val.
235
236             Returns \p true if \p val is placed into the set, \p false otherwise.
237         */
238         bool insert( value_type& val )
239         {
240             bool bRet = bucket( val ).insert( val );
241             if ( bRet )
242                 ++m_ItemCounter;
243             return bRet;
244         }
245
246         /// Inserts new node
247         /**
248             This function is intended for derived non-intrusive containers.
249
250             The function allows to split creating of new item into two part:
251             - create item with key only
252             - insert new item into the set
253             - if inserting is success, calls  \p f functor to initialize value-field of \p val.
254
255             The functor signature is:
256             \code
257                 void func( value_type& val );
258             \endcode
259             where \p val is the item inserted. User-defined functor \p f should guarantee that during changing
260             \p val no any other changes could be made on this set's item by concurrent threads.
261             The user-defined functor is called only if the inserting is success and can be passed by reference
262             using \p std::ref.
263         */
264         template <typename Func>
265         bool insert( value_type& val, Func f )
266         {
267             bool bRet = bucket( val ).insert( val, f );
268             if ( bRet )
269                 ++m_ItemCounter;
270             return bRet;
271         }
272
273         /// Ensures that the \p item exists in the set
274         /**
275             The operation performs inserting or changing data with lock-free manner.
276
277             If the item \p val not found in the set, then \p val is inserted into the set.
278             Otherwise, the functor \p func is called with item found.
279             The functor signature is:
280             \code
281                 void func( bool bNew, value_type& item, value_type& val );
282             \endcode
283             with arguments:
284             - \p bNew - \p true if the item has been inserted, \p false otherwise
285             - \p item - item of the set
286             - \p val - argument \p val passed into the \p ensure function
287             If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
288             refers to the same thing.
289
290             The functor can change non-key fields of the \p item; however, \p func must guarantee
291             that during changing no any other modifications could be made on this item by concurrent threads.
292
293             You can pass \p func argument by value or by reference using \p std::ref.
294
295             Returns std::pair<bool, bool> where \p first is \p true if operation is successfull,
296             \p second is \p true if new item has been added or \p false if the item with \p key
297             already is in the set.
298         */
299         template <typename Func>
300         std::pair<bool, bool> ensure( value_type& val, Func func )
301         {
302             std::pair<bool, bool> bRet = bucket( val ).ensure( val, func );
303             if ( bRet.first && bRet.second )
304                 ++m_ItemCounter;
305             return bRet;
306         }
307
308         /// Unlinks the item \p val from the set
309         /**
310             The function searches the item \p val in the set and unlink it from the set
311             if it is found and is equal to \p val.
312
313             The function returns \p true if success and \p false otherwise.
314         */
315         bool unlink( value_type& val )
316         {
317             bool bRet = bucket( val ).unlink( val );
318             if ( bRet )
319                 --m_ItemCounter;
320             return bRet;
321         }
322
323         /// Deletes the item from the set
324         /** \anchor cds_intrusive_MichaelHashSet_rcu_erase
325             The function searches an item with key equal to \p val in the set,
326             unlinks it from the set, and returns \p true.
327             If the item with key equal to \p val is not found the function return \p false.
328
329             Note the hash functor should accept a parameter of type \p Q that may be not the same as \p value_type.
330         */
331         template <typename Q>
332         bool erase( Q const& val )
333         {
334             if ( bucket( val ).erase( val )) {
335                 --m_ItemCounter;
336                 return true;
337             }
338             return false;
339         }
340
341         /// Deletes the item from the set using \p pred predicate for searching
342         /**
343             The function is an analog of \ref cds_intrusive_MichaelHashSet_rcu_erase "erase(Q const&)"
344             but \p pred is used for key comparing.
345             \p Less functor has the interface like \p std::less.
346             \p pred must imply the same element order as the comparator used for building the set.
347         */
348         template <typename Q, typename Less>
349         bool erase_with( Q const& val, Less pred )
350         {
351             if ( bucket( val ).erase_with( val, pred )) {
352                 --m_ItemCounter;
353                 return true;
354             }
355             return false;
356         }
357
358         /// Deletes the item from the set
359         /** \anchor cds_intrusive_MichaelHashSet_rcu_erase_func
360             The function searches an item with key equal to \p val in the set,
361             call \p f functor with item found, and unlinks it from the set.
362             The \ref disposer specified in \p OrderedList class template parameter is called
363             by garbage collector \p GC asynchronously.
364
365             The \p Func interface is
366             \code
367             struct functor {
368                 void operator()( value_type const& item );
369             };
370             \endcode
371             The functor may be passed by reference with <tt>boost:ref</tt>
372
373             If the item with key equal to \p val is not found the function return \p false.
374
375             Note the hash functor should accept a parameter of type \p Q that can be not the same as \p value_type.
376         */
377         template <typename Q, typename Func>
378         bool erase( const Q& val, Func f )
379         {
380             if ( bucket( val ).erase( val, f )) {
381                 --m_ItemCounter;
382                 return true;
383             }
384             return false;
385         }
386
387         /// Deletes the item from the set using \p pred predicate for searching
388         /**
389             The function is an analog of \ref cds_intrusive_MichaelHashSet_rcu_erase_func "erase(Q const&)"
390             but \p pred is used for key comparing.
391             \p Less functor has the interface like \p std::less.
392             \p pred must imply the same element order as the comparator used for building the set.
393         */
394         template <typename Q, typename Less, typename Func>
395         bool erase_with( const Q& val, Less pred, Func f )
396         {
397             if ( bucket( val ).erase_with( val, pred, f )) {
398                 --m_ItemCounter;
399                 return true;
400             }
401             return false;
402         }
403
404         /// Extracts an item from the set
405         /** \anchor cds_intrusive_MichaelHashSet_rcu_extract
406             The function searches an item with key equal to \p val in the set,
407             unlinks it from the set, places item pointer into \p dest argument, and returns \p true.
408             If the item with the key equal to \p val is not found the function return \p false.
409
410             @note The function does NOT call RCU read-side lock or synchronization,
411             and does NOT dispose the item found. It just excludes the item from the set
412             and returns a pointer to item found.
413             You should lock RCU before calling of the function, and you should synchronize RCU
414             outside the RCU lock before reusing returned pointer.
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             rcu_michael_set::exempt_ptr p;
429             {
430                 // first, we should lock RCU
431                 rcu_michael_set::rcu_lock lock;
432
433                 // Now, you can apply extract function
434                 // Note that you must not delete the item found inside the RCU lock
435                 if ( theSet.extract( p, 10 )) {
436                     // do something with p
437                     ...
438                 }
439             }
440
441             // We may safely release p here
442             // release() passes the pointer to RCU reclamation cycle:
443             // it invokes RCU retire_ptr function with the disposer you provided for rcu_michael_list.
444             p.release();
445             \endcode
446         */
447         template <typename Q>
448         bool extract( exempt_ptr& dest, Q const& val )
449         {
450             if ( bucket( val ).extract( dest, val )) {
451                 --m_ItemCounter;
452                 return true;
453             }
454             return false;
455         }
456
457         /// Extracts an item from the set using \p pred predicate for searching
458         /**
459             The function is an analog of \ref cds_intrusive_MichaelHashSet_rcu_extract "extract(exempt_ptr&, Q const&)"
460             but \p pred is used for key comparing.
461             \p Less functor has the interface like \p std::less.
462             \p pred must imply the same element order as the comparator used for building the set.
463         */
464         template <typename Q, typename Less>
465         bool extract_with( exempt_ptr& dest, Q const& val, Less pred )
466         {
467             if ( bucket( val ).extract_with( dest, val, pred )) {
468                 --m_ItemCounter;
469                 return true;
470             }
471             return false;
472         }
473
474         /// Finds the key \p val
475         /** \anchor cds_intrusive_MichaelHashSet_rcu_find_val
476             The function searches the item with key equal to \p val
477             and returns \p true if \p val found or \p false otherwise.
478         */
479         template <typename Q>
480         bool find( Q const& val ) const
481         {
482             return bucket( val ).find( val );
483         }
484
485         /// Finds the key \p val using \p pred predicate for searching
486         /**
487             The function is an analog of \ref cds_intrusive_MichaelHashSet_rcu_find_val "find(Q const&)"
488             but \p pred is used for key comparing.
489             \p Less functor has the interface like \p std::less.
490             \p pred must imply the same element order as the comparator used for building the set.
491         */
492         template <typename Q, typename Less>
493         bool find_with( Q const& val, Less pred ) const
494         {
495             return bucket( val ).find_with( val, pred );
496         }
497
498         /// Find the key \p val
499         /** \anchor cds_intrusive_MichaelHashSet_rcu_find_func
500             The function searches the item with key equal to \p val and calls the functor \p f for item found.
501             The interface of \p Func functor is:
502             \code
503             struct functor {
504                 void operator()( value_type& item, Q& val );
505             };
506             \endcode
507             where \p item is the item found, \p val is the <tt>find</tt> function argument.
508
509             You can pass \p f argument by value or by reference using \p std::ref.
510
511             The functor can change non-key fields of \p item.
512             The functor does not serialize simultaneous access to the set \p item. If such access is
513             possible you must provide your own synchronization schema on item level to exclude unsafe item modifications.
514
515             The \p val argument is non-const since it can be used as \p f functor destination i.e., the functor
516             can modify both arguments.
517
518             Note the hash functor specified for class \p Traits template parameter
519             should accept a parameter of type \p Q that can be not the same as \p value_type.
520
521             The function returns \p true if \p val is found, \p false otherwise.
522         */
523         template <typename Q, typename Func>
524         bool find( Q& val, Func f ) const
525         {
526             return bucket( val ).find( val, f );
527         }
528
529         /// Finds the key \p val using \p pred predicate for searching
530         /**
531             The function is an analog of \ref cds_intrusive_MichaelHashSet_rcu_find_func "find(Q&, Func)"
532             but \p pred is used for key comparing.
533             \p Less functor has the interface like \p std::less.
534             \p pred must imply the same element order as the comparator used for building the set.
535         */
536         template <typename Q, typename Less, typename Func>
537         bool find_with( Q& val, Less pred, Func f ) const
538         {
539             return bucket( val ).find_with( val, pred, f );
540         }
541
542         /// Find the key \p val
543         /** \anchor cds_intrusive_MichaelHashSet_rcu_find_cfunc
544             The function searches the item with key equal to \p val and calls the functor \p f for item found.
545             The interface of \p Func functor is:
546             \code
547             struct functor {
548                 void operator()( value_type& item, Q const& val );
549             };
550             \endcode
551             where \p item is the item found, \p val is the <tt>find</tt> function argument.
552
553             You can pass \p f argument by value or by reference using \p std::ref.
554
555             The functor can change non-key fields of \p item.
556             The functor does not serialize simultaneous access to the set \p item. If such access is
557             possible you must provide your own synchronization schema on item level to exclude unsafe item modifications.
558
559             Note the hash functor specified for class \p Traits template parameter
560             should accept a parameter of type \p Q that can be not the same as \p value_type.
561
562             The function returns \p true if \p val is found, \p false otherwise.
563         */
564         template <typename Q, typename Func>
565         bool find( Q const& val, Func f ) const
566         {
567             return bucket( val ).find( val, f );
568         }
569
570         /// Finds the key \p val using \p pred predicate for searching
571         /**
572             The function is an analog of \ref cds_intrusive_MichaelHashSet_rcu_find_cfunc "find(Q const&, Func)"
573             but \p pred is used for key comparing.
574             \p Less functor has the interface like \p std::less.
575             \p pred must imply the same element order as the comparator used for building the set.
576         */
577         template <typename Q, typename Less, typename Func>
578         bool find_with( Q const& val, Less pred, Func f ) const
579         {
580             return bucket( val ).find_with( val, pred, f );
581         }
582
583         /// Finds the key \p val and return the item found
584         /** \anchor cds_intrusive_MichaelHashSet_rcu_get
585             The function searches the item with key equal to \p val and returns the pointer to item found.
586             If \p val is not found it returns \p nullptr.
587
588             Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
589
590             RCU should be locked before call of this function.
591             Returned item is valid only while RCU is locked:
592             \code
593             typedef cds::intrusive::MichaelHashSet< your_template_parameters > hash_set;
594             hash_set theSet;
595             // ...
596             {
597                 // Lock RCU
598                 hash_set::rcu_lock lock;
599
600                 foo * pVal = theSet.get( 5 );
601                 if ( pVal ) {
602                     // Deal with pVal
603                     //...
604                 }
605                 // Unlock RCU by rcu_lock destructor
606                 // pVal can be retired by disposer at any time after RCU has been unlocked
607             }
608             \endcode
609         */
610         template <typename Q>
611         value_type * get( Q const& val ) const
612         {
613             return bucket( val ).get( val );
614         }
615
616         /// Finds the key \p val and return the item found
617         /**
618             The function is an analog of \ref cds_intrusive_MichaelHashSet_rcu_get "get(Q const&)"
619             but \p pred is used for comparing the keys.
620
621             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
622             in any order.
623             \p pred must imply the same element order as the comparator used for building the set.
624         */
625         template <typename Q, typename Less>
626         value_type * get_with( Q const& val, Less pred ) const
627         {
628             return bucket( val ).get_with( val, pred );
629         }
630
631         /// Clears the set (non-atomic)
632         /**
633             The function unlink all items from the set.
634             The function is not atomic. It cleans up each bucket and then resets the item counter to zero.
635             If there are a thread that performs insertion while \p clear is working the result is undefined in general case:
636             <tt> empty() </tt> may return \p true but the set may contain item(s).
637             Therefore, \p clear may be used only for debugging purposes.
638
639             For each item the \p disposer is called after unlinking.
640         */
641         void clear()
642         {
643             for ( size_t i = 0; i < bucket_count(); ++i )
644                 m_Buckets[i].clear();
645             m_ItemCounter.reset();
646         }
647
648
649         /// Checks if the set is empty
650         /**
651             Emptiness is checked by item counting: if item count is zero then the set is empty.
652             Thus, the correct item counting feature is an important part of Michael's set implementation.
653         */
654         bool empty() const
655         {
656             return size() == 0;
657         }
658
659         /// Returns item count in the set
660         size_t size() const
661         {
662             return m_ItemCounter;
663         }
664
665         /// Returns the size of hash table
666         /**
667             Since %MichaelHashSet cannot dynamically extend the hash table size,
668             the value returned is an constant depending on object initialization parameters;
669             see \ref cds_intrusive_MichaelHashSet_hp "MichaelHashSet" for explanation.
670         */
671         size_t bucket_count() const
672         {
673             return m_nHashBitmask + 1;
674         }
675
676     };
677
678 }} // namespace cds::intrusive
679
680 #endif // #ifndef __CDS_INTRUSIVE_MICHAEL_SET_NOGC_H
681