Fixed iterator issues in set/map
[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         typedef cds::urcu::exempt_ptr< gc, node_type, value_type, typename maker::intrusive_traits::disposer > exempt_ptr; ///< 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             The functor may be passed by reference with <tt>boost:ref</tt>
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             return erase_at( head(), key, typename maker::template less_wrapper<Less>::type(), f );
488         }
489
490         /// Extracts an item from the list
491         /**
492         @anchor cds_nonintrusive_LazyList_rcu_extract
493             The function searches an item with key equal to \p key in the list,
494             unlinks it from the list, and returns pointer to an item found in \p dest argument.
495             If the item with the key equal to \p key is not found the function returns \p false.
496
497             @note The function does NOT call RCU read-side lock or synchronization,
498             and does NOT dispose the item found. It just excludes the item from the list
499             and returns a pointer to item found.
500             You should lock RCU before calling this function.
501
502             \code
503             #include <cds/urcu/general_buffered.h>
504             #include <cds/container/lazy_list_rcu.h>
505
506             typedef cds::urcu::gc< general_buffered<> > rcu;
507             typedef cds::container::LazyList< rcu, Foo > rcu_lazy_list;
508
509             rcu_lazy_list theList;
510             // ...
511
512             rcu_lazy_list::exempt_ptr p;
513             {
514                 // first, we should lock RCU
515                 rcu_lazy_list::rcu_lock sl;
516
517                 // Now, you can apply extract function
518                 // Note that you must not delete the item found inside the RCU lock
519                 if ( theList.extract( p, 10 )) {
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         bool extract( exempt_ptr& dest, Q const& key )
531         {
532             dest = extract_at( head(), key, intrusive_key_comparator() );
533             return !dest.empty();
534         }
535
536         /// Extracts an item from the list using \p pred predicate for searching
537         /**
538             This function is the analog for \ref cds_nonintrusive_LazyList_rcu_extract "extract(exempt_ptr&, Q const&)".
539
540             The \p pred is a predicate used for key comparing.
541             \p Less has the interface like \p std::less.
542             \p pred must imply the same element order as \ref key_comparator.
543         */
544         template <typename Q, typename Less>
545         bool extract_with( exempt_ptr& dest, Q const& key, Less pred )
546         {
547             dest = extract_at( head(), key, typename maker::template less_wrapper<Less>::type() );
548             return !dest.empty();
549         }
550
551         /// Finds the key \p key
552         /** \anchor cds_nonintrusive_LazyList_rcu_find_val
553             The function searches the item with key equal to \p key
554             and returns \p true if it is found, and \p false otherwise.
555
556             The function makes RCU lock internally.
557         */
558         template <typename Q>
559         bool find( Q const& key ) const
560         {
561             return find_at( head(), key, intrusive_key_comparator() );
562         }
563
564         /// Finds the key \p key using \p pred predicate for searching
565         /**
566             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_find_val "find(Q const&)"
567             but \p pred is used for key comparing.
568             \p Less functor has the interface like \p std::less.
569             \p pred must imply the same element order as the comparator used for building the list.
570         */
571         template <typename Q, typename Less>
572         bool find_with( Q const& key, Less pred ) const
573         {
574             return find_at( head(), key, typename maker::template less_wrapper<Less>::type() );
575         }
576
577         /// Finds the key \p key and performs an action with it
578         /** \anchor cds_nonintrusive_LazyList_rcu_find_func
579             The function searches an item with key equal to \p key and calls the functor \p f for the item found.
580             The interface of \p Func functor is:
581             \code
582             struct functor {
583                 void operator()( value_type& item, Q& key );
584             };
585             \endcode
586             where \p item is the item found, \p key is the \p find() function argument.
587
588             You may pass \p f argument by reference using \p std::ref.
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
608         /// Finds the key \p key using \p pred predicate for searching
609         /**
610             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_find_func "find(Q&, Func)"
611             but \p pred is used for key comparing.
612             \p Less functor has the interface like \p std::less.
613             \p pred must imply the same element order as the comparator used for building the list.
614         */
615         template <typename Q, typename Less, typename Func>
616         bool find_with( Q& key, Less pred, Func f ) const
617         {
618             return find_at( head(), key, typename maker::template less_wrapper<Less>::type(), f );
619         }
620
621         /// Finds the key \p key and return the item found
622         /** \anchor cds_nonintrusive_LazyList_rcu_get
623             The function searches the item with key equal to \p key and returns the pointer to item found.
624             If \p key is not found it returns \p nullptr.
625
626             Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
627
628             RCU should be locked before call of this function.
629             Returned item is valid only while RCU is locked:
630             \code
631             typedef cds::container::LazyList< cds::urcu::gc< cds::urcu::general_buffered<> >, foo, my_traits > ord_list;
632             ord_list theList;
633             // ...
634             {
635                 // Lock RCU
636                 ord_list::rcu_lock lock;
637
638                 foo * pVal = theList.get( 5 );
639                 if ( pVal ) {
640                     // Deal with pVal
641                     //...
642                 }
643                 // Unlock RCU by rcu_lock destructor
644                 // pVal can be freed at any time after RCU has been unlocked
645             }
646             \endcode
647         */
648         template <typename Q>
649         value_type * get( Q const& key ) const
650         {
651             return get_at( head(), key, intrusive_key_comparator());
652         }
653
654         /// Finds the key \p key and return the item found
655         /**
656             The function is an analog of \ref cds_nonintrusive_LazyList_rcu_get "get(Q const&)"
657             but \p pred is used for comparing the keys.
658
659             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
660             in any order.
661             \p pred must imply the same element order as the comparator used for building the list.
662         */
663         template <typename Q, typename Less>
664         value_type * get_with( Q const& key, Less pred ) const
665         {
666             return get_at( head(), key, typename maker::template less_wrapper<Less>::type());
667         }
668
669         /// Checks if the list is empty
670         bool empty() const
671         {
672             return base_class::empty();
673         }
674
675         /// Returns list's item count
676         /**
677             The value returned depends on \p Traits::item_counter type. For \p atomicity::empty_item_counter,
678             this function always returns 0.
679
680             @note Even if you use real item counter and it returns 0, this fact is not mean that the list
681             is empty. To check list emptyness use \ref empty() method.
682         */
683         size_t size() const
684         {
685             return base_class::size();
686         }
687
688         /// Clears the list
689         void clear()
690         {
691             base_class::clear();
692         }
693
694     protected:
695         //@cond
696         bool insert_node_at( head_type& refHead, node_type * pNode )
697         {
698             assert( pNode != nullptr );
699             scoped_node_ptr p( pNode );
700
701             if ( base_class::insert_at( &refHead, *pNode )) {
702                 p.release();
703                 return true;
704             }
705
706             return false;
707         }
708
709         template <typename Q>
710         bool insert_at( head_type& refHead, Q const& val )
711         {
712             return insert_node_at( refHead, alloc_node( val ));
713         }
714
715         template <typename... Args>
716         bool emplace_at( head_type& refHead, Args&&... args )
717         {
718             return insert_node_at( refHead, alloc_node( std::forward<Args>(args)... ));
719         }
720
721         template <typename Q, typename Func>
722         bool insert_at( head_type& refHead, Q const& key, Func f )
723         {
724             scoped_node_ptr pNode( alloc_node( key ));
725
726             if ( base_class::insert_at( &refHead, *pNode, [&f](node_type& node){ f( node_to_value(node) ); } )) {
727                 pNode.release();
728                 return true;
729             }
730             return false;
731         }
732
733         template <typename Q, typename Compare, typename Func>
734         bool erase_at( head_type& refHead, Q const& key, Compare cmp, Func f )
735         {
736             return base_class::erase_at( &refHead, key, cmp, [&f](node_type const& node){ f( node_to_value(node) ); } );
737         }
738
739         template <typename Q, typename Compare>
740         node_type * extract_at( head_type& refHead, Q const& key, Compare cmp )
741         {
742             return base_class::extract_at( &refHead, key, cmp );
743         }
744
745         template <typename Q, typename Func>
746         std::pair<bool, bool> ensure_at( head_type& refHead, Q const& key, Func f )
747         {
748             scoped_node_ptr pNode( alloc_node( key ));
749
750             std::pair<bool, bool> ret = base_class::ensure_at( &refHead, *pNode,
751                 [&f, &key](bool bNew, node_type& node, node_type&){f( bNew, node_to_value(node), key ); });
752             if ( ret.first && ret.second )
753                 pNode.release();
754
755             return ret;
756         }
757
758         template <typename Q, typename Compare>
759         bool find_at( head_type& refHead, Q const& key, Compare cmp ) const
760         {
761             return base_class::find_at( &refHead, key, cmp, [](node_type&, Q const &) {} );
762         }
763
764         template <typename Q, typename Compare, typename Func>
765         bool find_at( head_type& refHead, Q& val, Compare cmp, Func f ) const
766         {
767             return base_class::find_at( &refHead, val, cmp, [&f](node_type& node, Q& val){ f( node_to_value(node), val ); });
768         }
769
770         template <typename Q, typename Compare>
771         value_type * get_at( head_type& refHead, Q const& val, Compare cmp ) const
772         {
773             node_type * pNode = base_class::get_at( &refHead, val, cmp );
774             return pNode ? &pNode->m_Value : nullptr;
775         }
776
777         //@endcond
778     };
779
780 }} // namespace cds::container
781
782 #endif // #ifndef __CDS_CONTAINER_LAZY_LIST_RCU_H