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