package org.infinispan.persistence.sifs;
import java.util.ArrayDeque;
/**
* Multiple producer-single consumer queue. The producers are expected to call pushAndWait(),
* the consumer should call pop() in a loop and if a null is returned (meaning that the queue is either empty
* or the limit of elements processed in a loop has been reached, to call notifyAndWait().
*
* Example:
* while (running) {
* T element = queue.pop();
* if (element != null) {
* process(element);
* } else {
* flush();
* queue.notifyAndWait();
* }
* }
* // terminate producers and process the rest of the queue
* queue.notifyNoWait();
*
* @author Radim Vansa <rvansa@redhat.com>
*/
public class SyncProcessingQueue<T> {
private final ArrayDeque<T> queue = new ArrayDeque<T>();
private final int maxPoppedInRow;
private final Object sync = new Object();
private volatile long popIndex = 0;
private long pushIndex = 0;
private long processorPopIndex = 0;
private int poppedInRow = 0;
private volatile boolean error;
public SyncProcessingQueue() {
this(Integer.MAX_VALUE);
}
public SyncProcessingQueue(int maxPoppedInRow) {
this.maxPoppedInRow = maxPoppedInRow;
}
public void pushAndWait(T element) throws InterruptedException {
waitFor(push(element));
}
public long push(T element) {
synchronized (queue) {
queue.push(element);
queue.notify();
pushIndex++;
return pushIndex;
}
}
protected void waitFor(long myIndex) throws InterruptedException {
synchronized (sync) {
while (myIndex > popIndex) {
sync.wait();
//Thread.yield();
}
}
if (error) {
throw new IllegalStateException("Exception in consumer");
}
}
public T pop() {
if (poppedInRow >= maxPoppedInRow) {
return null;
}
T element;
synchronized (queue) {
element = queue.poll();
}
if (element == null) {
return null;
} else {
processorPopIndex++;
poppedInRow++;
return element;
}
}
public void notifyAndWait() {
poppedInRow = 0;
popIndex = processorPopIndex;
synchronized (sync) {
sync.notifyAll();
}
synchronized (queue) {
if (queue.isEmpty()) {
try {
queue.wait();
} catch (InterruptedException e) {
return;
}
}
}
}
public void notifyNoWait() {
poppedInRow = 0;
popIndex = processorPopIndex;
synchronized (sync) {
sync.notifyAll();
}
}
public void notifyError() {
error = true;
popIndex = Long.MAX_VALUE;
synchronized (sync) {
sync.notifyAll();
}
}
}