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