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