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