3 #ifndef __CDS_INTRUSIVE_LAZY_LIST_RCU_H
4 #define __CDS_INTRUSIVE_LAZY_LIST_RCU_H
6 #include <mutex> // unique_lock
7 #include <cds/intrusive/details/lazy_list_base.h>
8 #include <cds/urcu/details/check_deadlock.h>
9 #include <cds/details/binary_functor_wrapper.h>
10 #include <cds/urcu/exempt_ptr.h>
12 namespace cds { namespace intrusive {
14 /// Lazy list node for \ref cds_urcu_desc "RCU"
17 - Tag - a tag used to distinguish between different implementation
19 template <class RCU, typename Lock, typename Tag>
20 struct node<cds::urcu::gc<RCU>, Lock, Tag>
22 typedef cds::urcu::gc<RCU> gc ; ///< RCU schema
23 typedef Lock lock_type ; ///< Lock type
24 typedef Tag tag ; ///< tag
26 typedef cds::details::marked_ptr<node, 1> marked_ptr ; ///< marked pointer
27 typedef atomics::atomic<marked_ptr> atomic_marked_ptr ; ///< atomic marked pointer specific for GC
29 atomic_marked_ptr m_pNext ; ///< pointer to the next node in the list
30 mutable lock_type m_Lock ; ///< Node lock
32 /// Checks if node is marked
33 bool is_marked() const
35 return m_pNext.load(atomics::memory_order_relaxed).bits() != 0;
43 /// Clears internal fields
46 m_pNext.store( marked_ptr(), atomics::memory_order_release );
49 } // namespace lazy_list
52 /// Lazy ordered single-linked list (template specialization for \ref cds_urcu_desc "RCU")
53 /** @ingroup cds_intrusive_list
54 \anchor cds_intrusive_LazyList_rcu
56 Usually, ordered single-linked list is used as a building block for the hash table implementation.
57 The complexity of searching is <tt>O(N)</tt>.
60 - \p RCU - one of \ref cds_urcu_gc "RCU type"
61 - \p T - type to be stored in the list
62 - \p Traits - type traits. See lazy_list::type_traits for explanation.
64 It is possible to declare option-based list with \p %cds::intrusive::lazy_list::make_traits metafunction istead of \p Traits template
65 argument. Template argument list \p Options of cds::intrusive::lazy_list::make_traits metafunction are:
66 - opt::hook - hook used. Possible values are: lazy_list::base_hook, lazy_list::member_hook, lazy_list::traits_hook.
67 If the option is not specified, <tt>lazy_list::base_hook<></tt> is used.
68 - opt::compare - key comparison functor. No default functor is provided.
69 If the option is not specified, the opt::less is used.
70 - opt::less - specifies binary predicate used for key comparison. Default is \p std::less<T>.
71 - opt::back_off - back-off strategy used. If the option is not specified, the cds::backoff::empty is used.
72 - opt::disposer - the functor used for dispose removed items. Default is opt::v::empty_disposer
73 - opt::rcu_check_deadlock - a deadlock checking policy. Default is opt::v::rcu_throw_deadlock
74 - opt::item_counter - the type of item counting feature. Default is \ref atomicity::empty_item_counter
75 - opt::memory_model - C++ memory ordering model. Can be opt::v::relaxed_ordering (relaxed memory model, the default)
76 or opt::v::sequential_consistent (sequentially consisnent memory model).
79 Before including <tt><cds/intrusive/lazy_list_rcu.h></tt> you should include appropriate RCU header file,
80 see \ref cds_urcu_gc "RCU type" for list of existing RCU class and corresponding header files.
81 For example, for \ref cds_urcu_general_buffered_gc "general-purpose buffered RCU" you should include:
83 #include <cds/urcu/general_buffered.h>
84 #include <cds/intrusive/lazy_list_rcu.h>
86 // Now, you can declare lazy list for type Foo and default traits:
87 typedef cds::intrusive::LazyList<cds::urcu::gc< cds::urcu::general_buffered<> >, Foo > rcu_lazy_list;
94 #ifdef CDS_DOXYGEN_INVOKED
95 ,class Traits = lazy_list::type_traits
100 class LazyList<cds::urcu::gc<RCU>, T, Traits>
103 typedef T value_type ; ///< type of value stored in the list
104 typedef Traits options ; ///< Traits template parameter
106 typedef typename options::hook hook ; ///< hook type
107 typedef typename hook::node_type node_type ; ///< node type
109 # ifdef CDS_DOXYGEN_INVOKED
110 typedef implementation_defined key_comparator ; ///< key compare functor based on opt::compare and opt::less option setter.
112 typedef typename opt::details::make_comparator< value_type, options >::type key_comparator;
115 typedef typename options::disposer disposer ; ///< disposer used
116 typedef typename get_node_traits< value_type, node_type, hook>::type node_traits ; ///< node traits
117 typedef typename lazy_list::get_link_checker< node_type, options::link_checker >::type link_checker ; ///< link checker
119 typedef cds::urcu::gc<RCU> gc ; ///< RCU schema
120 typedef typename options::back_off back_off ; ///< back-off strategy (not used)
121 typedef typename options::item_counter item_counter ; ///< Item counting policy used
122 typedef typename options::memory_model memory_model ; ///< C++ memory ordering (see lazy_list::type_traits::memory_model)
123 typedef typename options::rcu_check_deadlock rcu_check_deadlock ; ///< Deadlock checking policy
125 typedef typename gc::scoped_lock rcu_lock ; ///< RCU scoped lock
126 static CDS_CONSTEXPR const bool c_bExtractLockExternal = true; ///< Group of \p extract_xxx functions require external locking
129 // Rebind options (split-list support)
130 template <typename... Options>
131 struct rebind_options {
135 , typename cds::opt::make_options< options, Options...>::type
141 typedef typename node_type::marked_ptr marked_node_ptr ; ///< Node marked pointer
142 typedef node_type * auxiliary_head ; ///< Auxiliary head type (for split-list support)
145 node_type m_Head ; ///< List head (dummy node)
146 node_type m_Tail; ///< List tail (dummy node)
147 item_counter m_ItemCounter ; ///< Item counter
151 /// Position pointer for item search
153 node_type * pPred ; ///< Previous node
154 node_type * pCur ; ///< Current node
156 /// Locks nodes \p pPred and \p pCur
159 pPred->m_Lock.lock();
163 /// Unlocks nodes \p pPred and \p pCur
166 pCur->m_Lock.unlock();
167 pPred->m_Lock.unlock();
171 class auto_lock_position {
174 auto_lock_position( position& pos )
179 ~auto_lock_position()
185 typedef cds::urcu::details::check_deadlock_policy< gc, rcu_check_deadlock> check_deadlock_policy;
190 static void clear_links( node_type * pNode )
192 pNode->m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed );
195 struct clear_and_dispose {
196 void operator()( value_type * p )
198 assert( p != nullptr );
199 clear_links( node_traits::to_node_ptr(p));
204 static void dispose_node( node_type * pNode )
207 assert( !gc::is_locked() );
209 gc::template retire_ptr<clear_and_dispose>( node_traits::to_value_ptr( *pNode ) );
212 void link_node( node_type * pNode, node_type * pPred, node_type * pCur )
214 assert( pPred->m_pNext.load(memory_model::memory_order_relaxed).ptr() == pCur );
216 pNode->m_pNext.store( marked_node_ptr(pCur), memory_model::memory_order_release );
217 pPred->m_pNext.store( marked_node_ptr(pNode), memory_model::memory_order_release );
220 void unlink_node( node_type * pPred, node_type * pCur, node_type * pHead )
222 assert( pPred->m_pNext.load(memory_model::memory_order_relaxed).ptr() == pCur );
223 assert( pCur != &m_Tail );
225 node_type * pNext = pCur->m_pNext.load(memory_model::memory_order_relaxed).ptr();
226 //pCur->m_pNext.store( marked_node_ptr( pNext, 1), memory_model::memory_order_relaxed) ; // logically deleting
227 pCur->m_pNext.store( marked_node_ptr( pHead, 1 ), memory_model::memory_order_relaxed ) ; // logical deletion + back-link for search
228 pPred->m_pNext.store( marked_node_ptr( pNext ), memory_model::memory_order_relaxed); // physically deleting
229 //pCur->m_pNext.store( marked_node_ptr( pHead, 1 ), memory_model::memory_order_relaxed ) ; // back-link for search
235 typedef cds::urcu::exempt_ptr< gc, value_type, value_type, clear_and_dispose, void > exempt_ptr ; ///< pointer to extracted node
239 template <bool IsConst>
242 friend class LazyList;
245 value_type * m_pNode;
249 assert( m_pNode != nullptr );
251 node_type * pNode = node_traits::to_node_ptr( m_pNode );
252 node_type * pNext = pNode->m_pNext.load(memory_model::memory_order_relaxed).ptr();
253 if ( pNext != nullptr )
254 m_pNode = node_traits::to_value_ptr( pNext );
259 if ( m_pNode != nullptr ) {
260 node_type * pNode = node_traits::to_node_ptr( m_pNode );
262 // Dummy tail node could not be marked
263 while ( pNode->is_marked() )
264 pNode = pNode->m_pNext.load(memory_model::memory_order_relaxed).ptr();
266 if ( pNode != node_traits::to_node_ptr( m_pNode ) )
267 m_pNode = node_traits::to_value_ptr( pNode );
271 iterator_type( node_type * pNode )
273 m_pNode = node_traits::to_value_ptr( pNode );
278 typedef typename cds::details::make_const_type<value_type, IsConst>::pointer value_ptr;
279 typedef typename cds::details::make_const_type<value_type, IsConst>::reference value_ref;
285 iterator_type( iterator_type const& src )
286 : m_pNode( src.m_pNode )
289 value_ptr operator ->() const
294 value_ref operator *() const
296 assert( m_pNode != nullptr );
301 iterator_type& operator ++()
309 iterator_type operator ++(int)
311 iterator_type i(*this);
317 iterator_type& operator = (iterator_type const& src)
319 m_pNode = src.m_pNode;
324 bool operator ==(iterator_type<C> const& i ) const
326 return m_pNode == i.m_pNode;
329 bool operator !=(iterator_type<C> const& i ) const
331 return m_pNode != i.m_pNode;
338 typedef iterator_type<false> iterator;
339 /// Const forward iterator
340 typedef iterator_type<true> const_iterator;
342 /// Returns a forward iterator addressing the first element in a list
344 For empty list \code begin() == end() \endcode
348 iterator it( &m_Head );
349 ++it ; // skip dummy head
353 /// Returns an iterator that addresses the location succeeding the last element in a list
355 Do not use the value returned by <tt>end</tt> function to access any item.
357 The returned value can be used only to control reaching the end of the list.
358 For empty list \code begin() == end() \endcode
362 return iterator( &m_Tail );
365 /// Returns a forward const iterator addressing the first element in a list
367 const_iterator begin() const
369 return get_const_begin();
371 const_iterator cbegin()
373 return get_const_begin();
377 /// Returns an const iterator that addresses the location succeeding the last element in a list
379 const_iterator end() const
381 return get_const_end();
383 const_iterator cend()
385 return get_const_end();
391 const_iterator get_const_begin() const
393 const_iterator it( const_cast<node_type *>( &m_Head ));
394 ++it ; // skip dummy head
397 const_iterator get_const_end() const
399 return const_iterator( const_cast<node_type *>( &m_Tail ));
404 /// Default constructor initializes empty list
407 static_assert( (std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type" );
408 m_Head.m_pNext.store( marked_node_ptr( &m_Tail ), memory_model::memory_order_relaxed );
411 /// Destroys the list object
416 assert( m_Head.m_pNext.load(memory_model::memory_order_relaxed).ptr() == &m_Tail );
417 m_Head.m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed );
422 The function inserts \p val in the list if the list does not contain
423 an item with key equal to \p val.
425 Returns \p true if \p val is linked into the list, \p false otherwise.
427 bool insert( value_type& val )
429 return insert_at( &m_Head, val );
434 This function is intended for derived non-intrusive containers.
436 The function allows to split new item creating into two part:
437 - create item with key only
438 - insert new item into the list
439 - if inserting is success, calls \p f functor to initialize value-field of \p val.
441 The functor signature is:
443 void func( value_type& val );
445 where \p val is the item inserted.
446 While the functor \p f is working the item \p val is locked.
447 The user-defined functor is called only if the inserting is success.
449 @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
451 template <typename Func>
452 bool insert( value_type& val, Func f )
454 return insert_at( &m_Head, val, f );
457 /// Ensures that the \p item exists in the list
459 The operation performs inserting or changing data with lock-free manner.
461 If the item \p val not found in the list, then \p val is inserted into the list.
462 Otherwise, the functor \p func is called with item found.
463 The functor signature is:
466 void operator()( bool bNew, value_type& item, value_type& val );
470 - \p bNew - \p true if the item has been inserted, \p false otherwise
471 - \p item - item of the list
472 - \p val - argument \p val passed into the \p ensure function
473 If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
474 refers to the same thing.
476 The functor may change non-key fields of the \p item.
477 While the functor \p f is calling the item \p item is locked.
479 Returns <tt> std::pair<bool, bool> </tt> where \p first is true if operation is successfull,
480 \p second is true if new item has been added or \p false if the item with \p key
481 already is in the list.
483 @warning See \ref cds_intrusive_item_creating "insert item troubleshooting"
486 template <typename Func>
487 std::pair<bool, bool> ensure( value_type& val, Func func )
489 return ensure_at( &m_Head, val, func );
492 /// Unlinks the item \p val from the list
494 The function searches the item \p val in the list and unlink it from the list
495 if it is found and it is equal to \p val.
497 Difference between \ref erase and \p unlink functions: \p erase finds <i>a key</i>
498 and deletes the item found. \p unlink finds an item by key and deletes it
499 only if \p val is an item of that list, i.e. the pointer to item found
500 is equal to <tt> &val </tt>.
502 The function returns \p true if success and \p false otherwise.
504 RCU \p synchronize method can be called.
505 Note that depending on RCU type used the \ref disposer call can be deferred.
507 The function can throw cds::urcu::rcu_deadlock exception if deadlock is encountered and
508 deadlock checking policy is opt::v::rcu_throw_deadlock.
510 bool unlink( value_type& val )
512 return unlink_at( &m_Head, val );
515 /// Deletes the item from the list
516 /** \anchor cds_intrusive_LazyList_rcu_find_erase
517 The function searches an item with key equal to \p val in the list,
518 unlinks it from the list, and returns \p true.
519 If the item with the key equal to \p val is not found the function return \p false.
521 RCU \p synchronize method can be called.
522 Note that depending on RCU type used the \ref disposer call can be deferred.
524 The function can throw \ref cds_urcu_rcu_deadlock "cds::urcu::rcu_deadlock" exception if deadlock is encountered and
525 deadlock checking policy is opt::v::rcu_throw_deadlock.
527 template <typename Q>
528 bool erase( Q const& val )
530 return erase_at( &m_Head, val, key_comparator() );
533 /// Deletes the item from the list using \p pred predicate for searching
535 The function is an analog of \ref cds_intrusive_LazyList_rcu_find_erase "erase(Q const&)"
536 but \p pred is used for key comparing.
537 \p Less functor has the interface like \p std::less.
538 \p pred must imply the same element order as the comparator used for building the list.
540 template <typename Q, typename Less>
541 bool erase_with( Q const& val, Less pred )
543 return erase_at( &m_Head, val, cds::opt::details::make_comparator_from_less<Less>());
547 /// Deletes the item from the list
548 /** \anchor cds_intrusive_LazyList_rcu_find_erase_func
549 The function searches an item with key equal to \p val in the list,
550 call \p func functor with item found, unlinks it from the list, and returns \p true.
551 The \p Func interface is
554 void operator()( value_type const& item );
557 The functor may be passed by reference using <tt>boost:ref</tt>
559 If the item with the key equal to \p val is not found the function return \p false.
561 RCU \p synchronize method can be called.
562 Note that depending on RCU type used the \ref disposer call can be deferred.
564 The function can throw \ref cds_urcu_rcu_deadlock "cds::urcu::rcu_deadlock" exception if deadlock is encountered and
565 deadlock checking policy is opt::v::rcu_throw_deadlock.
567 template <typename Q, typename Func>
568 bool erase( Q const& val, Func func )
570 return erase_at( &m_Head, val, key_comparator(), func );
573 /// Deletes the item from the list using \p pred predicate for searching
575 The function is an analog of \ref cds_intrusive_LazyList_rcu_find_erase_func "erase(Q const&, Func)"
576 but \p pred is used for key comparing.
577 \p Less functor has the interface like \p std::less.
578 \p pred must imply the same element order as the comparator used for building the list.
580 template <typename Q, typename Less, typename Func>
581 bool erase_with( Q const& val, Less pred, Func func )
583 return erase_at( &m_Head, val, cds::opt::details::make_comparator_from_less<Less>(), func );
586 /// Extracts an item from the list
588 \anchor cds_intrusive_LazyList_rcu_extract
589 The function searches an item with key equal to \p val in the list,
590 unlinks it from the list, and returns pointer to an item found in \p dest parameter.
591 If the item with the key equal to \p val is not found the function returns \p false,
594 @note The function does NOT call RCU read-side lock or synchronization,
595 and does NOT dispose the item found. It just excludes the item from the list
596 and returns a pointer to item found.
597 You should lock RCU before calling this function, and you should manually synchronize RCU
598 outside the RCU lock region before reusing returned pointer.
601 #include <cds/urcu/general_buffered.h>
602 #include <cds/intrusive/lazy_list_rcu.h>
604 typedef cds::urcu::gc< general_buffered<> > rcu;
605 typedef cds::intrusive::LazyList< rcu, Foo > rcu_lazy_list;
607 rcu_lazy_list theList;
610 rcu_lazy_list::exempt_ptr p1;
612 // first, we should lock RCU
615 // Now, you can apply extract function
616 // Note that you must not delete the item found inside the RCU lock
617 if ( theList.extract( p1, 10 )) {
618 // do something with p1
623 // We may safely release p1 here
624 // release() passes the pointer to RCU reclamation cycle:
625 // it invokes RCU retire_ptr function with the disposer you provided for the list.
629 template <typename Q>
630 bool extract( exempt_ptr& dest, Q const& val )
632 dest = extract_at( &m_Head, val, key_comparator() );
633 return !dest.empty();
636 /// Extracts an item from the list using \p pred predicate for searching
638 This function is the analog for \ref cds_intrusive_LazyList_rcu_extract "extract(exempt_ptr&, Q const&)".
640 The \p pred is a predicate used for key comparing.
641 \p Less has the interface like \p std::less.
642 \p pred must imply the same element order as \ref key_comparator.
644 template <typename Q, typename Less>
645 bool extract_with( exempt_ptr& dest, Q const& val, Less pred )
647 dest = extract_at( &m_Head, val, cds::opt::details::make_comparator_from_less<Less>() );
648 return !dest.empty();
651 /// Finds the key \p val
652 /** \anchor cds_intrusive_LazyList_rcu_find_func
653 The function searches the item with key equal to \p val
654 and calls the functor \p f for item found.
655 The interface of \p Func functor is:
658 void operator()( value_type& item, Q& val );
661 where \p item is the item found, \p val is the <tt>find</tt> function argument.
663 You may pass \p f argument by reference using \p std::ref.
665 The functor may change non-key fields of \p item.
666 While the functor \p f is calling the item found \p item is locked.
668 The function returns \p true if \p val is found, \p false otherwise.
670 template <typename Q, typename Func>
671 bool find( Q& val, Func f ) const
673 return find_at( const_cast<node_type *>( &m_Head ), val, key_comparator(), f );
676 /// Finds the key \p val using \p pred predicate for searching
678 The function is an analog of \ref cds_intrusive_LazyList_rcu_find_func "find(Q&, Func)"
679 but \p pred is used for key comparing.
680 \p Less functor has the interface like \p std::less.
681 \p pred must imply the same element order as the comparator used for building the list.
683 template <typename Q, typename Less, typename Func>
684 bool find_with( Q& val, Less pred, Func f ) const
686 return find_at( const_cast<node_type *>( &m_Head ), val, cds::opt::details::make_comparator_from_less<Less>(), f );
689 /// Finds the key \p val
690 /** \anchor cds_intrusive_LazyList_rcu_find_cfunc
691 The function searches the item with key equal to \p val
692 and calls the functor \p f for item found.
693 The interface of \p Func functor is:
696 void operator()( value_type& item, Q const& val );
699 where \p item is the item found, \p val is the <tt>find</tt> function argument.
701 You may pass \p f argument by reference using \p std::ref.
703 The functor may change non-key fields of \p item.
704 While the functor \p f is calling the item found \p item is locked.
706 The function returns \p true if \p val is found, \p false otherwise.
708 template <typename Q, typename Func>
709 bool find( Q const& val, Func f ) const
711 return find_at( const_cast<node_type *>( &m_Head ), val, key_comparator(), f );
714 /// Finds the key \p val using \p pred predicate for searching
716 The function is an analog of \ref cds_intrusive_LazyList_rcu_find_cfunc "find(Q&, Func)"
717 but \p pred is used for key comparing.
718 \p Less functor has the interface like \p std::less.
719 \p pred must imply the same element order as the comparator used for building the list.
721 template <typename Q, typename Less, typename Func>
722 bool find_with( Q const& val, Less pred, Func f ) const
724 return find_at( const_cast<node_type *>( &m_Head ), val, cds::opt::details::make_comparator_from_less<Less>(), f );
727 /// Finds the key \p val
728 /** \anchor cds_intrusive_LazyList_rcu_find_val
729 The function searches the item with key equal to \p val
730 and returns \p true if \p val found or \p false otherwise.
732 template <typename Q>
733 bool find( Q const& val ) const
735 return find_at( const_cast<node_type *>( &m_Head ), val, key_comparator() );
738 /// Finds the key \p val using \p pred predicate for searching
740 The function is an analog of \ref cds_intrusive_LazyList_rcu_find_val "find(Q const&)"
741 but \p pred is used for key comparing.
742 \p Less functor has the interface like \p std::less.
743 \p pred must imply the same element order as the comparator used for building the list.
745 template <typename Q, typename Less>
746 bool find_with( Q const& val, Less pred ) const
748 return find_at( const_cast<node_type *>( &m_Head ), val, cds::opt::details::make_comparator_from_less<Less>() );
751 /// Finds the key \p val and return the item found
752 /** \anchor cds_intrusive_LazyList_rcu_get
753 The function searches the item with key equal to \p val and returns the pointer to item found.
754 If \p val is not found it returns \p nullptr.
756 Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
758 RCU should be locked before call of this function.
759 Returned item is valid only while RCU is locked:
761 typedef cds::intrusive::LazyList< cds::urcu::gc< cds::urcu::general_buffered<> >, foo, my_traits > ord_list;
766 ord_list::rcu_lock lock;
768 foo * pVal = theList.get( 5 );
773 // Unlock RCU by rcu_lock destructor
774 // pVal can be retired by disposer at any time after RCU has been unlocked
778 template <typename Q>
779 value_type * get( Q const& val ) const
781 return get_at( const_cast<node_type *>( &m_Head ), val, key_comparator());
784 /// Finds the key \p val and return the item found
786 The function is an analog of \ref cds_intrusive_LazyList_rcu_get "get(Q const&)"
787 but \p pred is used for comparing the keys.
789 \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
791 \p pred must imply the same element order as the comparator used for building the list.
793 template <typename Q, typename Less>
794 value_type * get_with( Q const& val, Less pred ) const
796 return get_at( const_cast<node_type *>( &m_Head ), val, cds::opt::details::make_comparator_from_less<Less>());
799 /// Clears the list using default disposer
801 The function clears the list using default (provided in class template) disposer functor.
803 RCU \p synchronize method can be called.
804 Note that depending on RCU type used the \ref disposer call can be deferred.
806 The function can throw cds::urcu::rcu_deadlock exception if deadlock is encountered and
807 deadlock checking policy is opt::v::rcu_throw_deadlock.
812 check_deadlock_policy::check();
818 pHead = m_Head.m_pNext.load(memory_model::memory_order_acquire).ptr();
819 if ( pHead == &m_Tail )
822 m_Head.m_Lock.lock();
823 pHead->m_Lock.lock();
825 if ( m_Head.m_pNext.load(memory_model::memory_order_relaxed).all() == pHead )
826 unlink_node( &m_Head, pHead, &m_Head );
828 pHead->m_Lock.unlock();
829 m_Head.m_Lock.unlock();
833 dispose_node( pHead );
838 /// Checks if the list is empty
841 return m_Head.m_pNext.load(memory_model::memory_order_relaxed).ptr() == &m_Tail;
844 /// Returns list's item count
846 The value returned depends on opt::item_counter option. For atomicity::empty_item_counter,
847 this function always returns 0.
849 <b>Warning</b>: even if you use real item counter and it returns 0, this fact is not mean that the list
850 is empty. To check list emptyness use \ref empty() method.
854 return m_ItemCounter.value();
859 // split-list support
860 bool insert_aux_node( node_type * pNode )
862 return insert_aux_node( &m_Head, pNode );
865 // split-list support
866 bool insert_aux_node( node_type * pHead, node_type * pNode )
868 assert( pHead != nullptr );
869 assert( pNode != nullptr );
871 // Hack: convert node_type to value_type.
872 // In principle, auxiliary node can be non-reducible to value_type
873 // We assume that comparator can correctly distinguish aux and regular node.
874 return insert_at( pHead, *node_traits::to_value_ptr( pNode ) );
877 bool insert_at( node_type * pHead, value_type& val, bool bLock = true )
879 link_checker::is_empty( node_traits::to_node_ptr( val ) );
885 search( pHead, val, pos );
887 auto_lock_position alp( pos );
888 if ( validate( pos.pPred, pos.pCur )) {
889 if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
890 // failed: key already in list
894 link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
903 template <typename Func>
904 bool insert_at( node_type * pHead, value_type& val, Func f )
906 link_checker::is_empty( node_traits::to_node_ptr( val ) );
912 search( pHead, val, pos );
914 auto_lock_position alp( pos );
915 if ( validate( pos.pPred, pos.pCur )) {
916 if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
917 // failed: key already in list
921 link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
931 iterator insert_at_( node_type * pHead, value_type& val, bool bLock = true )
934 if ( insert_at( pHead, val, false ))
935 return iterator( node_traits::to_node_ptr( val ));
940 template <typename Func>
941 std::pair<iterator, bool> ensure_at_( node_type * pHead, value_type& val, Func func, bool bLock = true )
948 search( pHead, val, pos );
950 auto_lock_position alp( pos );
951 if ( validate( pos.pPred, pos.pCur )) {
952 if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
953 // key already in the list
955 func( false, *node_traits::to_value_ptr( *pos.pCur ) , val );
956 return std::make_pair( iterator( pos.pCur ), false );
960 link_checker::is_empty( node_traits::to_node_ptr( val ) );
962 link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
963 func( true, val, val );
965 return std::make_pair( iterator( node_traits::to_node_ptr( val )), true );
972 template <typename Func>
973 std::pair<bool, bool> ensure_at( node_type * pHead, value_type& val, Func func, bool bLock = true )
976 std::pair<iterator, bool> ret = ensure_at_( pHead, val, func, false );
977 return std::make_pair( ret.first != end(), ret.second );
980 bool unlink_at( node_type * pHead, value_type& val )
984 check_deadlock_policy::check();
990 search( pHead, val, pos );
992 auto_lock_position alp( pos );
993 if ( validate( pos.pPred, pos.pCur ) ) {
994 if ( pos.pCur != &m_Tail
995 && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0
996 && node_traits::to_value_ptr( pos.pCur ) == &val )
999 unlink_node( pos.pPred, pos.pCur, pHead );
1010 if ( nResult > 0 ) {
1011 dispose_node( pos.pCur );
1019 template <typename Q, typename Compare, typename Func>
1020 bool erase_at( node_type * pHead, Q const& val, Compare cmp, Func f, position& pos )
1022 check_deadlock_policy::check();
1028 search( pHead, val, pos, cmp );
1030 auto_lock_position alp( pos );
1031 if ( validate( pos.pPred, pos.pCur )) {
1032 if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
1034 unlink_node( pos.pPred, pos.pCur, pHead );
1035 f( *node_traits::to_value_ptr( *pos.pCur ) );
1047 if ( nResult > 0 ) {
1048 dispose_node( pos.pCur );
1056 template <typename Q, typename Compare, typename Func>
1057 bool erase_at( node_type * pHead, Q const& val, Compare cmp, Func f )
1060 return erase_at( pHead, val, cmp, f, pos );
1063 template <typename Q, typename Compare>
1064 bool erase_at( node_type * pHead, Q const& val, Compare cmp )
1067 return erase_at( pHead, val, cmp, [](value_type const &){}, pos );
1070 template <typename Q, typename Compare>
1071 value_type * extract_at( node_type * pHead, Q const& val, Compare cmp )
1074 assert( gc::is_locked() ) ; // RCU must be locked!!!
1077 search( pHead, val, pos, cmp );
1080 auto_lock_position alp( pos );
1081 if ( validate( pos.pPred, pos.pCur )) {
1082 if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
1084 unlink_node( pos.pPred, pos.pCur, pHead );
1096 return node_traits::to_value_ptr( pos.pCur );
1102 template <typename Q, typename Compare, typename Func>
1103 bool find_at( node_type * pHead, Q& val, Compare cmp, Func f, bool bLock = true ) const
1107 rcu_lock l( bLock );
1108 search( pHead, val, pos, cmp );
1109 if ( pos.pCur != &m_Tail ) {
1110 std::unique_lock< typename node_type::lock_type> al( pos.pCur->m_Lock );
1111 if ( cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 )
1113 f( *node_traits::to_value_ptr( *pos.pCur ), val );
1120 template <typename Q, typename Compare>
1121 bool find_at( node_type * pHead, Q& val, Compare cmp ) const
1124 return find_at_( pHead, val, cmp ) != end();
1127 template <typename Q, typename Compare>
1128 const_iterator find_at_( node_type * pHead, Q& val, Compare cmp ) const
1130 assert( gc::is_locked() );
1134 search( pHead, val, pos, cmp );
1135 if ( pos.pCur != &m_Tail ) {
1136 std::unique_lock< typename node_type::lock_type> al( pos.pCur->m_Lock );
1137 if ( cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 )
1139 return const_iterator( pos.pCur );
1145 template <typename Q, typename Compare>
1146 value_type * get_at( node_type * pHead, Q const& val, Compare cmp ) const
1148 value_type * pFound = nullptr;
1149 return find_at( pHead, val, cmp, [&pFound](value_type& found, Q const& ) { pFound = &found; } )
1157 template <typename Q>
1158 void search( node_type * pHead, Q const& key, position& pos ) const
1160 search( pHead, key, pos, key_comparator() );
1163 template <typename Q, typename Compare>
1164 void search( node_type * pHead, Q const& key, position& pos, Compare cmp ) const
1166 // RCU should be locked!!!
1167 assert( gc::is_locked() );
1169 node_type const* pTail = &m_Tail;
1171 marked_node_ptr pCur(pHead);
1172 marked_node_ptr pPrev(pHead);
1174 while ( pCur.ptr() != pTail && ( pCur.ptr() == pHead || cmp( *node_traits::to_value_ptr( *pCur.ptr() ), key ) < 0 )) {
1176 pCur = pCur->m_pNext.load(memory_model::memory_order_relaxed);
1179 pos.pCur = pCur.ptr();
1180 pos.pPred = pPrev.ptr();
1183 static bool validate( node_type * pPred, node_type * pCur )
1185 // RCU lock should be locked!!!
1186 assert( gc::is_locked() );
1188 return !pPred->is_marked()
1189 && !pCur->is_marked()
1190 && pPred->m_pNext.load(memory_model::memory_order_relaxed) == pCur;
1196 }} // namespace cds::intrusive
1198 #endif // #ifndef __CDS_INTRUSIVE_LAZY_LIST_RCU_H