3 #ifndef CDSLIB_INTRUSIVE_LAZY_LIST_NOGC_H
4 #define CDSLIB_INTRUSIVE_LAZY_LIST_NOGC_H
6 #include <mutex> // unique_lock
7 #include <cds/intrusive/details/lazy_list_base.h>
8 #include <cds/gc/nogc.h>
10 namespace cds { namespace intrusive {
12 /// Lazy list node for \p gc::nogc
15 - Lock - lock type. Default is \p cds::sync::spin
16 - Tag - a \ref cds_intrusive_hook_tag "tag"
19 #ifdef CDS_DOXYGEN_INVOKED
20 typename Lock = cds::sync::spin,
21 typename Tag = opt::none
27 struct node<gc::nogc, Lock, Tag>
29 typedef gc::nogc gc; ///< Garbage collector
30 typedef Lock lock_type; ///< Lock type
31 typedef Tag tag; ///< tag
33 atomics::atomic<node *> m_pNext; ///< pointer to the next node in the list
34 mutable lock_type m_Lock; ///< Node lock
40 } // namespace lazy_list
43 /// Lazy ordered single-linked list (template specialization for \p gc::nogc)
44 /** @ingroup cds_intrusive_list
45 \anchor cds_intrusive_LazyList_nogc
47 This specialization is append-only list when no item
48 reclamation may be performed. The class does not support deleting of list item.
50 See \ref cds_intrusive_LazyList_hp "LazyList" for description of template parameters.
54 #ifdef CDS_DOXYGEN_INVOKED
55 ,class Traits = lazy_list::traits
60 class LazyList<gc::nogc, T, Traits>
63 typedef gc::nogc gc; ///< Garbage collector
64 typedef T value_type; ///< type of value stored in the list
65 typedef Traits traits; ///< Traits template parameter
67 typedef typename traits::hook hook; ///< hook type
68 typedef typename hook::node_type node_type; ///< node type
70 # ifdef CDS_DOXYGEN_INVOKED
71 typedef implementation_defined key_comparator ; ///< key comparison functor based on opt::compare and opt::less option setter.
72 typedef implementation_defined equal_to_comparator;
73 typedef implementation_defined predicate_type;
75 typedef typename opt::details::make_comparator< value_type, traits >::type key_comparator;
76 typedef typename opt::details::make_equal_to< value_type, traits >::type equal_to_comparator;
77 typedef typename std::conditional< traits::sort, key_comparator, equal_to_comparator >::type predicate_type;
79 typedef typename traits::back_off back_off; ///< Back-off strategy
80 typedef typename traits::disposer disposer; ///< disposer
81 typedef typename get_node_traits< value_type, node_type, hook>::type node_traits; ///< node traits
82 typedef typename lazy_list::get_link_checker< node_type, traits::link_checker >::type link_checker; ///< link checker
84 typedef typename traits::item_counter item_counter; ///< Item counting policy used
85 typedef typename traits::memory_model memory_model; ///< C++ memory ordering (see lazy_list::traits::memory_model)
88 // Rebind traits (split-list support)
89 template <typename... Options>
90 struct rebind_traits {
94 , typename cds::opt::make_options< traits, Options...>::type
100 typedef node_type * auxiliary_head ; ///< Auxiliary head type (for split-list support)
103 node_type m_Head; ///< List head (dummy node)
104 node_type m_Tail; ///< List tail (dummy node)
105 item_counter m_ItemCounter; ///< Item counter
109 /// Position pointer for item search
111 node_type * pPred ; ///< Previous node
112 node_type * pCur ; ///< Current node
114 /// Locks nodes \p pPred and \p pCur
117 pPred->m_Lock.lock();
121 /// Unlocks nodes \p pPred and \p pCur
124 pCur->m_Lock.unlock();
125 pPred->m_Lock.unlock();
129 class auto_lock_position {
132 auto_lock_position( position& pos )
137 ~auto_lock_position()
146 void clear_links( node_type * pNode )
148 pNode->m_pNext.store( nullptr, memory_model::memory_order_relaxed );
151 template <class Disposer>
152 void dispose_node( node_type * pNode, Disposer disp )
154 clear_links( pNode );
155 disp( node_traits::to_value_ptr( *pNode ));
158 template <class Disposer>
159 void dispose_value( value_type& val, Disposer disp )
161 dispose_node( node_traits::to_node_ptr( val ), disp );
164 void link_node( node_type * pNode, node_type * pPred, node_type * pCur )
166 assert( pPred->m_pNext.load(memory_model::memory_order_relaxed) == pCur );
168 pNode->m_pNext.store( pCur, memory_model::memory_order_release );
169 pPred->m_pNext.store( pNode, memory_model::memory_order_release );
175 template <bool IsConst>
178 friend class LazyList;
181 value_type * m_pNode;
185 assert( m_pNode != nullptr );
187 node_type * pNode = node_traits::to_node_ptr( m_pNode );
188 node_type * pNext = pNode->m_pNext.load(memory_model::memory_order_relaxed);
189 if ( pNext != nullptr )
190 m_pNode = node_traits::to_value_ptr( pNext );
193 iterator_type( node_type * pNode )
195 m_pNode = node_traits::to_value_ptr( pNode );
199 typedef typename cds::details::make_const_type<value_type, IsConst>::pointer value_ptr;
200 typedef typename cds::details::make_const_type<value_type, IsConst>::reference value_ref;
206 iterator_type( const iterator_type& src )
207 : m_pNode( src.m_pNode )
210 value_ptr operator ->() const
215 value_ref operator *() const
217 assert( m_pNode != nullptr );
222 iterator_type& operator ++()
229 iterator_type operator ++(int)
231 iterator_type i(*this);
236 iterator_type& operator = (const iterator_type& src)
238 m_pNode = src.m_pNode;
243 bool operator ==(iterator_type<C> const& i ) const
245 return m_pNode == i.m_pNode;
248 bool operator !=(iterator_type<C> const& i ) const
250 return m_pNode != i.m_pNode;
257 typedef iterator_type<false> iterator;
258 /// Const forward iterator
259 typedef iterator_type<true> const_iterator;
261 /// Returns a forward iterator addressing the first element in a list
263 For empty list \code begin() == end() \endcode
267 iterator it( &m_Head );
268 ++it ; // skip dummy head
272 /// Returns an iterator that addresses the location succeeding the last element in a list
274 Do not use the value returned by <tt>end</tt> function to access any item.
276 The returned value can be used only to control reaching the end of the list.
277 For empty list \code begin() == end() \endcode
281 return iterator( &m_Tail );
284 /// Returns a forward const iterator addressing the first element in a list
285 const_iterator begin() const
289 /// Returns a forward const iterator addressing the first element in a list
290 const_iterator cbegin() const
292 const_iterator it( const_cast<node_type *>(&m_Head) );
293 ++it; // skip dummy head
297 /// Returns an const iterator that addresses the location succeeding the last element in a list
298 const_iterator end() const
302 /// Returns an const iterator that addresses the location succeeding the last element in a list
303 const_iterator cend() const
305 return const_iterator( const_cast<node_type *>(&m_Tail) );
309 /// Default constructor initializes empty list
312 static_assert( (std::is_same< gc, typename node_type::gc >::value), "GC and node_type::gc must be the same type" );
313 m_Head.m_pNext.store( &m_Tail, memory_model::memory_order_relaxed );
316 /// Destroys the list object
320 assert( m_Head.m_pNext.load(memory_model::memory_order_relaxed) == &m_Tail );
321 m_Head.m_pNext.store( nullptr, memory_model::memory_order_relaxed );
326 The function inserts \p val in the list if the list does not contain
327 an item with key equal to \p val.
329 Returns \p true if \p val is linked into the list, \p false otherwise.
331 bool insert( value_type& val )
333 return insert_at( &m_Head, val );
336 /// Ensures that the \p item exists in the list
338 The operation performs inserting or changing data with lock-free manner.
340 If the item \p val not found in the list, then \p val is inserted into the list.
341 Otherwise, the functor \p func is called with item found.
342 The functor signature is:
345 void operator()( bool bNew, value_type& item, value_type& val );
349 - \p bNew - \p true if the item has been inserted, \p false otherwise
350 - \p item - item of the list
351 - \p val - argument \p val passed into the \p ensure function
352 If new item has been inserted (i.e. \p bNew is \p true) then \p item and \p val arguments
353 refers to the same thing.
355 The functor may change non-key fields of the \p item.
356 While the functor \p f is calling the item \p item is locked.
358 Returns <tt> std::pair<bool, bool> </tt> where \p first is true if operation is successfull,
359 \p second is true if new item has been added or \p false if the item with \p key
360 already is in the list.
363 template <typename Func>
364 std::pair<bool, bool> ensure( value_type& val, Func func )
366 return ensure_at( &m_Head, val, func );
369 /// Finds the key \p key
370 /** \anchor cds_intrusive_LazyList_nogc_find_func
371 The function searches the item with key equal to \p key
372 and calls the functor \p f for item found.
373 The interface of \p Func functor is:
376 void operator()( value_type& item, Q& key );
379 where \p item is the item found, \p key is the <tt>find</tt> function argument.
381 The functor may change non-key fields of \p item.
382 While the functor \p f is calling the item found \p item is locked.
384 The function returns \p true if \p key is found, \p false otherwise.
386 template <typename Q, typename Func>
387 bool find( Q& key, Func f )
389 return find_at( &m_Head, key, predicate_type(), f );
392 template <typename Q, typename Func>
393 bool find( Q const& key, Func f )
395 return find_at( &m_Head, key, predicate_type(), f );
399 /// Finds the key \p key using \p less predicate for searching. Disabled for unordered lists.
401 The function is an analog of \ref cds_intrusive_LazyList_nogc_find_func "find(Q&, Func)"
402 but \p pred is used for key comparing.
403 \p Less functor has the interface like \p std::less.
404 \p pred must imply the same element order as the comparator used for building the list.
406 template <typename Q, typename Less, typename Func, bool Sort = traits::sort>
407 typename std::enable_if<Sort, bool>::type find_with( Q& key, Less less, Func f )
410 return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>(), f );
413 /// Finds the key \p key using \p equal predicate for searching. Disabled for ordered lists.
415 The function is an analog of \ref cds_intrusive_LazyList_nogc_find_func "find(Q&, Func)"
416 but \p equal is used for key comparing.
417 \p Equal functor has the interface like \p std::equal_to.
419 template <typename Q, typename Equal, typename Func, bool Sort = traits::sort>
420 typename std::enable_if<!Sort, bool>::type find_with( Q& key, Equal equal, Func f )
423 return find_at( &m_Head, key, Equal(), f );
426 template <typename Q, typename Less, typename Func, bool Sort = traits::sort>
427 typename std::enable_if<Sort, bool>::type find_with( Q const& key, Less pred, Func f )
430 return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>(), f );
433 template <typename Q, typename Equal, typename Func, bool Sort = traits::sort>
434 typename std::enable_if<!Sort, bool>::type find_with( Q const& key, Equal equal, Func f )
437 return find_at( &m_Head, key, Equal(), f );
441 /// Finds the key \p key
442 /** \anchor cds_intrusive_LazyList_nogc_find_val
443 The function searches the item with key equal to \p key
444 and returns pointer to value found or \p nullptr.
446 template <typename Q>
447 value_type * find( Q const& key )
449 return find_at( &m_Head, key, predicate_type() );
452 /// Finds the key \p key using \p pred predicate for searching. Disabled for unordered lists.
454 The function is an analog of \ref cds_intrusive_LazyList_nogc_find_val "find(Q const&)"
455 but \p pred is used for key comparing.
456 \p Less functor has the interface like \p std::less.
457 \p pred must imply the same element order as the comparator used for building the list.
459 template <typename Q, typename Less, bool Sort = traits::sort>
460 typename std::enable_if<Sort, value_type *>::type find_with( Q const& key, Less pred )
463 return find_at( &m_Head, key, cds::opt::details::make_comparator_from_less<Less>() );
466 /// Finds the key \p key using \p equal predicate for searching. Disabled for ordered lists.
468 The function is an analog of \ref cds_intrusive_LazyList_nogc_find_val "find(Q const&)"
469 but \p equal is used for key comparing.
470 \p Equal functor has the interface like \p std::equal_to.
472 template <typename Q, typename Equal, bool Sort = traits::sort>
473 typename std::enable_if<!Sort, value_type *>::type find_with( Q const& key, Equal equal )
476 return find_at( &m_Head, key, equal );
481 The function unlink all items from the list.
482 For each unlinked item the item disposer \p disp is called after unlinking.
484 This function is not thread-safe.
486 template <typename Disposer>
487 void clear( Disposer disp )
489 node_type * pHead = m_Head.m_pNext.exchange( &m_Tail, memory_model::memory_order_release );
491 while ( pHead != &m_Tail ) {
492 node_type * p = pHead->m_pNext.load(memory_model::memory_order_relaxed);
493 dispose_node( pHead, disp );
498 /// Clears the list using default disposer
500 The function clears the list using default (provided in class template) disposer functor.
507 /// Checks if the list is empty
510 return m_Head.m_pNext.load(memory_model::memory_order_relaxed) == &m_Tail;
513 /// Returns list's item count
515 The value returned depends on opt::item_counter option. For atomicity::empty_item_counter,
516 this function always returns 0.
518 <b>Warning</b>: even if you use real item counter and it returns 0, this fact is not mean that the list
519 is empty. To check list emptyness use \ref empty() method.
523 return m_ItemCounter.value();
528 // split-list support
529 bool insert_aux_node( node_type * pNode )
531 return insert_aux_node( &m_Head, pNode );
534 // split-list support
535 bool insert_aux_node( node_type * pHead, node_type * pNode )
537 assert( pHead != nullptr );
538 assert( pNode != nullptr );
540 // Hack: convert node_type to value_type.
541 // In principle, auxiliary node can be non-reducible to value_type
542 // We assume that comparator can correctly distinguish aux and regular node.
543 return insert_at( pHead, *node_traits::to_value_ptr( pNode ) );
546 bool insert_at( node_type * pHead, value_type& val )
548 link_checker::is_empty( node_traits::to_node_ptr( val ) );
553 search( pHead, val, pos, pred );
555 auto_lock_position alp( pos );
556 if ( validate( pos.pPred, pos.pCur )) {
557 if ( pos.pCur != &m_Tail && equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred ) ) {
558 // failed: key already in list
562 link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
571 iterator insert_at_( node_type * pHead, value_type& val )
573 if ( insert_at( pHead, val ))
574 return iterator( node_traits::to_node_ptr( val ));
579 template <typename Func>
580 std::pair<iterator, bool> ensure_at_( node_type * pHead, value_type& val, Func func )
586 search( pHead, val, pos, pred );
588 auto_lock_position alp( pos );
589 if ( validate( pos.pPred, pos.pCur )) {
590 if ( pos.pCur != &m_Tail && equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred ) ) {
591 // key already in the list
593 func( false, *node_traits::to_value_ptr( *pos.pCur ) , val );
594 return std::make_pair( iterator( pos.pCur ), false );
598 link_checker::is_empty( node_traits::to_node_ptr( val ) );
600 link_node( node_traits::to_node_ptr( val ), pos.pPred, pos.pCur );
601 func( true, val, val );
603 return std::make_pair( iterator( node_traits::to_node_ptr( val )), true );
610 template <typename Func>
611 std::pair<bool, bool> ensure_at( node_type * pHead, value_type& val, Func func )
613 std::pair<iterator, bool> ret = ensure_at_( pHead, val, func );
614 return std::make_pair( ret.first != end(), ret.second );
617 template <typename Q, typename Pred, typename Func>
618 bool find_at( node_type * pHead, Q& val, Pred pred, Func f )
622 search( pHead, val, pos, pred );
623 if ( pos.pCur != &m_Tail ) {
624 std::unique_lock< typename node_type::lock_type> al( pos.pCur->m_Lock );
625 if ( equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred ) )
627 f( *node_traits::to_value_ptr( *pos.pCur ), val );
634 template <typename Q, typename Pred>
635 value_type * find_at( node_type * pHead, Q& val, Pred pred)
637 iterator it = find_at_( pHead, val, pred );
643 template <typename Q, typename Pred>
644 iterator find_at_( node_type * pHead, Q& val, Pred pred)
648 search( pHead, val, pos, pred );
649 if ( pos.pCur != &m_Tail ) {
650 if ( equal( *node_traits::to_value_ptr( *pos.pCur ), val, pred ) )
651 return iterator( pos.pCur );
660 template <typename Q, typename Equal, bool Sort = traits::sort>
661 typename std::enable_if<!Sort, void>::type search( node_type * pHead, const Q& key, position& pos, Equal eq )
663 const node_type * pTail = &m_Tail;
665 node_type * pCur = pHead;
666 node_type * pPrev = pHead;
668 while ( pCur != pTail && ( pCur == pHead || !equal( *node_traits::to_value_ptr( *pCur ), key, eq ) )) {
670 pCur = pCur->m_pNext.load(memory_model::memory_order_acquire);
677 template <typename Q, typename Compare, bool Sort = traits::sort>
678 typename std::enable_if<Sort, void>::type search( node_type * pHead, const Q& key, position& pos, Compare cmp )
680 const node_type * pTail = &m_Tail;
682 node_type * pCur = pHead;
683 node_type * pPrev = pHead;
685 while ( pCur != pTail && ( pCur == pHead || cmp( *node_traits::to_value_ptr( *pCur ), key ) < 0 )) {
687 pCur = pCur->m_pNext.load(memory_model::memory_order_acquire);
694 template <typename L, typename R, typename Equal, bool Sort = traits::sort>
695 static typename std::enable_if<!Sort, bool>::type equal( L const& l, R const& r, Equal eq )
700 template <typename L, typename R, typename Compare, bool Sort = traits::sort>
701 static typename std::enable_if<Sort, bool>::type equal( L const& l, R const& r, Compare cmp )
703 return cmp(l, r) == 0;
706 static bool validate( node_type * pPred, node_type * pCur )
708 return pPred->m_pNext.load(memory_model::memory_order_acquire) == pCur;
714 }} // namespace cds::intrusive
716 #endif // #ifndef CDSLIB_INTRUSIVE_LAZY_LIST_NOGC_H