package org.jctools.queues;

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * This is a direct Java port of the MPSC algorithm as presented <a
 * href="http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue"> on 1024
 * Cores</a> by D. Vyukov. The original has been adapted to Java and it's quirks with regards to memory model and
 * layout:
 * <ol>
 * <li>Use inheritance to ensure no false sharing occurs between producer/consumer node reference fields.
 * <li>Use {@link AtomicReferenceFieldUpdater} to provide XCHG functionality to the best of the JDK ability.
 * </ol>
 * The queue is initialized with a stub node which is set to both the producer and consumer node references. From this
 * point follow the notes on offer/poll.
 * 
 * @author nitsanw
 * 
 * @param <E>
 */
abstract class MpscLinkedQueue<E> extends BaseLinkedQueue<E> {
    protected MpscLinkedQueue() {
        consumerNode = new LinkedQueueNode<E>();
        xchgProducerNode(consumerNode);// this ensures correct construction: StoreLoad
    }
    protected abstract LinkedQueueNode<E> xchgProducerNode(LinkedQueueNode<E> nextNode);

    /**
     * {@inheritDoc} <br>
     * <p>
     * IMPLEMENTATION NOTES:<br>
     * Offer is allowed from multiple threads.<br>
     * Offer allocates a new node and:
     * <ol>
     * <li>Swaps it atomically with current producer node (only one producer 'wins')
     * <li>Sets the new node as the node following from the swapped producer node
     * </ol>
     * This works because each producer is guaranteed to 'plant' a new node and link the old node. No 2 producers can
     * get the same producer node as part of XCHG guarantee.
     * 
     * @see MessagePassingQueue#offer(Object)
     * @see java.util.Queue#offer(java.lang.Object)
     */
    @Override
    public final boolean offer(final E nextValue) {
        if (nextValue == null) {
            throw new IllegalArgumentException("null elements not allowed");
        }
        final LinkedQueueNode<E> nextNode = new LinkedQueueNode<E>(nextValue);
        final LinkedQueueNode<E> prevProducerNode = xchgProducerNode(nextNode);
        // Should a producer thread get interrupted here the chain WILL be broken until that thread is resumed
        // and completes the store in prev.next.
        prevProducerNode.soNext(nextNode); // StoreStore
        return true;
    }

    /**
     * {@inheritDoc} <br>
     * <p>
     * IMPLEMENTATION NOTES:<br>
     * Poll is allowed from a SINGLE thread.<br>
     * Poll reads the next node from the consumerNode and:
     * <ol>
     * <li>If it is null, the queue is assumed empty (though it might not be).
     * <li>If it is not null set it as the consumer node and return it's now evacuated value.
     * </ol>
     * This means the consumerNode.value is always null, which is also the starting point for the queue. Because null
     * values are not allowed to be offered this is the only node with it's value set to null at any one time.
     * 
     * @see MessagePassingQueue#poll()
     * @see java.util.Queue#poll()
     */
    @Override
    public final E poll() {
        LinkedQueueNode<E> nextNode = consumerNode.lvNext();
        if (nextNode != null) {
            // we have to null out the value because we are going to hang on to the node
            final E nextValue = nextNode.getAndNullValue();
            consumerNode = nextNode;
            return nextValue;
        }
        else if (!isEmpty()) {
            // spin, we are no longer wait free
            while((nextNode = consumerNode.lvNext()) == null);
            // got the next node...
            
            // we have to null out the value because we are going to hang on to the node
            final E nextValue = nextNode.getAndNullValue();
            consumerNode = nextNode;
            return nextValue;
        }
        return null;
    }

    @Override
    public final E peek() {
        LinkedQueueNode<E> nextNode = consumerNode.lvNext();
        if (nextNode != null) {
            return nextNode.lpValue();
        }
        else if (!isEmpty()) {
            // spin, we are no longer wait free
            while((nextNode = consumerNode.lvNext()) == null);
            // got the next node...
            return nextNode.lpValue();
        }
        return null;
    }
}

@@ -3147,24 +3147,122 @@
  E poll() %7B%0A
+        LinkedQueueNode%3CE%3E currConsumerNode = lpConsumerNode(); // don't load twice, it's alright%0A
         Link
@@ -3280,32 +3280,36 @@
 %3CE%3E nextNode = c
+urrC
 onsumerNode.lvNe
@@ -3499,33 +3499,35 @@
 ();%0A            
-c
+spC
 onsumerNode = ne
@@ -3517,35 +3517,33 @@
 ConsumerNode
- = 
+(
 nextNode
 ;%0A          
@@ -3522,32 +3522,33 @@
 merNode(nextNode
+)
 ;%0A            re
@@ -3586,32 +3586,58 @@
    else if (
-!isEmpty
+currConsumerNode != lvProducerNode
 ()) %7B%0A      
@@ -3701,32 +3701,36 @@
 le((nextNode = c
+urrC
 onsumerNode.lvNe
@@ -4086,24 +4086,118 @@
  E peek() %7B%0A
+        LinkedQueueNode%3CE%3E currConsumerNode = consumerNode; // don't load twice, it's alright%0A
         Link
@@ -4215,32 +4215,36 @@
 %3CE%3E nextNode = c
+urrC
 onsumerNode.lvNe
@@ -4351,16 +4351,42 @@
 if (
-!isEmpty
+currConsumerNode == lvProducerNode
 ()) 
@@ -4458,32 +4458,36 @@
 le((nextNode = c
+urrC
 onsumerNode.lvNe

remove redundant field loads from poll/peek