d5bec4bf54adaac5a00e27344778fcf74109e096
[libcds.git] / cds / intrusive / lazy_list_rcu.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_INTRUSIVE_LAZY_LIST_RCU_H
32 #define CDSLIB_INTRUSIVE_LAZY_LIST_RCU_H
33
34 #include <mutex>        // unique_lock
35 #include <cds/intrusive/details/lazy_list_base.h>
36 #include <cds/urcu/details/check_deadlock.h>
37 #include <cds/details/binary_functor_wrapper.h>
38 #include <cds/urcu/exempt_ptr.h>
39
40 namespace cds { namespace intrusive {
41     namespace lazy_list {
42         /// Lazy list node for \ref cds_urcu_desc "RCU"
43         /**
44             Template parameters:
45             - Tag - a tag used to distinguish between different implementation
46         */
47         template <class RCU, typename Lock, typename Tag>
48         struct node<cds::urcu::gc<RCU>, Lock, Tag>
49         {
50             typedef cds::urcu::gc<RCU>  gc  ;   ///< RCU schema
51             typedef Lock        lock_type   ;   ///< Lock type
52             typedef Tag         tag         ;   ///< tag
53
54             typedef cds::details::marked_ptr<node, 1>   marked_ptr          ;   ///< marked pointer
55             typedef atomics::atomic<marked_ptr>      atomic_marked_ptr   ;   ///< atomic marked pointer specific for GC
56
57             atomic_marked_ptr   m_pNext ; ///< pointer to the next node in the list
58             mutable lock_type   m_Lock  ; ///< Node lock
59
60             /// Checks if node is marked
61             bool is_marked() const
62             {
63                 return m_pNext.load(atomics::memory_order_relaxed).bits() != 0;
64             }
65
66             /// Default ctor
67             node()
68                 : m_pNext( nullptr )
69             {}
70
71             /// Clears internal fields
72             void clear()
73             {
74                 m_pNext.store( marked_ptr(), atomics::memory_order_release );
75             }
76         };
77     }   // namespace lazy_list
78
79
80     /// Lazy ordered single-linked list (template specialization for \ref cds_urcu_desc "RCU")
81     /** @ingroup cds_intrusive_list
82         \anchor cds_intrusive_LazyList_rcu
83
84         Usually, ordered single-linked list is used as a building block for the hash table implementation.
85         The complexity of searching is <tt>O(N)</tt>.
86
87         Template arguments:
88         - \p RCU - one of \ref cds_urcu_gc "RCU type"
89         - \p T - type to be stored in the list
90         - \p Traits - type traits. See \p lazy_list::traits for explanation.
91             It is possible to declare option-based list with \p %cds::intrusive::lazy_list::make_traits metafunction instead of \p Traits template
92             argument.
93
94         \par Usage
95             Before including <tt><cds/intrusive/lazy_list_rcu.h></tt> you should include appropriate RCU header file,
96             see \ref cds_urcu_gc "RCU type" for list of existing RCU class and corresponding header files.
97             For example, for \ref cds_urcu_general_buffered_gc "general-purpose buffered RCU" you should include:
98             \code
99             #include <cds/urcu/general_buffered.h>
100             #include <cds/intrusive/lazy_list_rcu.h>
101
102             // Now, you can declare lazy list for type Foo and default traits:
103             typedef cds::intrusive::LazyList<cds::urcu::gc< cds::urcu::general_buffered<> >, Foo > rcu_lazy_list;
104             \endcode
105
106     */
107     template <
108         typename RCU
109         ,typename T
110 #ifdef CDS_DOXYGEN_INVOKED
111         ,class Traits = lazy_list::traits
112 #else
113         ,class Traits
114 #endif
115     >
116     class LazyList<cds::urcu::gc<RCU>, T, Traits>
117     {
118     public:
119         typedef cds::urcu::gc<RCU> gc;      ///< RCU schema
120         typedef T                  value_type;   ///< type of value stored in the list
121         typedef Traits             traits;  ///< Traits template parameter
122
123         typedef typename traits::hook    hook;      ///< hook type
124         typedef typename hook::node_type node_type; ///< node type
125
126 #   ifdef CDS_DOXYGEN_INVOKED
127         typedef implementation_defined key_comparator  ;    ///< key compare functor based on opt::compare and opt::less option setter.
128 #   else
129         typedef typename opt::details::make_comparator< value_type, traits >::type key_comparator;
130 #   endif
131
132         typedef typename traits::disposer  disposer;   ///< disposer used
133         typedef typename get_node_traits< value_type, node_type, hook>::type node_traits;    ///< node traits
134         typedef typename lazy_list::get_link_checker< node_type, traits::link_checker >::type link_checker;   ///< link checker
135
136         typedef typename traits::back_off     back_off;     ///< back-off strategy (not used)
137         typedef typename traits::item_counter item_counter; ///< Item counting policy used
138         typedef typename traits::memory_model memory_model; ///< C++ memory ordering (see \p lazy_list::traits::memory_model)
139         typedef typename traits::stat         stat;         ///< Internal statistics
140         typedef typename traits::rcu_check_deadlock rcu_check_deadlock; ///< Deadlock checking policy
141
142         typedef typename gc::scoped_lock    rcu_lock ; ///< RCU scoped lock
143         static CDS_CONSTEXPR const bool c_bExtractLockExternal = true; ///< Group of \p extract_xxx functions require external locking
144
145         static_assert((std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type");
146
147         //@cond
148         // Rebind traits (split-list support)
149         template <typename... Options>
150         struct rebind_traits {
151             typedef LazyList<
152                 gc
153                 , value_type
154                 , typename cds::opt::make_options< traits, Options...>::type
155             >   type;
156         };
157         //@endcond
158
159     protected:
160         //@cond
161         typedef typename node_type::marked_ptr  marked_node_ptr;  ///< Node marked pointer
162         typedef node_type *                     auxiliary_head;   ///< Auxiliary head type (for split-list support)
163         //@endcond
164
165     protected:
166         node_type       m_Head;        ///< List head (dummy node)
167         node_type       m_Tail;        ///< List tail (dummy node)
168         item_counter    m_ItemCounter; ///< Item counter
169         mutable stat    m_Stat;        ///< Internal statistics
170
171         //@cond
172
173         /// Position pointer for item search
174         struct position {
175             node_type *     pPred; ///< Previous node
176             node_type *     pCur;  ///< Current node
177
178             /// Locks nodes \p pPred and \p pCur
179             void lock()
180             {
181                 pPred->m_Lock.lock();
182                 pCur->m_Lock.lock();
183             }
184
185             /// Unlocks nodes \p pPred and \p pCur
186             void unlock()
187             {
188                 pCur->m_Lock.unlock();
189                 pPred->m_Lock.unlock();
190             }
191         };
192
193         typedef std::unique_lock< position > scoped_position_lock;
194
195         typedef cds::urcu::details::check_deadlock_policy< gc, rcu_check_deadlock>   deadlock_policy;
196         //@endcond
197
198     protected:
199         //@cond
200         static void clear_links( node_type * pNode )
201         {
202             pNode->m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed );
203         }
204
205         struct clear_and_dispose {
206             void operator()( value_type * p )
207             {
208                 assert( p != nullptr );
209                 clear_links( node_traits::to_node_ptr(p));
210                 disposer()( p );
211             }
212         };
213
214         static void dispose_node( node_type * pNode )
215         {
216             assert( pNode );
217             assert( !gc::is_locked());
218
219             gc::template retire_ptr<clear_and_dispose>( node_traits::to_value_ptr( *pNode ));
220         }
221
222         static void link_node( node_type * pNode, node_type * pPred, node_type * pCur )
223         {
224             assert( pPred->m_pNext.load(memory_model::memory_order_relaxed).ptr() == pCur );
225             link_checker::is_empty( pNode );
226
227             pNode->m_pNext.store( marked_node_ptr(pCur), memory_model::memory_order_relaxed );
228             pPred->m_pNext.store( marked_node_ptr(pNode), memory_model::memory_order_release );
229         }
230
231         void unlink_node( node_type * pPred, node_type * pCur, node_type * pHead )
232         {
233             assert( pPred->m_pNext.load(memory_model::memory_order_relaxed).ptr() == pCur );
234             assert( pCur != &m_Tail );
235
236             node_type * pNext = pCur->m_pNext.load(memory_model::memory_order_relaxed).ptr();
237             pCur->m_pNext.store( marked_node_ptr( pHead, 1 ), memory_model::memory_order_relaxed ); // logical deletion + back-link for search
238             pPred->m_pNext.store( marked_node_ptr( pNext ), memory_model::memory_order_release); // physically deleting
239         }
240
241         //@endcond
242
243     public:
244         /// pointer to extracted node
245         using exempt_ptr = cds::urcu::exempt_ptr< gc, value_type, value_type, clear_and_dispose, void >;
246         /// Type of \p get() member function return value
247         typedef value_type * raw_ptr;
248
249     protected:
250         //@cond
251         template <bool IsConst>
252         class iterator_type
253         {
254             friend class LazyList;
255
256         protected:
257             value_type * m_pNode;
258
259             void next()
260             {
261                 assert( m_pNode != nullptr );
262
263                 node_type * pNode = node_traits::to_node_ptr( m_pNode );
264                 node_type * pNext = pNode->m_pNext.load(memory_model::memory_order_acquire).ptr();
265                 if ( pNext != nullptr )
266                     m_pNode = node_traits::to_value_ptr( pNext );
267             }
268
269             void skip_deleted()
270             {
271                 if ( m_pNode != nullptr ) {
272                     node_type * pNode = node_traits::to_node_ptr( m_pNode );
273
274                     // Dummy tail node could not be marked
275                     while ( pNode->is_marked())
276                         pNode = pNode->m_pNext.load(memory_model::memory_order_acquire).ptr();
277
278                     if ( pNode != node_traits::to_node_ptr( m_pNode ))
279                         m_pNode = node_traits::to_value_ptr( pNode );
280                 }
281             }
282
283             iterator_type( node_type * pNode )
284             {
285                 m_pNode = node_traits::to_value_ptr( pNode );
286                 skip_deleted();
287             }
288
289         public:
290             typedef typename cds::details::make_const_type<value_type, IsConst>::pointer   value_ptr;
291             typedef typename cds::details::make_const_type<value_type, IsConst>::reference value_ref;
292
293             iterator_type()
294                 : m_pNode( nullptr )
295             {}
296
297             iterator_type( iterator_type const& src )
298                 : m_pNode( src.m_pNode )
299             {}
300
301             value_ptr operator ->() const
302             {
303                 return m_pNode;
304             }
305
306             value_ref operator *() const
307             {
308                 assert( m_pNode != nullptr );
309                 return *m_pNode;
310             }
311
312             /// Pre-increment
313             iterator_type& operator ++()
314             {
315                 next();
316                 skip_deleted();
317                 return *this;
318             }
319
320             /// Post-increment
321             iterator_type operator ++(int)
322             {
323                 iterator_type i(*this);
324                 next();
325                 skip_deleted();
326                 return i;
327             }
328
329             iterator_type& operator = (iterator_type const& src)
330             {
331                 m_pNode = src.m_pNode;
332                 return *this;
333             }
334
335             template <bool C>
336             bool operator ==(iterator_type<C> const& i ) const
337             {
338                 return m_pNode == i.m_pNode;
339             }
340             template <bool C>
341             bool operator !=(iterator_type<C> const& i ) const
342             {
343                 return m_pNode != i.m_pNode;
344             }
345         };
346         //@endcond
347
348     public:
349     ///@name Forward iterators (thread-safe only under RCU lock)
350     //@{
351         /// Forward iterator
352         /**
353             You may safely use iterators in multi-threaded environment only under RCU lock.
354             Otherwise, a crash is possible if another thread deletes the item the iterator points to.
355         */
356         typedef iterator_type<false>    iterator;
357
358         /// Const forward iterator
359         typedef iterator_type<true>     const_iterator;
360
361         /// Returns a forward iterator addressing the first element in a list
362         /**
363             For empty list \code begin() == end() \endcode
364         */
365         iterator begin()
366         {
367             iterator it( &m_Head );
368             ++it        ;   // skip dummy head
369             return it;
370         }
371
372         /// Returns an iterator that addresses the location succeeding the last element in a list
373         /**
374             Do not use the value returned by <tt>end</tt> function to access any item.
375
376             The returned value can be used only to control reaching the end of the list.
377             For empty list \code begin() == end() \endcode
378         */
379         iterator end()
380         {
381             return iterator( &m_Tail );
382         }
383
384         /// Returns a forward const iterator addressing the first element in a list
385         const_iterator begin() const
386         {
387             return get_const_begin();
388         }
389
390         /// Returns a forward const iterator addressing the first element in a list
391         const_iterator cbegin() const
392         {
393             return get_const_begin();
394         }
395
396         /// Returns an const iterator that addresses the location succeeding the last element in a list
397         const_iterator end() const
398         {
399             return get_const_end();
400         }
401
402         /// Returns an const iterator that addresses the location succeeding the last element in a list
403         const_iterator cend() const
404         {
405             return get_const_end();
406         }
407     //@}
408
409     private:
410         //@cond
411         const_iterator get_const_begin() const
412         {
413             const_iterator it( const_cast<node_type *>( &m_Head ));
414             ++it        ;   // skip dummy head
415             return it;
416         }
417         const_iterator get_const_end() const
418         {
419             return const_iterator( const_cast<node_type *>( &m_Tail ));
420         }
421         //@endcond
422
423     public:
424         /// Default constructor initializes empty list
425         LazyList()
426         {
427             m_Head.m_pNext.store( marked_node_ptr( &m_Tail ), memory_model::memory_order_relaxed );
428         }
429
430         //@cond
431         template <typename Stat, typename = std::enable_if<std::is_same<stat, lazy_list::wrapped_stat<Stat>>::value >>
432         explicit LazyList( Stat& st )
433             : m_Stat( st )
434         {
435             m_Head.m_pNext.store( marked_node_ptr( &m_Tail ), memory_model::memory_order_relaxed );
436         }
437         //@endcond
438
439         /// Destroys the list object
440         ~LazyList()
441         {
442             clear();
443
444             assert( m_Head.m_pNext.load(memory_model::memory_order_relaxed).ptr() == &m_Tail );
445             m_Head.m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed );
446         }
447
448         /// Inserts new node
449         /**
450             The function inserts \p val in the list if the list does not contain
451             an item with key equal to \p val.
452
453             Returns \p true if \p val is linked into the list, \p false otherwise.
454         */
455         bool insert( value_type& val )
456         {
457             return insert_at( &m_Head, val );
458         }
459
460         /// Inserts new node
461         /**
462             This function is intended for derived non-intrusive containers.
463
464             The function allows to split new item creating into two part:
465             - create item with key only
466             - insert new item into the list
467             - if inserting is success, calls  \p f functor to initialize value-field of \p val.
468
469             The functor signature is:
470             \code
471                 void func( value_type& val );
472             \endcode
473             where \p val is the item inserted.
474             While the functor \p f is working the item \p val is locked.
475             The user-defined functor is called only if the inserting is success.
476         */
477         template <typename Func>
478         bool insert( value_type& val, Func f )
479         {
480             return insert_at( &m_Head, val, f );
481         }
482
483         /// Updates the item
484         /**
485             The operation performs inserting or changing data with lock-free manner.
486
487             If the item \p val not found in the list, then \p val is inserted into the list
488             iff \p bAllowInsert is \p true.
489             Otherwise, the functor \p func is called with item found.
490             The functor signature is:
491             \code
492             struct functor {
493                 void operator()( bool bNew, value_type& item, value_type& val );
494             };
495             \endcode
496             with arguments:
497             - \p bNew - \p true if the item has been inserted, \p false otherwise
498             - \p item - item of the list
499             - \p val - argument \p val passed into the \p update() function
500             If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
501             refer to the same thing.
502
503             The functor may change non-key fields of the \p item.
504             While the functor \p f is calling the item \p item is locked.
505
506             Returns <tt> std::pair<bool, bool>  </tt> where \p first is \p true if operation is successful,
507             \p second is \p true if new item has been added or \p false if the item with \p key
508             already is in the list.
509
510             The function makes RCU lock internally.
511         */
512         template <typename Func>
513         std::pair<bool, bool> update( value_type& val, Func func, bool bAllowInsert = true )
514         {
515             return update_at( &m_Head, val, func, bAllowInsert );
516         }
517         //@cond
518         template <typename Func>
519         CDS_DEPRECATED("ensure() is deprecated, use update()")
520         std::pair<bool, bool> ensure( value_type& val, Func func )
521         {
522             return update( val, func, true );
523         }
524         //@endcond
525
526         /// Unlinks the item \p val from the list
527         /**
528             The function searches the item \p val in the list and unlink it from the list
529             if it is found and it is equal to \p val.
530
531             Difference between \p erase() and \p %unlink() functions: \p %erase() finds <i>a key</i>
532             and deletes the item found. \p %unlink() finds an item by key and deletes it
533             only if \p val is an item of that list, i.e. the pointer to item found
534             is equal to <tt> &val </tt>.
535
536             The function returns \p true if success and \p false otherwise.
537
538             RCU \p synchronize method can be called. The RCU should not be locked.
539             Note that depending on RCU type used the \ref disposer call can be deferred.
540
541             \p disposer specified in \p Traits is called for unlinked item.
542
543             The function can throw \p cds::urcu::rcu_deadlock exception if deadlock is encountered and
544             deadlock checking policy is \p opt::v::rcu_throw_deadlock.
545         */
546         bool unlink( value_type& val )
547         {
548             return unlink_at( &m_Head, val );
549         }
550
551         /// Deletes the item from the list
552         /**
553             The function searches an item with key equal to \p key in the list,
554             unlinks it from the list, and returns \p true.
555             If the item with the key equal to \p key is not found the function return \p false.
556
557             RCU \p synchronize method can be called. The RCU should not be locked.
558             Note that depending on RCU type used the \ref disposer call can be deferred.
559
560             \p disposer specified in \p Traits is called for deleted item.
561
562             The function can throw \ref cds_urcu_rcu_deadlock "cds::urcu::rcu_deadlock" exception if deadlock is encountered and
563             deadlock checking policy is \p opt::v::rcu_throw_deadlock.
564         */
565         template <typename Q>
566         bool erase( Q const& key )
567         {
568             return erase_at( &m_Head, key, key_comparator());
569         }
570
571         /// Deletes the item from the list using \p pred predicate for searching
572         /**
573             The function is an analog of \p erase(Q const&)
574             but \p pred is used for key comparing.
575             \p Less functor has the interface like \p std::less.
576             \p pred must imply the same element order as the comparator used for building the list.
577
578             \p disposer specified in \p Traits is called for deleted item.
579         */
580         template <typename Q, typename Less>
581         bool erase_with( Q const& key, Less pred )
582         {
583             CDS_UNUSED( pred );
584             return erase_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>());
585         }
586
587         /// Deletes the item from the list
588         /**
589             The function searches an item with key equal to \p key in the list,
590             call \p func functor with item found, unlinks it from the list, and returns \p true.
591             The \p Func interface is
592             \code
593             struct functor {
594                 void operator()( value_type const& item );
595             };
596             \endcode
597
598             If the item with the key equal to \p key is not found the function return \p false.
599
600             RCU \p synchronize method can be called. The RCU should not be locked.
601             Note that depending on RCU type used the \ref disposer call can be deferred.
602
603             \p disposer specified in \p Traits is called for deleted item.
604
605             The function can throw \ref cds_urcu_rcu_deadlock "cds::urcu::rcu_deadlock" exception if deadlock is encountered and
606             deadlock checking policy is \p opt::v::rcu_throw_deadlock.
607         */
608         template <typename Q, typename Func>
609         bool erase( Q const& key, Func func )
610         {
611             return erase_at( &m_Head, key, key_comparator(), func );
612         }
613
614         /// Deletes the item from the list using \p pred predicate for searching
615         /**
616             The function is an analog of \p erase(Q const&, Func)
617             but \p pred is used for key comparing.
618             \p Less functor has the interface like \p std::less.
619             \p pred must imply the same element order as the comparator used for building the list.
620
621             \p disposer specified in \p Traits is called for deleted item.
622         */
623         template <typename Q, typename Less, typename Func>
624         bool erase_with( Q const& key, Less pred, Func func )
625         {
626             CDS_UNUSED( pred );
627             return erase_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>(), func );
628         }
629
630         /// Extracts an item from the list
631         /**
632             The function searches an item with key equal to \p key in the list,
633             unlinks it from the list, and returns \ref cds::urcu::exempt_ptr "exempt_ptr" pointer to an item found.
634             If the item is not found the function returns empty \p exempt_ptr.
635
636             @note The function does NOT call RCU read-side lock or synchronization,
637             and does NOT dispose the item found. It just unlinks the item from the list
638             and returns a pointer to it.
639             You should manually lock RCU before calling this function, and you should manually release
640             the returned exempt pointer outside the RCU lock region before reusing returned pointer.
641
642             \code
643             #include <cds/urcu/general_buffered.h>
644             #include <cds/intrusive/lazy_list_rcu.h>
645
646             typedef cds::urcu::gc< general_buffered<> > rcu;
647             typedef cds::intrusive::LazyList< rcu, Foo > rcu_lazy_list;
648
649             rcu_lazy_list theList;
650             // ...
651
652             rcu_lazy_list::exempt_ptr p1;
653             {
654                 // first, we should lock RCU
655                 rcu::scoped_lock sl;
656
657                 // Now, you can apply extract function
658                 // Note that you must not delete the item found inside the RCU lock
659                 p1 = theList.extract(  10 )
660                 if ( p1 ) {
661                     // do something with p1
662                     ...
663                 }
664             }
665
666             // We may safely release p1 here
667             // release() passes the pointer to RCU reclamation cycle:
668             // it invokes RCU retire_ptr function with the disposer you provided for the list.
669             p1.release();
670             \endcode
671         */
672         template <typename Q>
673         exempt_ptr extract( Q const& key )
674         {
675             return exempt_ptr( extract_at( &m_Head, key, key_comparator()));
676         }
677
678         /// Extracts an item from the list using \p pred predicate for searching
679         /**
680             This function is the analog for \p extract(Q const&).
681
682             The \p pred is a predicate used for key comparing.
683             \p Less has the interface like \p std::less.
684             \p pred must imply the same element order as \ref key_comparator.
685         */
686         template <typename Q, typename Less>
687         exempt_ptr extract_with( Q const& key, Less pred )
688         {
689             CDS_UNUSED( pred );
690             return exempt_ptr( extract_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>()));
691         }
692
693         /// Finds the key \p key
694         /**
695             The function searches the item with key equal to \p key
696             and calls the functor \p f for item found.
697             The interface of \p Func functor is:
698             \code
699             struct functor {
700                 void operator()( value_type& item, Q& key );
701             };
702             \endcode
703             where \p item is the item found, \p key is the <tt>find</tt> function argument.
704
705             The functor may change non-key fields of \p item.
706             While the functor \p f is calling the item found \p item is locked.
707
708             The function returns \p true if \p key is found, \p false otherwise.
709         */
710         template <typename Q, typename Func>
711         bool find( Q& key, Func f ) const
712         {
713             return find_at( const_cast<node_type *>( &m_Head ), key, key_comparator(), f );
714         }
715         //@cond
716         template <typename Q, typename Func>
717         bool find( Q const& key, Func f ) const
718         {
719             return find_at( const_cast<node_type *>(&m_Head), key, key_comparator(), f );
720         }
721         //@endcond
722
723         /// Finds the key \p key using \p pred predicate for searching
724         /**
725             The function is an analog of \p find( Q&, Func ) but \p pred is used for key comparing.
726             \p Less functor has the interface like \p std::less.
727             \p pred must imply the same element order as the comparator used for building the list.
728         */
729         template <typename Q, typename Less, typename Func>
730         bool find_with( Q& key, Less pred, Func f ) const
731         {
732             CDS_UNUSED( pred );
733             return find_at( const_cast<node_type *>( &m_Head ), key, cds::opt::details::make_comparator_from_less<Less>(), f );
734         }
735         //@cond
736         template <typename Q, typename Less, typename Func>
737         bool find_with( Q const& key, Less pred, Func f ) const
738         {
739             CDS_UNUSED( pred );
740             return find_at( const_cast<node_type *>(&m_Head), key, cds::opt::details::make_comparator_from_less<Less>(), f );
741         }
742         //@endcond
743
744         /// Checks whether the list contains \p key
745         /**
746             The function searches the item with key equal to \p key
747             and returns \p true if it is found, and \p false otherwise.
748         */
749         template <typename Q>
750         bool contains( Q const& key ) const
751         {
752             return find_at( const_cast<node_type *>( &m_Head ), key, key_comparator());
753         }
754         //@cond
755         template <typename Q>
756         CDS_DEPRECATED("deprecated, use contains()")
757         bool find( Q const& key ) const
758         {
759             return contains( key );
760         }
761         //@endcond
762
763         /// Checks whether the map contains \p key using \p pred predicate for searching
764         /**
765             The function is an analog of \p contains( Q const& ) but \p pred is used for key comparing.
766             \p Less functor has the interface like \p std::less.
767             \p Less must imply the same element order as the comparator used for building the list.
768         */
769         template <typename Q, typename Less>
770         bool contains( Q const& key, Less pred ) const
771         {
772             CDS_UNUSED( pred );
773             return find_at( const_cast<node_type *>( &m_Head ), key, cds::opt::details::make_comparator_from_less<Less>());
774         }
775         //@cond
776         template <typename Q, typename Less>
777         CDS_DEPRECATED("deprecated, use contains()")
778         bool find_with( Q const& key, Less pred ) const
779         {
780             return contains( key, pred );
781         }
782         //@endcond
783
784         /// Finds the key \p key and return the item found
785         /** \anchor cds_intrusive_LazyList_rcu_get
786             The function searches the item with key equal to \p key and returns the pointer to item found.
787             If \p key is not found it returns \p nullptr.
788
789             Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
790
791             RCU should be locked before call of this function.
792             Returned item is valid only while RCU is locked:
793             \code
794             typedef cds::intrusive::LazyList< cds::urcu::gc< cds::urcu::general_buffered<> >, foo, my_traits > ord_list;
795             ord_list theList;
796             // ...
797             {
798                 // Lock RCU
799                 typename ord_list::rcu_lock lock;
800
801                 foo * pVal = theList.get( 5 );
802                 if ( pVal ) {
803                     // Deal with pVal
804                     //...
805                 }
806                 // Unlock RCU by rcu_lock destructor
807                 // pVal can be retired by disposer at any time after RCU has been unlocked
808             }
809             \endcode
810         */
811         template <typename Q>
812         value_type * get( Q const& key ) const
813         {
814             return get_at( const_cast<node_type *>( &m_Head ), key, key_comparator());
815         }
816
817         /// Finds the key \p key and return the item found
818         /**
819             The function is an analog of \ref cds_intrusive_LazyList_rcu_get "get(Q const&)"
820             but \p pred is used for comparing the keys.
821
822             \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
823             in any order.
824             \p pred must imply the same element order as the comparator used for building the list.
825         */
826         template <typename Q, typename Less>
827         value_type * get_with( Q const& key, Less pred ) const
828         {
829             CDS_UNUSED( pred );
830             return get_at( const_cast<node_type *>( &m_Head ), key, cds::opt::details::make_comparator_from_less<Less>());
831         }
832
833         /// Clears the list using default disposer
834         /**
835             The function clears the list using default (provided in class template) disposer functor.
836
837             RCU \p synchronize method can be called.
838             Note that depending on RCU type used the \ref disposer call can be deferred.
839
840             The function can throw \p cds::urcu::rcu_deadlock exception if deadlock is encountered and
841             deadlock checking policy is \p opt::v::rcu_throw_deadlock.
842         */
843         void clear()
844         {
845             if( !empty()) {
846                 deadlock_policy::check();
847
848                 node_type * pHead;
849                 for (;;) {
850                     {
851                         rcu_lock l;
852                         pHead = m_Head.m_pNext.load(memory_model::memory_order_acquire).ptr();
853                         if ( pHead == &m_Tail )
854                             break;
855
856                         m_Head.m_Lock.lock();
857                         pHead->m_Lock.lock();
858
859                         if ( m_Head.m_pNext.load(memory_model::memory_order_relaxed).all() == pHead )
860                             unlink_node( &m_Head, pHead, &m_Head );
861
862                         pHead->m_Lock.unlock();
863                         m_Head.m_Lock.unlock();
864                     }
865
866                     --m_ItemCounter;
867                     dispose_node( pHead );
868                 }
869             }
870         }
871
872         /// Checks if the list is empty
873         bool empty() const
874         {
875             return m_Head.m_pNext.load(memory_model::memory_order_relaxed).ptr() == &m_Tail;
876         }
877
878         /// Returns list's item count
879         /**
880             The value returned depends on opt::item_counter option. For atomicity::empty_item_counter,
881             this function always returns 0.
882
883             <b>Warning</b>: even if you use real item counter and it returns 0, this fact is not mean that the list
884             is empty. To check list emptiness use \ref empty() method.
885         */
886         size_t size() const
887         {
888             return m_ItemCounter.value();
889         }
890
891         /// Returns const reference to internal statistics
892         stat const& statistics() const
893         {
894             return m_Stat;
895         }
896
897     protected:
898         //@cond
899         // split-list support
900         bool insert_aux_node( node_type * pNode )
901         {
902             return insert_aux_node( &m_Head, pNode );
903         }
904
905         // split-list support
906         bool insert_aux_node( node_type * pHead, node_type * pNode )
907         {
908             assert( pHead != nullptr );
909             assert( pNode != nullptr );
910
911             // Hack: convert node_type to value_type.
912             // Actually, an auxiliary node should not be converted to value_type
913             // We assume that comparator can correctly distinguish aux and regular node.
914             return insert_at( pHead, *node_traits::to_value_ptr( pNode ));
915         }
916
917         bool insert_at( node_type * pHead, value_type& val )
918         {
919             rcu_lock l;
920             return insert_at_locked( pHead, val );
921         }
922
923         template <typename Func>
924         bool insert_at( node_type * pHead, value_type& val, Func f )
925         {
926             position pos;
927             key_comparator  cmp;
928
929             rcu_lock l;
930             while ( true ) {
931                 search( pHead, val, pos );
932                 {
933                     scoped_position_lock sl( pos );
934                     if ( validate( pos.pPred, pos.pCur )) {
935                         if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
936                             // failed: key already in list
937                             m_Stat.onInsertFailed();
938                             return false;
939                         }
940
941                         f( val );
942                         link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
943                         break;
944                     }
945                 }
946
947                 m_Stat.onInsertRetry();
948             }
949
950             ++m_ItemCounter;
951             m_Stat.onInsertSuccess();
952             return true;
953         }
954
955         iterator insert_at_( node_type * pHead, value_type& val )
956         {
957             rcu_lock l;
958             if ( insert_at_locked( pHead, val ))
959                 return iterator( node_traits::to_node_ptr( val ));
960             return end();
961         }
962
963
964         template <typename Func>
965         std::pair<iterator, bool> update_at_( node_type * pHead, value_type& val, Func func, bool bAllowInsert )
966         {
967             rcu_lock l;
968             return update_at_locked( pHead, val, func, bAllowInsert );
969         }
970
971         template <typename Func>
972         std::pair<bool, bool> update_at( node_type * pHead, value_type& val, Func func, bool bAllowInsert )
973         {
974             rcu_lock l;
975             std::pair<iterator, bool> ret = update_at_locked( pHead, val, func, bAllowInsert );
976             return std::make_pair( ret.first != end(), ret.second );
977         }
978
979         bool unlink_at( node_type * pHead, value_type& val )
980         {
981             position pos;
982             key_comparator  cmp;
983             deadlock_policy::check();
984
985             while ( true ) {
986                 int nResult = 0;
987                 {
988                     rcu_lock l;
989                     search( pHead, val, pos );
990                     {
991                         scoped_position_lock alp( pos );
992                         if ( validate( pos.pPred, pos.pCur )) {
993                             if ( pos.pCur != &m_Tail
994                                 && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0
995                                 && node_traits::to_value_ptr( pos.pCur ) == &val )
996                             {
997                                 // item found
998                                 unlink_node( pos.pPred, pos.pCur, pHead );
999                                 nResult = 1;
1000                             }
1001                             else
1002                                 nResult = -1;
1003                         }
1004                     }
1005                 }
1006
1007                 if ( nResult ) {
1008                     if ( nResult > 0 ) {
1009                         --m_ItemCounter;
1010                         dispose_node( pos.pCur );
1011                         m_Stat.onEraseSuccess();
1012                         return true;
1013                     }
1014
1015                     m_Stat.onEraseFailed();
1016                     return false;
1017                 }
1018
1019                 m_Stat.onEraseRetry();
1020             }
1021         }
1022
1023         template <typename Q, typename Compare, typename Func>
1024         bool erase_at( node_type * const pHead, Q const& val, Compare cmp, Func f, position& pos )
1025         {
1026             deadlock_policy::check();
1027
1028             while ( true ) {
1029                 int nResult = 0;
1030                 {
1031                     rcu_lock l;
1032                     search( pHead, val, pos, cmp );
1033                     {
1034                         scoped_position_lock alp( pos );
1035                         if ( validate( pos.pPred, pos.pCur )) {
1036                             if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
1037                                 // key found
1038                                 unlink_node( pos.pPred, pos.pCur, pHead );
1039                                 f( *node_traits::to_value_ptr( *pos.pCur ));
1040                                 nResult = 1;
1041                             }
1042                             else
1043                                 nResult = -1;
1044                         }
1045                     }
1046                 }
1047
1048                 if ( nResult ) {
1049                     if ( nResult > 0 ) {
1050                         --m_ItemCounter;
1051                         dispose_node( pos.pCur );
1052                         m_Stat.onEraseSuccess();
1053                         return true;
1054                     }
1055
1056                     m_Stat.onEraseFailed();
1057                     return false;
1058                 }
1059
1060                 m_Stat.onEraseRetry();
1061             }
1062         }
1063
1064         template <typename Q, typename Compare, typename Func>
1065         bool erase_at( node_type * pHead, Q const& val, Compare cmp, Func f )
1066         {
1067             position pos;
1068             return erase_at( pHead, val, cmp, f, pos );
1069         }
1070
1071         template <typename Q, typename Compare>
1072         bool erase_at( node_type * pHead, Q const& val, Compare cmp )
1073         {
1074             position pos;
1075             return erase_at( pHead, val, cmp, [](value_type const&){}, pos );
1076         }
1077
1078         template <typename Q, typename Compare>
1079         value_type * extract_at( node_type * const pHead, Q const& val, Compare cmp )
1080         {
1081             position pos;
1082             assert( gc::is_locked()) ; // RCU must be locked
1083
1084             while ( true ) {
1085                 search( pHead, val, pos, cmp );
1086                 int nResult = 0;
1087                 {
1088                     scoped_position_lock alp( pos );
1089                     if ( validate( pos.pPred, pos.pCur )) {
1090                         if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
1091                             // key found
1092                             unlink_node( pos.pPred, pos.pCur, pHead );
1093                             nResult = 1;
1094                         }
1095                         else {
1096                             nResult = -1;
1097                         }
1098                     }
1099                 }
1100
1101                 if ( nResult ) {
1102                     if ( nResult > 0 ) {
1103                         --m_ItemCounter;
1104                         m_Stat.onEraseSuccess();
1105                         return node_traits::to_value_ptr( pos.pCur );
1106                     }
1107
1108                     m_Stat.onEraseFailed();
1109                     return nullptr;
1110                 }
1111
1112                 m_Stat.onEraseRetry();
1113             }
1114         }
1115
1116         template <typename Q, typename Compare, typename Func>
1117         bool find_at( node_type * pHead, Q& val, Compare cmp, Func f ) const
1118         {
1119             position pos;
1120
1121             rcu_lock l;
1122             search( pHead, val, pos, cmp );
1123             if ( pos.pCur != &m_Tail ) {
1124                 std::unique_lock< typename node_type::lock_type> al( pos.pCur->m_Lock );
1125                 if ( cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
1126                     f( *node_traits::to_value_ptr( *pos.pCur ), val );
1127                     m_Stat.onFindSuccess();
1128                     return true;
1129                 }
1130             }
1131
1132             m_Stat.onFindFailed();
1133             return false;
1134         }
1135
1136         template <typename Q, typename Compare>
1137         bool find_at( node_type * pHead, Q& val, Compare cmp ) const
1138         {
1139             rcu_lock l;
1140             return find_at_( pHead, val, cmp ) != end();
1141         }
1142
1143         template <typename Q, typename Compare>
1144         const_iterator find_at_( node_type * pHead, Q& val, Compare cmp ) const
1145         {
1146             assert( gc::is_locked());
1147
1148             position pos;
1149
1150             search( pHead, val, pos, cmp );
1151             if ( pos.pCur != &m_Tail ) {
1152                 if ( cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
1153                     m_Stat.onFindSuccess();
1154                     return const_iterator( pos.pCur );
1155                 }
1156             }
1157
1158             m_Stat.onFindFailed();
1159             return end();
1160         }
1161
1162         template <typename Q, typename Compare>
1163         value_type * get_at( node_type * pHead, Q const& val, Compare cmp ) const
1164         {
1165             value_type * pFound = nullptr;
1166             return find_at( pHead, val, cmp, [&pFound](value_type& found, Q const& ) { pFound = &found; } )
1167                 ? pFound : nullptr;
1168         }
1169
1170         //@endcond
1171
1172     protected:
1173         //@cond
1174         template <typename Q>
1175         void search( node_type * const pHead, Q const& key, position& pos ) const
1176         {
1177             search( pHead, key, pos, key_comparator());
1178         }
1179
1180         template <typename Q, typename Compare>
1181         void search( node_type * const pHead, Q const& key, position& pos, Compare cmp ) const
1182         {
1183             // RCU should be locked
1184             assert( gc::is_locked());
1185
1186             node_type const* pTail = &m_Tail;
1187
1188             marked_node_ptr pCur(pHead);
1189             marked_node_ptr pPrev(pHead);
1190
1191             while ( pCur != pTail && ( pCur == pHead || cmp( *node_traits::to_value_ptr( *pCur.ptr()), key ) < 0 )) {
1192                 pPrev = pCur;
1193                 pCur = pCur->m_pNext.load(memory_model::memory_order_acquire);
1194                 if ( pCur.bits())
1195                     pPrev = pCur = pHead;
1196             }
1197
1198             pos.pCur = pCur.ptr();
1199             pos.pPred = pPrev.ptr();
1200         }
1201
1202         bool validate( node_type * pPred, node_type * pCur ) CDS_NOEXCEPT
1203         {
1204             if ( validate_link( pPred, pCur ) ) {
1205                 m_Stat.onValidationSuccess();
1206                 return true;
1207             }
1208
1209             m_Stat.onValidationFailed();
1210             return false;
1211         }
1212
1213         static bool validate_link( node_type * pPred, node_type * pCur ) CDS_NOEXCEPT
1214         {
1215             // RCU lock should be locked
1216             assert( gc::is_locked());
1217
1218             return !pPred->is_marked()
1219                 && !pCur->is_marked()
1220                 && pPred->m_pNext.load(memory_model::memory_order_relaxed) == pCur;
1221         }
1222
1223         //@endcond
1224
1225     private:
1226         //@cond
1227         bool insert_at_locked( node_type * pHead, value_type& val )
1228         {
1229             // RCU lock should be locked
1230             assert( gc::is_locked());
1231
1232             position pos;
1233             key_comparator  cmp;
1234
1235             while ( true ) {
1236                 search( pHead, val, pos );
1237                 {
1238                     scoped_position_lock alp( pos );
1239                     if ( validate( pos.pPred, pos.pCur )) {
1240                         if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
1241                             // failed: key already in list
1242                             m_Stat.onInsertFailed();
1243                             return false;
1244                         }
1245
1246                         link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
1247                         break;
1248                     }
1249                 }
1250
1251                 m_Stat.onInsertRetry();
1252             }
1253
1254             ++m_ItemCounter;
1255             m_Stat.onInsertSuccess();
1256             return true;
1257
1258         }
1259
1260         template <typename Func>
1261         std::pair<iterator, bool> update_at_locked( node_type * pHead, value_type& val, Func func, bool bAllowInsert )
1262         {
1263             // RCU lock should be locked
1264             assert( gc::is_locked());
1265
1266             position pos;
1267             key_comparator  cmp;
1268
1269             while ( true ) {
1270                 search( pHead, val, pos );
1271                 {
1272                     scoped_position_lock alp( pos );
1273                     if ( validate( pos.pPred, pos.pCur )) {
1274                         if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
1275                             // key already in the list
1276
1277                             func( false, *node_traits::to_value_ptr( *pos.pCur ), val );
1278                             m_Stat.onUpdateExisting();
1279                             return std::make_pair( iterator( pos.pCur ), false );
1280                         }
1281                         else {
1282                             // new key
1283                             if ( !bAllowInsert ) {
1284                                 m_Stat.onUpdateFailed();
1285                                 return std::make_pair( end(), false );
1286                             }
1287
1288                             func( true, val, val );
1289                             link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
1290                             break;
1291                         }
1292                     }
1293                 }
1294
1295                 m_Stat.onUpdateRetry();
1296             }
1297
1298             ++m_ItemCounter;
1299             m_Stat.onUpdateNew();
1300             return std::make_pair( iterator( node_traits::to_node_ptr( val )), true );
1301         }
1302         //@endcond
1303     };
1304
1305 }}  // namespace cds::intrusive
1306
1307 #endif  // #ifndef CDSLIB_INTRUSIVE_LAZY_LIST_RCU_H