movable exempt_ptr: LazyList
[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             return erase_at( head(), key, typename maker::template less_wrapper<Less>::type(), [](value_type const&){} );
447         }
448
449         /// Deletes \p key from the list
450         /** \anchor cds_nonintrusive_LazyList_rcu_erase_func
451             The function searches an item with key \p key, calls \p f functor
452             and deletes the item. If \p key is not found, the functor is not called.
453
454             The functor \p Func interface:
455             \code
456             struct extractor {
457                 void operator()(value_type const& val) { ... }
458             };
459             \endcode
460
461             Since the key of LazyList's item type \p T is not explicitly specified,
462             template parameter \p Q defines the key type searching in the list.
463             The list item comparator should be able to compare the type \p T of list item
464             and the type \p Q.
465
466             RCU \p synchronize method can be called. RCU should not be locked.
467
468             Return \p true if key is found and deleted, \p false otherwise
469         */
470         template <typename Q, typename Func>
471         bool erase( Q const& key, Func f )
472         {
473             return erase_at( head(), key, intrusive_key_comparator(), f );
474         }
475
476         /// Deletes the item from the list using \p pred predicate for searching
477         /**
478             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_erase_func "erase(Q const&, Func)"
479             but \p pred is used for key comparing.
480             \p Less functor has the interface like \p std::less.
481             \p pred must imply the same element order as the comparator used for building the list.
482         */
483         template <typename Q, typename Less, typename Func>
484         bool erase_with( Q const& key, Less pred, Func f )
485         {
486             return erase_at( head(), key, typename maker::template less_wrapper<Less>::type(), f );
487         }
488
489         /// Extracts an item from the list
490         /**
491         @anchor cds_nonintrusive_LazyList_rcu_extract
492             The function searches an item with key equal to \p key in the list,
493             unlinks it from the list, and returns \ref cds::urcu::exempt_ptr "exempt_ptr" pointer to an item found.
494             If the item with the key equal to \p key is not found the function returns an empty \p exempt_ptr.
495
496             @note The function does NOT call RCU read-side lock or synchronization,
497             and does NOT dispose the item found. It just excludes the item from the list
498             and returns a pointer to item found.
499             You should lock RCU before calling this function.
500
501             \code
502             #include <cds/urcu/general_buffered.h>
503             #include <cds/container/lazy_list_rcu.h>
504
505             typedef cds::urcu::gc< general_buffered<> > rcu;
506             typedef cds::container::LazyList< rcu, Foo > rcu_lazy_list;
507
508             rcu_lazy_list theList;
509             // ...
510
511             rcu_lazy_list::exempt_ptr p;
512             {
513                 // first, we should lock RCU
514                 rcu_lazy_list::rcu_lock sl;
515
516                 // Now, you can apply extract function
517                 // Note that you must not delete the item found inside the RCU lock
518                 p = theList.extract( 10 );
519                 if ( p ) {
520                     // do something with p
521                     ...
522                 }
523             }
524             // Outside RCU lock section we may safely release extracted pointer.
525             // release() passes the pointer to RCU reclamation cycle.
526             p.release();
527             \endcode
528         */
529         template <typename Q>
530         exempt_ptr extract( Q const& key )
531         {
532             return exempt_ptr(extract_at( head(), key, intrusive_key_comparator()));
533         }
534
535         /// Extracts an item from the list using \p pred predicate for searching
536         /**
537             This function is the analog for \ref cds_nonintrusive_LazyList_rcu_extract "extract(exempt_ptr&, Q const&)".
538
539             The \p pred is a predicate used for key comparing.
540             \p Less has the interface like \p std::less.
541             \p pred must imply the same element order as \ref key_comparator.
542         */
543         template <typename Q, typename Less>
544         exempt_ptr extract_with( Q const& key, Less pred )
545         {
546             return exempt_ptr( extract_at( head(), key, typename maker::template less_wrapper<Less>::type()));
547         }
548
549         /// Finds the key \p key
550         /** \anchor cds_nonintrusive_LazyList_rcu_find_val
551             The function searches the item with key equal to \p key
552             and returns \p true if it is found, and \p false otherwise.
553
554             The function makes RCU lock internally.
555         */
556         template <typename Q>
557         bool find( Q const& key ) const
558         {
559             return find_at( head(), key, intrusive_key_comparator() );
560         }
561
562         /// Finds the key \p key using \p pred predicate for searching
563         /**
564             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_find_val "find(Q const&)"
565             but \p pred is used for key comparing.
566             \p Less functor has the interface like \p std::less.
567             \p pred must imply the same element order as the comparator used for building the list.
568         */
569         template <typename Q, typename Less>
570         bool find_with( Q const& key, Less pred ) const
571         {
572             return find_at( head(), key, typename maker::template less_wrapper<Less>::type() );
573         }
574
575         /// Finds the key \p key and performs an action with it
576         /** \anchor cds_nonintrusive_LazyList_rcu_find_func
577             The function searches an item with key equal to \p key and calls the functor \p f for the item found.
578             The interface of \p Func functor is:
579             \code
580             struct functor {
581                 void operator()( value_type& item, Q& key );
582             };
583             \endcode
584             where \p item is the item found, \p key is the \p find() function argument.
585
586             The functor may change non-key fields of \p item. Note that the function is only guarantee
587             that \p item cannot be deleted during functor is executing.
588             The function does not serialize simultaneous access to the list \p item. If such access is
589             possible you must provide your own synchronization schema to exclude unsafe item modifications.
590
591             The \p key argument is non-const since it can be used as \p f functor destination i.e., the functor
592             may modify both arguments.
593
594             The function makes RCU lock internally.
595
596             The function returns \p true if \p key is found, \p false otherwise.
597         */
598         template <typename Q, typename Func>
599         bool find( Q& key, Func f ) const
600         {
601             return find_at( head(), key, intrusive_key_comparator(), f );
602         }
603
604         /// Finds the key \p key using \p pred predicate for searching
605         /**
606             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_find_func "find(Q&, Func)"
607             but \p pred is used for key comparing.
608             \p Less functor has the interface like \p std::less.
609             \p pred must imply the same element order as the comparator used for building the list.
610         */
611         template <typename Q, typename Less, typename Func>
612         bool find_with( Q& key, Less pred, Func f ) const
613         {
614             return find_at( head(), key, typename maker::template less_wrapper<Less>::type(), f );
615         }
616
617         /// Finds the key \p key and return the item found
618         /** \anchor cds_nonintrusive_LazyList_rcu_get
619             The function searches the item with key equal to \p key and returns the pointer to item found.
620             If \p key is not found it returns \p nullptr.
621
622             Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
623
624             RCU should be locked before call of this function.
625             Returned item is valid only while RCU is locked:
626             \code
627             typedef cds::container::LazyList< cds::urcu::gc< cds::urcu::general_buffered<> >, foo, my_traits > ord_list;
628             ord_list theList;
629             // ...
630             {
631                 // Lock RCU
632                 ord_list::rcu_lock lock;
633
634                 foo * pVal = theList.get( 5 );
635                 if ( pVal ) {
636                     // Deal with pVal
637                     //...
638                 }
639                 // Unlock RCU by rcu_lock destructor
640                 // pVal can be freed at any time after RCU has been unlocked
641             }
642             \endcode
643         */
644         template <typename Q>
645         value_type * get( Q const& key ) const
646         {
647             return get_at( head(), key, intrusive_key_comparator());
648         }
649
650         /// Finds the key \p key and return the item found
651         /**
652             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_get "get(Q const&)"
653             but \p pred is used for comparing the keys.
654
655             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
656             in any order.
657             \p pred must imply the same element order as the comparator used for building the list.
658         */
659         template <typename Q, typename Less>
660         value_type * get_with( Q const& key, Less pred ) const
661         {
662             return get_at( head(), key, typename maker::template less_wrapper<Less>::type());
663         }
664
665         /// Checks if the list is empty
666         bool empty() const
667         {
668             return base_class::empty();
669         }
670
671         /// Returns list's item count
672         /**
673             The value returned depends on \p Traits::item_counter type. For \p atomicity::empty_item_counter,
674             this function always returns 0.
675
676             @note Even if you use real item counter and it returns 0, this fact is not mean that the list
677             is empty. To check list emptyness use \ref empty() method.
678         */
679         size_t size() const
680         {
681             return base_class::size();
682         }
683
684         /// Clears the list
685         void clear()
686         {
687             base_class::clear();
688         }
689
690     protected:
691         //@cond
692         bool insert_node_at( head_type& refHead, node_type * pNode )
693         {
694             assert( pNode != nullptr );
695             scoped_node_ptr p( pNode );
696
697             if ( base_class::insert_at( &refHead, *pNode )) {
698                 p.release();
699                 return true;
700             }
701
702             return false;
703         }
704
705         template <typename Q>
706         bool insert_at( head_type& refHead, Q const& val )
707         {
708             return insert_node_at( refHead, alloc_node( val ));
709         }
710
711         template <typename... Args>
712         bool emplace_at( head_type& refHead, Args&&... args )
713         {
714             return insert_node_at( refHead, alloc_node( std::forward<Args>(args)... ));
715         }
716
717         template <typename Q, typename Func>
718         bool insert_at( head_type& refHead, Q const& key, Func f )
719         {
720             scoped_node_ptr pNode( alloc_node( key ));
721
722             if ( base_class::insert_at( &refHead, *pNode, [&f](node_type& node){ f( node_to_value(node) ); } )) {
723                 pNode.release();
724                 return true;
725             }
726             return false;
727         }
728
729         template <typename Q, typename Compare, typename Func>
730         bool erase_at( head_type& refHead, Q const& key, Compare cmp, Func f )
731         {
732             return base_class::erase_at( &refHead, key, cmp, [&f](node_type const& node){ f( node_to_value(node) ); } );
733         }
734
735         template <typename Q, typename Compare>
736         node_type * extract_at( head_type& refHead, Q const& key, Compare cmp )
737         {
738             return base_class::extract_at( &refHead, key, cmp );
739         }
740
741         template <typename Q, typename Func>
742         std::pair<bool, bool> ensure_at( head_type& refHead, Q const& key, Func f )
743         {
744             scoped_node_ptr pNode( alloc_node( key ));
745
746             std::pair<bool, bool> ret = base_class::ensure_at( &refHead, *pNode,
747                 [&f, &key](bool bNew, node_type& node, node_type&){f( bNew, node_to_value(node), key ); });
748             if ( ret.first && ret.second )
749                 pNode.release();
750
751             return ret;
752         }
753
754         template <typename Q, typename Compare>
755         bool find_at( head_type& refHead, Q const& key, Compare cmp ) const
756         {
757             return base_class::find_at( &refHead, key, cmp, [](node_type&, Q const &) {} );
758         }
759
760         template <typename Q, typename Compare, typename Func>
761         bool find_at( head_type& refHead, Q& val, Compare cmp, Func f ) const
762         {
763             return base_class::find_at( &refHead, val, cmp, [&f](node_type& node, Q& val){ f( node_to_value(node), val ); });
764         }
765
766         template <typename Q, typename Compare>
767         value_type * get_at( head_type& refHead, Q const& val, Compare cmp ) const
768         {
769             node_type * pNode = base_class::get_at( &refHead, val, cmp );
770             return pNode ? &pNode->m_Value : nullptr;
771         }
772
773         //@endcond
774     };
775
776 }} // namespace cds::container
777
778 #endif // #ifndef __CDS_CONTAINER_LAZY_LIST_RCU_H