/*
    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_MOIR_QUEUE_H
#define CDSLIB_INTRUSIVE_MOIR_QUEUE_H

#include <cds/intrusive/msqueue.h>

namespace cds { namespace intrusive {

    /// A variation of Michael & Scott's lock-free queue (intrusive variant)
    /** @ingroup cds_intrusive_queue
        This is slightly optimized Michael & Scott's queue algorithm that overloads \ref dequeue function.

        Source:
            - [2000] Simon Doherty, Lindsay Groves, Victor Luchangco, Mark Moir
                "Formal Verification of a practical lock-free queue algorithm"

        Cite from this work about difference from Michael & Scott algo:
        "Our algorithm differs from Michael and Scott's [MS98] in that we test whether \p Tail points to the header
        node only <b>after</b> \p Head has been updated, so a dequeuing process reads \p Tail only once. The dequeue in
        [MS98] performs this test before checking whether the next pointer in the dummy node is null, which
        means that it reads \p Tail every time a dequeuing process loops. Under high load, when operations retry
        frequently, our modification will reduce the number of accesses to global memory. This modification, however,
        introduces the possibility of \p Head and \p Tail 'crossing'."

        Explanation of template arguments see \p intrusive::MSQueue.

        \par Examples
        \code
        #include <cds/intrusive/moir_queue.h>
        #include <cds/gc/hp.h>

        namespace ci = cds::inrtusive;
        typedef cds::gc::HP hp_gc;

        // MoirQueue with Hazard Pointer garbage collector, base hook + item disposer:
        struct Foo: public ci::msqueue::node< hp_gc >
        {
            // Your data
            ...
        };

        // Disposer for Foo struct just deletes the object passed in
        struct fooDisposer {
            void operator()( Foo * p )
            {
                delete p;
            }
        };

        typedef ci::MoirQueue<
            hp_gc
            ,Foo
            typename ci::msqueue::make_traits<
                ,ci::opt::hook<
                    ci::msqueue::base_hook< ci::opt::gc<hp_gc> >
                >
                ,ci::opt::disposer< fooDisposer >
            >::type
        > fooQueue;

        // MoirQueue with Hazard Pointer garbage collector,
        // member hook + item disposer + item counter,
        // without padding of internal queue data:
        struct Bar
        {
            // Your data
            ...
            ci::msqueue::node< hp_gc > hMember;
        };

        struct barQueueTraits: public ci::msqueue::traits
        {
            typedef ci::msqueue::member_hook< offsetof(Bar, hMember), ,ci::opt::gc<hp_gc> > hook;
            typedef fooDisposer disposer;
            typedef cds::atomicity::item_counter item_counter;
            enum { padding = cds::opt::no_special_padding };
        };
        typedef ci::MoirQueue< hp_gc, Bar, barQueueTraits > barQueue;
        \endcode
    */
    template <typename GC, typename T, typename Traits = cds::intrusive::msqueue::traits>
    class MoirQueue: public MSQueue< GC, T, Traits >
    {
        //@cond
        typedef MSQueue< GC, T, Traits > base_class;
        typedef typename base_class::node_type node_type;
        //@endcond

    public:
        //@cond
        typedef typename base_class::value_type value_type;
        typedef typename base_class::back_off   back_off;
        typedef typename base_class::gc         gc;
        typedef typename base_class::node_traits node_traits;
        typedef typename base_class::memory_model   memory_model;
        //@endcond

        /// Rebind template arguments
        template < typename GC2, typename T2, typename Traits2 >
        struct rebind {
            typedef MoirQueue< GC2, T2, Traits2> other   ;   ///< Rebinding result
        };

    protected:
        //@cond
        typedef typename base_class::dequeue_result dequeue_result;

        bool do_dequeue( dequeue_result& res )
        {
            back_off bkoff;

            node_type * pNext;
            node_type * h;
            while ( true ) {
                h = res.guards.protect( 0, base_class::m_pHead, []( node_type * p ) -> value_type * { return node_traits::to_value_ptr( p );});
                pNext = res.guards.protect( 1, h->m_pNext, []( node_type * p ) -> value_type * { return node_traits::to_value_ptr( p );});

                if ( pNext == nullptr ) {
                    base_class::m_Stat.onEmptyDequeue();
                    return false;    // queue is empty
                }

                if ( base_class::m_pHead.compare_exchange_strong( h, pNext, memory_model::memory_order_release, atomics::memory_order_relaxed )) {
                    node_type * t = base_class::m_pTail.load(memory_model::memory_order_acquire);
                    if ( h == t )
                        base_class::m_pTail.compare_exchange_strong( t, pNext, memory_model::memory_order_release, atomics::memory_order_relaxed );
                    break;
                }

                base_class::m_Stat.onDequeueRace();
                bkoff();
            }

            --base_class::m_ItemCounter;
            base_class::m_Stat.onDequeue();

            res.pHead = h;
            res.pNext = pNext;
            return true;
        }
        //@endcond

    public:
        /// Dequeues a value from the queue
        /** @anchor cds_intrusive_MoirQueue_dequeue
            See warning about item disposing in \p MSQueue::dequeue.
        */
        value_type * dequeue()
        {
            dequeue_result res;
            if ( do_dequeue( res )) {
                base_class::dispose_result( res );
                return node_traits::to_value_ptr( *res.pNext );
            }
            return nullptr;
        }

        /// Synonym for \ref cds_intrusive_MoirQueue_dequeue "dequeue" function
        value_type * pop()
        {
            return dequeue();
        }
    };

}} // namespace cds::intrusive

#endif // #ifndef CDSLIB_INTRUSIVE_MOIR_QUEUE_H
