All Downloads are FREE. Search and download functionalities are using the official Maven repository.

co.paralleluniverse.strands.queues.ArrayQueue Maven / Gradle / Ivy

There is a newer version: 0.8.0
Show newest version
/*
 * 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 sun.misc.Unsafe;

/**
 *
 * @author pron
 */
public class ArrayQueue implements BasicQueue {
    final int capacity;
    final int mask;
    volatile int p001, p002, p003, p004, p005, p006, p007;
    volatile long head; // next element to be read
    volatile long p101, p102, p103, p104, p105, p106, p107;
    volatile long tail; // next element to be written
    volatile long p201, p202, p203, p204, p205, p206, p207;
    private long cachedHead;
    volatile long p301, p302, p303, p304, p305, p306, p307;
    private long cachedTail;
    volatile Object p401, p402, p403, p404, p405, p406, p407;
    private final Object[] array;

    public ArrayQueue(int capacity) {
        // size is a power of 2
        this.capacity = nextPowerOfTwo(capacity);
        this.mask = this.capacity - 1;
        this.array = new Object[this.capacity];
    }

    private static int nextPowerOfTwo(int v) {
        assert v >= 0;
        return 1 << (32 - Integer.numberOfLeadingZeros(v - 1));
    }

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

    @Override
    public boolean enq(E item) {
        if (item == null)
            throw new IllegalArgumentException("null values not allowed");
        final long i = preEnq();
        if (i < 0)
            return false;
        set((int) i & mask, item);
        return true;
    }

    private long preEnq() {
        long t, w;
        do {
            t = tail;
            w = t - capacity; // "wrap point"

            if (cachedHead <= w) {
                cachedHead = head; // only time a producer reads head. for this, head needs to be volatile. can we do better?
                if (cachedHead <= w)
                    return -1;
            }
        } while (!compareAndSetTail(t, t + 1));
        return t;
    }

    @Override
    public E poll() {
        long h;
        E v;
        do {
            h = head;
            if (h >= cachedTail) {
                cachedTail = tail;
                if (h >= cachedTail)
                    return null;
            }

            v = get((int) h & mask); // volatile read
        } while (v == null || !compareAndSetHead(h, h + 1));
        cas((int) h & mask, v, null);
        return v;
    }

    @Override
    public int size() {
        return (int) (tail - head);
    }

    @Override
    public boolean isEmpty() {
        return tail == head;
    }

    int next(int i) {
        return (i + 1) & mask;
    }

    int prev(int i) {
        return --i & mask;
    }
    ////////////////////////////////////////////////////////////////////////
    static final Unsafe UNSAFE = UtilUnsafe.getUnsafe();
    private static final int base;
    private static final int shift;
    private static final long headOffset;
    private static final long tailOffset;

    static {
        try {
            headOffset = UNSAFE.objectFieldOffset(ArrayQueue.class.getDeclaredField("head"));
            tailOffset = UNSAFE.objectFieldOffset(ArrayQueue.class.getDeclaredField("tail"));

            base = UNSAFE.arrayBaseOffset(Object[].class);
            int scale = UNSAFE.arrayIndexScale(Object[].class);
            if ((scale & (scale - 1)) != 0)
                throw new Error("data type scale not a power of two");
            shift = 31 - Integer.numberOfLeadingZeros(scale);
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }

    /**
     * CAS tail field. Used only by preEnq.
     */
    private boolean compareAndSetTail(long expect, long update) {
        return UNSAFE.compareAndSwapLong(this, tailOffset, expect, update);
    }

    private boolean compareAndSetHead(long expect, long update) {
        return UNSAFE.compareAndSwapLong(this, headOffset, expect, update);
    }

    private void orderedSetHead(long value) {
        UNSAFE.putOrderedLong(this, headOffset, value);
    }

    private static long byteOffset(int i) {
        return ((long) i << shift) + base;
    }

    private void set(int i, E value) {
        UNSAFE.putObjectVolatile(array, byteOffset(i), value);
    }

    private void orderedSet(int i, E value) {
        UNSAFE.putOrderedObject(array, byteOffset(i), value);
    }

    private E get(int i) {
        return (E) UNSAFE.getObjectVolatile(array, byteOffset(i));
    }

    private boolean cas(int i, E expected, E update) {
        return UNSAFE.compareAndSwapObject(array, byteOffset(i), expected, update);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy