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