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