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