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 istead 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)
208 typedef typename gc::template guarded_ptr< value_type > guarded_ptr; ///< Guarded pointer
210 static CDS_CONSTEXPR const size_t c_nHazardPtrCount = 4; ///< Count of hazard pointer required for the algorithm
213 // Rebind traits (split-list support)
214 template <typename... Options>
215 struct rebind_traits {
219 , typename cds::opt::make_options< traits, Options...>::type
225 typedef typename node_type::marked_ptr marked_node_ptr; ///< Node marked pointer
226 typedef node_type * auxiliary_head; ///< Auxiliary head type (for split-list support)
233 item_counter m_ItemCounter ; ///< Item counter
236 struct clean_disposer {
237 void operator()( value_type * p )
239 lazy_list::node_cleaner<gc, node_type, memory_model>()( node_traits::to_node_ptr( p ));
244 /// Position pointer for item search
246 node_type * pPred ; ///< Previous node
247 node_type * pCur ; ///< Current node
249 typename gc::template GuardArray<2> guards ; ///< Guards array
256 /// Locks nodes \p pPred and \p pCur
259 pPred->m_Lock.lock();
263 /// Unlocks nodes \p pPred and \p pCur
266 pCur->m_Lock.unlock();
267 pPred->m_Lock.unlock();
271 typedef std::unique_lock< position > scoped_position_lock;
276 void link_node( node_type * pNode, node_type * pPred, node_type * pCur )
278 assert( pPred->m_pNext.load(memory_model::memory_order_relaxed).ptr() == pCur );
280 pNode->m_pNext.store( marked_node_ptr(pCur), memory_model::memory_order_release );
281 pPred->m_pNext.store( marked_node_ptr(pNode), memory_model::memory_order_release );
284 void unlink_node( node_type * pPred, node_type * pCur, node_type * pHead )
286 assert( pPred->m_pNext.load(memory_model::memory_order_relaxed).ptr() == pCur );
288 node_type * pNext = pCur->m_pNext.load(memory_model::memory_order_relaxed).ptr();
289 pCur->m_pNext.store( marked_node_ptr( pHead, 1 ), memory_model::memory_order_release ); // logical removal + back-link for search
290 pPred->m_pNext.store( marked_node_ptr( pNext ), memory_model::memory_order_release); // physically deleting
293 void retire_node( node_type * pNode )
295 assert( pNode != nullptr );
296 gc::template retire<clean_disposer>( node_traits::to_value_ptr( *pNode ));
302 template <bool IsConst>
305 friend class LazyList;
308 value_type * m_pNode;
309 typename gc::Guard m_Guard;
313 assert( m_pNode != nullptr );
316 typename gc::Guard g;
317 node_type * pCur = node_traits::to_node_ptr( m_pNode );
318 if ( pCur->m_pNext.load( memory_model::memory_order_relaxed ).ptr() != nullptr ) { // if pCur is not tail node
321 pNext = pCur->m_pNext.load(memory_model::memory_order_relaxed).ptr();
322 g.assign( node_traits::to_value_ptr( pNext ));
323 } while ( pNext != pCur->m_pNext.load(memory_model::memory_order_relaxed).ptr());
325 m_pNode = m_Guard.assign( g.template get<value_type>());
332 if ( m_pNode != nullptr ) {
333 typename gc::Guard g;
334 node_type * pNode = node_traits::to_node_ptr( m_pNode );
336 // Dummy tail node could not be marked
337 while ( pNode->is_marked()) {
338 node_type * p = pNode->m_pNext.load(memory_model::memory_order_relaxed).ptr();
339 g.assign( node_traits::to_value_ptr( p ));
340 if ( p == pNode->m_pNext.load(memory_model::memory_order_relaxed).ptr())
343 if ( pNode != node_traits::to_node_ptr( m_pNode ))
344 m_pNode = m_Guard.assign( g.template get<value_type>());
348 iterator_type( node_type * pNode )
350 m_pNode = m_Guard.assign( node_traits::to_value_ptr( pNode ));
355 typedef typename cds::details::make_const_type<value_type, IsConst>::pointer value_ptr;
356 typedef typename cds::details::make_const_type<value_type, IsConst>::reference value_ref;
362 iterator_type( iterator_type const& src )
365 m_pNode = m_Guard.assign( src.m_pNode );
371 value_ptr operator ->() const
376 value_ref operator *() const
378 assert( m_pNode != nullptr );
383 iterator_type& operator ++()
390 iterator_type& operator = (iterator_type const& src)
392 m_pNode = src.m_pNode;
393 m_Guard.assign( m_pNode );
398 bool operator ==(iterator_type<C> const& i ) const
400 return m_pNode == i.m_pNode;
403 bool operator !=(iterator_type<C> const& i ) const
405 return m_pNode != i.m_pNode;
413 The forward iterator for lazy list has some features:
414 - it has no post-increment operator
415 - to protect the value, the iterator contains a GC-specific guard + another guard is required locally for increment operator.
416 For some GC (\p gc::HP), a guard is limited resource per thread, so an exception (or assertion) "no free guard"
417 may be thrown if a limit of guard count per thread is exceeded.
418 - The iterator cannot be moved across thread boundary since it contains GC's guard that is thread-private GC data.
419 - Iterator ensures thread-safety even if you delete the item that iterator points to. However, in case of concurrent
420 deleting operations it is no guarantee that you iterate all item in the list.
422 Therefore, the use of iterators in concurrent environment is not good idea. Use the iterator on the concurrent container
423 for debug purpose only.
425 typedef iterator_type<false> iterator;
426 /// Const forward iterator
428 For iterator's features and requirements see \ref iterator
430 typedef iterator_type<true> const_iterator;
432 /// Returns a forward iterator addressing the first element in a list
434 For empty list \code begin() == end() \endcode
438 iterator it( &m_Head );
439 ++it ; // skip dummy head
443 /// Returns an iterator that addresses the location succeeding the last element in a list
445 Do not use the value returned by <tt>end</tt> function to access any item.
447 The returned value can be used only to control reaching the end of the list.
448 For empty list \code begin() == end() \endcode
452 return iterator( &m_Tail );
455 /// Returns a forward const iterator addressing the first element in a list
457 const_iterator begin() const
459 return get_const_begin();
461 const_iterator cbegin() const
463 return get_const_begin();
467 /// Returns an const iterator that addresses the location succeeding the last element in a list
469 const_iterator end() const
471 return get_const_end();
473 const_iterator cend() const
475 return get_const_end();
481 const_iterator get_const_begin() const
483 const_iterator it( const_cast<node_type *>( &m_Head ));
484 ++it ; // skip dummy head
487 const_iterator get_const_end() const
489 return const_iterator( const_cast<node_type *>(&m_Tail));
494 /// Default constructor initializes empty list
497 static_assert( (std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type" );
498 m_Head.m_pNext.store( marked_node_ptr( &m_Tail ), memory_model::memory_order_relaxed );
501 /// Destroys the list object
505 assert( m_Head.m_pNext.load( memory_model::memory_order_relaxed ).ptr() == &m_Tail );
506 m_Head.m_pNext.store( marked_node_ptr(), memory_model::memory_order_relaxed );
511 The function inserts \p val in the list if the list does not contain
512 an item with key equal to \p val.
514 Returns \p true if \p val is linked into the list, \p false otherwise.
516 bool insert( value_type& val )
518 return insert_at( &m_Head, val );
523 This function is intended for derived non-intrusive containers.
525 The function allows to split new item creating into two part:
526 - create item with key only
527 - insert new item into the list
528 - if inserting is success, calls \p f functor to initialize value-field of \p val.
530 The functor signature is:
532 void func( value_type& val );
534 where \p val is the item inserted.
535 While the functor \p f is called the item \p val is locked so
536 the functor has an exclusive access to the item.
537 The user-defined functor is called only if the inserting is success.
539 template <typename Func>
540 bool insert( value_type& val, Func f )
542 return insert_at( &m_Head, val, f );
547 The operation performs inserting or changing data with lock-free manner.
549 If the item \p val not found in the list, then \p val is inserted into the list
550 iff \p bAllowInsert is \p true.
551 Otherwise, the functor \p func is called with item found.
552 The functor signature is:
555 void operator()( bool bNew, value_type& item, value_type& val );
559 - \p bNew - \p true if the item has been inserted, \p false otherwise
560 - \p item - item of the list
561 - \p val - argument \p val passed into the \p update() function
562 If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
563 refer to the same thing.
565 The functor may change non-key fields of the \p item.
566 While the functor \p f is working the item \p item is locked,
567 so \p func has exclusive access to the item.
569 Returns <tt> std::pair<bool, bool> </tt> where \p first is \p true if operation is successfull,
570 \p second is \p true if new item has been added or \p false if the item with \p key
571 already is in the list.
573 The function makes RCU lock internally.
575 template <typename Func>
576 std::pair<bool, bool> update( value_type& val, Func func, bool bAllowInsert = true )
578 return update_at( &m_Head, val, func, bAllowInsert );
581 template <typename Func>
582 CDS_DEPRECATED("ensure() is deprecated, use update()")
583 std::pair<bool, bool> ensure( value_type& val, Func func )
585 return update( val, func, true );
589 /// Unlinks the item \p val from the list
591 The function searches the item \p val in the list and unlink it from the list
592 if it is found and it is equal to \p val.
594 Difference between \ref erase and \p unlink functions: \p erase finds <i>a key</i>
595 and deletes the item found. \p unlink finds an item by key and deletes it
596 only if \p val is an item of that list, i.e. the pointer to item found
597 is equal to <tt> &val </tt>.
599 The function returns \p true if success and \p false otherwise.
601 \p disposer specified in \p Traits is called for unlinked item.
603 bool unlink( value_type& val )
605 return unlink_at( &m_Head, val );
608 /// Deletes the item from the list
609 /** \anchor cds_intrusive_LazyList_hp_erase_val
610 The function searches an item with key equal to \p key in the list,
611 unlinks it from the list, and returns \p true.
612 If the item with the key equal to \p key is not found the function return \p false.
614 \p disposer specified in \p Traits is called for deleted item.
616 template <typename Q>
617 bool erase( Q const& key )
619 return erase_at( &m_Head, key, key_comparator());
622 /// Deletes the item from the list using \p pred predicate for searching
624 The function is an analog of \ref cds_intrusive_LazyList_hp_erase_val "erase(Q const&)"
625 but \p pred is used for key comparing.
626 \p Less functor has the interface like \p std::less.
627 \p pred must imply the same element order as the comparator used for building the list.
629 \p disposer specified in \p Traits is called for deleted item.
631 template <typename Q, typename Less>
632 bool erase_with( Q const& key, Less pred )
635 return erase_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>());
638 /// Deletes the item from the list
639 /** \anchor cds_intrusive_LazyList_hp_erase_func
640 The function searches an item with key equal to \p key in the list,
641 call \p func functor with item found, unlinks it from the list, and returns \p true.
642 The \p Func interface is
645 void operator()( value_type const& item );
649 If \p key is not found the function return \p false.
651 \p disposer specified in \p Traits is called for deleted item.
653 template <typename Q, typename Func>
654 bool erase( const Q& key, Func func )
656 return erase_at( &m_Head, key, key_comparator(), func );
659 /// Deletes the item from the list using \p pred predicate for searching
661 The function is an analog of \ref cds_intrusive_LazyList_hp_erase_func "erase(Q const&, Func)"
662 but \p pred is used for key comparing.
663 \p Less functor has the interface like \p std::less.
664 \p pred must imply the same element order as the comparator used for building the list.
666 \p disposer specified in \p Traits is called for deleted item.
668 template <typename Q, typename Less, typename Func>
669 bool erase_with( const Q& key, Less pred, Func func )
672 return erase_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>(), func );
675 /// Extracts the item from the list with specified \p key
676 /** \anchor cds_intrusive_LazyList_hp_extract
677 The function searches an item with key equal to \p key,
678 unlinks it from the list, and returns it as \p guarded_ptr.
679 If \p key is not found the function returns an empty guarded pointer.
681 Note the compare functor should accept a parameter of type \p Q that can be not the same as \p value_type.
683 The \ref disposer specified in \p Traits class template parameter is called automatically
684 by garbage collector \p GC specified in class' template parameters when returned \p guarded_ptr object
685 will be destroyed or released.
686 @note Each \p guarded_ptr object uses the GC's guard that can be limited resource.
690 typedef cds::intrusive::LazyList< cds::gc::HP, foo, my_traits > ord_list;
694 ord_list::guarded_ptr gp( theList.extract( 5 ));
698 // Destructor of gp releases internal HP guard
702 template <typename Q>
703 guarded_ptr extract( Q const& key )
706 extract_at( &m_Head, gp.guard(), key, key_comparator());
710 /// Extracts the item from the list with comparing functor \p pred
712 The function is an analog of \ref cds_intrusive_LazyList_hp_extract "extract(Q const&)"
713 but \p pred predicate is used for key comparing.
715 \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
717 \p pred must imply the same element order as the comparator used for building the list.
719 template <typename Q, typename Less>
720 guarded_ptr extract_with( Q const& key, Less pred )
724 extract_at( &m_Head, gp.guard(), key, cds::opt::details::make_comparator_from_less<Less>());
728 /// Finds the key \p key
729 /** \anchor cds_intrusive_LazyList_hp_find
730 The function searches the item with key equal to \p key and calls the functor \p f for item found.
731 The interface of \p Func functor is:
734 void operator()( value_type& item, Q& key );
737 where \p item is the item found, \p key is the <tt>find</tt> function argument.
739 The functor may change non-key fields of \p item.
740 While the functor \p f is calling the item \p item is locked.
742 The function returns \p true if \p key is found, \p false otherwise.
744 template <typename Q, typename Func>
745 bool find( Q& key, Func f )
747 return find_at( &m_Head, key, key_comparator(), f );
750 template <typename Q, typename Func>
751 bool find( Q const& key, Func f )
753 return find_at( &m_Head, key, key_comparator(), f );
757 /// Finds the key \p key using \p pred predicate for searching
759 The function is an analog of \ref cds_intrusive_LazyList_hp_find "find(Q&, Func)"
760 but \p pred is used for key comparing.
761 \p Less functor has the interface like \p std::less.
762 \p pred must imply the same element order as the comparator used for building the list.
764 template <typename Q, typename Less, typename Func>
765 bool find_with( Q& key, Less pred, Func f )
768 return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>(), f );
771 template <typename Q, typename Less, typename Func>
772 bool find_with( Q const& key, Less pred, Func f )
775 return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>(), f );
779 /// Checks whether the list contains \p key
781 The function searches the item with key equal to \p key
782 and returns \p true if it is found, and \p false otherwise.
784 template <typename Q>
785 bool contains( Q const& key )
787 return find_at( &m_Head, key, key_comparator());
790 template <typename Q>
791 CDS_DEPRECATED("deprecated, use contains()")
792 bool find( Q const& key )
794 return contains( key );
798 /// Checks whether the map contains \p key using \p pred predicate for searching
800 The function is an analog of <tt>contains( key )</tt> but \p pred is used for key comparing.
801 \p Less functor has the interface like \p std::less.
802 \p Less must imply the same element order as the comparator used for building the list.
804 template <typename Q, typename Less>
805 bool contains( Q const& key, Less pred )
808 return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>());
811 template <typename Q, typename Less>
812 CDS_DEPRECATED("deprecated, use contains()")
813 bool find_with( Q const& key, Less pred )
815 return contains( key, pred );
819 /// Finds \p key and return the item found
820 /** \anchor cds_intrusive_LazyList_hp_get
821 The function searches the item with key equal to \p key
822 and returns an guarded pointer to it.
823 If \p key is not found the function returns an empty guarded pointer.
825 The \ref disposer specified in \p Traits class template parameter is called
826 by garbage collector \p GC automatically when returned \p guarded_ptr object
827 will be destroyed or released.
828 @note Each \p guarded_ptr object uses one GC's guard which can be limited resource.
832 typedef cds::intrusive::LazyList< cds::gc::HP, foo, my_traits > ord_list;
836 ord_list::guarded_ptr gp(theList.get( 5 ));
841 // Destructor of guarded_ptr releases internal HP guard
845 Note the compare functor specified for class \p Traits template parameter
846 should accept a parameter of type \p Q that can be not the same as \p value_type.
848 template <typename Q>
849 guarded_ptr get( Q const& key )
852 get_at( &m_Head, gp.guard(), key, key_comparator());
856 /// Finds \p key and return the item found
858 The function is an analog of \ref cds_intrusive_LazyList_hp_get "get( Q const&)"
859 but \p pred is used for comparing the keys.
861 \p Less functor has the semantics like \p std::less but should take arguments of type \ref value_type and \p Q
863 \p pred must imply the same element order as the comparator used for building the list.
865 template <typename Q, typename Less>
866 guarded_ptr get_with( Q const& key, Less pred )
870 get_at( &m_Head, gp.guard(), key, cds::opt::details::make_comparator_from_less<Less>());
877 typename gc::Guard guard;
880 h = m_Head.m_pNext.load( memory_model::memory_order_relaxed );
881 guard.assign( node_traits::to_value_ptr( h.ptr()));
882 if ( m_Head.m_pNext.load(memory_model::memory_order_acquire) == h ) {
883 m_Head.m_Lock.lock();
886 unlink_node( &m_Head, h.ptr(), &m_Head );
889 m_Head.m_Lock.unlock();
891 retire_node( h.ptr()) ; // free node
896 /// Checks if the list is empty
899 return m_Head.m_pNext.load( memory_model::memory_order_relaxed ).ptr() == &m_Tail;
902 /// Returns list's item count
904 The value returned depends on item counter provided by \p Traits. For \p atomicity::empty_item_counter,
905 this function always returns 0.
907 @note Even if you use real item counter and it returns 0, this fact does not mean that the list
908 is empty. To check list emptyness use \p empty() method.
912 return m_ItemCounter.value();
917 // split-list support
918 bool insert_aux_node( node_type * pNode )
920 return insert_aux_node( &m_Head, pNode );
923 // split-list support
924 bool insert_aux_node( node_type * pHead, node_type * pNode )
926 assert( pNode != nullptr );
928 // Hack: convert node_type to value_type.
929 // In principle, auxiliary node cannot be reducible to value_type
930 // We assume that internal comparator can correctly distinguish aux and regular node.
931 return insert_at( pHead, *node_traits::to_value_ptr( pNode ));
934 bool insert_at( node_type * pHead, value_type& val )
936 link_checker::is_empty( node_traits::to_node_ptr( val ));
941 search( pHead, val, pos, key_comparator());
943 scoped_position_lock alp( pos );
944 if ( validate( pos.pPred, pos.pCur )) {
945 if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
946 // failed: key already in list
950 link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
959 template <typename Func>
960 bool insert_at( node_type * pHead, value_type& val, Func f )
962 link_checker::is_empty( node_traits::to_node_ptr( val ));
967 search( pHead, val, pos, key_comparator());
969 scoped_position_lock alp( pos );
970 if ( validate( pos.pPred, pos.pCur )) {
971 if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
972 // failed: key already in list
976 link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
986 template <typename Func>
987 std::pair<bool, bool> update_at( node_type * pHead, value_type& val, Func func, bool bAllowInsert )
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 // key already in the list
1000 func( false, *node_traits::to_value_ptr( *pos.pCur ) , val );
1001 return std::make_pair( true, false );
1005 if ( !bAllowInsert )
1006 return std::make_pair( false, false );
1008 link_checker::is_empty( node_traits::to_node_ptr( val ));
1010 link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
1011 func( true, val, val );
1013 return std::make_pair( true, true );
1020 bool unlink_at( node_type * pHead, value_type& val )
1026 search( pHead, val, pos, key_comparator());
1030 scoped_position_lock alp( pos );
1031 if ( validate( pos.pPred, pos.pCur )) {
1032 if ( pos.pCur != &m_Tail
1033 && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0
1034 && node_traits::to_value_ptr( pos.pCur ) == &val )
1037 unlink_node( pos.pPred, pos.pCur, pHead );
1046 if ( nResult > 0 ) {
1047 retire_node( pos.pCur );
1056 template <typename Q, typename Compare, typename Func>
1057 bool erase_at( node_type * pHead, const Q& val, Compare cmp, Func f, position& pos )
1060 search( pHead, val, pos, cmp );
1064 scoped_position_lock alp( pos );
1065 if ( validate( pos.pPred, pos.pCur )) {
1066 if ( pos.pCur != &m_Tail && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 ) {
1068 unlink_node( pos.pPred, pos.pCur, pHead );
1069 f( *node_traits::to_value_ptr( *pos.pCur ));
1079 if ( nResult > 0 ) {
1080 retire_node( pos.pCur );
1089 template <typename Q, typename Compare, typename Func>
1090 bool erase_at( node_type * pHead, const Q& val, Compare cmp, Func f )
1093 return erase_at( pHead, val, cmp, f, pos );
1096 template <typename Q, typename Compare>
1097 bool erase_at( node_type * pHead, const Q& val, Compare cmp )
1100 return erase_at( pHead, val, cmp, [](value_type const &){}, pos );
1103 template <typename Q, typename Compare>
1104 bool extract_at( node_type * pHead, typename guarded_ptr::native_guard& gp, const Q& val, Compare cmp )
1107 if ( erase_at( pHead, val, cmp, [](value_type const &){}, pos )) {
1108 gp.set( pos.guards.template get<value_type>(position::guard_current_item));
1114 template <typename Q, typename Compare, typename Func>
1115 bool find_at( node_type * pHead, Q& val, Compare cmp, Func f )
1119 search( pHead, val, pos, cmp );
1120 if ( pos.pCur != &m_Tail ) {
1121 std::unique_lock< typename node_type::lock_type> al( pos.pCur->m_Lock );
1122 if ( !pos.pCur->is_marked()
1123 && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 )
1125 f( *node_traits::to_value_ptr( *pos.pCur ), val );
1132 template <typename Q, typename Compare>
1133 bool find_at( node_type * pHead, Q const& val, Compare cmp )
1137 search( pHead, val, pos, cmp );
1138 return pos.pCur != &m_Tail
1139 && !pos.pCur->is_marked()
1140 && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0;
1143 template <typename Q, typename Compare>
1144 bool get_at( node_type * pHead, typename guarded_ptr::native_guard& gp, Q const& val, Compare cmp )
1148 search( pHead, val, pos, cmp );
1149 if ( pos.pCur != &m_Tail
1150 && !pos.pCur->is_marked()
1151 && cmp( *node_traits::to_value_ptr( *pos.pCur ), val ) == 0 )
1153 gp.set( pos.guards.template get<value_type>( position::guard_current_item ));
1163 template <typename Q, typename Compare>
1164 void search( node_type * pHead, const Q& key, position& pos, Compare cmp )
1166 node_type const* pTail = &m_Tail;
1168 marked_node_ptr pCur( pHead );
1169 marked_node_ptr pPrev( pHead );
1171 while ( pCur.ptr() != pTail ) {
1172 if ( pCur.ptr() != pHead ) {
1173 if ( cmp( *node_traits::to_value_ptr( *pCur.ptr()), key ) >= 0 )
1177 pos.guards.copy( position::guard_prev_item, position::guard_current_item );
1180 pCur = pos.guards.protect( position::guard_current_item, pPrev->m_pNext,
1181 []( marked_node_ptr p ) { return node_traits::to_value_ptr( p.ptr()); }
1183 assert( pCur.ptr() != nullptr );
1185 pPrev = pCur = pHead;
1188 pos.pCur = pCur.ptr();
1189 pos.pPred = pPrev.ptr();
1192 static bool validate( node_type * pPred, node_type * pCur )
1194 return !pPred->is_marked()
1195 && !pCur->is_marked()
1196 && pPred->m_pNext.load(memory_model::memory_order_relaxed) == pCur;
1201 }} // namespace cds::intrusive
1203 #endif // CDSLIB_INTRUSIVE_IMPL_LAZY_LIST_H