757e57dac0776a74e5d9b50cc147559d095f61da
[libcds.git] / cds / container / lazy_list_rcu.h
1 //$$CDS-header$$
2
3 #ifndef __CDS_CONTAINER_LAZY_LIST_RCU_H
4 #define __CDS_CONTAINER_LAZY_LIST_RCU_H
5
6 #include <memory>
7 #include <cds/container/details/lazy_list_base.h>
8 #include <cds/intrusive/lazy_list_rcu.h>
9 #include <cds/details/binary_functor_wrapper.h>
10 #include <cds/container/details/make_lazy_list.h>
11
12 namespace cds { namespace container {
13
14     /// Lazy ordered list (template specialization for \ref cds_urcu_desc "RCU")
15     /** @ingroup cds_nonintrusive_list
16         \anchor cds_nonintrusive_LazyList_rcu
17
18         Usually, ordered single-linked list is used as a building block for the hash table implementation.
19         The complexity of searching is <tt>O(N)</tt>.
20
21         Source:
22         - [2005] Steve Heller, Maurice Herlihy, Victor Luchangco, Mark Moir, William N. Scherer III, and Nir Shavit
23             "A Lazy Concurrent List-Based Set Algorithm"
24
25         The lazy list is based on an optimistic locking scheme for inserts and removes,
26         eliminating the need to use the equivalent of an atomically markable
27         reference. It also has a novel wait-free membership \p find operation
28         that does not need to perform cleanup operations and is more efficient.
29
30         It is non-intrusive version of \p cds::intrusive::LazyList class
31
32         Template arguments:
33         - \p RCU - one of \ref cds_urcu_gc "RCU type"
34         - \p T - type to be stored in the list.
35         - \p Traits - type traits, default is lazy_list::traits
36             It is possible to declare option-based list with cds::container::lazy_list::make_traits metafunction istead of \p Traits template
37             argument. For example, the following traits-based declaration of \p gc::HP lazy list
38             \code
39             #include <cds/urcu/general_instant.h>
40             #include <cds/container/lazy_list_rcu.h>
41             // Declare comparator for the item
42             struct my_compare {
43                 int operator ()( int i1, int i2 )
44                 {
45                     return i1 - i2;
46                 }
47             };
48
49             // Declare traits
50             struct my_traits: public cds::container::lazy_list::traits
51             {
52                 typedef my_compare compare;
53             };
54
55             // Declare traits-based list
56             typedef cds::container::LazyList< cds::urcu::gc< cds::urcu::general_instant<> >, int, my_traits > traits_based_list;
57             \endcode
58             is equal to the following option-based list
59             \code
60             #include <cds/urcu/general_instant.h>
61             #include <cds/container/lazy_list_rcu.h>
62
63             // my_compare is the same
64
65             // Declare option-based list
66             typedef cds::container::LazyList< cds::urcu::gc< cds::urcu::general_instant<> >, int,
67                 typename cds::container::lazy_list::make_traits<
68                     cds::container::opt::compare< my_compare >     // item comparator option
69                 >::type
70             >     option_based_list;
71             \endcode
72
73         The implementation does not divide type \p T into key and value part and
74         may be used as main building block for some hash set containers.
75         The key is a function (or a part) of type \p T, and this function is specified by \p Traits::compare functor
76         or \p Traits::less predicate
77
78         \ref cds_nonintrusive_LazyKVList_rcu "LazyKVList" is a key-value version
79         of lazy non-intrusive list that is closer to the C++ std library approach.
80
81         @note Before including <tt><cds/container/lazy_list_rcu.h></tt> you should include
82         appropriate RCU header file, see \ref cds_urcu_gc "RCU type" for list
83         of existing RCU class and corresponding header files.
84     */
85     template <
86         typename RCU,
87         typename T,
88 #ifdef CDS_DOXYGEN_INVOKED
89         typename Traits = lazy_list::traits
90 #else
91         typename Traits
92 #endif
93     >
94     class LazyList< cds::urcu::gc<RCU>, T, Traits >:
95 #ifdef CDS_DOXYGEN_INVOKED
96         protected intrusive::LazyList< cds::urcu::gc<RCU>, T, Traits >
97 #else
98         protected details::make_lazy_list< cds::urcu::gc<RCU>, T, Traits >::type
99 #endif
100     {
101         //@cond
102         typedef details::make_lazy_list< cds::urcu::gc<RCU>, T, Traits > maker;
103         typedef typename maker::type  base_class;
104         //@endcond
105
106     public:
107         typedef cds::urcu::gc<RCU> gc; ///< Garbage collector
108         typedef T value_type;          ///< Type of value stored in the list
109         typedef Traits traits;         ///< List traits
110
111         typedef typename base_class::back_off       back_off;       ///< Back-off strategy
112         typedef typename maker::allocator_type      allocator_type; ///< Allocator type used for allocate/deallocate the nodes
113         typedef typename base_class::item_counter   item_counter;   ///< Item counting policy used
114         typedef typename maker::key_comparator      key_comparator; ///< key compare functor
115         typedef typename base_class::memory_model   memory_model;   ///< Memory ordering. See cds::opt::memory_model option
116         typedef typename base_class::rcu_check_deadlock rcu_check_deadlock; ///< Deadlock checking policy
117
118         typedef typename gc::scoped_lock  rcu_lock ;  ///< RCU scoped lock
119         static CDS_CONSTEXPR const bool c_bExtractLockExternal = base_class::c_bExtractLockExternal; ///< Group of \p extract_xxx functions require external locking
120
121     protected:
122         //@cond
123         typedef typename base_class::value_type     node_type;
124         typedef typename maker::cxx_allocator       cxx_allocator;
125         typedef typename maker::node_deallocator    node_deallocator;
126         typedef typename maker::intrusive_traits::compare intrusive_key_comparator;
127
128         typedef typename base_class::node_type head_type;
129         //@endcond
130
131     public:
132         using exempt_ptr = cds::urcu::exempt_ptr< gc, node_type, value_type, typename maker::intrusive_traits::disposer >; ///< pointer to extracted node
133
134     private:
135         //@cond
136         static value_type& node_to_value( node_type& n )
137         {
138             return n.m_Value;
139         }
140         static value_type const& node_to_value( node_type const& n )
141         {
142             return n.m_Value;
143         }
144         //@endcond
145
146     protected:
147         //@cond
148         template <typename Q>
149         static node_type * alloc_node( Q const& v )
150         {
151             return cxx_allocator().New( v );
152         }
153
154         template <typename... Args>
155         static node_type * alloc_node( Args&&... args )
156         {
157             return cxx_allocator().MoveNew( std::forward<Args>(args)... );
158         }
159
160         static void free_node( node_type * pNode )
161         {
162             cxx_allocator().Delete( pNode );
163         }
164
165         struct node_disposer {
166             void operator()( node_type * pNode )
167             {
168                 free_node( pNode );
169             }
170         };
171         typedef std::unique_ptr< node_type, node_disposer >     scoped_node_ptr;
172
173         head_type& head()
174         {
175             return base_class::m_Head;
176         }
177
178         head_type& head() const
179         {
180             return const_cast<head_type&>( base_class::m_Head );
181         }
182
183         head_type& tail()
184         {
185             return base_class::m_Tail;
186         }
187
188         head_type const&  tail() const
189         {
190             return base_class::m_Tail;
191         }
192         //@endcond
193
194     protected:
195                 //@cond
196         template <bool IsConst>
197         class iterator_type: protected base_class::template iterator_type<IsConst>
198         {
199             typedef typename base_class::template iterator_type<IsConst>    iterator_base;
200
201             iterator_type( head_type const& pNode )
202                 : iterator_base( const_cast<head_type *>( &pNode ))
203             {}
204
205             iterator_type( head_type const * pNode )
206                 : iterator_base( const_cast<head_type *>( pNode ))
207             {}
208
209             friend class LazyList;
210
211         public:
212             typedef typename cds::details::make_const_type<value_type, IsConst>::pointer   value_ptr;
213             typedef typename cds::details::make_const_type<value_type, IsConst>::reference value_ref;
214
215             iterator_type()
216             {}
217
218             iterator_type( iterator_type const& src )
219                 : iterator_base( src )
220             {}
221
222             value_ptr operator ->() const
223             {
224                 typename iterator_base::value_ptr p = iterator_base::operator ->();
225                 return p ? &(p->m_Value) : nullptr;
226             }
227
228             value_ref operator *() const
229             {
230                 return (iterator_base::operator *()).m_Value;
231             }
232
233             /// Pre-increment
234             iterator_type& operator ++()
235             {
236                 iterator_base::operator ++();
237                 return *this;
238             }
239
240             template <bool C>
241             bool operator ==(iterator_type<C> const& i ) const
242             {
243                 return iterator_base::operator ==(i);
244             }
245             template <bool C>
246             bool operator !=(iterator_type<C> const& i ) const
247             {
248                 return iterator_base::operator !=(i);
249             }
250         };
251         //@endcond
252
253     public:
254         /// Forward iterator
255         typedef iterator_type<false>    iterator;
256
257         /// Const forward iterator
258         /**
259             For iterator's features and requirements see \ref iterator
260         */
261         typedef iterator_type<true>     const_iterator;
262
263         /// Returns a forward iterator addressing the first element in a list
264         /**
265             For empty list \code begin() == end() \endcode
266         */
267         iterator begin()
268         {
269             iterator it( head() );
270             ++it        ;   // skip dummy head node
271             return it;
272         }
273
274         /// Returns an iterator that addresses the location succeeding the last element in a list
275         /**
276             Do not use the value returned by <tt>end</tt> function to access any item.
277
278             The returned value can be used only to control reaching the end of the list.
279             For empty list \code begin() == end() \endcode
280         */
281         iterator end()
282         {
283             return iterator( tail() );
284         }
285
286         /// Returns a forward const iterator addressing the first element in a list
287         //@{
288         const_iterator begin() const
289         {
290             const_iterator it( head() );
291             ++it        ;   // skip dummy head node
292             return it;
293         }
294         const_iterator cbegin() const
295         {
296             const_iterator it( head() );
297             ++it        ;   // skip dummy head node
298             return it;
299         }
300         //@}
301
302         /// Returns an const iterator that addresses the location succeeding the last element in a list
303         //@{
304         const_iterator end() const
305         {
306             return const_iterator( tail() );
307         }
308         const_iterator cend() const
309         {
310             return const_iterator( tail() );
311         }
312         //@}
313
314     public:
315         /// Default constructor
316         LazyList()
317         {}
318
319         /// Desctructor clears the list
320         ~LazyList()
321         {
322             clear();
323         }
324
325         /// Inserts new node
326         /**
327             The function creates a node with copy of \p val value
328             and then inserts the node created into the list.
329
330             The type \p Q should contain as minimum the complete key of the node.
331             The object of \p value_type should be constructible from \p val of type \p Q.
332             In trivial case, \p Q is equal to \p value_type.
333
334             The function makes RCU lock internally.
335
336             Returns \p true if inserting successful, \p false otherwise.
337         */
338         template <typename Q>
339         bool insert( Q const& val )
340         {
341             return insert_at( head(), val );
342         }
343
344         /// Inserts new node
345         /**
346             This function inserts new node with default-constructed value and then it calls
347             \p func functor with signature
348             \code void func( value_type& itemValue ) ;\endcode
349
350             The argument \p itemValue of user-defined functor \p func is the reference
351             to the list's item inserted.
352             The user-defined functor is called only if the inserting is success.
353
354             The type \p Q should contain the complete key of the node.
355             The object of \ref value_type should be constructible from \p key of type \p Q.
356
357             The function allows to split creating of new item into two part:
358             - create item from \p key with initializing key-fields only;
359             - insert new item into the list;
360             - if inserting is successful, initialize non-key fields of item by calling \p f functor
361
362             This can be useful if complete initialization of object of \p value_type is heavyweight and
363             it is preferable that the initialization should be completed only if inserting is successful.
364
365             The function makes RCU lock internally.
366         */
367         template <typename Q, typename Func>
368         bool insert( Q const& key, Func func )
369         {
370             return insert_at( head(), key, func );
371         }
372
373         /// Inserts data of type \p value_type constructed from \p args
374         /**
375             Returns \p true if inserting successful, \p false otherwise.
376
377             The function makes RCU lock internally.
378         */
379         template <typename... Args>
380         bool emplace( Args&&... args )
381         {
382             return emplace_at( head(), std::forward<Args>(args)... );
383         }
384
385         /// Ensures that the \p key exists in the list
386         /**
387             The operation performs inserting or changing data with lock-free manner.
388
389             If the \p key not found in the list, then the new item created from \p key
390             is inserted into the list. Otherwise, the functor \p func is called with the item found.
391             The functor \p Func signature is:
392             \code
393                 struct my_functor {
394                     void operator()( bool bNew, value_type& item, Q const& val );
395                 };
396             \endcode
397
398             with arguments:
399             - \p bNew - \p true if the item has been inserted, \p false otherwise
400             - \p item - item of the list
401             - \p val - argument \p key passed into the \p ensure function
402
403             The functor may change non-key fields of the \p item.
404
405             The function applies RCU lock internally.
406
407             Returns <tt> std::pair<bool, bool> </tt> where \p first is true if operation is successfull,
408             \p second is true if new item has been added or \p false if the item with \p key
409             already is in the list.
410
411             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
412         */
413         template <typename Q, typename Func>
414         std::pair<bool, bool> ensure( Q const& key, Func f )
415         {
416             return ensure_at( head(), key, f );
417         }
418
419         /// Deletes \p key from the list
420         /** \anchor cds_nonintrusive_LazyList_rcu_erase
421             Since the key of LazyList's item type \p T is not explicitly specified,
422             template parameter \p Q defines the key type searching in the list.
423             The list item comparator should be able to compare the type \p T of list item
424             and the type \p Q.
425
426             RCU \p synchronize method can be called. RCU should not be locked.
427
428             Return \p true if key is found and deleted, \p false otherwise
429         */
430         template <typename Q>
431         bool erase( Q const& key )
432         {
433             return erase_at( head(), key, intrusive_key_comparator(), [](value_type const&){} );
434         }
435
436         /// Deletes the item from the list using \p pred predicate for searching
437         /**
438             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_erase "erase(Q const&)"
439             but \p pred is used for key comparing.
440             \p Less functor has the interface like \p std::less.
441             \p pred must imply the same element order as the comparator used for building the list.
442         */
443         template <typename Q, typename Less>
444         bool erase_with( Q const& key, Less pred )
445         {
446             CDS_UNUSED( pred );
447             return erase_at( head(), key, typename maker::template less_wrapper<Less>::type(), [](value_type const&){} );
448         }
449
450         /// Deletes \p key from the list
451         /** \anchor cds_nonintrusive_LazyList_rcu_erase_func
452             The function searches an item with key \p key, calls \p f functor
453             and deletes the item. If \p key is not found, the functor is not called.
454
455             The functor \p Func interface:
456             \code
457             struct extractor {
458                 void operator()(value_type const& val) { ... }
459             };
460             \endcode
461
462             Since the key of LazyList's item type \p T is not explicitly specified,
463             template parameter \p Q defines the key type searching in the list.
464             The list item comparator should be able to compare the type \p T of list item
465             and the type \p Q.
466
467             RCU \p synchronize method can be called. RCU should not be locked.
468
469             Return \p true if key is found and deleted, \p false otherwise
470         */
471         template <typename Q, typename Func>
472         bool erase( Q const& key, Func f )
473         {
474             return erase_at( head(), key, intrusive_key_comparator(), f );
475         }
476
477         /// Deletes the item from the list using \p pred predicate for searching
478         /**
479             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_erase_func "erase(Q const&, Func)"
480             but \p pred is used for key comparing.
481             \p Less functor has the interface like \p std::less.
482             \p pred must imply the same element order as the comparator used for building the list.
483         */
484         template <typename Q, typename Less, typename Func>
485         bool erase_with( Q const& key, Less pred, Func f )
486         {
487             CDS_UNUSED( pred );
488             return erase_at( head(), key, typename maker::template less_wrapper<Less>::type(), f );
489         }
490
491         /// Extracts an item from the list
492         /**
493         @anchor cds_nonintrusive_LazyList_rcu_extract
494             The function searches an item with key equal to \p key in the list,
495             unlinks it from the list, and returns \ref cds::urcu::exempt_ptr "exempt_ptr" pointer to an item found.
496             If the item with the key equal to \p key is not found the function returns an empty \p exempt_ptr.
497
498             @note The function does NOT call RCU read-side lock or synchronization,
499             and does NOT dispose the item found. It just excludes the item from the list
500             and returns a pointer to item found.
501             You should lock RCU before calling this function.
502
503             \code
504             #include <cds/urcu/general_buffered.h>
505             #include <cds/container/lazy_list_rcu.h>
506
507             typedef cds::urcu::gc< general_buffered<> > rcu;
508             typedef cds::container::LazyList< rcu, Foo > rcu_lazy_list;
509
510             rcu_lazy_list theList;
511             // ...
512
513             rcu_lazy_list::exempt_ptr p;
514             {
515                 // first, we should lock RCU
516                 rcu_lazy_list::rcu_lock sl;
517
518                 // Now, you can apply extract function
519                 // Note that you must not delete the item found inside the RCU lock
520                 p = theList.extract( 10 );
521                 if ( p ) {
522                     // do something with p
523                     ...
524                 }
525             }
526             // Outside RCU lock section we may safely release extracted pointer.
527             // release() passes the pointer to RCU reclamation cycle.
528             p.release();
529             \endcode
530         */
531         template <typename Q>
532         exempt_ptr extract( Q const& key )
533         {
534             return exempt_ptr(extract_at( head(), key, intrusive_key_comparator()));
535         }
536
537         /// Extracts an item from the list using \p pred predicate for searching
538         /**
539             This function is the analog for \ref cds_nonintrusive_LazyList_rcu_extract "extract(exempt_ptr&, Q const&)".
540
541             The \p pred is a predicate used for key comparing.
542             \p Less has the interface like \p std::less.
543             \p pred must imply the same element order as \ref key_comparator.
544         */
545         template <typename Q, typename Less>
546         exempt_ptr extract_with( Q const& key, Less pred )
547         {
548             CDS_UNUSED( pred );
549             return exempt_ptr( extract_at( head(), key, typename maker::template less_wrapper<Less>::type()));
550         }
551
552         /// Finds the key \p key
553         /** \anchor cds_nonintrusive_LazyList_rcu_find_val
554             The function searches the item with key equal to \p key
555             and returns \p true if it is found, and \p false otherwise.
556
557             The function makes RCU lock internally.
558         */
559         template <typename Q>
560         bool find( Q const& key ) const
561         {
562             return find_at( head(), key, intrusive_key_comparator() );
563         }
564
565         /// Finds the key \p key using \p pred predicate for searching
566         /**
567             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_find_val "find(Q const&)"
568             but \p pred is used for key comparing.
569             \p Less functor has the interface like \p std::less.
570             \p pred must imply the same element order as the comparator used for building the list.
571         */
572         template <typename Q, typename Less>
573         bool find_with( Q const& key, Less pred ) const
574         {
575             CDS_UNUSED( pred );
576             return find_at( head(), key, typename maker::template less_wrapper<Less>::type() );
577         }
578
579         /// Finds the key \p key and performs an action with it
580         /** \anchor cds_nonintrusive_LazyList_rcu_find_func
581             The function searches an item with key equal to \p key and calls the functor \p f for the item found.
582             The interface of \p Func functor is:
583             \code
584             struct functor {
585                 void operator()( value_type& item, Q& key );
586             };
587             \endcode
588             where \p item is the item found, \p key is the \p find() function argument.
589
590             The functor may change non-key fields of \p item. Note that the function is only guarantee
591             that \p item cannot be deleted during functor is executing.
592             The function does not serialize simultaneous access to the list \p item. If such access is
593             possible you must provide your own synchronization schema to exclude unsafe item modifications.
594
595             The \p key argument is non-const since it can be used as \p f functor destination i.e., the functor
596             may modify both arguments.
597
598             The function makes RCU lock internally.
599
600             The function returns \p true if \p key is found, \p false otherwise.
601         */
602         template <typename Q, typename Func>
603         bool find( Q& key, Func f ) const
604         {
605             return find_at( head(), key, intrusive_key_comparator(), f );
606         }
607         //@cond
608         template <typename Q, typename Func>
609         bool find( Q const& key, Func f ) const
610         {
611             return find_at( head(), key, intrusive_key_comparator(), f );
612         }
613         //@endcond
614
615         /// Finds the key \p key using \p pred predicate for searching
616         /**
617             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_find_func "find(Q&, Func)"
618             but \p pred is used for key comparing.
619             \p Less functor has the interface like \p std::less.
620             \p pred must imply the same element order as the comparator used for building the list.
621         */
622         template <typename Q, typename Less, typename Func>
623         bool find_with( Q& key, Less pred, Func f ) const
624         {
625             CDS_UNUSED( pred );
626             return find_at( head(), key, typename maker::template less_wrapper<Less>::type(), f );
627         }
628         //@cond
629         template <typename Q, typename Less, typename Func>
630         bool find_with( Q const& key, Less pred, Func f ) const
631         {
632             CDS_UNUSED( pred );
633             return find_at( head(), key, typename maker::template less_wrapper<Less>::type(), f );
634         }
635         //@endcond
636
637         /// Finds the key \p key and return the item found
638         /** \anchor cds_nonintrusive_LazyList_rcu_get
639             The function searches the item with key equal to \p key and returns the pointer to item found.
640             If \p key is not found it returns \p nullptr.
641
642             Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
643
644             RCU should be locked before call of this function.
645             Returned item is valid only while RCU is locked:
646             \code
647             typedef cds::container::LazyList< cds::urcu::gc< cds::urcu::general_buffered<> >, foo, my_traits > ord_list;
648             ord_list theList;
649             // ...
650             {
651                 // Lock RCU
652                 ord_list::rcu_lock lock;
653
654                 foo * pVal = theList.get( 5 );
655                 if ( pVal ) {
656                     // Deal with pVal
657                     //...
658                 }
659                 // Unlock RCU by rcu_lock destructor
660                 // pVal can be freed at any time after RCU has been unlocked
661             }
662             \endcode
663         */
664         template <typename Q>
665         value_type * get( Q const& key ) const
666         {
667             return get_at( head(), key, intrusive_key_comparator());
668         }
669
670         /// Finds the key \p key and return the item found
671         /**
672             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_get "get(Q const&)"
673             but \p pred is used for comparing the keys.
674
675             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
676             in any order.
677             \p pred must imply the same element order as the comparator used for building the list.
678         */
679         template <typename Q, typename Less>
680         value_type * get_with( Q const& key, Less pred ) const
681         {
682             CDS_UNUSED( pred );
683             return get_at( head(), key, typename maker::template less_wrapper<Less>::type());
684         }
685
686         /// Checks if the list is empty
687         bool empty() const
688         {
689             return base_class::empty();
690         }
691
692         /// Returns list's item count
693         /**
694             The value returned depends on \p Traits::item_counter type. For \p atomicity::empty_item_counter,
695             this function always returns 0.
696
697             @note Even if you use real item counter and it returns 0, this fact is not mean that the list
698             is empty. To check list emptyness use \ref empty() method.
699         */
700         size_t size() const
701         {
702             return base_class::size();
703         }
704
705         /// Clears the list
706         void clear()
707         {
708             base_class::clear();
709         }
710
711     protected:
712         //@cond
713         bool insert_node_at( head_type& refHead, node_type * pNode )
714         {
715             assert( pNode != nullptr );
716             scoped_node_ptr p( pNode );
717
718             if ( base_class::insert_at( &refHead, *pNode )) {
719                 p.release();
720                 return true;
721             }
722
723             return false;
724         }
725
726         template <typename Q>
727         bool insert_at( head_type& refHead, Q const& val )
728         {
729             return insert_node_at( refHead, alloc_node( val ));
730         }
731
732         template <typename... Args>
733         bool emplace_at( head_type& refHead, Args&&... args )
734         {
735             return insert_node_at( refHead, alloc_node( std::forward<Args>(args)... ));
736         }
737
738         template <typename Q, typename Func>
739         bool insert_at( head_type& refHead, Q const& key, Func f )
740         {
741             scoped_node_ptr pNode( alloc_node( key ));
742
743             if ( base_class::insert_at( &refHead, *pNode, [&f](node_type& node){ f( node_to_value(node) ); } )) {
744                 pNode.release();
745                 return true;
746             }
747             return false;
748         }
749
750         template <typename Q, typename Compare, typename Func>
751         bool erase_at( head_type& refHead, Q const& key, Compare cmp, Func f )
752         {
753             return base_class::erase_at( &refHead, key, cmp, [&f](node_type const& node){ f( node_to_value(node) ); } );
754         }
755
756         template <typename Q, typename Compare>
757         node_type * extract_at( head_type& refHead, Q const& key, Compare cmp )
758         {
759             return base_class::extract_at( &refHead, key, cmp );
760         }
761
762         template <typename Q, typename Func>
763         std::pair<bool, bool> ensure_at( head_type& refHead, Q const& key, Func f )
764         {
765             scoped_node_ptr pNode( alloc_node( key ));
766
767             std::pair<bool, bool> ret = base_class::ensure_at( &refHead, *pNode,
768                 [&f, &key](bool bNew, node_type& node, node_type&){f( bNew, node_to_value(node), key ); });
769             if ( ret.first && ret.second )
770                 pNode.release();
771
772             return ret;
773         }
774
775         template <typename Q, typename Compare>
776         bool find_at( head_type& refHead, Q const& key, Compare cmp ) const
777         {
778             return base_class::find_at( &refHead, key, cmp, [](node_type&, Q const &) {} );
779         }
780
781         template <typename Q, typename Compare, typename Func>
782         bool find_at( head_type& refHead, Q& val, Compare cmp, Func f ) const
783         {
784             return base_class::find_at( &refHead, val, cmp, [&f](node_type& node, Q& val){ f( node_to_value(node), val ); });
785         }
786
787         template <typename Q, typename Compare>
788         value_type * get_at( head_type& refHead, Q const& val, Compare cmp ) const
789         {
790             node_type * pNode = base_class::get_at( &refHead, val, cmp );
791             return pNode ? &pNode->m_Value : nullptr;
792         }
793
794         //@endcond
795     };
796
797 }} // namespace cds::container
798
799 #endif // #ifndef __CDS_CONTAINER_LAZY_LIST_RCU_H