/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* (C) Copyright IBM Corporation 2006-2010.
*/
package x10.core;
import java.lang.reflect.Field;
import sun.misc.Unsafe;
import x10.core.Ref;
import x10.rtt.NamedType;
import x10.rtt.RuntimeType;
import x10.rtt.Type;
public class Deque extends Ref {
private static final long serialVersionUID = 1L;
public void $_serialize(x10.serialization.X10JavaSerializer $serializer) throws java.io.IOException {
// TODO need check
$serializer.write(base);
$serializer.write(sp);
$serializer.write(queue);
}
public static x10.serialization.X10JavaSerializable $_deserialize_body(Deque $_obj, x10.serialization.X10JavaDeserializer $deserializer) throws java.io.IOException {
// TODO need check
$_obj.base = $deserializer.readInt();
$_obj.sp = $deserializer.readInt();
$_obj.queue = (java.lang.Object[]) $deserializer.readArrayUsingReflection(java.lang.Object.class);
return $_obj;
}
public static x10.serialization.X10JavaSerializable $_deserializer(x10.serialization.X10JavaDeserializer $deserializer) throws java.io.IOException {
Deque $_obj = new Deque((java.lang.System[]) null);
$deserializer.record_reference($_obj);
return $_deserialize_body($_obj, $deserializer);
}
/**
* Capacity of work-stealing queue array upon initialization.
* Must be a power of two. Initial size must be at least 2, but is
* padded to minimize cache effects.
*/
private static final int INITIAL_QUEUE_CAPACITY = 1 << 13;
/**
* Maximum work-stealing queue array size. Must be less than or
* equal to 1 << 28 to ensure lack of index wraparound. (This
* is less than usual bounds, because we need leftshift by 3
* to be in int range).
*/
private static final int MAXIMUM_QUEUE_CAPACITY = 1 << 28;
/**
* The work-stealing queue array. Size must be a power of two.
* Initialized when thread starts, to improve memory locality.
*/
private Object[] queue;
/**
* Index (mod queue.length) of next queue slot to push to or pop
* from. It is written only by owner thread, via ordered store.
* Both sp and base are allowed to wrap around on overflow, but
* (sp - base) still estimates size.
*/
private volatile int sp;
/**
* Index (mod queue.length) of least valid queue slot, which is
* always the next position to steal from if nonempty.
*/
private volatile int base;
// constructor just for allocation
public Deque(java.lang.System[] $dummy) {super($dummy);}
public final Deque x10$lang$Deque$$init$S() {
queue = new Object[INITIAL_QUEUE_CAPACITY];
return this;
}
/**
* Creates a Deque.
*/
public Deque() {
// Allocate while starting to improve chances of thread-local
// isolation
queue = new Object[INITIAL_QUEUE_CAPACITY];
}
// Intrinsics-based support for queue operations.
/**
* Add in store-order the given task at given slot of q to
* null. Caller must ensure q is nonnull and index is in range.
*/
private void setSlot(Object[] q, int i, Object t){
// DAVE G: Egregious hack to get code running on IBM SDKs that don't have putOrderedObject.
// _unsafe.putOrderedObject(q, (i << qShift) + qBase, t);
_unsafe.putObject(q, (i << qShift) + qBase, t);
}
/**
* CAS given slot of q to null. Caller must ensure q is nonnull
* and index is in range.
*/
private boolean casSlotNull(Object[] q, int i, Object t) {
return _unsafe.compareAndSwapObject(q, (i << qShift) + qBase, t, null);
}
/**
* Sets sp in store-order.
*/
private void storeSp(int s) {
// DAVE G: Egregious hack to get code running on IBM SDKs that don't have putOrderedInt.
// _unsafe.putOrderedInt(this, spOffset, s);
_unsafe.putInt(this, spOffset, s);
}
// Main queue methods
/**
* Pushes a task. Called only by current thread.
* @param t the task. Caller must ensure nonnull
*/
public final void push(Object t) {
Object[] q = queue;
int mask = q.length - 1;
int s = sp;
setSlot(q, s & mask, t);
storeSp(++s);
if ((s -= base) == 1)
;
else if (s >= mask)
growQueue();
}
/**
* Tries to take a task from the base of the queue, failing if
* either empty or contended.
* @return a task, or null if none or contended.
*/
public final Object steal() {
Object t;
Object[] q;
int i;
int b;
if (sp != (b = base) &&
(q = queue) != null && // must read q after b
(t = q[i = (q.length - 1) & b]) != null &&
casSlotNull(q, i, t)) {
base = b + 1;
return t;
}
return null;
}
/**
* Returns a popped task, or null if empty. Ensures active status
* if nonnull. Called only by current thread.
*/
public final Object poll() {
int s = sp;
while (s != base) {
Object[] q = queue;
int mask = q.length - 1;
int i = (s - 1) & mask;
Object t = q[i];
if (t == null || !casSlotNull(q, i, t))
break;
storeSp(s - 1);
return t;
}
return null;
}
/**
* Returns next task to pop.
*/
public final Object peekTask() {
Object[] q = queue;
return q == null? null : q[(sp - 1) & (q.length - 1)];
}
/**
* Doubles queue array size. Transfers elements by emulating
* steals (deqs) from old array and placing, oldest first, into
* new array.
*/
private void growQueue() {
Object[] oldQ = queue;
int oldSize = oldQ.length;
int newSize = oldSize << 1;
if (newSize > MAXIMUM_QUEUE_CAPACITY)
throw new RuntimeException("Queue capacity exceeded");
Object[] newQ = queue = new Object[newSize];
int b = base;
int bf = b + oldSize;
int oldMask = oldSize - 1;
int newMask = newSize - 1;
do {
int oldIndex = b & oldMask;
Object t = oldQ[oldIndex];
if (t != null && !casSlotNull(oldQ, oldIndex, t))
t = null;
setSlot(newQ, b & newMask, t);
} while (++b != bf);
}
/**
* Returns an estimate of the number of tasks in the queue.
*/
public final int size$O() {
int n = sp - base;
return n < 0? 0 : n; // suppress momentarily negative values
}
//
// Runtime type information
//
public static final RuntimeType<Deque> $RTT = NamedType.<Deque> make(
"x10.lang.Deque",
Deque.class
);
public RuntimeType<Deque> $getRTT() {return $RTT;}
public Type<?> $getParam(int i) { return null; }
// Temporary Unsafe mechanics for preliminary release
static final Unsafe _unsafe;
static final long baseOffset;
static final long spOffset;
static final long qBase;
static final int qShift;
static {
try {
if (Deque.class.getClassLoader() != null) {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
_unsafe = (Unsafe)f.get(null);
}
else
_unsafe = Unsafe.getUnsafe();
baseOffset = _unsafe.objectFieldOffset
(Deque.class.getDeclaredField("base"));
spOffset = _unsafe.objectFieldOffset
(Deque.class.getDeclaredField("sp"));
qBase = _unsafe.arrayBaseOffset(Object[].class);
int s = _unsafe.arrayIndexScale(Object[].class);
if ((s & (s-1)) != 0)
throw new Error("data type scale not a power of two");
qShift = 31 - Integer.numberOfLeadingZeros(s);
} catch (Exception e) {
throw new RuntimeException("Could not initialize intrinsics", e);
}
}
}