Renamed get_result typedef to raw_ptr
[libcds.git] / cds / container / lazy_list_rcu.h
1 //$$CDS-header$$
2
3 #ifndef CDSLIB_CONTAINER_LAZY_LIST_RCU_H
4 #define CDSLIB_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         /// Type of \p get() member function return value
134         typedef value_type * raw_ptr;
135
136     private:
137         //@cond
138         static value_type& node_to_value( node_type& n )
139         {
140             return n.m_Value;
141         }
142         static value_type const& node_to_value( node_type const& n )
143         {
144             return n.m_Value;
145         }
146         //@endcond
147
148     protected:
149         //@cond
150         template <typename Q>
151         static node_type * alloc_node( Q const& v )
152         {
153             return cxx_allocator().New( v );
154         }
155
156         template <typename... Args>
157         static node_type * alloc_node( Args&&... args )
158         {
159             return cxx_allocator().MoveNew( std::forward<Args>(args)... );
160         }
161
162         static void free_node( node_type * pNode )
163         {
164             cxx_allocator().Delete( pNode );
165         }
166
167         struct node_disposer {
168             void operator()( node_type * pNode )
169             {
170                 free_node( pNode );
171             }
172         };
173         typedef std::unique_ptr< node_type, node_disposer >     scoped_node_ptr;
174
175         head_type& head()
176         {
177             return base_class::m_Head;
178         }
179
180         head_type& head() const
181         {
182             return const_cast<head_type&>( base_class::m_Head );
183         }
184
185         head_type& tail()
186         {
187             return base_class::m_Tail;
188         }
189
190         head_type const&  tail() const
191         {
192             return base_class::m_Tail;
193         }
194         //@endcond
195
196     protected:
197                 //@cond
198         template <bool IsConst>
199         class iterator_type: protected base_class::template iterator_type<IsConst>
200         {
201             typedef typename base_class::template iterator_type<IsConst>    iterator_base;
202
203             iterator_type( head_type const& pNode )
204                 : iterator_base( const_cast<head_type *>( &pNode ))
205             {}
206
207             iterator_type( head_type const * pNode )
208                 : iterator_base( const_cast<head_type *>( pNode ))
209             {}
210
211             friend class LazyList;
212
213         public:
214             typedef typename cds::details::make_const_type<value_type, IsConst>::pointer   value_ptr;
215             typedef typename cds::details::make_const_type<value_type, IsConst>::reference value_ref;
216
217             iterator_type()
218             {}
219
220             iterator_type( iterator_type const& src )
221                 : iterator_base( src )
222             {}
223
224             value_ptr operator ->() const
225             {
226                 typename iterator_base::value_ptr p = iterator_base::operator ->();
227                 return p ? &(p->m_Value) : nullptr;
228             }
229
230             value_ref operator *() const
231             {
232                 return (iterator_base::operator *()).m_Value;
233             }
234
235             /// Pre-increment
236             iterator_type& operator ++()
237             {
238                 iterator_base::operator ++();
239                 return *this;
240             }
241
242             template <bool C>
243             bool operator ==(iterator_type<C> const& i ) const
244             {
245                 return iterator_base::operator ==(i);
246             }
247             template <bool C>
248             bool operator !=(iterator_type<C> const& i ) const
249             {
250                 return iterator_base::operator !=(i);
251             }
252         };
253         //@endcond
254
255     public:
256         /// Forward iterator
257         typedef iterator_type<false>    iterator;
258
259         /// Const forward iterator
260         /**
261             For iterator's features and requirements see \ref iterator
262         */
263         typedef iterator_type<true>     const_iterator;
264
265         /// Returns a forward iterator addressing the first element in a list
266         /**
267             For empty list \code begin() == end() \endcode
268         */
269         iterator begin()
270         {
271             iterator it( head() );
272             ++it        ;   // skip dummy head node
273             return it;
274         }
275
276         /// Returns an iterator that addresses the location succeeding the last element in a list
277         /**
278             Do not use the value returned by <tt>end</tt> function to access any item.
279
280             The returned value can be used only to control reaching the end of the list.
281             For empty list \code begin() == end() \endcode
282         */
283         iterator end()
284         {
285             return iterator( tail() );
286         }
287
288         /// Returns a forward const iterator addressing the first element in a list
289         //@{
290         const_iterator begin() const
291         {
292             const_iterator it( head() );
293             ++it        ;   // skip dummy head node
294             return it;
295         }
296         const_iterator cbegin() const
297         {
298             const_iterator it( head() );
299             ++it        ;   // skip dummy head node
300             return it;
301         }
302         //@}
303
304         /// Returns an const iterator that addresses the location succeeding the last element in a list
305         //@{
306         const_iterator end() const
307         {
308             return const_iterator( tail() );
309         }
310         const_iterator cend() const
311         {
312             return const_iterator( tail() );
313         }
314         //@}
315
316     public:
317         /// Default constructor
318         LazyList()
319         {}
320
321         /// Desctructor clears the list
322         ~LazyList()
323         {
324             clear();
325         }
326
327         /// Inserts new node
328         /**
329             The function creates a node with copy of \p val value
330             and then inserts the node created into the list.
331
332             The type \p Q should contain as minimum the complete key of the node.
333             The object of \p value_type should be constructible from \p val of type \p Q.
334             In trivial case, \p Q is equal to \p value_type.
335
336             The function makes RCU lock internally.
337
338             Returns \p true if inserting successful, \p false otherwise.
339         */
340         template <typename Q>
341         bool insert( Q const& val )
342         {
343             return insert_at( head(), val );
344         }
345
346         /// Inserts new node
347         /**
348             This function inserts new node with default-constructed value and then it calls
349             \p func functor with signature
350             \code void func( value_type& itemValue ) ;\endcode
351
352             The argument \p itemValue of user-defined functor \p func is the reference
353             to the list's item inserted.
354             The user-defined functor is called only if the inserting is success.
355
356             The type \p Q should contain the complete key of the node.
357             The object of \ref value_type should be constructible from \p key of type \p Q.
358
359             The function allows to split creating of new item into two part:
360             - create item from \p key with initializing key-fields only;
361             - insert new item into the list;
362             - if inserting is successful, initialize non-key fields of item by calling \p f functor
363
364             This can be useful if complete initialization of object of \p value_type is heavyweight and
365             it is preferable that the initialization should be completed only if inserting is successful.
366
367             The function makes RCU lock internally.
368         */
369         template <typename Q, typename Func>
370         bool insert( Q const& key, Func func )
371         {
372             return insert_at( head(), key, func );
373         }
374
375         /// Inserts data of type \p value_type constructed from \p args
376         /**
377             Returns \p true if inserting successful, \p false otherwise.
378
379             The function makes RCU lock internally.
380         */
381         template <typename... Args>
382         bool emplace( Args&&... args )
383         {
384             return emplace_at( head(), std::forward<Args>(args)... );
385         }
386
387         /// Ensures that the \p key exists in the list
388         /**
389             The operation performs inserting or changing data with lock-free manner.
390
391             If the \p key not found in the list, then the new item created from \p key
392             is inserted into the list. Otherwise, the functor \p func is called with the item found.
393             The functor \p Func signature is:
394             \code
395                 struct my_functor {
396                     void operator()( bool bNew, value_type& item, Q const& val );
397                 };
398             \endcode
399
400             with arguments:
401             - \p bNew - \p true if the item has been inserted, \p false otherwise
402             - \p item - item of the list
403             - \p val - argument \p key passed into the \p ensure function
404
405             The functor may change non-key fields of the \p item.
406
407             The function applies RCU lock internally.
408
409             Returns <tt> std::pair<bool, bool> </tt> where \p first is true if operation is successfull,
410             \p second is true if new item has been added or \p false if the item with \p key
411             already is in the list.
412
413             @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
414         */
415         template <typename Q, typename Func>
416         std::pair<bool, bool> ensure( Q const& key, Func f )
417         {
418             return ensure_at( head(), key, f );
419         }
420
421         /// Deletes \p key from the list
422         /** \anchor cds_nonintrusive_LazyList_rcu_erase
423             Since the key of LazyList's item type \p T is not explicitly specified,
424             template parameter \p Q defines the key type searching in the list.
425             The list item comparator should be able to compare the type \p T of list item
426             and the type \p Q.
427
428             RCU \p synchronize method can be called. RCU should not be locked.
429
430             Return \p true if key is found and deleted, \p false otherwise
431         */
432         template <typename Q>
433         bool erase( Q const& key )
434         {
435             return erase_at( head(), key, intrusive_key_comparator(), [](value_type const&){} );
436         }
437
438         /// Deletes the item from the list using \p pred predicate for searching
439         /**
440             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_erase "erase(Q const&)"
441             but \p pred is used for key comparing.
442             \p Less functor has the interface like \p std::less.
443             \p pred must imply the same element order as the comparator used for building the list.
444         */
445         template <typename Q, typename Less>
446         bool erase_with( Q const& key, Less pred )
447         {
448             CDS_UNUSED( pred );
449             return erase_at( head(), key, typename maker::template less_wrapper<Less>::type(), [](value_type const&){} );
450         }
451
452         /// Deletes \p key from the list
453         /** \anchor cds_nonintrusive_LazyList_rcu_erase_func
454             The function searches an item with key \p key, calls \p f functor
455             and deletes the item. If \p key is not found, the functor is not called.
456
457             The functor \p Func interface:
458             \code
459             struct extractor {
460                 void operator()(value_type const& val) { ... }
461             };
462             \endcode
463
464             Since the key of LazyList's item type \p T is not explicitly specified,
465             template parameter \p Q defines the key type searching in the list.
466             The list item comparator should be able to compare the type \p T of list item
467             and the type \p Q.
468
469             RCU \p synchronize method can be called. RCU should not be locked.
470
471             Return \p true if key is found and deleted, \p false otherwise
472         */
473         template <typename Q, typename Func>
474         bool erase( Q const& key, Func f )
475         {
476             return erase_at( head(), key, intrusive_key_comparator(), f );
477         }
478
479         /// Deletes the item from the list using \p pred predicate for searching
480         /**
481             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_erase_func "erase(Q const&, Func)"
482             but \p pred is used for key comparing.
483             \p Less functor has the interface like \p std::less.
484             \p pred must imply the same element order as the comparator used for building the list.
485         */
486         template <typename Q, typename Less, typename Func>
487         bool erase_with( Q const& key, Less pred, Func f )
488         {
489             CDS_UNUSED( pred );
490             return erase_at( head(), key, typename maker::template less_wrapper<Less>::type(), f );
491         }
492
493         /// Extracts an item from the list
494         /**
495         @anchor cds_nonintrusive_LazyList_rcu_extract
496             The function searches an item with key equal to \p key in the list,
497             unlinks it from the list, and returns \ref cds::urcu::exempt_ptr "exempt_ptr" pointer to an item found.
498             If the item with the key equal to \p key is not found the function returns an empty \p exempt_ptr.
499
500             @note The function does NOT call RCU read-side lock or synchronization,
501             and does NOT dispose the item found. It just excludes the item from the list
502             and returns a pointer to item found.
503             You should lock RCU before calling this function.
504
505             \code
506             #include <cds/urcu/general_buffered.h>
507             #include <cds/container/lazy_list_rcu.h>
508
509             typedef cds::urcu::gc< general_buffered<> > rcu;
510             typedef cds::container::LazyList< rcu, Foo > rcu_lazy_list;
511
512             rcu_lazy_list theList;
513             // ...
514
515             rcu_lazy_list::exempt_ptr p;
516             {
517                 // first, we should lock RCU
518                 rcu_lazy_list::rcu_lock sl;
519
520                 // Now, you can apply extract function
521                 // Note that you must not delete the item found inside the RCU lock
522                 p = theList.extract( 10 );
523                 if ( p ) {
524                     // do something with p
525                     ...
526                 }
527             }
528             // Outside RCU lock section we may safely release extracted pointer.
529             // release() passes the pointer to RCU reclamation cycle.
530             p.release();
531             \endcode
532         */
533         template <typename Q>
534         exempt_ptr extract( Q const& key )
535         {
536             return exempt_ptr(extract_at( head(), key, intrusive_key_comparator()));
537         }
538
539         /// Extracts an item from the list using \p pred predicate for searching
540         /**
541             This function is the analog for \p extract(Q const&).
542
543             The \p pred is a predicate used for key comparing.
544             \p Less has the interface like \p std::less.
545             \p pred must imply the same element order as \ref key_comparator.
546         */
547         template <typename Q, typename Less>
548         exempt_ptr extract_with( Q const& key, Less pred )
549         {
550             CDS_UNUSED( pred );
551             return exempt_ptr( extract_at( head(), key, typename maker::template less_wrapper<Less>::type()));
552         }
553
554         /// Finds the key \p key
555         /** \anchor cds_nonintrusive_LazyList_rcu_find_val
556             The function searches the item with key equal to \p key
557             and returns \p true if it is found, and \p false otherwise.
558
559             The function makes RCU lock internally.
560         */
561         template <typename Q>
562         bool find( Q const& key ) const
563         {
564             return find_at( head(), key, intrusive_key_comparator() );
565         }
566
567         /// Finds the key \p key using \p pred predicate for searching
568         /**
569             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_find_val "find(Q const&)"
570             but \p pred is used for key comparing.
571             \p Less functor has the interface like \p std::less.
572             \p pred must imply the same element order as the comparator used for building the list.
573         */
574         template <typename Q, typename Less>
575         bool find_with( Q const& key, Less pred ) const
576         {
577             CDS_UNUSED( pred );
578             return find_at( head(), key, typename maker::template less_wrapper<Less>::type() );
579         }
580
581         /// Finds the key \p key and performs an action with it
582         /** \anchor cds_nonintrusive_LazyList_rcu_find_func
583             The function searches an item with key equal to \p key and calls the functor \p f for the item found.
584             The interface of \p Func functor is:
585             \code
586             struct functor {
587                 void operator()( value_type& item, Q& key );
588             };
589             \endcode
590             where \p item is the item found, \p key is the \p find() function argument.
591
592             The functor may change non-key fields of \p item. Note that the function is only guarantee
593             that \p item cannot be deleted during functor is executing.
594             The function does not serialize simultaneous access to the list \p item. If such access is
595             possible you must provide your own synchronization schema to exclude unsafe item modifications.
596
597             The \p key argument is non-const since it can be used as \p f functor destination i.e., the functor
598             may modify both arguments.
599
600             The function makes RCU lock internally.
601
602             The function returns \p true if \p key is found, \p false otherwise.
603         */
604         template <typename Q, typename Func>
605         bool find( Q& key, Func f ) const
606         {
607             return find_at( head(), key, intrusive_key_comparator(), f );
608         }
609         //@cond
610         template <typename Q, typename Func>
611         bool find( Q const& key, Func f ) const
612         {
613             return find_at( head(), key, intrusive_key_comparator(), f );
614         }
615         //@endcond
616
617         /// Finds the key \p key using \p pred predicate for searching
618         /**
619             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_find_func "find(Q&, Func)"
620             but \p pred is used for key comparing.
621             \p Less functor has the interface like \p std::less.
622             \p pred must imply the same element order as the comparator used for building the list.
623         */
624         template <typename Q, typename Less, typename Func>
625         bool find_with( Q& key, Less pred, Func f ) const
626         {
627             CDS_UNUSED( pred );
628             return find_at( head(), key, typename maker::template less_wrapper<Less>::type(), f );
629         }
630         //@cond
631         template <typename Q, typename Less, typename Func>
632         bool find_with( Q const& key, Less pred, Func f ) const
633         {
634             CDS_UNUSED( pred );
635             return find_at( head(), key, typename maker::template less_wrapper<Less>::type(), f );
636         }
637         //@endcond
638
639         /// Finds the key \p key and return the item found
640         /** \anchor cds_nonintrusive_LazyList_rcu_get
641             The function searches the item with key equal to \p key and returns the pointer to item found.
642             If \p key is not found it returns \p nullptr.
643
644             Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
645
646             RCU should be locked before call of this function.
647             Returned item is valid only while RCU is locked:
648             \code
649             typedef cds::container::LazyList< cds::urcu::gc< cds::urcu::general_buffered<> >, foo, my_traits > ord_list;
650             ord_list theList;
651             // ...
652             {
653                 // Lock RCU
654                 ord_list::rcu_lock lock;
655
656                 foo * pVal = theList.get( 5 );
657                 if ( pVal ) {
658                     // Deal with pVal
659                     //...
660                 }
661                 // Unlock RCU by rcu_lock destructor
662                 // pVal can be freed at any time after RCU has been unlocked
663             }
664             \endcode
665         */
666         template <typename Q>
667         value_type * get( Q const& key ) const
668         {
669             return get_at( head(), key, intrusive_key_comparator());
670         }
671
672         /// Finds the key \p key and return the item found
673         /**
674             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_get "get(Q const&)"
675             but \p pred is used for comparing the keys.
676
677             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
678             in any order.
679             \p pred must imply the same element order as the comparator used for building the list.
680         */
681         template <typename Q, typename Less>
682         value_type * get_with( Q const& key, Less pred ) const
683         {
684             CDS_UNUSED( pred );
685             return get_at( head(), key, typename maker::template less_wrapper<Less>::type());
686         }
687
688         /// Checks if the list is empty
689         bool empty() const
690         {
691             return base_class::empty();
692         }
693
694         /// Returns list's item count
695         /**
696             The value returned depends on \p Traits::item_counter type. For \p atomicity::empty_item_counter,
697             this function always returns 0.
698
699             @note Even if you use real item counter and it returns 0, this fact is not mean that the list
700             is empty. To check list emptyness use \ref empty() method.
701         */
702         size_t size() const
703         {
704             return base_class::size();
705         }
706
707         /// Clears the list
708         void clear()
709         {
710             base_class::clear();
711         }
712
713     protected:
714         //@cond
715         bool insert_node_at( head_type& refHead, node_type * pNode )
716         {
717             assert( pNode != nullptr );
718             scoped_node_ptr p( pNode );
719
720             if ( base_class::insert_at( &refHead, *pNode )) {
721                 p.release();
722                 return true;
723             }
724
725             return false;
726         }
727
728         template <typename Q>
729         bool insert_at( head_type& refHead, Q const& val )
730         {
731             return insert_node_at( refHead, alloc_node( val ));
732         }
733
734         template <typename... Args>
735         bool emplace_at( head_type& refHead, Args&&... args )
736         {
737             return insert_node_at( refHead, alloc_node( std::forward<Args>(args)... ));
738         }
739
740         template <typename Q, typename Func>
741         bool insert_at( head_type& refHead, Q const& key, Func f )
742         {
743             scoped_node_ptr pNode( alloc_node( key ));
744
745             if ( base_class::insert_at( &refHead, *pNode, [&f](node_type& node){ f( node_to_value(node) ); } )) {
746                 pNode.release();
747                 return true;
748             }
749             return false;
750         }
751
752         template <typename Q, typename Compare, typename Func>
753         bool erase_at( head_type& refHead, Q const& key, Compare cmp, Func f )
754         {
755             return base_class::erase_at( &refHead, key, cmp, [&f](node_type const& node){ f( node_to_value(node) ); } );
756         }
757
758         template <typename Q, typename Compare>
759         node_type * extract_at( head_type& refHead, Q const& key, Compare cmp )
760         {
761             return base_class::extract_at( &refHead, key, cmp );
762         }
763
764         template <typename Q, typename Func>
765         std::pair<bool, bool> ensure_at( head_type& refHead, Q const& key, Func f )
766         {
767             scoped_node_ptr pNode( alloc_node( key ));
768
769             std::pair<bool, bool> ret = base_class::ensure_at( &refHead, *pNode,
770                 [&f, &key](bool bNew, node_type& node, node_type&){f( bNew, node_to_value(node), key ); });
771             if ( ret.first && ret.second )
772                 pNode.release();
773
774             return ret;
775         }
776
777         template <typename Q, typename Compare>
778         bool find_at( head_type& refHead, Q const& key, Compare cmp ) const
779         {
780             return base_class::find_at( &refHead, key, cmp, [](node_type&, Q const &) {} );
781         }
782
783         template <typename Q, typename Compare, typename Func>
784         bool find_at( head_type& refHead, Q& val, Compare cmp, Func f ) const
785         {
786             return base_class::find_at( &refHead, val, cmp, [&f](node_type& node, Q& val){ f( node_to_value(node), val ); });
787         }
788
789         template <typename Q, typename Compare>
790         value_type * get_at( head_type& refHead, Q const& val, Compare cmp ) const
791         {
792             node_type * pNode = base_class::get_at( &refHead, val, cmp );
793             return pNode ? &pNode->m_Value : nullptr;
794         }
795
796         //@endcond
797     };
798
799 }} // namespace cds::container
800
801 #endif // #ifndef CDSLIB_CONTAINER_LAZY_LIST_RCU_H