e3473d2bdfe1c1e3b1bdaaa45f5dc6dd2b0651f0
[libcds.git] / cds / intrusive / michael_list_rcu.h
1 //$$CDS-header$$
2
3 #ifndef CDSLIB_INTRUSIVE_MICHAEL_LIST_RCU_H
4 #define CDSLIB_INTRUSIVE_MICHAEL_LIST_RCU_H
5
6 #include <cds/intrusive/details/michael_list_base.h>
7 #include <cds/urcu/details/check_deadlock.h>
8 #include <cds/details/binary_functor_wrapper.h>
9 #include <cds/details/make_const_type.h>
10 #include <cds/urcu/exempt_ptr.h>
11 #include <cds/urcu/raw_ptr.h>
12
13 namespace cds { namespace intrusive {
14
15     //@cond
16     namespace michael_list {
17
18         /// Node specialization for uRCU
19         template <class RCU, typename Tag>
20         struct node< cds::urcu::gc< RCU >, Tag >
21         {
22             typedef cds::urcu::gc< RCU > gc;   ///< Garbage collector
23             typedef Tag                  tag;  ///< tag
24
25             typedef cds::details::marked_ptr<node, 1>   marked_ptr         ;   ///< marked pointer
26             typedef typename gc::template atomic_marked_ptr< marked_ptr>     atomic_marked_ptr   ;   ///< atomic marked pointer specific for GC
27
28             atomic_marked_ptr m_pNext ; ///< pointer to the next node in the container
29             node *            m_pDelChain; ///< Deleted node chain (local for a thread)
30
31             CDS_CONSTEXPR node() CDS_NOEXCEPT
32                 : m_pNext( nullptr )
33                 , m_pDelChain( nullptr )
34             {}
35         };
36     } // namespace michael_list
37     //@endcond
38
39     /// Michael's lock-free ordered single-linked list (template specialization for \ref cds_urcu_desc "RCU")
40     /** @ingroup cds_intrusive_list
41         \anchor cds_intrusive_MichaelList_rcu
42
43         Usually, ordered single-linked list is used as a building block for the hash table implementation.
44         The complexity of searching is <tt>O(N)</tt>.
45
46         Template arguments:
47         - \p RCU - one of \ref cds_urcu_gc "RCU type"
48         - \p T - type to be stored in the list; the type \p T should be based on (or has a member of type)
49             cds::intrusive::micheal_list::node
50         - \p Traits - type traits. See \p michael_list::traits for explanation. It is possible to declare option-based
51              list with \p cds::intrusive::michael_list::make_traits metafunction,
52              see \ref cds_intrusive_MichaelList_hp "here" for explanations.
53
54         \par Usage
55             Before including <tt><cds/intrusive/michael_list_rcu.h></tt> you should include appropriate RCU header file,
56             see \ref cds_urcu_gc "RCU type" for list of existing RCU class and corresponding header files.
57             For example, for \ref cds_urcu_general_buffered_gc "general-purpose buffered RCU" you should include:
58             \code
59             #include <cds/urcu/general_buffered.h>
60             #include <cds/intrusive/michael_list_rcu.h>
61
62             // Now, you can declare Michael's list for type Foo and default traits:
63             typedef cds::intrusive::MichaelList<cds::urcu::gc< cds::urcu::general_buffered<> >, Foo > rcu_michael_list;
64             \endcode
65     */
66     template < typename RCU, typename T,
67 #ifdef CDS_DOXYGEN_INVOKED
68     class Traits = michael_list::traits
69 #else
70     class Traits
71 #endif
72     >
73     class MichaelList<cds::urcu::gc<RCU>, T, Traits>
74     {
75     public:
76         typedef T       value_type; ///< type of value stored in the list
77         typedef Traits  traits;     ///< Traits template parameter
78
79         typedef typename traits::hook    hook;      ///< hook type
80         typedef typename hook::node_type node_type; ///< node type
81
82 #   ifdef CDS_DOXYGEN_INVOKED
83         typedef implementation_defined key_comparator  ;    ///< key comparison functor based on opt::compare and opt::less option setter.
84 #   else
85         typedef typename opt::details::make_comparator< value_type, traits >::type key_comparator;
86 #   endif
87
88         typedef typename traits::disposer  disposer;   ///< disposer used
89         typedef typename get_node_traits< value_type, node_type, hook>::type node_traits ;    ///< node traits
90         typedef typename michael_list::get_link_checker< node_type, traits::link_checker >::type link_checker;   ///< link checker
91
92         typedef cds::urcu::gc<RCU>                     gc;           ///< RCU schema
93         typedef typename traits::back_off              back_off;     ///< back-off strategy
94         typedef typename traits::item_counter          item_counter; ///< Item counting policy used
95         typedef typename traits::memory_model          memory_model; ///< Memory ordering. See cds::opt::memory_model option
96         typedef typename traits::rcu_check_deadlock    rcu_check_deadlock; ///< Deadlock checking policy
97
98         typedef typename gc::scoped_lock    rcu_lock ;  ///< RCU scoped lock
99         static CDS_CONSTEXPR const bool c_bExtractLockExternal = false; ///< Group of \p extract_xxx functions do not require external locking
100
101         //@cond
102         // Rebind traits (split-list support)
103         template <typename... Options>
104         struct rebind_traits {
105             typedef MichaelList<
106                 gc
107                 , value_type
108                 , typename cds::opt::make_options< traits, Options...>::type
109             >   type;
110         };
111         //@endcond
112
113     protected:
114         typedef typename node_type::marked_ptr         marked_node_ptr ; ///< Marked node pointer
115         typedef typename node_type::atomic_marked_ptr  atomic_node_ptr ; ///< Atomic node pointer
116         typedef atomic_node_ptr                 auxiliary_head      ;   ///< Auxiliary head type (for split-list support)
117
118         atomic_node_ptr     m_pHead         ;   ///< Head pointer
119         item_counter        m_ItemCounter   ;   ///< Item counter
120
121     protected:
122         //@cond
123         enum erase_node_mask
124         {
125             erase_mask   = 1,
126             extract_mask = 3
127         };
128
129         typedef cds::urcu::details::check_deadlock_policy< gc, rcu_check_deadlock>   check_deadlock_policy;
130
131         static void clear_links( node_type * pNode )
132         {
133             pNode->m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed );
134             pNode->m_pDelChain = nullptr;
135         }
136
137         struct clear_and_dispose {
138             void operator()( value_type * p )
139             {
140                 assert( p != nullptr );
141                 clear_links( node_traits::to_node_ptr(p));
142                 disposer()( p );
143             }
144         };
145
146         static void dispose_node( node_type * pNode )
147         {
148             assert( pNode );
149             assert( !gc::is_locked() );
150
151             gc::template retire_ptr<clear_and_dispose>( node_traits::to_value_ptr( *pNode ) );
152         }
153
154         static void dispose_chain( node_type * pChain )
155         {
156             if ( pChain ) {
157                 assert( !gc::is_locked() );
158
159                 auto f = [&pChain]() -> cds::urcu::retired_ptr {
160                     node_type * p = pChain;
161                     pChain = p->m_pDelChain;
162                     return cds::urcu::make_retired_ptr<clear_and_dispose>( node_traits::to_value_ptr( p ));
163                 };
164                 gc::batch_retire(std::ref(f));
165             }
166         }
167
168         /// Position pointer for item search
169         struct position {
170             atomic_node_ptr * pPrev ;   ///< Previous node
171             node_type * pCur        ;   ///< Current node
172             node_type * pNext       ;   ///< Next node
173
174             atomic_node_ptr& refHead;
175             node_type * pDelChain; ///< Head of deleted node chain
176
177             position( atomic_node_ptr& head )
178                 : refHead( head )
179                 , pDelChain( nullptr )
180             {}
181
182             ~position()
183             {
184                 dispose_chain( pDelChain );
185             }
186         };
187
188         //@endcond
189
190     public:
191         using exempt_ptr = cds::urcu::exempt_ptr< gc, value_type, value_type, clear_and_dispose, void >; ///< pointer to extracted node
192
193     private:
194         //@cond
195         struct raw_ptr_disposer
196         {
197             node_type *     pReclaimedChain;
198
199             raw_ptr_disposer()
200                 : pReclaimedChain( nullptr )
201             {}
202
203             raw_ptr_disposer( position& pos )
204                 : pReclaimedChain( pos.pDelChain )
205             {
206                 pos.pDelChain = nullptr;
207             }
208
209             raw_ptr_disposer( raw_ptr_disposer&& d )
210                 : pReclaimedChain( d.pReclaimedChain )
211             {
212                 d.pReclaimedChain = nullptr;
213             }
214
215             raw_ptr_disposer( raw_ptr_disposer const& ) = delete;
216
217             ~raw_ptr_disposer()
218             {
219                 apply();
220             }
221
222             void apply()
223             {
224                 if ( pReclaimedChain ) {
225                     dispose_chain( pReclaimedChain );
226                     pReclaimedChain = nullptr;
227                 }
228             }
229         };
230         //@endcond
231
232     public:
233         /// Result of \p get(), \p get_with() functions - pointer to the node found
234         typedef cds::urcu::raw_ptr< gc, value_type, raw_ptr_disposer > raw_ptr; 
235
236     protected:
237         //@cond
238
239         bool link_node( node_type * pNode, position& pos )
240         {
241             assert( pNode != nullptr );
242             link_checker::is_empty( pNode );
243
244             marked_node_ptr p( pos.pCur );
245             pNode->m_pNext.store( p, memory_model::memory_order_release );
246             return pos.pPrev->compare_exchange_strong( p, marked_node_ptr(pNode), memory_model::memory_order_release, atomics::memory_order_relaxed );
247         }
248
249         static void link_to_remove_chain( position& pos, node_type * pDel )
250         {
251             assert( pDel->m_pDelChain == nullptr );
252
253             pDel->m_pDelChain = pos.pDelChain;
254             pos.pDelChain = pDel;
255         }
256
257         bool unlink_node( position& pos, erase_node_mask nMask )
258         {
259             // Mark the node (logical deletion)
260             marked_node_ptr next(pos.pNext, 0);
261
262             if ( pos.pCur->m_pNext.compare_exchange_strong( next, next | nMask, memory_model::memory_order_acq_rel, atomics::memory_order_relaxed )) {
263
264                 // Try physical removal - fast path
265                 marked_node_ptr cur(pos.pCur);
266                 if ( pos.pPrev->compare_exchange_strong(cur, marked_node_ptr(pos.pNext), memory_model::memory_order_acquire, atomics::memory_order_relaxed) ) {
267                     if ( nMask == erase_mask )
268                         link_to_remove_chain( pos, pos.pCur );
269                 }
270                 else {
271                     // Slow path
272                     search( pos.refHead, *node_traits::to_value_ptr( pos.pCur ), pos, key_comparator() );
273                 }
274                 return true;
275             }
276             return false;
277         }
278         //@endcond
279
280     protected:
281         //@cond
282         template <bool IsConst>
283         class iterator_type
284         {
285             friend class MichaelList;
286             value_type * m_pNode;
287
288             void next()
289             {
290                 if ( m_pNode ) {
291                     node_type * p = node_traits::to_node_ptr( *m_pNode )->m_pNext.load(memory_model::memory_order_relaxed).ptr();
292                     m_pNode = p ? node_traits::to_value_ptr( p ) : nullptr;
293                 }
294             }
295
296         protected:
297             explicit iterator_type( node_type * pNode)
298             {
299                 if ( pNode )
300                     m_pNode = node_traits::to_value_ptr( *pNode );
301                 else
302                     m_pNode = nullptr;
303             }
304             explicit iterator_type( atomic_node_ptr const& refNode)
305             {
306                 node_type * pNode = refNode.load(memory_model::memory_order_relaxed).ptr();
307                 m_pNode = pNode ? node_traits::to_value_ptr( *pNode ) : nullptr;
308             }
309
310         public:
311             typedef typename cds::details::make_const_type<value_type, IsConst>::pointer   value_ptr;
312             typedef typename cds::details::make_const_type<value_type, IsConst>::reference value_ref;
313
314             iterator_type()
315                 : m_pNode( nullptr )
316             {}
317
318             iterator_type( const iterator_type& src )
319                 : m_pNode( src.m_pNode )
320             {}
321
322             value_ptr operator ->() const
323             {
324                 return m_pNode;
325             }
326
327             value_ref operator *() const
328             {
329                 assert( m_pNode != nullptr );
330                 return *m_pNode;
331             }
332
333             /// Pre-increment
334             iterator_type& operator ++()
335             {
336                 next();
337                 return *this;
338             }
339
340             /// Post-increment
341             iterator_type operator ++(int)
342             {
343                 iterator_type i(*this);
344                 next();
345                 return i;
346             }
347
348             iterator_type& operator = (const iterator_type& src)
349             {
350                 m_pNode = src.m_pNode;
351                 return *this;
352             }
353
354             template <bool C>
355             bool operator ==(iterator_type<C> const& i ) const
356             {
357                 return m_pNode == i.m_pNode;
358             }
359             template <bool C>
360             bool operator !=(iterator_type<C> const& i ) const
361             {
362                 return m_pNode != i.m_pNode;
363             }
364         };
365         //@endcond
366
367     public:
368         /// Forward iterator
369         typedef iterator_type<false>    iterator;
370         /// Const forward iterator
371         typedef iterator_type<true>     const_iterator;
372
373         /// Returns a forward iterator addressing the first element in a list
374         /**
375             For empty list \code begin() == end() \endcode
376         */
377         iterator begin()
378         {
379             return iterator( m_pHead );
380         }
381
382         /// Returns an iterator that addresses the location succeeding the last element in a list
383         /**
384             Do not use the value returned by <tt>end</tt> function to access any item.
385             Internally, <tt>end</tt> returning value equals to \p nullptr.
386
387             The returned value can be used only to control reaching the end of the list.
388             For empty list \code begin() == end() \endcode
389         */
390         iterator end()
391         {
392             return iterator();
393         }
394
395         /// Returns a forward const iterator addressing the first element in a list
396         const_iterator begin() const
397         {
398             return const_iterator(m_pHead );
399         }
400         /// Returns a forward const iterator addressing the first element in a list
401         const_iterator cbegin() const
402         {
403             return const_iterator(m_pHead );
404         }
405
406         /// Returns an const iterator that addresses the location succeeding the last element in a list
407         const_iterator end() const
408         {
409             return const_iterator();
410         }
411         /// Returns an const iterator that addresses the location succeeding the last element in a list
412         const_iterator cend() const
413         {
414             return const_iterator();
415         }
416
417     public:
418         /// Default constructor initializes empty list
419         MichaelList()
420             : m_pHead( nullptr )
421         {
422             static_assert( (std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type" );
423         }
424
425         /// Destroy list
426         ~MichaelList()
427         {
428             clear();
429         }
430
431         /// Inserts new node
432         /**
433             The function inserts \p val in the list if the list does not contain
434             an item with key equal to \p val.
435
436             The function makes RCU lock internally.
437
438             Returns \p true if \p val is linked into the list, \p false otherwise.
439         */
440         bool insert( value_type& val )
441         {
442             return insert_at( m_pHead, val );
443         }
444
445         /// Inserts new node
446         /**
447             This function is intended for derived non-intrusive containers.
448
449             The function allows to split new item creating into two part:
450             - create item with key only
451             - insert new item into the list
452             - if inserting is success, calls \p f functor to initialize value-field of \p val.
453
454             The functor signature is:
455             \code
456                 void func( value_type& val );
457             \endcode
458             where \p val is the item inserted. User-defined functor \p f should guarantee that during changing
459             \p val no any other changes could be made on this list's item by concurrent threads.
460             The user-defined functor is called only if the inserting is success.
461
462             The function makes RCU lock internally.
463
464             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
465         */
466         template <typename Func>
467         bool insert( value_type& val, Func f )
468         {
469             return insert_at( m_pHead, val, f );
470         }
471
472         /// Ensures that the \p item exists in the list
473         /**
474             The operation performs inserting or changing data with lock-free manner.
475
476             If the item \p val not found in the list, then \p val is inserted into the list.
477             Otherwise, the functor \p func is called with item found.
478             The functor signature is:
479             \code
480             struct functor {
481                 void operator()( bool bNew, value_type& item, value_type& val );
482             };
483             \endcode
484             with arguments:
485             - \p bNew - \p true if the item has been inserted, \p false otherwise
486             - \p item - item of the list
487             - \p val - argument \p val passed into the \p ensure function
488             If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
489             refer to the same thing.
490
491             The functor may change non-key fields of the \p item; however, \p func must guarantee
492             that during changing no any other modifications could be made on this item by concurrent threads.
493
494             Returns <tt> std::pair<bool, bool>  </tt> where \p first is true if operation is successfull,
495             \p second is true if new item has been added or \p false if the item with \p key
496             already is in the list.
497
498             The function makes RCU lock internally.
499
500             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
501         */
502         template <typename Func>
503         std::pair<bool, bool> ensure( value_type& val, Func func )
504         {
505             return ensure_at( m_pHead, val, func );
506         }
507
508         /// Unlinks the item \p val from the list
509         /**
510             The function searches the item \p val in the list and unlink it from the list
511             if it is found and it is equal to \p val.
512
513             Difference between \ref erase and \p unlink functions: \p erase finds <i>a key</i>
514             and deletes the item found. \p unlink finds an item by key and deletes it
515             only if \p val is an item of that list, i.e. the pointer to the item found
516             is equal to <tt> &val </tt>.
517
518             The function returns \p true if success and \p false otherwise.
519
520             RCU \p synchronize method can be called.
521             Note that depending on RCU type used the \ref disposer call can be deferred.
522
523             The function can throw cds::urcu::rcu_deadlock exception if deadlock is encountered and
524             deadlock checking policy is opt::v::rcu_throw_deadlock.
525         */
526         bool unlink( value_type& val )
527         {
528             return unlink_at( m_pHead, val );
529         }
530
531         /// Deletes the item from the list
532         /** \anchor cds_intrusive_MichaelList_rcu_erase_val
533             The function searches an item with key equal to \p key in the list,
534             unlinks it from the list, and returns \p true.
535             If the item with the key equal to \p key is not found the function return \p false.
536
537             RCU \p synchronize method can be called.
538             Note that depending on RCU type used the \ref disposer call can be deferred.
539
540             The function can throw \ref cds_urcu_rcu_deadlock "cds::urcu::rcu_deadlock" exception if a deadlock is detected and
541             the deadlock checking policy is \p opt::v::rcu_throw_deadlock.
542         */
543         template <typename Q>
544         bool erase( Q const& key )
545         {
546             return erase_at( m_pHead, key, key_comparator() );
547         }
548
549         /// Deletes the item from the list using \p pred predicate for searching
550         /**
551             The function is an analog of \ref cds_intrusive_MichaelList_rcu_erase_val "erase(Q const&)"
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 list.
555         */
556         template <typename Q, typename Less>
557         bool erase_with( Q const& key, Less pred )
558         {
559             CDS_UNUSED( pred );
560             return erase_at( m_pHead, key, cds::opt::details::make_comparator_from_less<Less>() );
561         }
562
563         /// Deletes the item from the list
564         /** \anchor cds_intrusive_MichaelList_rcu_erase_func
565             The function searches an item with key equal to \p key in the list,
566             call \p func functor with item found, unlinks it from the list, and returns \p true.
567             The \p Func interface is
568             \code
569             struct functor {
570                 void operator()( value_type const& item );
571             };
572             \endcode
573
574             If the item with the key equal to \p key is not found the function return \p false.
575
576             RCU \p synchronize method can be called.
577             Note that depending on RCU type used the \ref disposer call can be deferred.
578
579             The function can throw \ref cds_urcu_rcu_deadlock "cds::urcu::rcu_deadlock" exception if a deadlock is detected and
580             the deadlock checking policy is \p opt::v::rcu_throw_deadlock.
581         */
582         template <typename Q, typename Func>
583         bool erase( Q const& key, Func func )
584         {
585             return erase_at( m_pHead, key, key_comparator(), func );
586         }
587
588         /// Deletes the item from the list using \p pred predicate for searching
589         /**
590             The function is an analog of \ref cds_intrusive_MichaelList_rcu_erase_func "erase(Q const&, Func)"
591             but \p pred is used for key comparing.
592             \p Less functor has the interface like \p std::less.
593             \p pred must imply the same element order as the comparator used for building the list.
594         */
595         template <typename Q, typename Less, typename Func>
596         bool erase_with( Q const& key, Less pred, Func func )
597         {
598             CDS_UNUSED( pred );
599             return erase_at( m_pHead, key, cds::opt::details::make_comparator_from_less<Less>(), func );
600         }
601
602         /// Extracts an item from the list
603         /**
604         @anchor cds_intrusive_MichaelList_rcu_extract
605             The function searches an item with key equal to \p key in the list,
606             unlinks it from the list, and returns \ref cds::urcu::exempt_ptr "exempt_ptr" pointer to the item found.
607             If \p key is not found the function returns an empty \p exempt_ptr.
608
609             @note The function does NOT dispose the item found. It just unlinks the item from the list
610             and returns a pointer to item found.
611             You shouldn't lock RCU for current thread before calling this function, and you should manually release
612             \p dest exempt pointer outside the RCU lock before reusing it.
613
614             \code
615             #include <cds/urcu/general_buffered.h>
616             #include <cds/intrusive/michael_list_rcu.h>
617
618             typedef cds::urcu::gc< general_buffered<> > rcu;
619             typedef cds::intrusive::MichaelList< rcu, Foo > rcu_michael_list;
620
621             rcu_michael_list theList;
622             // ...
623
624             rcu_michael_list::exempt_ptr p1;
625
626             // The RCU should NOT be locked when extract() is called!
627             assert( !rcu::is_locked() );
628
629             // You can call extract() function
630             p1 = theList.extract( 10 );
631             if ( p1 ) {
632                 // do something with p1
633                 ...
634             }
635
636             // We may safely release p1 here
637             // release() passes the pointer to RCU reclamation cycle:
638             // it invokes RCU retire_ptr function with the disposer you provided for the list.
639             p1.release();
640             \endcode
641         */
642         template <typename Q>
643         exempt_ptr extract( Q const& key )
644         {
645             return exempt_ptr( extract_at( m_pHead, key, key_comparator() ));
646         }
647
648         /// Extracts an item from the list using \p pred predicate for searching
649         /**
650             This function is the analog for \p extract(Q const&)
651
652             The \p pred is a predicate used for key comparing.
653             \p Less has the interface like \p std::less.
654             \p pred must imply the same element order as \ref key_comparator.
655         */
656         template <typename Q, typename Less>
657         exempt_ptr extract_with( Q const& key, Less pred )
658         {
659             CDS_UNUSED( pred );
660             return exempt_ptr( extract_at( m_pHead, key, cds::opt::details::make_comparator_from_less<Less>() ));
661         }
662
663         /// Find the key \p val
664         /** \anchor cds_intrusive_MichaelList_rcu_find_func
665             The function searches the item with key equal to \p key
666             and calls the functor \p f for item found.
667             The interface of \p Func functor is:
668             \code
669             struct functor {
670                 void operator()( value_type& item, Q& key );
671             };
672             \endcode
673             where \p item is the item found, \p key is the <tt>find</tt> function argument.
674
675             The functor can change non-key fields of \p item.
676             The function \p find does not serialize simultaneous access to the list \p item. If such access is
677             possible you must provide your own synchronization schema to exclude unsafe item modifications.
678
679             The function makes RCU lock internally.
680
681             The function returns \p true if \p val is found, \p false otherwise.
682         */
683         template <typename Q, typename Func>
684         bool find( Q& key, Func f )
685         {
686             return find_at( m_pHead, key, key_comparator(), f );
687         }
688         //@cond
689         template <typename Q, typename Func>
690         bool find( Q const& key, Func f )
691         {
692             return find_at( m_pHead, key, key_comparator(), f );
693         }
694         //@endcond
695
696         /// Finds \p key using \p pred predicate for searching
697         /**
698             The function is an analog of \ref cds_intrusive_MichaelList_rcu_find_func "find(Q&, Func)"
699             but \p pred is used for key comparing.
700             \p Less functor has the interface like \p std::less.
701             \p pred must imply the same element order as the comparator used for building the list.
702         */
703         template <typename Q, typename Less, typename Func>
704         bool find_with( Q& key, Less pred, Func f )
705         {
706             CDS_UNUSED( pred );
707             return find_at( m_pHead, key, cds::opt::details::make_comparator_from_less<Less>(), f );
708         }
709         //@cond
710         template <typename Q, typename Less, typename Func>
711         bool find_with( Q const& key, Less pred, Func f )
712         {
713             CDS_UNUSED( pred );
714             return find_at( m_pHead, key, cds::opt::details::make_comparator_from_less<Less>(), f );
715         }
716         //@endcond
717
718         /// Finds \p key
719         /** \anchor cds_intrusive_MichaelList_rcu_find_val
720             The function searches the item with key equal to \p key
721             and returns \p true if \p val found or \p false otherwise.
722         */
723         template <typename Q>
724         bool find( Q const& key )
725         {
726             return find_at( m_pHead, key, key_comparator() );
727         }
728
729         /// Finds \p key using \p pred predicate for searching
730         /**
731             The function is an analog of \ref cds_intrusive_MichaelList_rcu_find_val "find(Q const&)"
732             but \p pred is used for key comparing.
733             \p Less functor has the interface like \p std::less.
734             \p pred must imply the same element order as the comparator used for building the list.
735         */
736         template <typename Q, typename Less>
737         bool find_with( Q const& key, Less pred )
738         {
739             CDS_UNUSED( pred );
740             return find_at( m_pHead, key, cds::opt::details::make_comparator_from_less<Less>() );
741         }
742
743         /// Finds \p key and return the item found
744         /** \anchor cds_intrusive_MichaelList_rcu_get
745             The function searches the item with key equal to \p key and returns the pointer to item found.
746             If \p key is not found it returns empty \p raw_ptr object.
747
748             Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
749
750             RCU should be locked before call of this function.
751             Returned item is valid only while RCU is locked:
752             \code
753             typedef cds::intrusive::MichaelList< cds::urcu::gc< cds::urcu::general_buffered<> >, foo, my_traits > ord_list;
754             ord_list theList;
755             // ...
756             typename ord_list::raw_ptr rp;
757             {
758                 // Lock RCU
759                 ord_list::rcu_lock lock;
760
761                 rp = theList.get( 5 );
762                 if ( rp ) {
763                     // Deal with rp
764                     //...
765                 }
766                 // Unlock RCU by rcu_lock destructor
767                 // Node owned by rp can be retired by disposer at any time after RCU has been unlocked
768             }
769             // You can manually release rp after RCU-locked section
770             rp.release();
771             \endcode
772         */
773         template <typename Q>
774         raw_ptr get( Q const& key )
775         {
776             return get_at( m_pHead, key, key_comparator());
777         }
778
779         /// Finds \p key and return the item found
780         /**
781             The function is an analog of \ref cds_intrusive_MichaelList_rcu_get "get(Q const&)"
782             but \p pred is used for comparing the keys.
783
784             \p Less functor has the semantics like \p std::less but should take arguments of type \p value_type and \p Q
785             in any order.
786             \p pred must imply the same element order as the comparator used for building the list.
787         */
788         template <typename Q, typename Less>
789         raw_ptr get_with( Q const& key, Less pred )
790         {
791             CDS_UNUSED( pred );
792             return get_at( m_pHead, key, cds::opt::details::make_comparator_from_less<Less>());
793         }
794
795         /// Clears the list using default disposer
796         /**
797             The function clears the list using default (provided by \p Traits class template argument) disposer functor.
798
799             RCU \p synchronize method can be called.
800             Note that depending on RCU type used the \ref disposer invocation can be deferred.
801
802             The function can throw \p cds::urcu::rcu_deadlock exception if an deadlock is encountered and
803             deadlock checking policy is \p opt::v::rcu_throw_deadlock.
804         */
805         void clear()
806         {
807             if( !empty() ) {
808                 check_deadlock_policy::check();
809
810                 marked_node_ptr pHead;
811                 for (;;) {
812                     {
813                         rcu_lock l;
814                         pHead = m_pHead.load(memory_model::memory_order_acquire);
815                         if ( !pHead.ptr() )
816                             break;
817                         marked_node_ptr pNext( pHead->m_pNext.load(memory_model::memory_order_relaxed) );
818                         if ( !pHead->m_pNext.compare_exchange_weak( pNext, pNext | 1, memory_model::memory_order_acquire, memory_model::memory_order_relaxed ))
819                             continue;
820                         if ( !m_pHead.compare_exchange_weak( pHead, marked_node_ptr(pNext.ptr()), memory_model::memory_order_release, memory_model::memory_order_relaxed ))
821                             continue;
822                     }
823
824                     --m_ItemCounter;
825                     dispose_node( pHead.ptr() );
826                 }
827             }
828         }
829
830         /// Check if the list is empty
831         bool empty() const
832         {
833             return m_pHead.load( memory_model::memory_order_relaxed ).all() == nullptr;
834         }
835
836         /// Returns list's item count
837         /**
838             The value returned depends on item counter provided by \p Traits. For \p atomicity::empty_item_counter,
839             this function always returns 0.
840
841             @note Even if you use real item counter and it returns 0, this fact does not mean that the list
842             is empty. To check list emptyness use \p empty() method.
843         */
844         size_t size() const
845         {
846             return m_ItemCounter.value();
847         }
848
849     protected:
850         //@cond
851         // split-list support
852         bool insert_aux_node( node_type * pNode )
853         {
854             return insert_aux_node( m_pHead, pNode );
855         }
856
857         // split-list support
858         bool insert_aux_node( atomic_node_ptr& refHead, node_type * pNode )
859         {
860             assert( pNode != nullptr );
861
862             // Hack: convert node_type to value_type.
863             // In principle, auxiliary node can be non-reducible to value_type
864             // We assume that comparator can correctly distinguish between aux and regular node.
865             return insert_at( refHead, *node_traits::to_value_ptr( pNode ) );
866         }
867
868         bool insert_at( atomic_node_ptr& refHead, value_type& val )
869         {
870             position pos( refHead );
871             {
872                 rcu_lock l;
873                 return insert_at_locked( pos, val );
874             }
875         }
876
877         template <typename Func>
878         bool insert_at( atomic_node_ptr& refHead, value_type& val, Func f )
879         {
880             link_checker::is_empty( node_traits::to_node_ptr( val ) );
881             position pos( refHead );
882
883             {
884                 rcu_lock l;
885                 while ( true ) {
886                     if ( search( refHead, val, pos, key_comparator()))
887                         return false;
888
889                     if ( link_node( node_traits::to_node_ptr( val ), pos ) ) {
890                         f( val );
891                         ++m_ItemCounter;
892                         return true;
893                     }
894
895                     // clear next field
896                     node_traits::to_node_ptr( val )->m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed );
897                 }
898             }
899
900         }
901
902         iterator insert_at_( atomic_node_ptr& refHead, value_type& val )
903         {
904             rcu_lock l;
905             if ( insert_at_locked( refHead, val ))
906                 return iterator( node_traits::to_node_ptr( val ));
907             return end();
908         }
909
910         template <typename Func>
911         std::pair<iterator, bool> ensure_at_( atomic_node_ptr& refHead, value_type& val, Func func )
912         {
913             position pos( refHead );
914             {
915                 rcu_lock l;
916                 return ensure_at_locked( pos, val, func );
917             }
918         }
919
920         template <typename Func>
921         std::pair<bool, bool> ensure_at( atomic_node_ptr& refHead, value_type& val, Func func )
922         {
923             position pos( refHead );
924             {
925                 rcu_lock l;
926                 std::pair<iterator, bool> ret = ensure_at_locked( pos, val, func );
927                 return std::make_pair( ret.first != end(), ret.second );
928             }
929         }
930
931         bool unlink_at( atomic_node_ptr& refHead, value_type& val )
932         {
933             position pos( refHead );
934             back_off bkoff;
935             check_deadlock_policy::check();
936
937             for (;;) {
938                 {
939                     rcu_lock l;
940                     if ( !search( refHead, val, pos, key_comparator() ) || node_traits::to_value_ptr( *pos.pCur ) != &val )
941                         return false;
942                     if ( !unlink_node( pos, erase_mask )) {
943                         bkoff();
944                         continue;
945                     }
946                 }
947
948                 --m_ItemCounter;
949                 return true;
950             }
951         }
952
953         template <typename Q, typename Compare, typename Func>
954         bool erase_at( position& pos, Q const& val, Compare cmp, Func f )
955         {
956             back_off bkoff;
957             check_deadlock_policy::check();
958
959             for (;;) {
960                 {
961                     rcu_lock l;
962                     if ( !search( pos.refHead, val, pos, cmp ) )
963                         return false;
964                     if ( !unlink_node( pos, erase_mask )) {
965                         bkoff();
966                         continue;
967                     }
968                 }
969
970                 f( *node_traits::to_value_ptr( *pos.pCur ) );
971                 --m_ItemCounter;
972                 return true;
973             }
974         }
975
976         template <typename Q, typename Compare, typename Func>
977         bool erase_at( atomic_node_ptr& refHead, Q const& val, Compare cmp, Func f )
978         {
979             position pos( refHead );
980             return erase_at( pos, val, cmp, f );
981         }
982
983         template <typename Q, typename Compare>
984         bool erase_at( atomic_node_ptr& refHead, const Q& val, Compare cmp )
985         {
986             position pos( refHead );
987             return erase_at( pos, val, cmp, [](value_type const&){} );
988         }
989
990         template <typename Q, typename Compare >
991         value_type * extract_at( atomic_node_ptr& refHead, Q const& val, Compare cmp )
992         {
993             position pos( refHead );
994             back_off bkoff;
995             assert( !gc::is_locked() )  ;   // RCU must not be locked!!!
996
997             {
998                 rcu_lock l;
999                 for (;;) {
1000                     if ( !search( refHead, val, pos, cmp ) )
1001                         return nullptr;
1002                     if ( !unlink_node( pos, extract_mask )) {
1003                         bkoff();
1004                         continue;
1005                     }
1006
1007                     --m_ItemCounter;
1008                     return node_traits::to_value_ptr( pos.pCur );
1009                 }
1010             }
1011         }
1012
1013         template <typename Q, typename Compare, typename Func>
1014         bool find_at( atomic_node_ptr& refHead, Q& val, Compare cmp, Func f )
1015         {
1016             position pos( refHead );
1017
1018             {
1019                 rcu_lock l;
1020                 if ( search( refHead, val, pos, cmp ) ) {
1021                     assert( pos.pCur != nullptr );
1022                     f( *node_traits::to_value_ptr( *pos.pCur ), val );
1023                     return true;
1024                 }
1025                 return false;
1026             }
1027         }
1028
1029         template <typename Q, typename Compare>
1030         bool find_at( atomic_node_ptr& refHead, Q const& val, Compare cmp )
1031         {
1032             position pos( refHead );
1033             {
1034                 rcu_lock l;
1035                 return find_at_locked( pos, val, cmp ) != cend();
1036             }
1037         }
1038
1039         template <typename Q, typename Compare>
1040         raw_ptr get_at( atomic_node_ptr& refHead, Q const& val, Compare cmp )
1041         {
1042             // RCU should be locked!
1043             assert(gc::is_locked() );
1044
1045             position pos( refHead );
1046
1047             if ( search( refHead, val, pos, cmp ))
1048                 return raw_ptr( node_traits::to_value_ptr( pos.pCur ), raw_ptr_disposer( pos ));
1049             return raw_ptr( raw_ptr_disposer( pos ));
1050         }
1051         //@endcond
1052
1053     protected:
1054
1055         //@cond
1056         template <typename Q, typename Compare>
1057         bool search( atomic_node_ptr& refHead, const Q& val, position& pos, Compare cmp )
1058         {
1059             // RCU lock should be locked!!!
1060             assert( gc::is_locked() );
1061
1062             atomic_node_ptr * pPrev;
1063             marked_node_ptr pNext;
1064             marked_node_ptr pCur;
1065
1066             back_off        bkoff;
1067
1068         try_again:
1069             pPrev = &refHead;
1070             pCur = pPrev->load(memory_model::memory_order_acquire);
1071             pNext = nullptr;
1072
1073             while ( true ) {
1074                 if ( !pCur.ptr() ) {
1075                     pos.pPrev = pPrev;
1076                     pos.pCur = nullptr;
1077                     pos.pNext = nullptr;
1078                     return false;
1079                 }
1080
1081                 pNext = pCur->m_pNext.load(memory_model::memory_order_acquire);
1082
1083                 if ( pPrev->load(memory_model::memory_order_relaxed) != pCur
1084                     || pNext != pCur->m_pNext.load(memory_model::memory_order_relaxed))
1085                 {
1086                     bkoff();
1087                     goto try_again;
1088                 }
1089
1090                 if ( pNext.bits() ) {
1091                     // pCur is marked as deleted. Try to unlink it from the list
1092                     if ( pPrev->compare_exchange_weak( pCur, marked_node_ptr( pNext.ptr() ), memory_model::memory_order_acquire, atomics::memory_order_relaxed ) ) {
1093                         if ( pNext.bits() == erase_mask )
1094                             link_to_remove_chain( pos, pCur.ptr() );
1095                     }
1096
1097                     goto try_again;
1098                 }
1099
1100                 assert( pCur.ptr() != nullptr );
1101                 int nCmp = cmp( *node_traits::to_value_ptr( pCur.ptr() ), val );
1102                 if ( nCmp >= 0 ) {
1103                     pos.pPrev = pPrev;
1104                     pos.pCur = pCur.ptr();
1105                     pos.pNext = pNext.ptr();
1106                     return nCmp == 0;
1107                 }
1108                 pPrev = &( pCur->m_pNext );
1109                 pCur = pNext;
1110             }
1111         }
1112         //@endcond
1113
1114     private:
1115         //@cond
1116         bool insert_at_locked( position& pos, value_type& val )
1117         {
1118             // RCU lock should be locked!!!
1119             assert( gc::is_locked() );
1120             link_checker::is_empty( node_traits::to_node_ptr( val ) );
1121
1122             while ( true ) {
1123                 if ( search( pos.refHead, val, pos, key_comparator() ) )
1124                     return false;
1125
1126                 if ( link_node( node_traits::to_node_ptr( val ), pos ) ) {
1127                     ++m_ItemCounter;
1128                     return true;
1129                 }
1130
1131                 // clear next field
1132                 node_traits::to_node_ptr( val )->m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed );
1133             }
1134         }
1135
1136         template <typename Func>
1137         std::pair<iterator, bool> ensure_at_locked( position& pos, value_type& val, Func func )
1138         {
1139             // RCU lock should be locked!!!
1140             assert( gc::is_locked() );
1141
1142             while ( true ) {
1143                 if ( search( pos.refHead, val, pos, key_comparator() ) ) {
1144                     assert( key_comparator()( val, *node_traits::to_value_ptr( *pos.pCur ) ) == 0 );
1145
1146                     func( false, *node_traits::to_value_ptr( *pos.pCur ), val );
1147                     return std::make_pair( iterator( pos.pCur ), false );
1148                 }
1149                 else {
1150                     link_checker::is_empty( node_traits::to_node_ptr( val ) );
1151
1152                     if ( link_node( node_traits::to_node_ptr( val ), pos ) ) {
1153                         ++m_ItemCounter;
1154                         func( true, val , val );
1155                         return std::make_pair( iterator( node_traits::to_node_ptr( val )), true );
1156                     }
1157
1158                     // clear the next field
1159                     node_traits::to_node_ptr( val )->m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed );
1160                 }
1161             }
1162         }
1163
1164         template <typename Q, typename Compare>
1165         const_iterator find_at_locked( position& pos, Q const& val, Compare cmp )
1166         {
1167             assert( gc::is_locked() );
1168
1169             if ( search( pos.refHead, val, pos, cmp ) ) {
1170                 assert( pos.pCur != nullptr );
1171                 return const_iterator( pos.pCur );
1172             }
1173             return cend();
1174         }
1175         //@endcond
1176     };
1177
1178 }}  // namespace cds::intrusive
1179
1180 #endif  // #ifndef CDSLIB_INTRUSIVE_MICHAEL_LIST_NOGC_H