fixed adding file problem
[c11concurrency-benchmarks.git] / gdax-orderbook-hpp / demo / dependencies / libcds-2.3.2 / cds / intrusive / segmented_queue.h
diff --git a/gdax-orderbook-hpp/demo/dependencies/libcds-2.3.2/cds/intrusive/segmented_queue.h b/gdax-orderbook-hpp/demo/dependencies/libcds-2.3.2/cds/intrusive/segmented_queue.h
new file mode 100644 (file)
index 0000000..95d8e2e
--- /dev/null
@@ -0,0 +1,725 @@
+/*
+    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_SEGMENTED_QUEUE_H
+#define CDSLIB_INTRUSIVE_SEGMENTED_QUEUE_H
+
+#include <mutex>
+#include <cds/intrusive/details/base.h>
+#include <cds/details/marked_ptr.h>
+#include <cds/algo/int_algo.h>
+#include <cds/sync/spinlock.h>
+#include <cds/opt/permutation.h>
+
+#include <boost/intrusive/slist.hpp>
+
+#if CDS_COMPILER == CDS_COMPILER_MSVC
+#   pragma warning( push )
+#   pragma warning( disable: 4355 ) // warning C4355: 'this' : used in base member initializer list
+#endif
+
+namespace cds { namespace intrusive {
+
+    /// SegmentedQueue -related declarations
+    namespace segmented_queue {
+
+        /// SegmentedQueue internal statistics. May be used for debugging or profiling
+        template <typename Counter = cds::atomicity::event_counter >
+        struct stat
+        {
+            typedef Counter  counter_type;  ///< Counter type
+
+            counter_type    m_nPush;            ///< Push count
+            counter_type    m_nPushPopulated;   ///< Number of attempts to push to populated (non-empty) cell
+            counter_type    m_nPushContended;   ///< Number of failed CAS when pushing
+            counter_type    m_nPop;             ///< Pop count
+            counter_type    m_nPopEmpty;        ///< Number of dequeuing from empty queue
+            counter_type    m_nPopContended;    ///< Number of failed CAS when popping
+
+            counter_type    m_nCreateSegmentReq;    ///< Number of request to create new segment
+            counter_type    m_nDeleteSegmentReq;    ///< Number to request to delete segment
+            counter_type    m_nSegmentCreated;  ///< Number of created segments
+            counter_type    m_nSegmentDeleted;  ///< Number of deleted segments
+
+            //@cond
+            void onPush()               { ++m_nPush; }
+            void onPushPopulated()      { ++m_nPushPopulated; }
+            void onPushContended()      { ++m_nPushContended; }
+            void onPop()                { ++m_nPop;  }
+            void onPopEmpty()           { ++m_nPopEmpty; }
+            void onPopContended()       { ++m_nPopContended; }
+            void onCreateSegmentReq()   { ++m_nCreateSegmentReq; }
+            void onDeleteSegmentReq()   { ++m_nDeleteSegmentReq; }
+            void onSegmentCreated()     { ++m_nSegmentCreated; }
+            void onSegmentDeleted()     { ++m_nSegmentDeleted; }
+            //@endcond
+        };
+
+        /// Dummy SegmentedQueue statistics, no overhead
+        struct empty_stat {
+            //@cond
+            void onPush() const             {}
+            void onPushPopulated() const    {}
+            void onPushContended() const    {}
+            void onPop() const              {}
+            void onPopEmpty() const         {}
+            void onPopContended() const     {}
+            void onCreateSegmentReq() const {}
+            void onDeleteSegmentReq() const {}
+            void onSegmentCreated() const   {}
+            void onSegmentDeleted() const   {}
+            //@endcond
+        };
+
+        /// SegmentedQueue default traits
+        struct traits {
+            /// Element disposer that is called when the item to be dequeued. Default is opt::v::empty_disposer (no disposer)
+            typedef opt::v::empty_disposer disposer;
+
+            /// Item counter, default is atomicity::item_counter
+            /**
+                The item counting is an essential part of segmented queue algorithm.
+                The \p empty() member function is based on checking <tt>size() == 0</tt>.
+                Therefore, dummy item counter like atomicity::empty_item_counter is not the proper counter.
+            */
+            typedef atomicity::item_counter item_counter;
+
+            /// Internal statistics, possible predefined types are \ref stat, \ref empty_stat (the default)
+            typedef segmented_queue::empty_stat        stat;
+
+            /// Memory model, default is opt::v::relaxed_ordering. See cds::opt::memory_model for the full list of possible types
+            typedef opt::v::relaxed_ordering  memory_model;
+
+            /// Alignment of critical data, default is cache line alignment. See cds::opt::alignment option specification
+            enum { alignment = opt::cache_line_alignment };
+
+            /// Padding of segment data, default is no special padding
+            /**
+                The segment is just an array of atomic data pointers,
+                so, the high load leads to false sharing and performance degradation.
+                A padding of segment data can eliminate false sharing issue.
+                On the other hand, the padding leads to increase segment size.
+            */
+            enum { padding = opt::no_special_padding };
+
+            /// Segment allocator. Default is \ref CDS_DEFAULT_ALLOCATOR
+            typedef CDS_DEFAULT_ALLOCATOR allocator;
+
+            /// Lock type used to maintain an internal list of allocated segments
+            typedef cds::sync::spin lock_type;
+
+            /// Random \ref cds::opt::permutation_generator "permutation generator" for sequence [0, quasi_factor)
+            typedef cds::opt::v::random2_permutation<int>    permutation_generator;
+        };
+
+        /// Metafunction converting option list to traits for SegmentedQueue
+        /**
+            The metafunction can be useful if a few fields in \p segmented_queue::traits should be changed.
+            For example:
+            \code
+            typedef cds::intrusive::segmented_queue::make_traits<
+                cds::opt::item_counter< cds::atomicity::item_counter >
+            >::type my_segmented_queue_traits;
+            \endcode
+            This code creates \p %SegmentedQueue type traits with item counting feature,
+            all other \p %segmented_queue::traits members left unchanged.
+
+            \p Options are:
+            - \p opt::disposer - the functor used to dispose removed items.
+            - \p opt::stat - internal statistics, possible type: \p segmented_queue::stat, \p segmented_queue::empty_stat (the default)
+            - \p opt::item_counter - item counting feature. Note that \p atomicity::empty_item_counetr is not suitable
+                for segmented queue.
+            - \p opt::memory_model - memory model, default is \p opt::v::relaxed_ordering.
+                See option description for the full list of possible models
+            - \p opt::alignment - the alignment for critical data, see option description for explanation
+            - \p opt::padding - the padding of segment data, default no special padding.
+                See \p traits::padding for explanation.
+            - \p opt::allocator - the allocator to be used for maintaining segments.
+            - \p opt::lock_type - a mutual exclusion lock type used to maintain internal list of allocated
+                segments. Default is \p cds::opt::Spin, \p std::mutex is also suitable.
+            - \p opt::permutation_generator - a random permutation generator for sequence [0, quasi_factor),
+                default is \p cds::opt::v::random2_permutation<int>
+        */
+        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 segmented_queue
+
+    /// Segmented queue
+    /** @ingroup cds_intrusive_queue
+
+        The queue is based on work
+        - [2010] Afek, Korland, Yanovsky "Quasi-Linearizability: relaxed consistency for improved concurrency"
+
+        In this paper the authors offer a relaxed version of linearizability, so-called quasi-linearizability,
+        that preserves some of the intuition, provides a flexible way to control the level of relaxation
+        and supports th implementation of more concurrent and scalable data structure.
+        Intuitively, the linearizability requires each run to be equivalent in some sense to a serial run
+        of the algorithm. This equivalence to some serial run imposes strong synchronization requirements
+        that in many cases results in limited scalability and synchronization bottleneck.
+
+        The general idea is that the queue maintains a linked list of segments, each segment is an array of
+        nodes in the size of the quasi factor, and each node has a deleted boolean marker, which states
+        if it has been dequeued. Each producer iterates over last segment in the linked list in some random
+        permutation order. Whet it finds an empty cell it performs a CAS operation attempting to enqueue its
+        new element. In case the entire segment has been scanned and no available cell is found (implying
+        that the segment is full), then it attempts to add a new segment to the list.
+
+        The dequeue operation is similar: the consumer iterates over the first segment in the linked list
+        in some random permutation order. When it finds an item which has not yet been dequeued, it performs
+        CAS on its deleted marker in order to "delete" it, if succeeded this item is considered dequeued.
+        In case the entire segment was scanned and all the nodes have already been dequeued (implying that
+        the segment is empty), then it attempts to remove this segment from the linked list and starts
+        the same process on the next segment. If there is no next segment, the queue is considered empty.
+
+        Based on the fact that most of the time threads do not add or remove segments, most of the work
+        is done in parallel on different cells in the segments. This ensures a controlled contention
+        depending on the segment size, which is quasi factor.
+
+        The segmented queue is an <i>unfair</i> queue since it violates the strong FIFO order but no more than
+        quasi factor. This means that the consumer dequeues <i>any</i> item from the current first segment.
+
+        Template parameters:
+        - \p GC - a garbage collector, possible types are cds::gc::HP, cds::gc::DHP
+        - \p T - the type of values stored in the queue
+        - \p Traits - queue type traits, default is \p segmented_queue::traits.
+            \p segmented_queue::make_traits metafunction can be used to construct the
+            type traits.
+
+        The queue stores the pointers to enqueued items so no special node hooks are needed.
+    */
+    template <class GC, typename T, typename Traits = segmented_queue::traits >
+    class SegmentedQueue
+    {
+    public:
+        typedef GC  gc;         ///< Garbage collector
+        typedef T   value_type; ///< type of the value stored in the queue
+        typedef Traits traits;  ///< Queue traits
+
+        typedef typename traits::disposer      disposer    ;   ///< value disposer, called only in \p clear() when the element to be dequeued
+        typedef typename traits::allocator     allocator;   ///< Allocator maintaining the segments
+        typedef typename traits::memory_model  memory_model;   ///< Memory ordering. See cds::opt::memory_model option
+        typedef typename traits::item_counter  item_counter;   ///< Item counting policy, see cds::opt::item_counter option setter
+        typedef typename traits::stat          stat;   ///< Internal statistics policy
+        typedef typename traits::lock_type     lock_type;   ///< Type of mutex for maintaining an internal list of allocated segments.
+        typedef typename traits::permutation_generator permutation_generator; ///< Random permutation generator for sequence [0, quasi-factor)
+
+        static const size_t c_nHazardPtrCount = 2 ; ///< Count of hazard pointer required for the algorithm
+
+    protected:
+        //@cond
+        // Segment cell. LSB is used as deleted mark
+        typedef cds::details::marked_ptr< value_type, 1 > regular_cell;
+        typedef atomics::atomic< regular_cell > atomic_cell;
+        typedef typename cds::opt::details::apply_padding< atomic_cell, traits::padding >::type cell;
+
+        // Segment
+        struct segment: public boost::intrusive::slist_base_hook<>
+        {
+            cell * cells;    // Cell array of size \ref m_nQuasiFactor
+            size_t version;  // version tag (ABA prevention tag)
+            // cell array is placed here in one continuous memory block
+
+            // Initializes the segment
+            explicit segment( size_t nCellCount )
+                // MSVC warning C4355: 'this': used in base member initializer list
+                : cells( reinterpret_cast< cell *>( this + 1 ))
+                , version( 0 )
+            {
+                init( nCellCount );
+            }
+
+            segment() = delete;
+
+            void init( size_t nCellCount )
+            {
+                cell * pLastCell = cells + nCellCount;
+                for ( cell* pCell = cells; pCell < pLastCell; ++pCell )
+                    pCell->data.store( regular_cell(), atomics::memory_order_relaxed );
+                atomics::atomic_thread_fence( memory_model::memory_order_release );
+            }
+        };
+
+        typedef typename opt::details::alignment_setter< atomics::atomic<segment *>, traits::alignment >::type aligned_segment_ptr;
+        //@endcond
+
+    protected:
+        //@cond
+        class segment_list
+        {
+            typedef boost::intrusive::slist< segment, boost::intrusive::cache_last< true > > list_impl;
+            typedef std::unique_lock< lock_type > scoped_lock;
+
+            aligned_segment_ptr m_pHead;
+            aligned_segment_ptr m_pTail;
+
+            list_impl           m_List;
+            mutable lock_type   m_Lock;
+            size_t const        m_nQuasiFactor;
+            stat&               m_Stat;
+
+        private:
+            struct segment_disposer
+            {
+                void operator()( segment * pSegment )
+                {
+                    assert( pSegment != nullptr );
+                    free_segment( pSegment );
+                }
+            };
+
+            struct gc_segment_disposer
+            {
+                void operator()( segment * pSegment )
+                {
+                    assert( pSegment != nullptr );
+                    retire_segment( pSegment );
+                }
+            };
+
+        public:
+            segment_list( size_t nQuasiFactor, stat& st )
+                : m_pHead( nullptr )
+                , m_pTail( nullptr )
+                , m_nQuasiFactor( nQuasiFactor )
+                , m_Stat( st )
+            {
+                assert( cds::beans::is_power2( nQuasiFactor ));
+            }
+
+            ~segment_list()
+            {
+                m_List.clear_and_dispose( gc_segment_disposer());
+            }
+
+            segment * head( typename gc::Guard& guard )
+            {
+                return guard.protect( m_pHead );
+            }
+
+            segment * tail( typename gc::Guard& guard )
+            {
+                return guard.protect( m_pTail );
+            }
+
+#       ifdef _DEBUG
+            bool populated( segment const& s ) const
+            {
+                // The lock should be held
+                cell const * pLastCell = s.cells + quasi_factor();
+                for ( cell const * pCell = s.cells; pCell < pLastCell; ++pCell ) {
+                    if ( !pCell->data.load( memory_model::memory_order_relaxed ).all())
+                        return false;
+                }
+                return true;
+            }
+            bool exhausted( segment const& s ) const
+            {
+                // The lock should be held
+                cell const * pLastCell = s.cells + quasi_factor();
+                for ( cell const * pCell = s.cells; pCell < pLastCell; ++pCell ) {
+                    if ( !pCell->data.load( memory_model::memory_order_relaxed ).bits())
+                        return false;
+                }
+                return true;
+            }
+#       endif
+
+            segment * create_tail( segment * pTail, typename gc::Guard& guard )
+            {
+                // pTail is guarded by GC
+
+                m_Stat.onCreateSegmentReq();
+
+                scoped_lock l( m_Lock );
+
+                if ( !m_List.empty() && ( pTail != &m_List.back() || get_version(pTail) != m_List.back().version )) {
+                    m_pTail.store( &m_List.back(), memory_model::memory_order_relaxed );
+
+                    return guard.assign( &m_List.back());
+                }
+
+#           ifdef _DEBUG
+                assert( m_List.empty() || populated( m_List.back()));
+#           endif
+
+                segment * pNew = allocate_segment();
+                m_Stat.onSegmentCreated();
+
+                if ( m_List.empty())
+                    m_pHead.store( pNew, memory_model::memory_order_release );
+                m_List.push_back( *pNew );
+                m_pTail.store( pNew, memory_model::memory_order_release );
+                return guard.assign( pNew );
+            }
+
+            segment * remove_head( segment * pHead, typename gc::Guard& guard )
+            {
+                // pHead is guarded by GC
+                m_Stat.onDeleteSegmentReq();
+
+                segment * pRet;
+                {
+                    scoped_lock l( m_Lock );
+
+                    if ( m_List.empty()) {
+                        m_pTail.store( nullptr, memory_model::memory_order_relaxed );
+                        m_pHead.store( nullptr, memory_model::memory_order_relaxed );
+                        return guard.assign( nullptr );
+                    }
+
+                    if ( pHead != &m_List.front() || get_version(pHead) != m_List.front().version ) {
+                        m_pHead.store( &m_List.front(), memory_model::memory_order_relaxed );
+                        return guard.assign( &m_List.front());
+                    }
+
+#           ifdef _DEBUG
+                    assert( exhausted( m_List.front()));
+#           endif
+
+                    m_List.pop_front();
+                    if ( m_List.empty()) {
+                        pRet = guard.assign( nullptr );
+                        m_pTail.store( nullptr, memory_model::memory_order_relaxed );
+                    }
+                    else
+                        pRet = guard.assign( &m_List.front());
+                    m_pHead.store( pRet, memory_model::memory_order_release );
+                }
+
+                retire_segment( pHead );
+                m_Stat.onSegmentDeleted();
+
+                return pRet;
+            }
+
+            size_t quasi_factor() const
+            {
+                return m_nQuasiFactor;
+            }
+
+        private:
+            typedef cds::details::Allocator< segment, allocator >   segment_allocator;
+
+            static size_t get_version( segment * pSegment )
+            {
+                return pSegment ? pSegment->version : 0;
+            }
+
+            segment * allocate_segment()
+            {
+                return segment_allocator().NewBlock( sizeof(segment) + sizeof(cell) * m_nQuasiFactor, quasi_factor());
+            }
+
+            static void free_segment( segment * pSegment )
+            {
+                segment_allocator().Delete( pSegment );
+            }
+
+            static void retire_segment( segment * pSegment )
+            {
+                gc::template retire<segment_disposer>( pSegment );
+            }
+        };
+        //@endcond
+
+    protected:
+        segment_list              m_SegmentList;  ///< List of segments
+
+        item_counter              m_ItemCounter;  ///< Item counter
+        stat                      m_Stat;         ///< Internal statistics
+
+    public:
+        /// Initializes the empty queue
+        SegmentedQueue(
+            size_t nQuasiFactor     ///< Quasi factor. If it is not a power of 2 it is rounded up to nearest power of 2. Minimum is 2.
+            )
+            : m_SegmentList( cds::beans::ceil2(nQuasiFactor), m_Stat )
+        {
+            static_assert( (!std::is_same< item_counter, cds::atomicity::empty_item_counter >::value),
+                "cds::atomicity::empty_item_counter is not supported for SegmentedQueue"
+                );
+            assert( m_SegmentList.quasi_factor() > 1 );
+        }
+
+        /// Clears the queue and deletes all internal data
+        ~SegmentedQueue()
+        {
+            clear();
+        }
+
+        /// Inserts a new element at last segment of the queue
+        bool enqueue( value_type& val )
+        {
+            // LSB is used as a flag in marked pointer
+            assert( (reinterpret_cast<uintptr_t>( &val ) & 1) == 0 );
+
+            typename gc::Guard segmentGuard;
+            segment * pTailSegment = m_SegmentList.tail( segmentGuard );
+            if ( !pTailSegment ) {
+                // no segments, create the new one
+                pTailSegment = m_SegmentList.create_tail( pTailSegment, segmentGuard );
+                assert( pTailSegment );
+            }
+
+            permutation_generator gen( quasi_factor());
+
+            // First, increment item counter.
+            // We sure that the item will be enqueued
+            // but if we increment the counter after inserting we can get a negative counter value
+            // if dequeuing occurs before incrementing (enqueue/dequeue race)
+            ++m_ItemCounter;
+
+            while ( true ) {
+                CDS_DEBUG_ONLY( size_t nLoopCount = 0);
+                do {
+                    typename permutation_generator::integer_type i = gen;
+                    CDS_DEBUG_ONLY( ++nLoopCount );
+                    if ( pTailSegment->cells[i].data.load(memory_model::memory_order_relaxed).all()) {
+                        // Cell is not empty, go next
+                        m_Stat.onPushPopulated();
+                    }
+                    else {
+                        // Empty cell found, try to enqueue here
+                        regular_cell nullCell;
+                        if ( pTailSegment->cells[i].data.compare_exchange_strong( nullCell, regular_cell( &val ),
+                            memory_model::memory_order_release, atomics::memory_order_relaxed ))
+                        {
+                            // Ok to push item
+                            m_Stat.onPush();
+                            return true;
+                        }
+                        assert( nullCell.ptr());
+                        m_Stat.onPushContended();
+                    }
+                } while ( gen.next());
+
+                assert( nLoopCount == quasi_factor());
+
+                // No available position, create a new segment
+                pTailSegment = m_SegmentList.create_tail( pTailSegment, segmentGuard );
+
+                // Get new permutation
+                gen.reset();
+            }
+        }
+
+        /// Removes an element from first segment of the queue and returns it
+        /**
+            If the queue is empty the function returns \p nullptr.
+
+            The disposer specified in \p Traits template argument is <b>not</b> called for returned item.
+            You should manually dispose the item:
+            \code
+            struct my_disposer {
+                void operator()( foo * p )
+                {
+                    delete p;
+                }
+            };
+            cds::intrusive::SegmentedQueue< cds::gc::HP, foo > theQueue;
+            // ...
+
+            // Dequeue an item
+            foo * pItem = theQueue.dequeue();
+            // deal with pItem
+            //...
+
+            // pItem is not longer needed and can be deleted
+            // Do it via gc::HP::retire
+            cds::gc::HP::template retire< my_disposer >( pItem );
+            \endcode
+        */
+        value_type * dequeue()
+        {
+            typename gc::Guard itemGuard;
+            if ( do_dequeue( itemGuard )) {
+                value_type * pVal = itemGuard.template get<value_type>();
+                assert( pVal );
+                return pVal;
+            }
+            return nullptr;
+
+        }
+
+        /// Synonym for \p enqueue(value_type&) member function
+        bool push( value_type& val )
+        {
+            return enqueue( val );
+        }
+
+        /// Synonym for \p dequeue() member function
+        value_type * pop()
+        {
+            return dequeue();
+        }
+
+        /// Checks if the queue is empty
+        /**
+            The original segmented queue algorithm does not allow to check emptiness accurately
+            because \p empty() is unlinearizable.
+            This function tests queue's emptiness checking <tt>size() == 0</tt>,
+            so, the item counting feature is an essential part of queue's algorithm.
+        */
+        bool empty() const
+        {
+            return size() == 0;
+        }
+
+        /// Clear the queue
+        /**
+            The function repeatedly calls \p dequeue() until it returns \p nullptr.
+            The disposer specified in \p Traits template argument is called for each removed item.
+        */
+        void clear()
+        {
+            clear_with( disposer());
+        }
+
+        /// Clear the queue
+        /**
+            The function repeatedly calls \p dequeue() until it returns \p nullptr.
+            \p Disposer is called for each removed item.
+        */
+        template <class Disposer>
+        void clear_with( Disposer )
+        {
+            typename gc::Guard itemGuard;
+            while ( do_dequeue( itemGuard )) {
+                assert( itemGuard.template get<value_type>());
+                gc::template retire<Disposer>( itemGuard.template get<value_type>());
+                itemGuard.clear();
+            }
+        }
+
+        /// Returns queue's item count
+        size_t size() const
+        {
+            return m_ItemCounter.value();
+        }
+
+        /// Returns reference to internal statistics
+        /**
+            The type of internal statistics is specified by \p Traits template argument.
+        */
+        const stat& statistics() const
+        {
+            return m_Stat;
+        }
+
+        /// Returns quasi factor, a power-of-two number
+        size_t quasi_factor() const
+        {
+            return m_SegmentList.quasi_factor();
+        }
+
+    protected:
+        //@cond
+        bool do_dequeue( typename gc::Guard& itemGuard )
+        {
+            typename gc::Guard segmentGuard;
+            segment * pHeadSegment = m_SegmentList.head( segmentGuard );
+
+            permutation_generator gen( quasi_factor());
+            while ( true ) {
+                if ( !pHeadSegment ) {
+                    // Queue is empty
+                    m_Stat.onPopEmpty();
+                    return false;
+                }
+
+                bool bHadNullValue = false;
+                regular_cell item;
+                CDS_DEBUG_ONLY( size_t nLoopCount = 0 );
+                do {
+                    typename permutation_generator::integer_type i = gen;
+                    CDS_DEBUG_ONLY( ++nLoopCount );
+
+                    // Guard the item
+                    // In segmented queue the cell cannot be reused
+                    // So no loop is needed here to protect the cell
+                    item = pHeadSegment->cells[i].data.load( memory_model::memory_order_relaxed );
+                    itemGuard.assign( item.ptr());
+
+                    // Check if this cell is empty, which means an element
+                    // can be enqueued to this cell in the future
+                    if ( !item.ptr())
+                        bHadNullValue = true;
+                    else {
+                        // If the item is not deleted yet
+                        if ( !item.bits()) {
+                            // Try to mark the cell as deleted
+                            if ( pHeadSegment->cells[i].data.compare_exchange_strong( item, item | 1,
+                                memory_model::memory_order_acquire, atomics::memory_order_relaxed ))
+                            {
+                                --m_ItemCounter;
+                                m_Stat.onPop();
+
+                                return true;
+                            }
+                            assert( item.bits());
+                            m_Stat.onPopContended();
+                        }
+                    }
+                } while ( gen.next());
+
+                assert( nLoopCount == quasi_factor());
+
+                // scanning the entire segment without finding a candidate to dequeue
+                // If there was an empty cell, the queue is considered empty
+                if ( bHadNullValue ) {
+                    m_Stat.onPopEmpty();
+                    return false;
+                }
+
+                // All nodes have been dequeued, we can safely remove the first segment
+                pHeadSegment = m_SegmentList.remove_head( pHeadSegment, segmentGuard );
+
+                // Get new permutation
+                gen.reset();
+            }
+        }
+        //@endcond
+    };
+}} // namespace cds::intrusive
+
+#if CDS_COMPILER == CDS_COMPILER_MSVC
+#   pragma warning( pop )
+#endif
+
+#endif // #ifndef CDSLIB_INTRUSIVE_SEGMENTED_QUEUE_H