2013-03-07 15:58:26 +01:00
|
|
|
/**
|
2014-02-02 19:05:45 -06:00
|
|
|
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
2013-03-07 15:58:26 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package akka.dispatch;
|
|
|
|
|
|
|
|
|
|
import akka.util.Unsafe;
|
2014-01-23 20:02:18 +01:00
|
|
|
|
2013-03-07 15:58:26 +01:00
|
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Lock-free MPSC linked queue implementation based on Dmitriy Vyukov's non-intrusive MPSC queue:
|
|
|
|
|
* http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue
|
|
|
|
|
*/
|
2014-01-23 20:02:18 +01:00
|
|
|
@SuppressWarnings("serial")
|
2013-03-07 15:58:26 +01:00
|
|
|
public abstract class AbstractNodeQueue<T> extends AtomicReference<AbstractNodeQueue.Node<T>> {
|
|
|
|
|
// Extends AtomicReference for the "head" slot (which is the one that is appended to) since Unsafe does not expose XCHG operation intrinsically
|
2014-01-23 20:02:18 +01:00
|
|
|
@SuppressWarnings("unused")
|
2013-03-07 15:58:26 +01:00
|
|
|
private volatile Node<T> _tailDoNotCallMeDirectly;
|
|
|
|
|
|
|
|
|
|
protected AbstractNodeQueue() {
|
|
|
|
|
final Node<T> n = new Node<T>();
|
|
|
|
|
_tailDoNotCallMeDirectly = n;
|
|
|
|
|
set(n);
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-18 13:10:34 +02:00
|
|
|
/*
|
|
|
|
|
* !!! There is a copy of this code in pollNode() !!!
|
|
|
|
|
*/
|
2013-03-07 15:58:26 +01:00
|
|
|
@SuppressWarnings("unchecked")
|
2013-04-03 20:55:41 +02:00
|
|
|
protected final Node<T> peekNode() {
|
2013-05-28 15:16:15 +02:00
|
|
|
for(;;) {
|
|
|
|
|
final Node<T> tail = ((Node<T>)Unsafe.instance.getObjectVolatile(this, tailOffset));
|
|
|
|
|
final Node<T> next = tail.next();
|
|
|
|
|
if (next != null || get() == tail)
|
|
|
|
|
return next;
|
|
|
|
|
}
|
2013-03-07 15:58:26 +01:00
|
|
|
}
|
|
|
|
|
|
2013-04-03 20:55:41 +02:00
|
|
|
public final T peek() {
|
|
|
|
|
final Node<T> n = peekNode();
|
|
|
|
|
return (n != null) ? n.value : null;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-07 15:58:26 +01:00
|
|
|
public final void add(final T value) {
|
|
|
|
|
final Node<T> n = new Node<T>(value);
|
|
|
|
|
getAndSet(n).setNext(n);
|
|
|
|
|
}
|
2013-06-18 13:10:34 +02:00
|
|
|
|
|
|
|
|
public final void addNode(final Node<T> n) {
|
|
|
|
|
n.setNext(null);
|
|
|
|
|
getAndSet(n).setNext(n);
|
|
|
|
|
}
|
2013-03-07 15:58:26 +01:00
|
|
|
|
2013-04-03 20:05:20 +02:00
|
|
|
public final boolean isEmpty() {
|
|
|
|
|
return peek() == null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public final int count() {
|
|
|
|
|
int count = 0;
|
2013-04-03 20:55:41 +02:00
|
|
|
for(Node<T> n = peekNode();n != null; n = n.next())
|
2013-04-03 20:05:20 +02:00
|
|
|
++count;
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-18 13:10:34 +02:00
|
|
|
/*
|
|
|
|
|
* !!! There is a copy of this code in pollNode() !!!
|
|
|
|
|
*/
|
2013-03-07 15:58:26 +01:00
|
|
|
public final T poll() {
|
2013-04-03 20:55:41 +02:00
|
|
|
final Node<T> next = peekNode();
|
2013-03-07 15:58:26 +01:00
|
|
|
if (next == null) return null;
|
|
|
|
|
else {
|
|
|
|
|
final T ret = next.value;
|
2013-05-28 15:16:15 +02:00
|
|
|
next.value = null;
|
2013-03-07 15:58:26 +01:00
|
|
|
Unsafe.instance.putOrderedObject(this, tailOffset, next);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-06-18 13:10:34 +02:00
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
public final Node<T> pollNode() {
|
|
|
|
|
Node<T> tail;
|
|
|
|
|
Node<T> next;
|
|
|
|
|
for(;;) {
|
|
|
|
|
tail = ((Node<T>)Unsafe.instance.getObjectVolatile(this, tailOffset));
|
|
|
|
|
next = tail.next();
|
|
|
|
|
if (next != null || get() == tail)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (next == null) return null;
|
|
|
|
|
else {
|
|
|
|
|
tail.value = next.value;
|
|
|
|
|
next.value = null;
|
|
|
|
|
Unsafe.instance.putOrderedObject(this, tailOffset, next);
|
|
|
|
|
return tail;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-03-07 15:58:26 +01:00
|
|
|
|
|
|
|
|
private final static long tailOffset;
|
|
|
|
|
|
|
|
|
|
static {
|
|
|
|
|
try {
|
|
|
|
|
tailOffset = Unsafe.instance.objectFieldOffset(AbstractNodeQueue.class.getDeclaredField("_tailDoNotCallMeDirectly"));
|
|
|
|
|
} catch(Throwable t){
|
|
|
|
|
throw new ExceptionInInitializerError(t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static class Node<T> {
|
2013-06-18 13:10:34 +02:00
|
|
|
public T value;
|
2014-01-23 20:02:18 +01:00
|
|
|
@SuppressWarnings("unused")
|
2013-03-07 15:58:26 +01:00
|
|
|
private volatile Node<T> _nextDoNotCallMeDirectly;
|
|
|
|
|
|
2013-06-18 13:10:34 +02:00
|
|
|
public Node() {
|
2013-03-07 15:58:26 +01:00
|
|
|
this(null);
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-18 13:10:34 +02:00
|
|
|
public Node(final T value) {
|
2013-03-07 15:58:26 +01:00
|
|
|
this.value = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
public final Node<T> next() {
|
|
|
|
|
return (Node<T>)Unsafe.instance.getObjectVolatile(this, nextOffset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected final void setNext(final Node<T> newNext) {
|
2013-05-28 15:16:15 +02:00
|
|
|
Unsafe.instance.putOrderedObject(this, nextOffset, newNext);
|
2013-03-07 15:58:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private final static long nextOffset;
|
|
|
|
|
|
|
|
|
|
static {
|
|
|
|
|
try {
|
|
|
|
|
nextOffset = Unsafe.instance.objectFieldOffset(Node.class.getDeclaredField("_nextDoNotCallMeDirectly"));
|
|
|
|
|
} catch(Throwable t){
|
|
|
|
|
throw new ExceptionInInitializerError(t);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|