
co.paralleluniverse.strands.queues.ArrayQueue Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of quasar-core Show documentation
Show all versions of quasar-core Show documentation
Fibers, Channels and Actors for the JVM
/*
* 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