72d7e2abe4ab575103570331b2f2775b0d37239a
[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_options {
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()
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()
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
706         /// Finds the key \p key using \p pred predicate for searching
707         /**
708             The function is an analog of \ref cds_intrusive_LazyList_hp_find "find(Q&, Func)"
709             but \p pred is used for key comparing.
710             \p Less functor has the interface like \p std::less.
711             \p pred must imply the same element order as the comparator used for building the list.
712         */
713         template <typename Q, typename Less, typename Func>
714         bool find_with( Q& key, Less pred, Func f )
715         {
716             return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>(), f );
717         }
718
719         /// Finds the key \p key
720         /** \anchor cds_intrusive_LazyList_hp_find_val
721             The function searches the item with key equal to \p key
722             and returns \p true if it is found, and \p false otherwise
723         */
724         template <typename Q>
725         bool find( Q const & key )
726         {
727             return find_at( &m_Head, key, key_comparator() );
728         }
729
730         /// Finds \p key using \p pred predicate for searching
731         /**
732             The function is an analog of \ref cds_intrusive_LazyList_hp_find_val "find(Q const&)"
733             but \p pred is used for key comparing.
734             \p Less functor has the interface like \p std::less.
735             \p pred must imply the same element order as the comparator used for building the list.
736         */
737         template <typename Q, typename Less>
738         bool find_with( Q const& key, Less pred )
739         {
740             return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>() );
741         }
742
743         /// Finds \p key and return the item found
744         /** \anchor cds_intrusive_LazyList_hp_get
745             The function searches the item with key equal to \p key
746             and assigns the item found to guarded pointer \p ptr.
747             The function returns \p true if \p key is found, and \p false otherwise.
748             If \p key is not found the \p ptr parameter is not changed.
749
750             The \ref disposer specified in \p Traits class template parameter is called
751             by garbage collector \p GC automatically when returned \ref guarded_ptr object
752             will be destroyed or released.
753             @note Each \p guarded_ptr object uses one GC's guard which can be limited resource.
754
755             Usage:
756             \code
757             typedef cds::intrusive::LazyList< cds::gc::HP, foo, my_traits >  ord_list;
758             ord_list theList;
759             // ...
760             {
761                 ord_list::guarded_ptr gp;
762                 if ( theList.get( gp, 5 )) {
763                     // Deal with gp
764                     //...
765                 }
766                 // Destructor of guarded_ptr releases internal HP guard
767             }
768             \endcode
769
770             Note the compare functor specified for class \p Traits template parameter
771             should accept a parameter of type \p Q that can be not the same as \p value_type.
772         */
773         template <typename Q>
774         bool get( guarded_ptr& ptr, Q const& key )
775         {
776             return get_at( &m_Head, ptr.guard(), key, key_comparator() );
777         }
778
779         /// Finds \p key and return the item found
780         /**
781             The function is an analog of \ref cds_intrusive_LazyList_hp_get "get( guarded_ptr& ptr, Q const&)"
782             but \p pred is used for comparing the keys.
783
784             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
785             in any order.
786             \p pred must imply the same element order as the comparator used for building the list.
787         */
788         template <typename Q, typename Less>
789         bool get_with( guarded_ptr& ptr, Q const& key, Less pred )
790         {
791             return get_at( &m_Head, ptr.guard(), key, cds::opt::details::make_comparator_from_less<Less>() );
792         }
793
794         /// Clears the list
795         void clear()
796         {
797             typename gc::Guard guard;
798             marked_node_ptr h;
799             while ( !empty() ) {
800                 h = m_Head.m_pNext.load( memory_model::memory_order_relaxed );
801                 guard.assign( node_traits::to_value_ptr( h.ptr() ));
802                 if ( m_Head.m_pNext.load(memory_model::memory_order_acquire) == h ) {
803                     m_Head.m_Lock.lock();
804                     h->m_Lock.lock();
805
806                     unlink_node( &m_Head, h.ptr(), &m_Head );
807
808                     h->m_Lock.unlock();
809                     m_Head.m_Lock.unlock();
810
811                     retire_node( h.ptr() ) ; // free node
812                 }
813             }
814         }
815
816         /// Checks if the list is empty
817         bool empty() const
818         {
819             return m_Head.m_pNext.load( memory_model::memory_order_relaxed ).ptr() == &m_Tail;
820         }
821
822         /// Returns list's item count
823         /**
824             The value returned depends on item counter provided by \p Traits. For \p atomicity::empty_item_counter,
825             this function always returns 0.
826
827             @note Even if you use real item counter and it returns 0, this fact does not mean that the list
828             is empty. To check list emptyness use \p empty() method.
829         */
830         size_t size() const
831         {
832             return m_ItemCounter.value();
833         }
834
835     protected:
836         //@cond
837         // split-list support
838         bool insert_aux_node( node_type * pNode )
839         {
840             return insert_aux_node( &m_Head, pNode );
841         }
842
843         // split-list support
844         bool insert_aux_node( node_type * pHead, node_type * pNode )
845         {
846             assert( pNode != nullptr );
847
848             // Hack: convert node_type to value_type.
849             // In principle, auxiliary node cannot be reducible to value_type
850             // We assume that internal comparator can correctly distinguish aux and regular node.
851             return insert_at( pHead, *node_traits::to_value_ptr( pNode ) );
852         }
853
854         bool insert_at( node_type * pHead, value_type& val )
855         {
856             link_checker::is_empty( node_traits::to_node_ptr( val ) );
857             position pos;
858             key_comparator  cmp;
859
860             while ( true ) {
861                 search( pHead, val, pos, key_comparator() );
862                 {
863                     auto_lock_position alp( pos );
864                     if ( validate( pos.pPred, pos.pCur )) {
865                         if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
866                             // failed: key already in list
867                             return false;
868                         }
869                         else {
870                             link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
871                             ++m_ItemCounter;
872                             return true;
873                         }
874                     }
875                 }
876             }
877         }
878
879         template <typename Func>
880         bool insert_at( node_type * pHead, value_type& val, Func f )
881         {
882             link_checker::is_empty( node_traits::to_node_ptr( val ) );
883             position pos;
884             key_comparator  cmp;
885
886             while ( true ) {
887                 search( pHead, val, pos, key_comparator() );
888                 {
889                     auto_lock_position alp( pos );
890                     if ( validate( pos.pPred, pos.pCur )) {
891                         if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
892                             // failed: key already in list
893                             return false;
894                         }
895                         else {
896                             link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
897                             f( val );
898                             ++m_ItemCounter;
899                             return true;
900                         }
901                     }
902                 }
903             }
904         }
905
906         template <typename Func>
907         std::pair<bool, bool> ensure_at( node_type * pHead, value_type& val, Func func )
908         {
909             position pos;
910             key_comparator  cmp;
911
912             while ( true ) {
913                 search( pHead, val, pos, key_comparator() );
914                 {
915                     auto_lock_position alp( pos );
916                     if ( validate( pos.pPred, pos.pCur )) {
917                         if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
918                             // key already in the list
919
920                             func( false, *node_traits::to_value_ptr( *pos.pCur ) , val );
921                             return std::make_pair( true, false );
922                         }
923                         else {
924                             // new key
925                             link_checker::is_empty( node_traits::to_node_ptr( val ) );
926
927                             link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
928                             func( true, val, val );
929                             ++m_ItemCounter;
930                             return std::make_pair( true, true );
931                         }
932                     }
933                 }
934             }
935         }
936
937         bool unlink_at( node_type * pHead, value_type& val )
938         {
939             position pos;
940             key_comparator  cmp;
941
942             while ( true ) {
943                 search( pHead, val, pos, key_comparator() );
944                 {
945                     int nResult = 0;
946                     {
947                         auto_lock_position alp( pos );
948                         if ( validate( pos.pPred, pos.pCur ) ) {
949                             if ( pos.pCur != &m_Tail
950                                 && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0
951                                 && node_traits::to_value_ptr( pos.pCur ) == &val )
952                             {
953                                 // item found
954                                 unlink_node( pos.pPred, pos.pCur, pHead );
955                                 --m_ItemCounter;
956                                 nResult = 1;
957                             }
958                             else
959                                 nResult = -1;
960                         }
961                     }
962                     if ( nResult ) {
963                         if ( nResult > 0 ) {
964                             retire_node( pos.pCur );
965                             return true;
966                         }
967                         return false;
968                     }
969                 }
970             }
971         }
972
973         template <typename Q, typename Compare, typename Func>
974         bool erase_at( node_type * pHead, const Q& val, Compare cmp, Func f, position& pos )
975         {
976             while ( true ) {
977                 search( pHead, val, pos, cmp );
978                 {
979                     int nResult = 0;
980                     {
981                         auto_lock_position alp( pos );
982                         if ( validate( pos.pPred, pos.pCur )) {
983                             if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
984                                 // key found
985                                 unlink_node( pos.pPred, pos.pCur, pHead );
986                                 f( *node_traits::to_value_ptr( *pos.pCur ) );
987                                 --m_ItemCounter;
988                                 nResult = 1;
989                             }
990                             else {
991                                 nResult = -1;
992                             }
993                         }
994                     }
995                     if ( nResult ) {
996                         if ( nResult > 0 ) {
997                             retire_node( pos.pCur );
998                             return true;
999                         }
1000                         return false;
1001                     }
1002                 }
1003             }
1004         }
1005
1006         template <typename Q, typename Compare, typename Func>
1007         bool erase_at( node_type * pHead, const Q& val, Compare cmp, Func f )
1008         {
1009             position pos;
1010             return erase_at( pHead, val, cmp, f, pos );
1011         }
1012
1013         template <typename Q, typename Compare>
1014         bool erase_at( node_type * pHead, const Q& val, Compare cmp )
1015         {
1016             position pos;
1017             return erase_at( pHead, val, cmp, [](value_type const &){}, pos );
1018         }
1019
1020         template <typename Q, typename Compare>
1021         bool extract_at( node_type * pHead, typename gc::Guard& gp, const Q& val, Compare cmp )
1022         {
1023             position pos;
1024             if ( erase_at( pHead, val, cmp, [](value_type const &){}, pos )) {
1025                 gp.assign( pos.guards.template get<value_type>(position::guard_current_item) );
1026                 return true;
1027             }
1028             return false;
1029         }
1030
1031         template <typename Q, typename Compare, typename Func>
1032         bool find_at( node_type * pHead, Q& val, Compare cmp, Func f )
1033         {
1034             position pos;
1035
1036             search( pHead, val, pos, cmp );
1037             if ( pos.pCur != &m_Tail ) {
1038                 std::unique_lock< typename node_type::lock_type> al( pos.pCur->m_Lock );
1039                 if ( !pos.pCur->is_marked()
1040                     && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 )
1041                 {
1042                     f( *node_traits::to_value_ptr( *pos.pCur ), val );
1043                     return true;
1044                 }
1045             }
1046             return false;
1047         }
1048
1049         template <typename Q, typename Compare>
1050         bool find_at( node_type * pHead, Q const& val, Compare cmp )
1051         {
1052             position pos;
1053
1054             search( pHead, val, pos, cmp );
1055             return pos.pCur != &m_Tail
1056                 && !pos.pCur->is_marked()
1057                 && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0;
1058         }
1059
1060         template <typename Q, typename Compare>
1061         bool get_at( node_type * pHead, typename gc::Guard& gp, Q const& val, Compare cmp )
1062         {
1063             position pos;
1064
1065             search( pHead, val, pos, cmp );
1066             if ( pos.pCur != &m_Tail
1067                 && !pos.pCur->is_marked()
1068                 && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 )
1069             {
1070                 gp.assign( pos.guards.template get<value_type>( position::guard_current_item ));
1071                 return true;
1072             }
1073             return false;
1074         }
1075
1076         //@endcond
1077
1078     protected:
1079         //@cond
1080         template <typename Q, typename Compare>
1081         void search( node_type * pHead, const Q& key, position& pos, Compare cmp )
1082         {
1083             const node_type * pTail = &m_Tail;
1084
1085             marked_node_ptr pCur( pHead );
1086             marked_node_ptr pPrev( pHead );
1087
1088             back_off        bkoff;
1089
1090             while ( pCur.ptr() != pTail )
1091             {
1092                 if ( pCur.ptr() != pHead ) {
1093                     if ( cmp( *node_traits::to_value_ptr( *pCur.ptr() ), key ) >= 0 )
1094                         break;
1095                 }
1096
1097                 pos.guards.copy( position::guard_prev_item, position::guard_current_item );
1098                 pPrev = pCur;
1099
1100                 for (;;) {
1101                     pCur = pPrev->m_pNext.load(memory_model::memory_order_relaxed);
1102                     pos.guards.assign( position::guard_current_item, node_traits::to_value_ptr( pCur.ptr() ));
1103                     if ( pCur == pPrev->m_pNext.load(memory_model::memory_order_acquire) )
1104                         break;
1105                     bkoff();
1106                 }
1107                 assert( pCur.ptr() != nullptr );
1108             }
1109
1110             pos.pCur = pCur.ptr();
1111             pos.pPred = pPrev.ptr();
1112         }
1113
1114         static bool validate( node_type * pPred, node_type * pCur )
1115         {
1116             return !pPred->is_marked()
1117                 && !pCur->is_marked()
1118                 && pPred->m_pNext.load(memory_model::memory_order_relaxed) == pCur;
1119         }
1120
1121         //@endcond
1122     };
1123 }}  // namespace cds::intrusive
1124
1125 #endif // __CDS_INTRUSIVE_IMPL_LAZY_LIST_H