2 This file is a part of libcds - Concurrent Data Structures library
4 (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2016
6 Source code repo: http://github.com/khizmax/libcds/
7 Download: http://sourceforge.net/projects/libcds/files/
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions are met:
12 * Redistributions of source code must retain the above copyright notice, this
13 list of conditions and the following disclaimer.
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.
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.
31 #ifndef CDSLIB_INTRUSIVE_IMPL_LAZY_LIST_H
32 #define CDSLIB_INTRUSIVE_IMPL_LAZY_LIST_H
34 #include <mutex> // unique_lock
35 #include <cds/intrusive/details/lazy_list_base.h>
37 namespace cds { namespace intrusive {
39 /// Lazy ordered single-linked list
40 /** @ingroup cds_intrusive_list
41 \anchor cds_intrusive_LazyList_hp
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>.
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"
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.
56 - \p GC - Garbage collector used. Note the \p GC must be the same as the GC used for item type \p T (see lazy_list::node).
57 - \p T - type to be stored in the list. The type must be based on lazy_list::node (for lazy_list::base_hook)
58 or it must have a member of type lazy_list::node (for lazy_list::member_hook).
59 - \p Traits - type traits. See lazy_list::traits for explanation.
60 It is possible to declare option-based list with cds::intrusive::lazy_list::make_traits metafunction instead of \p Traits template
61 argument. For example, the following traits-based declaration of \p gc::HP lazy list
63 #include <cds/intrusive/lazy_list_hp.h>
64 // Declare item stored in your list
65 struct item: public cds::intrusive::lazy_list::node< cds::gc::HP >
68 // Declare comparator for the item
69 struct my_compare { ... }
72 struct my_traits: public cds::intrusive::lazy_list::traits
74 typedef cds::intrusive::lazy_list::base_hook< cds::opt::gc< cds::gc::HP > > hook;
75 typedef my_compare compare;
78 // Declare traits-based list
79 typedef cds::intrusive::LazyList< cds::gc::HP, item, my_traits > traits_based_list;
81 is equivalent for the following option-based list
83 #include <cds/intrusive/lazy_list_hp.h>
85 // item struct and my_compare are the same
87 // Declare option-based list
88 typedef cds::intrusive::LazyList< cds::gc::HP, item,
89 typename cds::intrusive::lazy_list::make_traits<
90 cds::intrusive::opt::hook< cds::intrusive::lazy_list::base_hook< cds::opt::gc< cds::gc::HP > > > // hook option
91 ,cds::intrusive::opt::compare< my_compare > // item comparator option
97 There are different specializations of this template for each garbage collecting schema used.
98 You should select GC needed and include appropriate .h-file:
99 - for gc::HP: \code #include <cds/intrusive/lazy_list_hp.h> \endcode
100 - for gc::DHP: \code #include <cds/intrusive/lazy_list_dhp.h> \endcode
101 - for gc::nogc: \code #include <cds/intrusive/lazy_list_nogc.h> \endcode
102 - for \ref cds_urcu_type "RCU" - see \ref cds_intrusive_LazyList_rcu "LazyList RCU specialization"
104 Then, you should incorporate lazy_list::node into your struct \p T and provide
105 appropriate \p lazy_list::traits::hook in your \p Traits template parameters. Usually, for \p Traits
106 a struct based on \p lazy_list::traits should be defined.
108 Example for gc::DHP and base hook:
110 // Include GC-related lazy list specialization
111 #include <cds/intrusive/lazy_list_dhp.h>
113 // Data stored in lazy list
114 struct my_data: public cds::intrusive::lazy_list::node< cds::gc::DHP >
123 // my_data comparing functor
125 int operator()( const my_data& d1, const my_data& d2 )
127 return d1.strKey.compare( d2.strKey );
130 int operator()( const my_data& d, const std::string& s )
132 return d.strKey.compare(s);
135 int operator()( const std::string& s, const my_data& d )
137 return s.compare( d.strKey );
142 struct my_traits: public cds::intrusive::lazy_list::traits
144 typedef cds::intrusive::lazy_list::base_hook< cds::opt::gc< cds::gc::DHP > > hook;
145 typedef my_data_cmp compare;
149 typedef cds::intrusive::LazyList< cds::gc::DHP, my_data, my_traits > traits_based_list;
152 Equivalent option-based code:
154 // GC-related specialization
155 #include <cds/intrusive/lazy_list_dhp.h>
164 // Declare option-based list
165 typedef cds::intrusive::LazyList< cds::gc::DHP
167 , typename cds::intrusive::lazy_list::make_traits<
168 cds::intrusive::opt::hook< cds::intrusive::lazy_list::base_hook< cds::opt::gc< cds::gc::DHP > > >
169 ,cds::intrusive::opt::compare< my_data_cmp >
178 #ifdef CDS_DOXYGEN_INVOKED
179 ,class Traits = lazy_list::traits
187 typedef GC gc; ///< Garbage collector
188 typedef T value_type; ///< type of value stored in the list
189 typedef Traits traits; ///< Traits template parameter
191 typedef typename traits::hook hook; ///< hook type
192 typedef typename hook::node_type node_type; ///< node type
194 # ifdef CDS_DOXYGEN_INVOKED
195 typedef implementation_defined key_comparator; ///< key comparison functor based on opt::compare and opt::less option setter.
197 typedef typename opt::details::make_comparator< value_type, traits >::type key_comparator;
200 typedef typename traits::disposer disposer; ///< disposer
201 typedef typename get_node_traits< value_type, node_type, hook>::type node_traits; ///< node traits
202 typedef typename lazy_list::get_link_checker< node_type, traits::link_checker >::type link_checker; ///< link checker
204 typedef typename traits::back_off back_off; ///< back-off strategy
205 typedef typename traits::item_counter item_counter; ///< Item counting policy used
206 typedef typename traits::memory_model memory_model; ///< C++ memory ordering (see \p lazy_list::traits::memory_model)
207 typedef typename traits::stat stat; ///< Internal statistics
209 static_assert((std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type");
211 typedef typename gc::template guarded_ptr< value_type > guarded_ptr; ///< Guarded pointer
213 static CDS_CONSTEXPR const size_t c_nHazardPtrCount = 4; ///< Count of hazard pointer required for the algorithm
216 // Rebind traits (split-list support)
217 template <typename... Options>
218 struct rebind_traits {
222 , typename cds::opt::make_options< traits, Options...>::type
228 typedef typename node_type::marked_ptr marked_node_ptr; ///< Node marked pointer
229 typedef node_type * auxiliary_head; ///< Auxiliary head type (for split-list support)
236 item_counter m_ItemCounter;
237 stat m_Stat; ///< Internal statistics
239 struct clean_disposer {
240 void operator()( value_type * p )
242 lazy_list::node_cleaner<gc, node_type, memory_model>()( node_traits::to_node_ptr( p ));
247 /// Position pointer for item search
249 node_type * pPred; ///< Previous node
250 node_type * pCur; ///< Current node
252 typename gc::template GuardArray<2> guards; ///< Guards array
259 /// Locks nodes \p pPred and \p pCur
262 pPred->m_Lock.lock();
266 /// Unlocks nodes \p pPred and \p pCur
269 pCur->m_Lock.unlock();
270 pPred->m_Lock.unlock();
274 typedef std::unique_lock< position > scoped_position_lock;
279 void link_node( node_type * pNode, node_type * pPred, node_type * pCur )
281 assert( pPred->m_pNext.load(memory_model::memory_order_relaxed).ptr() == pCur );
282 link_checker::is_empty( pNode );
284 pNode->m_pNext.store( marked_node_ptr(pCur), memory_model::memory_order_release );
285 pPred->m_pNext.store( marked_node_ptr(pNode), memory_model::memory_order_release );
288 void unlink_node( node_type * pPred, node_type * pCur, node_type * pHead )
290 assert( pPred->m_pNext.load(memory_model::memory_order_relaxed).ptr() == pCur );
292 node_type * pNext = pCur->m_pNext.load(memory_model::memory_order_relaxed).ptr();
293 pCur->m_pNext.store( marked_node_ptr( pHead, 1 ), memory_model::memory_order_release ); // logical removal + back-link for search
294 pPred->m_pNext.store( marked_node_ptr( pNext ), memory_model::memory_order_release); // physically deleting
297 void retire_node( node_type * pNode )
299 assert( pNode != nullptr );
300 gc::template retire<clean_disposer>( node_traits::to_value_ptr( *pNode ));
306 template <bool IsConst>
309 friend class LazyList;
312 value_type * m_pNode;
313 typename gc::Guard m_Guard;
317 assert( m_pNode != nullptr );
320 typename gc::Guard g;
321 node_type * pCur = node_traits::to_node_ptr( m_pNode );
322 if ( pCur->m_pNext.load( memory_model::memory_order_relaxed ).ptr() != nullptr ) { // if pCur is not tail node
325 pNext = pCur->m_pNext.load(memory_model::memory_order_relaxed).ptr();
326 g.assign( node_traits::to_value_ptr( pNext ));
327 } while ( pNext != pCur->m_pNext.load(memory_model::memory_order_relaxed).ptr());
329 m_pNode = m_Guard.assign( g.template get<value_type>());
336 if ( m_pNode != nullptr ) {
337 typename gc::Guard g;
338 node_type * pNode = node_traits::to_node_ptr( m_pNode );
340 // Dummy tail node could not be marked
341 while ( pNode->is_marked()) {
342 node_type * p = pNode->m_pNext.load(memory_model::memory_order_relaxed).ptr();
343 g.assign( node_traits::to_value_ptr( p ));
344 if ( p == pNode->m_pNext.load(memory_model::memory_order_relaxed).ptr())
347 if ( pNode != node_traits::to_node_ptr( m_pNode ))
348 m_pNode = m_Guard.assign( g.template get<value_type>());
352 iterator_type( node_type * pNode )
354 m_pNode = m_Guard.assign( node_traits::to_value_ptr( pNode ));
359 typedef typename cds::details::make_const_type<value_type, IsConst>::pointer value_ptr;
360 typedef typename cds::details::make_const_type<value_type, IsConst>::reference value_ref;
366 iterator_type( iterator_type const& src )
369 m_pNode = m_Guard.assign( src.m_pNode );
375 value_ptr operator ->() const
380 value_ref operator *() const
382 assert( m_pNode != nullptr );
387 iterator_type& operator ++()
394 iterator_type& operator = (iterator_type const& src)
396 m_pNode = src.m_pNode;
397 m_Guard.assign( m_pNode );
402 bool operator ==(iterator_type<C> const& i ) const
404 return m_pNode == i.m_pNode;
407 bool operator !=(iterator_type<C> const& i ) const
409 return m_pNode != i.m_pNode;
415 ///@name Forward iterators (only for debugging purpose)
419 The forward iterator for lazy list has some features:
420 - it has no post-increment operator
421 - to protect the value, the iterator contains a GC-specific guard + another guard is required locally for increment operator.
422 For some GC (\p gc::HP), a guard is limited resource per thread, so an exception (or assertion) "no free guard"
423 may be thrown if a limit of guard count per thread is exceeded.
424 - The iterator cannot be moved across thread boundary since it contains GC's guard that is thread-private GC data.
425 - Iterator ensures thread-safety even if you delete the item that iterator points to. However, in case of concurrent
426 deleting operations it is no guarantee that you iterate all item in the list.
427 Moreover, a crash is possible when you try to iterate the next element that has been deleted by concurrent thread.
429 @warning Use this iterator on the concurrent container for debugging purpose only.
431 typedef iterator_type<false> iterator;
432 /// Const forward iterator
434 For iterator's features and requirements see \ref iterator
436 typedef iterator_type<true> const_iterator;
438 /// Returns a forward iterator addressing the first element in a list
440 For empty list \code begin() == end() \endcode
444 iterator it( &m_Head );
445 ++it ; // skip dummy head
449 /// Returns an iterator that addresses the location succeeding the last element in a list
451 Do not use the value returned by <tt>end</tt> function to access any item.
453 The returned value can be used only to control reaching the end of the list.
454 For empty list \code begin() == end() \endcode
458 return iterator( &m_Tail );
461 /// Returns a forward const iterator addressing the first element in a list
462 const_iterator begin() const
464 return get_const_begin();
467 /// Returns a forward const iterator addressing the first element in a list
468 const_iterator cbegin() const
470 return get_const_begin();
473 /// Returns an const iterator that addresses the location succeeding the last element in a list
474 const_iterator end() const
476 return get_const_end();
479 /// Returns an const iterator that addresses the location succeeding the last element in a list
480 const_iterator cend() const
482 return get_const_end();
488 const_iterator get_const_begin() const
490 const_iterator it( const_cast<node_type *>( &m_Head ));
491 ++it ; // skip dummy head
494 const_iterator get_const_end() const
496 return const_iterator( const_cast<node_type *>(&m_Tail));
501 /// Default constructor initializes empty list
504 m_Head.m_pNext.store( marked_node_ptr( &m_Tail ), memory_model::memory_order_relaxed );
508 template <typename Stat, typename = std::enable_if<std::is_same<stat, lazy_list::wrapped_stat<Stat>>::value >>
509 explicit LazyList( Stat& st )
512 m_Head.m_pNext.store( marked_node_ptr( &m_Tail ), memory_model::memory_order_relaxed );
516 /// Destroys the list object
520 assert( m_Head.m_pNext.load( memory_model::memory_order_relaxed ).ptr() == &m_Tail );
521 m_Head.m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed );
526 The function inserts \p val in the list if the list does not contain
527 an item with key equal to \p val.
529 Returns \p true if \p val is linked into the list, \p false otherwise.
531 bool insert( value_type& val )
533 return insert_at( &m_Head, val );
538 This function is intended for derived non-intrusive containers.
540 The function allows to split new item creating into two part:
541 - create item with key only
542 - insert new item into the list
543 - if inserting is success, calls \p f functor to initialize value-field of \p val.
545 The functor signature is:
547 void func( value_type& val );
549 where \p val is the item inserted.
550 While the functor \p f is called the item \p val is locked so
551 the functor has an exclusive access to the item.
552 The user-defined functor is called only if the inserting is success.
554 template <typename Func>
555 bool insert( value_type& val, Func f )
557 return insert_at( &m_Head, val, f );
562 The operation performs inserting or changing data with lock-free manner.
564 If the item \p val not found in the list, then \p val is inserted into the list
565 iff \p bAllowInsert is \p true.
566 Otherwise, the functor \p func is called with item found.
567 The functor signature is:
570 void operator()( bool bNew, value_type& item, value_type& val );
574 - \p bNew - \p true if the item has been inserted, \p false otherwise
575 - \p item - item of the list
576 - \p val - argument \p val passed into the \p update() function
577 If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
578 refer to the same thing.
580 The functor may change non-key fields of the \p item.
581 While the functor \p f is working the item \p item is locked,
582 so \p func has exclusive access to the item.
584 Returns <tt> std::pair<bool, bool> </tt> where \p first is \p true if operation is successful,
585 \p second is \p true if new item has been added or \p false if the item with \p key
586 already is in the list.
588 The function makes RCU lock internally.
590 template <typename Func>
591 std::pair<bool, bool> update( value_type& val, Func func, bool bAllowInsert = true )
593 return update_at( &m_Head, val, func, bAllowInsert );
596 template <typename Func>
597 CDS_DEPRECATED("ensure() is deprecated, use update()")
598 std::pair<bool, bool> ensure( value_type& val, Func func )
600 return update( val, func, true );
604 /// Unlinks the item \p val from the list
606 The function searches the item \p val in the list and unlink it from the list
607 if it is found and it is equal to \p val.
609 Difference between \ref erase and \p unlink functions: \p erase finds <i>a key</i>
610 and deletes the item found. \p unlink finds an item by key and deletes it
611 only if \p val is an item of that list, i.e. the pointer to item found
612 is equal to <tt> &val </tt>.
614 The function returns \p true if success and \p false otherwise.
616 \p disposer specified in \p Traits is called for unlinked item.
618 bool unlink( value_type& val )
620 return unlink_at( &m_Head, val );
623 /// Deletes the item from the list
624 /** \anchor cds_intrusive_LazyList_hp_erase_val
625 The function searches an item with key equal to \p key in the list,
626 unlinks it from the list, and returns \p true.
627 If the item with the key equal to \p key is not found the function return \p false.
629 \p disposer specified in \p Traits is called for deleted item.
631 template <typename Q>
632 bool erase( Q const& key )
634 return erase_at( &m_Head, key, key_comparator());
637 /// Deletes the item from the list using \p pred predicate for searching
639 The function is an analog of \ref cds_intrusive_LazyList_hp_erase_val "erase(Q const&)"
640 but \p pred is used for key comparing.
641 \p Less functor has the interface like \p std::less.
642 \p pred must imply the same element order as the comparator used for building the list.
644 \p disposer specified in \p Traits is called for deleted item.
646 template <typename Q, typename Less>
647 bool erase_with( Q const& key, Less pred )
650 return erase_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>());
653 /// Deletes the item from the list
654 /** \anchor cds_intrusive_LazyList_hp_erase_func
655 The function searches an item with key equal to \p key in the list,
656 call \p func functor with item found, unlinks it from the list, and returns \p true.
657 The \p Func interface is
660 void operator()( value_type const& item );
664 If \p key is not found the function return \p false.
666 \p disposer specified in \p Traits is called for deleted item.
668 template <typename Q, typename Func>
669 bool erase( const Q& key, Func func )
671 return erase_at( &m_Head, key, key_comparator(), func );
674 /// Deletes the item from the list using \p pred predicate for searching
676 The function is an analog of \ref cds_intrusive_LazyList_hp_erase_func "erase(Q const&, Func)"
677 but \p pred is used for key comparing.
678 \p Less functor has the interface like \p std::less.
679 \p pred must imply the same element order as the comparator used for building the list.
681 \p disposer specified in \p Traits is called for deleted item.
683 template <typename Q, typename Less, typename Func>
684 bool erase_with( const Q& key, Less pred, Func func )
687 return erase_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>(), func );
690 /// Extracts the item from the list with specified \p key
691 /** \anchor cds_intrusive_LazyList_hp_extract
692 The function searches an item with key equal to \p key,
693 unlinks it from the list, and returns it as \p guarded_ptr.
694 If \p key is not found the function returns an empty guarded pointer.
696 Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
698 The \ref disposer specified in \p Traits class template parameter is called automatically
699 by garbage collector \p GC specified in class' template parameters when returned \p guarded_ptr object
700 will be destroyed or released.
701 @note Each \p guarded_ptr object uses the GC's guard that can be limited resource.
705 typedef cds::intrusive::LazyList< cds::gc::HP, foo, my_traits > ord_list;
709 ord_list::guarded_ptr gp( theList.extract( 5 ));
713 // Destructor of gp releases internal HP guard
717 template <typename Q>
718 guarded_ptr extract( Q const& key )
721 extract_at( &m_Head, gp.guard(), key, key_comparator());
725 /// Extracts the item from the list with comparing functor \p pred
727 The function is an analog of \ref cds_intrusive_LazyList_hp_extract "extract(Q const&)"
728 but \p pred predicate is used for key comparing.
730 \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
732 \p pred must imply the same element order as the comparator used for building the list.
734 template <typename Q, typename Less>
735 guarded_ptr extract_with( Q const& key, Less pred )
739 extract_at( &m_Head, gp.guard(), key, cds::opt::details::make_comparator_from_less<Less>());
743 /// Finds the key \p key
744 /** \anchor cds_intrusive_LazyList_hp_find
745 The function searches the item with key equal to \p key and calls the functor \p f for item found.
746 The interface of \p Func functor is:
749 void operator()( value_type& item, Q& key );
752 where \p item is the item found, \p key is the <tt>find</tt> function argument.
754 The functor may change non-key fields of \p item.
755 While the functor \p f is calling the item \p item is locked.
757 The function returns \p true if \p key is found, \p false otherwise.
759 template <typename Q, typename Func>
760 bool find( Q& key, Func f )
762 return find_at( &m_Head, key, key_comparator(), f );
765 template <typename Q, typename Func>
766 bool find( Q const& key, Func f )
768 return find_at( &m_Head, key, key_comparator(), f );
772 /// Finds the key \p key using \p pred predicate for searching
774 The function is an analog of \ref cds_intrusive_LazyList_hp_find "find(Q&, Func)"
775 but \p pred is used for key comparing.
776 \p Less functor has the interface like \p std::less.
777 \p pred must imply the same element order as the comparator used for building the list.
779 template <typename Q, typename Less, typename Func>
780 bool find_with( Q& key, Less pred, Func f )
783 return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>(), f );
786 template <typename Q, typename Less, typename Func>
787 bool find_with( Q const& key, Less pred, Func f )
790 return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>(), f );
794 /// Checks whether the list contains \p key
796 The function searches the item with key equal to \p key
797 and returns \p true if it is found, and \p false otherwise.
799 template <typename Q>
800 bool contains( Q const& key )
802 return find_at( &m_Head, key, key_comparator());
805 template <typename Q>
806 CDS_DEPRECATED("deprecated, use contains()")
807 bool find( Q const& key )
809 return contains( key );
813 /// Checks whether the map contains \p key using \p pred predicate for searching
815 The function is an analog of <tt>contains( key )</tt> but \p pred is used for key comparing.
816 \p Less functor has the interface like \p std::less.
817 \p Less must imply the same element order as the comparator used for building the list.
819 template <typename Q, typename Less>
820 bool contains( Q const& key, Less pred )
823 return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>());
826 template <typename Q, typename Less>
827 CDS_DEPRECATED("deprecated, use contains()")
828 bool find_with( Q const& key, Less pred )
830 return contains( key, pred );
834 /// Finds \p key and return the item found
835 /** \anchor cds_intrusive_LazyList_hp_get
836 The function searches the item with key equal to \p key
837 and returns an guarded pointer to it.
838 If \p key is not found the function returns an empty guarded pointer.
840 The \ref disposer specified in \p Traits class template parameter is called
841 by garbage collector \p GC automatically when returned \p guarded_ptr object
842 will be destroyed or released.
843 @note Each \p guarded_ptr object uses one GC's guard which can be limited resource.
847 typedef cds::intrusive::LazyList< cds::gc::HP, foo, my_traits > ord_list;
851 ord_list::guarded_ptr gp(theList.get( 5 ));
856 // Destructor of guarded_ptr releases internal HP guard
860 Note the compare functor specified for class \p Traits template parameter
861 should accept a parameter of type \p Q that can be not the same as \p value_type.
863 template <typename Q>
864 guarded_ptr get( Q const& key )
867 get_at( &m_Head, gp.guard(), key, key_comparator());
871 /// Finds \p key and return the item found
873 The function is an analog of \ref cds_intrusive_LazyList_hp_get "get( Q const&)"
874 but \p pred is used for comparing the keys.
876 \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
878 \p pred must imply the same element order as the comparator used for building the list.
880 template <typename Q, typename Less>
881 guarded_ptr get_with( Q const& key, Less pred )
885 get_at( &m_Head, gp.guard(), key, cds::opt::details::make_comparator_from_less<Less>());
892 typename gc::Guard guard;
895 h = m_Head.m_pNext.load( memory_model::memory_order_relaxed );
896 guard.assign( node_traits::to_value_ptr( h.ptr()));
897 if ( m_Head.m_pNext.load(memory_model::memory_order_acquire) == h ) {
898 m_Head.m_Lock.lock();
901 unlink_node( &m_Head, h.ptr(), &m_Head );
905 m_Head.m_Lock.unlock();
907 retire_node( h.ptr()) ; // free node
912 /// Checks if the list is empty
915 return m_Head.m_pNext.load( memory_model::memory_order_relaxed ).ptr() == &m_Tail;
918 /// Returns list's item count
920 The value returned depends on item counter provided by \p Traits. For \p atomicity::empty_item_counter,
921 this function always returns 0.
923 @note Even if you use real item counter and it returns 0, this fact does not mean that the list
924 is empty. To check list emptiness use \p empty() method.
928 return m_ItemCounter.value();
931 /// Returns const reference to internal statistics
932 stat const& statistics() const
939 // split-list support
940 bool insert_aux_node( node_type * pNode )
942 return insert_aux_node( &m_Head, pNode );
945 // split-list support
946 bool insert_aux_node( node_type * pHead, node_type * pNode )
948 assert( pNode != nullptr );
950 // Hack: convert node_type to value_type.
951 // In principle, auxiliary node cannot be reducible to value_type
952 // We assume that internal comparator can correctly distinguish aux and regular node.
953 return insert_at( pHead, *node_traits::to_value_ptr( pNode ));
956 bool insert_at( node_type * pHead, value_type& val )
962 search( pHead, val, pos, key_comparator());
964 scoped_position_lock alp( pos );
965 if ( validate( pos.pPred, pos.pCur )) {
966 if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
967 // failed: key already in list
968 m_Stat.onInsertFailed();
972 link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
978 m_Stat.onInsertRetry();
982 m_Stat.onInsertSuccess();
986 template <typename Func>
987 bool insert_at( node_type * pHead, value_type& val, Func f )
993 search( pHead, val, pos, key_comparator());
995 scoped_position_lock alp( pos );
996 if ( validate( pos.pPred, pos.pCur )) {
997 if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
998 // failed: key already in list
999 m_Stat.onInsertFailed();
1003 link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
1010 m_Stat.onInsertRetry();
1014 m_Stat.onInsertSuccess();
1018 template <typename Func>
1019 std::pair<bool, bool> update_at( node_type * pHead, value_type& val, Func func, bool bAllowInsert )
1025 search( pHead, val, pos, key_comparator());
1027 scoped_position_lock alp( pos );
1028 if ( validate( pos.pPred, pos.pCur )) {
1029 if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
1030 // key already in the list
1032 func( false, *node_traits::to_value_ptr( *pos.pCur ) , val );
1033 m_Stat.onUpdateExisting();
1034 return std::make_pair( true, false );
1038 if ( !bAllowInsert ) {
1039 m_Stat.onUpdateFailed();
1040 return std::make_pair( false, false );
1043 link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
1044 func( true, val, val );
1050 m_Stat.onUpdateRetry();
1054 m_Stat.onUpdateNew();
1055 return std::make_pair( true, true );
1058 bool unlink_at( node_type * pHead, value_type& val )
1064 search( pHead, val, pos, key_comparator());
1068 scoped_position_lock alp( pos );
1069 if ( validate( pos.pPred, pos.pCur )) {
1070 if ( pos.pCur != &m_Tail
1071 && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0
1072 && node_traits::to_value_ptr( pos.pCur ) == &val )
1075 unlink_node( pos.pPred, pos.pCur, pHead );
1084 if ( nResult > 0 ) {
1086 retire_node( pos.pCur );
1087 m_Stat.onEraseSuccess();
1091 m_Stat.onEraseFailed();
1096 m_Stat.onEraseRetry();
1100 template <typename Q, typename Compare, typename Func>
1101 bool erase_at( node_type * pHead, const Q& val, Compare cmp, Func f, position& pos )
1104 search( pHead, val, pos, cmp );
1108 scoped_position_lock alp( pos );
1109 if ( validate( pos.pPred, pos.pCur )) {
1110 if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
1112 unlink_node( pos.pPred, pos.pCur, pHead );
1113 f( *node_traits::to_value_ptr( *pos.pCur ));
1122 if ( nResult > 0 ) {
1124 retire_node( pos.pCur );
1125 m_Stat.onEraseSuccess();
1129 m_Stat.onEraseFailed();
1134 m_Stat.onEraseRetry();
1138 template <typename Q, typename Compare, typename Func>
1139 bool erase_at( node_type * pHead, const Q& val, Compare cmp, Func f )
1142 return erase_at( pHead, val, cmp, f, pos );
1145 template <typename Q, typename Compare>
1146 bool erase_at( node_type * pHead, const Q& val, Compare cmp )
1149 return erase_at( pHead, val, cmp, [](value_type const &){}, pos );
1152 template <typename Q, typename Compare>
1153 bool extract_at( node_type * pHead, typename guarded_ptr::native_guard& gp, const Q& val, Compare cmp )
1156 if ( erase_at( pHead, val, cmp, [](value_type const &){}, pos )) {
1157 gp.set( pos.guards.template get<value_type>(position::guard_current_item));
1163 template <typename Q, typename Compare, typename Func>
1164 bool find_at( node_type * pHead, Q& val, Compare cmp, Func f )
1168 search( pHead, val, pos, cmp );
1169 if ( pos.pCur != &m_Tail ) {
1170 std::unique_lock< typename node_type::lock_type> al( pos.pCur->m_Lock );
1171 if ( !pos.pCur->is_marked()
1172 && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 )
1174 f( *node_traits::to_value_ptr( *pos.pCur ), val );
1175 m_Stat.onFindSuccess();
1180 m_Stat.onFindFailed();
1184 template <typename Q, typename Compare>
1185 bool find_at( node_type * pHead, Q const& val, Compare cmp )
1189 search( pHead, val, pos, cmp );
1190 if ( pos.pCur != &m_Tail && !pos.pCur->is_marked() && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
1191 m_Stat.onFindSuccess();
1195 m_Stat.onFindFailed();
1199 template <typename Q, typename Compare>
1200 bool get_at( node_type * pHead, typename guarded_ptr::native_guard& gp, Q const& val, Compare cmp )
1204 search( pHead, val, pos, cmp );
1205 if ( pos.pCur != &m_Tail
1206 && !pos.pCur->is_marked()
1207 && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 )
1209 gp.set( pos.guards.template get<value_type>( position::guard_current_item ));
1210 m_Stat.onFindSuccess();
1214 m_Stat.onFindFailed();
1222 template <typename Q, typename Compare>
1223 void search( node_type * pHead, const Q& key, position& pos, Compare cmp )
1225 node_type const* pTail = &m_Tail;
1227 marked_node_ptr pCur( pHead );
1228 marked_node_ptr pPrev( pHead );
1230 while ( pCur.ptr() != pTail ) {
1231 if ( pCur.ptr() != pHead ) {
1232 if ( cmp( *node_traits::to_value_ptr( *pCur.ptr()), key ) >= 0 )
1236 pos.guards.copy( position::guard_prev_item, position::guard_current_item );
1239 pCur = pos.guards.protect( position::guard_current_item, pPrev->m_pNext,
1240 []( marked_node_ptr p ) { return node_traits::to_value_ptr( p.ptr()); }
1242 assert( pCur.ptr() != nullptr );
1244 pPrev = pCur = pHead;
1247 pos.pCur = pCur.ptr();
1248 pos.pPred = pPrev.ptr();
1251 bool validate( node_type * pPred, node_type * pCur ) CDS_NOEXCEPT
1253 if ( validate_link( pPred, pCur )) {
1254 m_Stat.onValidationSuccess();
1258 m_Stat.onValidationFailed();
1262 static bool validate_link( node_type * pPred, node_type * pCur ) CDS_NOEXCEPT
1264 return !pPred->is_marked()
1265 && !pCur->is_marked()
1266 && pPred->m_pNext.load(memory_model::memory_order_relaxed) == pCur;
1271 }} // namespace cds::intrusive
1273 #endif // CDSLIB_INTRUSIVE_IMPL_LAZY_LIST_H