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