--- /dev/null
+/*
+ This file is a part of libcds - Concurrent Data Structures library
+
+ (C) Copyright Maxim Khizhinsky (libcds.dev@gmail.com) 2006-2017
+
+ Source code repo: http://github.com/khizmax/libcds/
+ Download: http://sourceforge.net/projects/libcds/files/
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef CDSLIB_INTRUSIVE_BASKET_QUEUE_H
+#define CDSLIB_INTRUSIVE_BASKET_QUEUE_H
+
+#include <type_traits>
+#include <cds/intrusive/details/single_link_struct.h>
+#include <cds/details/marked_ptr.h>
+
+namespace cds { namespace intrusive {
+
+ /// BasketQueue -related definitions
+ /** @ingroup cds_intrusive_helper
+ */
+ namespace basket_queue {
+ /// BasketQueue node
+ /**
+ Template parameters:
+ Template parameters:
+ - GC - garbage collector used
+ - Tag - a \ref cds_intrusive_hook_tag "tag"
+ */
+ template <class GC, typename Tag = opt::none>
+ struct node
+ {
+ typedef GC gc ; ///< Garbage collector
+ typedef Tag tag ; ///< tag
+
+ typedef cds::details::marked_ptr<node, 1> marked_ptr; ///< marked pointer
+ typedef typename gc::template atomic_marked_ptr< marked_ptr> atomic_marked_ptr; ///< atomic marked pointer specific for GC
+
+ /// Rebind node for other template parameters
+ template <class GC2, typename Tag2 = tag>
+ struct rebind {
+ typedef node<GC2, Tag2> other ; ///< Rebinding result
+ };
+
+ atomic_marked_ptr m_pNext ; ///< pointer to the next node in the container
+
+ node()
+ {
+ m_pNext.store( marked_ptr(), atomics::memory_order_release );
+ }
+ };
+
+ using cds::intrusive::single_link::default_hook;
+
+ //@cond
+ template < typename HookType, typename... Options>
+ struct hook
+ {
+ typedef typename opt::make_options< default_hook, Options...>::type options;
+ typedef typename options::gc gc;
+ typedef typename options::tag tag;
+ typedef node<gc, tag> node_type;
+ typedef HookType hook_type;
+ };
+ //@endcond
+
+
+ /// Base hook
+ /**
+ \p Options are:
+ - opt::gc - garbage collector used.
+ - opt::tag - a \ref cds_intrusive_hook_tag "tag"
+ */
+ template < typename... Options >
+ struct base_hook: public hook< opt::base_hook_tag, Options... >
+ {};
+
+ /// Member hook
+ /**
+ \p MemberOffset defines offset in bytes of \ref node member into your structure.
+ Use \p offsetof macro to define \p MemberOffset
+
+ \p Options are:
+ - opt::gc - garbage collector used.
+ - opt::tag - a \ref cds_intrusive_hook_tag "tag"
+ */
+ template < size_t MemberOffset, typename... Options >
+ struct member_hook: public hook< opt::member_hook_tag, Options... >
+ {
+ //@cond
+ static const size_t c_nMemberOffset = MemberOffset;
+ //@endcond
+ };
+
+ /// Traits hook
+ /**
+ \p NodeTraits defines type traits for node.
+ See \ref node_traits for \p NodeTraits interface description
+
+ \p Options are:
+ - opt::gc - garbage collector used.
+ - opt::tag - a \ref cds_intrusive_hook_tag "tag"
+ */
+ template <typename NodeTraits, typename... Options >
+ struct traits_hook: public hook< opt::traits_hook_tag, Options... >
+ {
+ //@cond
+ typedef NodeTraits node_traits;
+ //@endcond
+ };
+
+ /// BasketQueue internal statistics. May be used for debugging or profiling
+ /**
+ Template argument \p Counter defines type of counter.
+ Default is \p cds::atomicity::event_counter, that is weak, i.e. it is not guaranteed
+ strict event counting.
+ You may use stronger type of counter like as \p cds::atomicity::item_counter,
+ or even integral type, for example, \p int.
+ */
+ template <typename Counter = cds::atomicity::event_counter >
+ struct stat
+ {
+ typedef Counter counter_type; ///< Counter type
+
+ counter_type m_EnqueueCount; ///< Enqueue call count
+ counter_type m_DequeueCount; ///< Dequeue call count
+ counter_type m_EnqueueRace; ///< Count of enqueue race conditions encountered
+ counter_type m_DequeueRace; ///< Count of dequeue race conditions encountered
+ counter_type m_AdvanceTailError;///< Count of "advance tail failed" events
+ counter_type m_BadTail; ///< Count of events "Tail is not pointed to the last item in the queue"
+ counter_type m_TryAddBasket; ///< Count of attemps adding new item to a basket (only or BasketQueue, for other queue this metric is not used)
+ counter_type m_AddBasketCount; ///< Count of events "Enqueue a new item into basket" (only or BasketQueue, for other queue this metric is not used)
+ counter_type m_EmptyDequeue; ///< Count of dequeue from empty queue
+
+ /// Register enqueue call
+ void onEnqueue() { ++m_EnqueueCount; }
+ /// Register dequeue call
+ void onDequeue() { ++m_DequeueCount; }
+ /// Register enqueue race event
+ void onEnqueueRace() { ++m_EnqueueRace; }
+ /// Register dequeue race event
+ void onDequeueRace() { ++m_DequeueRace; }
+ /// Register "advance tail failed" event
+ void onAdvanceTailFailed() { ++m_AdvanceTailError; }
+ /// Register event "Tail is not pointed to last item in the queue"
+ void onBadTail() { ++m_BadTail; }
+ /// Register an attempt t add new item to basket
+ void onTryAddBasket() { ++m_TryAddBasket; }
+ /// Register event "Enqueue a new item into basket" (only or BasketQueue, for other queue this metric is not used)
+ void onAddBasket() { ++m_AddBasketCount; }
+ /// Register dequeuing from empty queue
+ void onEmptyDequeue() { ++m_EmptyDequeue; }
+
+
+ //@cond
+ void reset()
+ {
+ m_EnqueueCount.reset();
+ m_DequeueCount.reset();
+ m_EnqueueRace.reset();
+ m_DequeueRace.reset();
+ m_AdvanceTailError.reset();
+ m_BadTail.reset();
+ m_TryAddBasket.reset();
+ m_AddBasketCount.reset();
+ m_EmptyDequeue.reset();
+ }
+
+ stat& operator +=( stat const& s )
+ {
+ m_EnqueueCount += s.m_EnqueueCount.get();
+ m_DequeueCount += s.m_DequeueCount.get();
+ m_EnqueueRace += s.m_EnqueueRace.get();
+ m_DequeueRace += s.m_DequeueRace.get();
+ m_AdvanceTailError += s.m_AdvanceTailError.get();
+ m_BadTail += s.m_BadTail.get();
+ m_TryAddBasket += s.m_TryAddBasket.get();
+ m_AddBasketCount += s.m_AddBasketCount.get();
+ m_EmptyDequeue += s.m_EmptyDequeue.get();
+ return *this;
+ }
+ //@endcond
+ };
+
+ /// Dummy BasketQueue statistics - no counting is performed, no overhead. Support interface like \p basket_queue::stat
+ struct empty_stat
+ {
+ //@cond
+ void onEnqueue() const {}
+ void onDequeue() const {}
+ void onEnqueueRace() const {}
+ void onDequeueRace() const {}
+ void onAdvanceTailFailed() const {}
+ void onBadTail() const {}
+ void onTryAddBasket() const {}
+ void onAddBasket() const {}
+ void onEmptyDequeue() const {}
+
+ void reset() {}
+ empty_stat& operator +=( empty_stat const& )
+ {
+ return *this;
+ }
+ //@endcond
+ };
+
+ /// BasketQueue default type traits
+ struct traits
+ {
+ /// Back-off strategy
+ typedef cds::backoff::empty back_off;
+
+ /// Hook, possible types are \p basket_queue::base_hook, \p basket_queue::member_hook, \p basket_queue::traits_hook
+ typedef basket_queue::base_hook<> hook;
+
+ /// The functor used for dispose removed items. Default is \p opt::v::empty_disposer. This option is used for dequeuing
+ typedef opt::v::empty_disposer disposer;
+
+ /// Item counting feature; by default, disabled. Use \p cds::atomicity::item_counter to enable item counting
+ typedef atomicity::empty_item_counter item_counter;
+
+ /// Internal statistics (by default, disabled)
+ /**
+ Possible option value are: \p basket_queue::stat, \p basket_queue::empty_stat (the default),
+ user-provided class that supports \p %basket_queue::stat interface.
+ */
+ typedef basket_queue::empty_stat stat;
+
+ /// C++ memory ordering model
+ /**
+ Can be \p opt::v::relaxed_ordering (relaxed memory model, the default)
+ or \p opt::v::sequential_consistent (sequentially consisnent memory model).
+ */
+ typedef opt::v::relaxed_ordering memory_model;
+
+ /// Link checking, see \p cds::opt::link_checker
+ static constexpr const opt::link_check_type link_checker = opt::debug_check_link;
+
+ /// Padding for internal critical atomic data. Default is \p opt::cache_line_padding
+ enum { padding = opt::cache_line_padding };
+ };
+
+
+ /// Metafunction converting option list to \p basket_queue::traits
+ /**
+ Supported \p Options are:
+ - \p opt::hook - hook used. Possible hooks are: \p basket_queue::base_hook, \p basket_queue::member_hook, \p basket_queue::traits_hook.
+ If the option is not specified, \p %basket_queue::base_hook<> is used.
+ - \p opt::back_off - back-off strategy used, default is \p cds::backoff::empty.
+ - \p opt::disposer - the functor used for dispose removed items. Default is \p opt::v::empty_disposer. This option is used
+ when dequeuing.
+ - \p opt::link_checker - the type of node's link fields checking. Default is \p opt::debug_check_link
+ - \p opt::item_counter - the type of item counting feature. Default is \p cds::atomicity::empty_item_counter (item counting disabled)
+ To enable item counting use \p cds::atomicity::item_counter
+ - \p opt::stat - the type to gather internal statistics.
+ Possible statistics types are: \p basket_queue::stat, \p basket_queue::empty_stat, user-provided class that supports \p %basket_queue::stat interface.
+ Default is \p %basket_queue::empty_stat (internal statistics disabled).
+ - \p opt::padding - padding for internal critical atomic data. Default is \p opt::cache_line_padding
+ - \p opt::memory_model - C++ memory ordering model. Can be \p opt::v::relaxed_ordering (relaxed memory model, the default)
+ or \p opt::v::sequential_consistent (sequentially consisnent memory model).
+
+ Example: declare \p %BasketQueue with item counting and internal statistics
+ \code
+ typedef cds::intrusive::BasketQueue< cds::gc::HP, Foo,
+ typename cds::intrusive::basket_queue::make_traits<
+ cds::intrusive::opt:hook< cds::intrusive::basket_queue::base_hook< cds::opt::gc<cds:gc::HP> >>,
+ cds::opt::item_counte< cds::atomicity::item_counter >,
+ cds::opt::stat< cds::intrusive::basket_queue::stat<> >
+ >::type
+ > myQueue;
+ \endcode
+ */
+ template <typename... Options>
+ struct make_traits {
+# ifdef CDS_DOXYGEN_INVOKED
+ typedef implementation_defined type; ///< Metafunction result
+# else
+ typedef typename cds::opt::make_options<
+ typename cds::opt::find_type_traits< traits, Options... >::type
+ , Options...
+ >::type type;
+# endif
+ };
+ } // namespace basket_queue
+
+ /// Basket lock-free queue (intrusive variant)
+ /** @ingroup cds_intrusive_queue
+ Implementation of basket queue algorithm.
+
+ \par Source:
+ [2007] Moshe Hoffman, Ori Shalev, Nir Shavit "The Baskets Queue"
+
+ <b>Key idea</b>
+
+ In the 'basket' approach, instead of
+ the traditional ordered list of nodes, the queue consists of an ordered list of groups
+ of nodes (logical baskets). The order of nodes in each basket need not be specified, and in
+ fact, it is easiest to maintain them in FIFO order. The baskets fulfill the following basic
+ rules:
+ - Each basket has a time interval in which all its nodes' enqueue operations overlap.
+ - The baskets are ordered by the order of their respective time intervals.
+ - For each basket, its nodes' dequeue operations occur after its time interval.
+ - The dequeue operations are performed according to the order of baskets.
+
+ Two properties define the FIFO order of nodes:
+ - The order of nodes in a basket is not specified.
+ - The order of nodes in different baskets is the FIFO-order of their respective baskets.
+
+ In algorithms such as the MS-queue or optimistic
+ queue, threads enqueue items by applying a Compare-and-swap (CAS) operation to the
+ queue's tail pointer, and all the threads that fail on a particular CAS operation (and also
+ the winner of that CAS) overlap in time. In particular, they share the time interval of
+ the CAS operation itself. Hence, all the threads that fail to CAS on the tail-node of
+ the queue may be inserted into the same basket. By integrating the basket-mechanism
+ as the back-off mechanism, the time usually spent on backing-off before trying to link
+ onto the new tail, can now be utilized to insert the failed operations into the basket,
+ allowing enqueues to complete sooner. In the meantime, the next successful CAS operations
+ by enqueues allow new baskets to be formed down the list, and these can be
+ filled concurrently. Moreover, the failed operations don't retry their link attempt on the
+ new tail, lowering the overall contention on it. This leads to a queue
+ algorithm that unlike all former concurrent queue algorithms requires virtually no tuning
+ of the backoff mechanisms to reduce contention, making the algorithm an attractive
+ out-of-the-box queue.
+
+ In order to enqueue, just as in \p MSQueue, a thread first tries to link the new node to
+ the last node. If it failed to do so, then another thread has already succeeded. Thus it
+ tries to insert the new node into the new basket that was created by the winner thread.
+ To dequeue a node, a thread first reads the head of the queue to obtain the
+ oldest basket. It may then dequeue any node in the oldest basket.
+
+ <b>Template arguments:</b>
+ - \p GC - garbage collector type: \p gc::HP, \p gc::DHP
+ - \p T - type of value to be stored in the queue
+ - \p Traits - queue traits, default is \p basket_queue::traits. You can use \p basket_queue::make_traits
+ metafunction to make your traits or just derive your traits from \p %basket_queue::traits:
+ \code
+ struct myTraits: public cds::intrusive::basket_queue::traits {
+ typedef cds::intrusive::basket_queue::stat<> stat;
+ typedef cds::atomicity::item_counter item_counter;
+ };
+ typedef cds::intrusive::BasketQueue< cds::gc::HP, Foo, myTraits > myQueue;
+
+ // Equivalent make_traits example:
+ typedef cds::intrusive::BasketQueue< cds::gc::HP, Foo,
+ typename cds::intrusive::basket_queue::make_traits<
+ cds::opt::stat< cds::intrusive::basket_queue::stat<> >,
+ cds::opt::item_counter< cds::atomicity::item_counter >
+ >::type
+ > myQueue;
+ \endcode
+
+ Garbage collecting schema \p GC must be consistent with the \p basket_queue::node GC.
+
+ \par About item disposing
+ Like \p MSQueue, the Baskets queue algo has a key feature: even if the queue is empty it contains one item that is "dummy" one from
+ the standpoint of the algo. See \p dequeue() function doc for explanation.
+
+ \par Examples
+ \code
+ #include <cds/intrusive/basket_queue.h>
+ #include <cds/gc/hp.h>
+
+ namespace ci = cds::inrtusive;
+ typedef cds::gc::HP hp_gc;
+
+ // Basket queue with Hazard Pointer garbage collector, base hook + item disposer:
+ struct Foo: public ci::basket_queue::node< hp_gc >
+ {
+ // Your data
+ ...
+ };
+
+ // Disposer for Foo struct just deletes the object passed in
+ struct fooDisposer {
+ void operator()( Foo * p )
+ {
+ delete p;
+ }
+ };
+
+ struct fooTraits: public ci::basket_queue::traits {
+ typedef ci::basket_queue::base_hook< ci::opt::gc<hp_gc> > hook;
+ typedef fooDisposer disposer;
+ };
+ typedef ci::BasketQueue< hp_gc, Foo, fooTraits > fooQueue;
+
+ // BasketQueue with Hazard Pointer garbage collector,
+ // member hook + item disposer + item counter,
+ // without padding of internal queue data:
+ struct Bar
+ {
+ // Your data
+ ...
+ ci::basket_queue::node< hp_gc > hMember;
+ };
+
+ struct barTraits: public
+ ci::basket_queue::make_traits<
+ ci::opt::hook<
+ ci::basket_queue::member_hook<
+ offsetof(Bar, hMember)
+ ,ci::opt::gc<hp_gc>
+ >
+ >
+ ,ci::opt::disposer< fooDisposer >
+ ,cds::opt::item_counter< cds::atomicity::item_counter >
+ ,cds::opt::padding< cds::opt::no_special_padding >
+ >::type
+ {};
+ typedef ci::BasketQueue< hp_gc, Bar, barTraits > barQueue;
+ \endcode
+ */
+ template <typename GC, typename T, typename Traits = basket_queue::traits >
+ class BasketQueue
+ {
+ public:
+ typedef GC gc; ///< Garbage collector
+ typedef T value_type; ///< type of value stored in the queue
+ typedef Traits traits; ///< Queue traits
+ typedef typename traits::hook hook; ///< hook type
+ typedef typename hook::node_type node_type; ///< node type
+ typedef typename traits::disposer disposer; ///< disposer used
+ typedef typename get_node_traits< value_type, node_type, hook>::type node_traits; ///< node traits
+ typedef typename single_link::get_link_checker< node_type, traits::link_checker >::type link_checker; ///< link checker
+
+ typedef typename traits::back_off back_off; ///< back-off strategy
+ typedef typename traits::item_counter item_counter; ///< Item counting policy used
+ typedef typename traits::stat stat; ///< Internal statistics policy used
+ typedef typename traits::memory_model memory_model; ///< Memory ordering. See cds::opt::memory_model option
+
+ /// Rebind template arguments
+ template <typename GC2, typename T2, typename Traits2>
+ struct rebind {
+ typedef BasketQueue< GC2, T2, Traits2> other ; ///< Rebinding result
+ };
+
+ static constexpr const size_t c_nHazardPtrCount = 6 ; ///< Count of hazard pointer required for the algorithm
+
+ protected:
+ //@cond
+ typedef typename node_type::marked_ptr marked_ptr;
+ typedef typename node_type::atomic_marked_ptr atomic_marked_ptr;
+
+ // GC and node_type::gc must be the same
+ static_assert( std::is_same<gc, typename node_type::gc>::value, "GC and node_type::gc must be the same");
+ //@endcond
+
+ atomic_marked_ptr m_pHead ; ///< Queue's head pointer (aligned)
+ //@cond
+ typename opt::details::apply_padding< atomic_marked_ptr, traits::padding >::padding_type pad1_;
+ //@endcond
+ atomic_marked_ptr m_pTail ; ///< Queue's tail pointer (aligned)
+ //@cond
+ typename opt::details::apply_padding< atomic_marked_ptr, traits::padding >::padding_type pad2_;
+ //@endcond
+ node_type m_Dummy ; ///< dummy node
+ //@cond
+ typename opt::details::apply_padding< node_type, traits::padding >::padding_type pad3_;
+ //@endcond
+ item_counter m_ItemCounter ; ///< Item counter
+ stat m_Stat ; ///< Internal statistics
+ //@cond
+ size_t const m_nMaxHops;
+ //@endcond
+
+ //@cond
+
+ struct dequeue_result {
+ typename gc::template GuardArray<3> guards;
+ node_type * pNext;
+ };
+
+ bool do_dequeue( dequeue_result& res, bool bDeque )
+ {
+ // Note:
+ // If bDeque == false then the function is called from empty method and no real dequeuing operation is performed
+
+ back_off bkoff;
+
+ marked_ptr h;
+ marked_ptr t;
+ marked_ptr pNext;
+
+ while ( true ) {
+ h = res.guards.protect( 0, m_pHead, []( marked_ptr p ) -> value_type * { return node_traits::to_value_ptr( p.ptr());});
+ t = res.guards.protect( 1, m_pTail, []( marked_ptr p ) -> value_type * { return node_traits::to_value_ptr( p.ptr());});
+ pNext = res.guards.protect( 2, h->m_pNext, []( marked_ptr p ) -> value_type * { return node_traits::to_value_ptr( p.ptr());});
+
+ if ( h == m_pHead.load( memory_model::memory_order_acquire )) {
+ if ( h.ptr() == t.ptr()) {
+ if ( !pNext.ptr()) {
+ m_Stat.onEmptyDequeue();
+ return false;
+ }
+
+ {
+ typename gc::Guard g;
+ while ( pNext->m_pNext.load(memory_model::memory_order_relaxed).ptr() && m_pTail.load(memory_model::memory_order_relaxed) == t ) {
+ pNext = g.protect( pNext->m_pNext, []( marked_ptr p ) -> value_type * { return node_traits::to_value_ptr( p.ptr());});
+ res.guards.copy( 2, g );
+ }
+ }
+
+ m_pTail.compare_exchange_weak( t, marked_ptr(pNext.ptr()), memory_model::memory_order_acquire, atomics::memory_order_relaxed );
+ }
+ else {
+ marked_ptr iter( h );
+ size_t hops = 0;
+
+ typename gc::Guard g;
+
+ while ( pNext.ptr() && pNext.bits() && iter.ptr() != t.ptr() && m_pHead.load(memory_model::memory_order_relaxed) == h ) {
+ iter = pNext;
+ g.assign( res.guards.template get<value_type>(2));
+ pNext = res.guards.protect( 2, pNext->m_pNext, []( marked_ptr p ) -> value_type * { return node_traits::to_value_ptr( p.ptr());});
+ ++hops;
+ }
+
+ if ( m_pHead.load(memory_model::memory_order_relaxed) != h )
+ continue;
+
+ if ( iter.ptr() == t.ptr())
+ free_chain( h, iter );
+ else if ( bDeque ) {
+ res.pNext = pNext.ptr();
+
+ if ( iter->m_pNext.compare_exchange_weak( pNext, marked_ptr( pNext.ptr(), 1 ), memory_model::memory_order_acquire, atomics::memory_order_relaxed )) {
+ if ( hops >= m_nMaxHops )
+ free_chain( h, pNext );
+ break;
+ }
+ }
+ else
+ return true;
+ }
+ }
+
+ if ( bDeque )
+ m_Stat.onDequeueRace();
+ bkoff();
+ }
+
+ if ( bDeque ) {
+ --m_ItemCounter;
+ m_Stat.onDequeue();
+ }
+
+ return true;
+ }
+
+ void free_chain( marked_ptr head, marked_ptr newHead )
+ {
+ // "head" and "newHead" are guarded
+
+ if ( m_pHead.compare_exchange_strong( head, marked_ptr(newHead.ptr()), memory_model::memory_order_release, atomics::memory_order_relaxed ))
+ {
+ typename gc::template GuardArray<2> guards;
+ guards.assign( 0, node_traits::to_value_ptr(head.ptr()));
+ while ( head.ptr() != newHead.ptr()) {
+ marked_ptr pNext = guards.protect( 1, head->m_pNext, []( marked_ptr p ) -> value_type * { return node_traits::to_value_ptr( p.ptr());});
+ assert( pNext.bits() != 0 );
+ dispose_node( head.ptr());
+ guards.copy( 0, 1 );
+ head = pNext;
+ }
+ }
+ }
+
+ static void clear_links( node_type * pNode )
+ {
+ pNode->m_pNext.store( marked_ptr( nullptr ), memory_model::memory_order_release );
+ }
+
+ void dispose_node( node_type * p )
+ {
+ if ( p != &m_Dummy ) {
+ struct internal_disposer
+ {
+ void operator()( value_type * p )
+ {
+ assert( p != nullptr );
+ BasketQueue::clear_links( node_traits::to_node_ptr( p ));
+ disposer()(p);
+ }
+ };
+ gc::template retire<internal_disposer>( node_traits::to_value_ptr(p));
+ }
+ }
+ //@endcond
+
+ public:
+ /// Initializes empty queue
+ BasketQueue()
+ : m_pHead( &m_Dummy )
+ , m_pTail( &m_Dummy )
+ , m_nMaxHops( 3 )
+ {}
+
+ /// Destructor clears the queue
+ /**
+ Since the baskets queue contains at least one item even
+ if the queue is empty, the destructor may call item disposer.
+ */
+ ~BasketQueue()
+ {
+ clear();
+
+ node_type * pHead = m_pHead.load(memory_model::memory_order_relaxed).ptr();
+ assert( pHead != nullptr );
+
+ {
+ node_type * pNext = pHead->m_pNext.load( memory_model::memory_order_relaxed ).ptr();
+ while ( pNext ) {
+ node_type * p = pNext;
+ pNext = pNext->m_pNext.load( memory_model::memory_order_relaxed ).ptr();
+ p->m_pNext.store( marked_ptr(), memory_model::memory_order_relaxed );
+ dispose_node( p );
+ }
+ pHead->m_pNext.store( marked_ptr(), memory_model::memory_order_relaxed );
+ //m_pTail.store( marked_ptr( pHead ), memory_model::memory_order_relaxed );
+ }
+
+ m_pHead.store( marked_ptr( nullptr ), memory_model::memory_order_relaxed );
+ m_pTail.store( marked_ptr( nullptr ), memory_model::memory_order_relaxed );
+
+ dispose_node( pHead );
+ }
+
+ /// Enqueues \p val value into the queue.
+ /** @anchor cds_intrusive_BasketQueue_enqueue
+ The function always returns \p true.
+ */
+ bool enqueue( value_type& val )
+ {
+ node_type * pNew = node_traits::to_node_ptr( val );
+ link_checker::is_empty( pNew );
+
+ typename gc::Guard guard;
+ typename gc::Guard gNext;
+ back_off bkoff;
+
+ marked_ptr t;
+ while ( true ) {
+ t = guard.protect( m_pTail, []( marked_ptr p ) -> value_type * { return node_traits::to_value_ptr( p.ptr());});
+
+ marked_ptr pNext = t->m_pNext.load(memory_model::memory_order_relaxed );
+
+ if ( pNext.ptr() == nullptr ) {
+ pNew->m_pNext.store( marked_ptr(), memory_model::memory_order_relaxed );
+ if ( t->m_pNext.compare_exchange_weak( pNext, marked_ptr(pNew), memory_model::memory_order_release, atomics::memory_order_relaxed )) {
+ if ( !m_pTail.compare_exchange_strong( t, marked_ptr(pNew), memory_model::memory_order_release, atomics::memory_order_relaxed ))
+ m_Stat.onAdvanceTailFailed();
+ break;
+ }
+
+ // Try adding to basket
+ m_Stat.onTryAddBasket();
+
+ // Reread tail next
+ try_again:
+ pNext = gNext.protect( t->m_pNext, []( marked_ptr p ) -> value_type * { return node_traits::to_value_ptr( p.ptr());});
+
+ // add to the basket
+ if ( m_pTail.load( memory_model::memory_order_relaxed ) == t
+ && t->m_pNext.load( memory_model::memory_order_relaxed) == pNext
+ && !pNext.bits())
+ {
+ bkoff();
+ pNew->m_pNext.store( pNext, memory_model::memory_order_relaxed );
+ if ( t->m_pNext.compare_exchange_weak( pNext, marked_ptr( pNew ), memory_model::memory_order_release, atomics::memory_order_relaxed )) {
+ m_Stat.onAddBasket();
+ break;
+ }
+ goto try_again;
+ }
+ }
+ else {
+ // Tail is misplaced, advance it
+
+ typename gc::template GuardArray<2> g;
+ g.assign( 0, node_traits::to_value_ptr( pNext.ptr()));
+ if ( m_pTail.load( memory_model::memory_order_acquire ) != t
+ || t->m_pNext.load( memory_model::memory_order_relaxed ) != pNext )
+ {
+ m_Stat.onEnqueueRace();
+ bkoff();
+ continue;
+ }
+
+ marked_ptr p;
+ bool bTailOk = true;
+ while ( (p = pNext->m_pNext.load( memory_model::memory_order_acquire )).ptr() != nullptr )
+ {
+ bTailOk = m_pTail.load( memory_model::memory_order_relaxed ) == t;
+ if ( !bTailOk )
+ break;
+
+ g.assign( 1, node_traits::to_value_ptr( p.ptr()));
+ if ( pNext->m_pNext.load( memory_model::memory_order_relaxed ) != p )
+ continue;
+ pNext = p;
+ g.assign( 0, g.template get<value_type>( 1 ));
+ }
+ if ( !bTailOk || !m_pTail.compare_exchange_weak( t, marked_ptr( pNext.ptr()), memory_model::memory_order_release, atomics::memory_order_relaxed ))
+ m_Stat.onAdvanceTailFailed();
+
+ m_Stat.onBadTail();
+ }
+
+ m_Stat.onEnqueueRace();
+ }
+
+ ++m_ItemCounter;
+ m_Stat.onEnqueue();
+
+ return true;
+ }
+
+ /// Synonym for \p enqueue() function
+ bool push( value_type& val )
+ {
+ return enqueue( val );
+ }
+
+ /// Dequeues a value from the queue
+ /** @anchor cds_intrusive_BasketQueue_dequeue
+ If the queue is empty the function returns \p nullptr.
+
+ @note See \p MSQueue::dequeue() note about item disposing
+ */
+ value_type * dequeue()
+ {
+ dequeue_result res;
+
+ if ( do_dequeue( res, true ))
+ return node_traits::to_value_ptr( *res.pNext );
+ return nullptr;
+ }
+
+ /// Synonym for \p dequeue() function
+ value_type * pop()
+ {
+ return dequeue();
+ }
+
+ /// Checks if the queue is empty
+ /**
+ Note that this function is not \p const.
+ The function is based on \p dequeue() algorithm
+ but really it does not dequeue any item.
+ */
+ bool empty()
+ {
+ dequeue_result res;
+ return !do_dequeue( res, false );
+ }
+
+ /// Clear the queue
+ /**
+ The function repeatedly calls \p dequeue() until it returns \p nullptr.
+ The disposer defined in template \p Traits is called for each item
+ that can be safely disposed.
+ */
+ void clear()
+ {
+ while ( dequeue());
+ }
+
+ /// Returns queue's item count
+ /**
+ The value returned depends on \p Traits (see basket_queue::traits::item_counter). For \p atomicity::empty_item_counter,
+ this function always returns 0.
+
+ @note Even if you use real item counter and it returns 0, this fact is not mean that the queue
+ is empty. To check queue emptyness use \p empty() method.
+ */
+ size_t size() const
+ {
+ return m_ItemCounter.value();
+ }
+
+ /// Returns reference to internal statistics
+ const stat& statistics() const
+ {
+ return m_Stat;
+ }
+ };
+
+}} // namespace cds::intrusive
+
+#endif // #ifndef CDSLIB_INTRUSIVE_BASKET_QUEUE_H