import java.util.NoSuchElementException;
import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;
public class LockFreeStack<T> implements MyStack<T> {
AtomicReference<Node> top = new AtomicReference<Node>(null);
// Use backoff, tweak these numbers to improve perf
static final int MIN_DELAY = 1;
static final int MAX_DELAY = 100;
Backoff backoff = new Backoff(MIN_DELAY, MAX_DELAY);
// Push logic
protected boolean tryPush(Node node) {
Node oldTop = top.get();
node.next = oldTop;
return(top.compareAndSet(oldTop, node));
}
public void push(T value) {
Node node = new Node(value);
while(true) {
if(tryPush(node)) {
return;
} else {
try {
backoff.backoff();
} catch (InterruptedException e) {
// do nothing
}
}
}
}
// Pop logic
protected Node tryPop() throws NoSuchElementException {
Node oldTop = top.get();
if(oldTop == null) {
throw new NoSuchElementException();
}
Node newTop = oldTop.next;
if(top.compareAndSet(oldTop, newTop)) {
return oldTop;
} else {
return null;
}
}
public T pop() throws NoSuchElementException {
while(true) {
Node returnNode = tryPop();
if(returnNode != null) {
return returnNode.value;
} else {
try {
backoff.backoff();
} catch (InterruptedException e) {
// do nothing
}
}
}
}
// Node class
public class Node {
public T value;
//public AtomicStampedReference<Node> next;
public Node next;
public Node(T value) {
this.value = value;
next = null;
}
}
// Backoff class
public class Backoff {
final int minDelay, maxDelay;
int limit;
final Random random;
public Backoff(int min, int max) {
minDelay = min;
maxDelay = max;
limit = minDelay;
random = new Random();
}
public void backoff() throws InterruptedException {
int delay = random.nextInt(limit);
limit = Math.min(maxDelay, 2*limit);
Thread.sleep(delay);
}
}
public static void main(String [] args) {
int runs = 10;
int numThreads = 6;
int numIterations = 120000;
double runtimes[] = new double[runs];
//If command line argument present, gives patience in seconds.
if (args.length > 0) {
try {
runs = Integer.parseInt(args[0].trim());
numThreads = Integer.parseInt(args[1].trim());
numIterations = Integer.parseInt(args[2].trim());
System.out.format("Runs = %d, NumThreads = %d, NumIterations = %d\n", runs, numThreads, numIterations);
} catch (NumberFormatException e) {
System.err.println("Argument must be an integer.");
System.exit(1);
}
}
for (int i = 0; i < runs; ++i) {
MyStack<Integer> stack = new LockFreeStack<Integer>();
StackTester tester = new StackTester(stack);
runtimes[i] = tester.runTest(numThreads, numIterations);
//System.out.println("Run time (" + (i + 1) + "): " + runtimes[i] + "s");
}
double totalRuntime = 0;
for (int i = 0; i < runs; ++i) {
totalRuntime += runtimes[i];
}
System.out.println("Average runtime of " + runs + " runs: " + (totalRuntime / runs) + "s");
}
}