co.paralleluniverse.strands.queues.SingleConsumerLinkedArrayQueue.java Source code

Java tutorial

Introduction

Here is the source code for co.paralleluniverse.strands.queues.SingleConsumerLinkedArrayQueue.java

Source

/*
 * Quasar: lightweight threads and actors for the JVM.
 * Copyright (c) 2013-2015, Parallel Universe Software Co. All rights reserved.
 * 
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation
 *  
 *   or (per the licensee's choosing)
 *  
 * under the terms of the GNU Lesser General Public License version 3.0
 * as published by the Free Software Foundation.
 */
package co.paralleluniverse.strands.queues;

import co.paralleluniverse.common.util.UtilUnsafe;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import sun.misc.Unsafe;

/**
 *
 * @author pron
 */
abstract class SingleConsumerLinkedArrayQueue<E> extends SingleConsumerQueue<E> {
    volatile Node head;
    int headIndex;
    volatile Object p001, p002, p003, p004, p005, p006, p007, p008, p009, p010, p011, p012, p013, p014, p015;
    volatile Node tail;
    volatile int p016, p017, p018, p019, p020, p021, p022, p023, p024, p025, p026, p027, p028, p029, p030;
    int seed = (int) System.nanoTime();

    @SuppressWarnings("OverridableMethodCallInConstructor")
    public SingleConsumerLinkedArrayQueue() {
        tail = head = newNode();
    }

    @Override
    public int capacity() {
        return -1;
    }

    abstract Node newNode();

    abstract boolean hasValue(Node n, int index);

    abstract boolean isDeleted(Node n, int index);

    abstract void markDeleted(Node n, int index);

    abstract int blockSize();

    abstract E value(Node n, int i);

    @SuppressWarnings("empty-statement")
    boolean prePeek() {
        // postconditions: head,headIndex point to the first element (i.e. empty/deleted slots are discarded)
        // returns whether an element exists
        final int blockSize = blockSize();
        Node n = head;
        int i = headIndex;
        boolean found = false;
        for (;;) {
            if (i >= blockSize) {
                if (tail == n)
                    return false;

                while (n.next == null)
                    ; // wait for next
                Node next = n.next; // can't be null because we're called by the consumer
                clearNext(n);
                clearPrev(next);

                n = next;
                i = 0;
            } else if (hasValue(n, i)) {
                if (isDeleted(n, i))
                    i++;
                else {
                    found = true;
                    break;
                }
            } else {
                // assert n == tail; - tail could have changed by now
                break;
            }
        }
        orderedSetHead(n); // 
        headIndex = i;
        return found;
    }

    @Override
    @SuppressWarnings("empty-statement")
    public E peek() {
        return prePeek() ? value(head, headIndex) : null;
    }

    @Override
    @SuppressWarnings("empty-statement")
    public E poll() {
        final E val = peek();
        if (val != null)
            deqHead();
        return val;
    }

    @Override
    public boolean isEmpty() {
        return peek() == null;
    }

    @SuppressWarnings("empty-statement")
    void deq(Node node, int index) {
        final int blockSize = blockSize();
        Node n = head;
        int i = headIndex;
        for (;;) {
            int maxI = (n != node ? blockSize - 1 : index);
            for (; i <= maxI; i++)
                markDeleted(n, i);

            if (n != node) {
                Node next = n.next; // can't be null because we're called by the consumer
                clearNext(n);
                clearPrev(next);

                n = next;
                i = 0;
            } else
                break;
        }
        ;

        // if (head != n) head = n; // save the volatile write
        orderedSetHead(n); // 
        headIndex = index + 1;
    }

    void deqHead() {
        markDeleted(head, headIndex);
        headIndex++;
    }

    boolean isHead(Node n, int i) {
        return n == head & i == headIndex;
    }

    boolean del(Node n, int i) {
        if (isHead(n, i)) {
            deq(n, i);
            return false;
        }

        markDeleted(n, i);
        return true;
    }

    @Override
    public int size() {
        final int blockSize = blockSize();
        int count = 0;
        for (Node p = tail; p != null; p = p.prev) {
            for (int i = (p == head ? headIndex : 0); i < blockSize; i++) {
                if (p == tail && !hasValue(p, i))
                    break;
                if (!isDeleted(p, i))
                    count++;
            }
        }
        return count;
    }

    @Override
    public List<E> snapshot() {
        final int blockSize = blockSize();
        ArrayList<E> list = new ArrayList<E>();
        for (Node p = tail; p != null; p = p.prev) {
            for (int i = (p == head ? headIndex : 0); i < blockSize; i++) {
                if (p == tail && !hasValue(p, i))
                    break;
                if (hasValue(p, i) && !isDeleted(p, i))
                    list.add(value(p, i));
            }
        }
        return Lists.reverse(list);
    }

    public int nodeCount() {
        int count = 0;
        for (Node p = tail; p != null; p = p.prev)
            count++;
        return count;
    }

    void backoff() {
        int spins = 1 << 8;
        int r = seed;
        while (spins >= 0) {
            r ^= r << 1;
            r ^= r >>> 3;
            r ^= r << 10; // xorshift
            if (r >= 0)
                --spins;
        }
        seed = r;
    }

    static abstract class Node {
        volatile Node next;
        volatile Node prev;
    }

    @Override
    public QueueIterator<E> iterator() {
        return new LinkedArrayQueueIterator();
    }

    class LinkedArrayQueueIterator implements QueueIterator<E> {
        Node n;
        int i;
        private boolean hasNextCalled;

        LinkedArrayQueueIterator(Node n, int i) {
            this.n = n;
            this.i = i;
        }

        LinkedArrayQueueIterator() {
            this(null, -1);
        }

        @Override
        public boolean hasNext() {
            if (succ()) {
                hasNextCalled = true;
                return true;
            }
            return false;
        }

        @Override
        public E next() {
            preNext();
            return value();
        }

        final void preNext() {
            if (!hasNextCalled)
                if (!succ())
                    throw new NoSuchElementException();
            hasNextCalled = false;
        }

        @Override
        public void remove() {
            del(n, i);
        }

        @Override
        public E value() {
            return SingleConsumerLinkedArrayQueue.this.value(n, i);
        }

        @Override
        public void deq() {
            SingleConsumerLinkedArrayQueue.this.deq(n, i);
        }

        @Override
        public void reset() {
            n = null;
            i = -1;
            hasNextCalled = false;
        }

        @SuppressWarnings("empty-statement")
        boolean succ() {
            final int blockSize = blockSize();
            Node n = this.n != null ? this.n : head;
            int i = this.i + 1;
            for (;;) {
                if (i >= blockSize) {
                    if (tail == n)
                        return false;

                    while (n.next == null)
                        ; // wait for next
                    n = n.next;
                    i = 0;
                } else if (hasValue(n, i)) {
                    if (isDeleted(n, i))
                        i++;
                    else {
                        this.i = i;
                        this.n = n;
                        return true;
                    }
                } else {
                    // assert n == tail; - tail could have changed by now
                    return false;
                }
            }
        }
    }

    ////////////////////////////////////////////////////////////////////////
    static final Unsafe UNSAFE = UtilUnsafe.getUnsafe();
    private static final long headOffset;
    private static final long tailOffset;
    private static final long nextOffset;
    private static final long prevOffset;

    static {
        try {
            headOffset = UNSAFE.objectFieldOffset(SingleConsumerLinkedArrayQueue.class.getDeclaredField("head"));
            tailOffset = UNSAFE.objectFieldOffset(SingleConsumerLinkedArrayQueue.class.getDeclaredField("tail"));
            nextOffset = UNSAFE.objectFieldOffset(Node.class.getDeclaredField("next"));
            prevOffset = UNSAFE.objectFieldOffset(Node.class.getDeclaredField("prev"));
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }

    boolean compareAndSetHead(Node update) {
        return UNSAFE.compareAndSwapObject(this, headOffset, null, update);
    }

    void orderedSetHead(Node value) {
        UNSAFE.putOrderedObject(this, headOffset, value);
    }

    boolean compareAndSetTail(Node expect, Node update) {
        return UNSAFE.compareAndSwapObject(this, tailOffset, expect, update);
    }

    static boolean compareAndSetNext(Node node, Node expect, Node update) {
        return UNSAFE.compareAndSwapObject(node, nextOffset, expect, update);
    }

    private static void clearNext(Node node) {
        UNSAFE.putOrderedObject(node, nextOffset, null);
    }

    private static void clearPrev(Node node) {
        UNSAFE.putOrderedObject(node, prevOffset, null);
    }
}