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