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