package org.multiverse.stms.gamma.integration.traditionalsynchronization;
import org.junit.Before;
import org.multiverse.TestThread;
import org.multiverse.api.Txn;
import org.multiverse.api.TxnExecutor;
import org.multiverse.api.callables.TxnCallable;
import org.multiverse.api.callables.TxnVoidCallable;
import org.multiverse.api.references.TxnBoolean;
import org.multiverse.api.references.TxnInteger;
import org.multiverse.api.references.TxnRef;
import org.multiverse.stms.gamma.GammaStm;
import static org.multiverse.TestUtils.joinAll;
import static org.multiverse.TestUtils.startAll;
import static org.multiverse.api.GlobalStmInstance.getGlobalStmInstance;
import static org.multiverse.api.StmUtils.*;
import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn;
public abstract class ConditionVariable_AbstractTest {
protected GammaStm stm;
private Stack stack;
private int itemCount = 10000000;
@Before
public void setUp() {
stm = (GammaStm) getGlobalStmInstance();
clearThreadLocalTxn();
}
protected abstract TxnExecutor newPopBlock();
protected abstract TxnExecutor newPushBlock();
public void run() {
stack = new Stack(100);
PushThread pushThread = new PushThread();
PopThread popThread = new PopThread();
startAll(pushThread, popThread);
joinAll(pushThread, popThread);
}
public class PushThread extends TestThread {
public PushThread() {
super("PushThread");
}
@Override
public void doRun() throws Exception {
for (int k = 0; k < itemCount; k++) {
stack.push("foo");
if (k % 100000 == 0) {
System.out.printf("%s is at %s\n", getName(), k);
}
}
}
}
public class PopThread extends TestThread {
@Override
public void doRun() throws Exception {
for (int k = 0; k < itemCount; k++) {
stack.pop();
if (k % 100000 == 0) {
System.out.printf("%s is at %s\n", getName(), k);
}
}
}
}
class Stack {
ConditionVariable isNotFull = new ConditionVariable(true);
ConditionVariable isNotEmpty = new ConditionVariable(false);
TxnRef<Node> head = newTxnRef();
TxnInteger size = newTxnInteger();
final int capacity;
final TxnExecutor pushBlock = newPushBlock();
final TxnExecutor popBlock = newPopBlock();
Stack(int capacity) {
this.capacity = capacity;
}
void push(final String item) {
pushBlock.execute(new TxnVoidCallable() {
@Override
public void call(Txn tx) throws Exception {
isNotFull.awaitTrue();
head.set(new Node(item, head.get()));
size.increment();
if (size.get() == capacity) {
isNotFull.set(false);
}
isNotEmpty.set(true);
}
});
}
String pop() {
return popBlock.execute(new TxnCallable<String>() {
@Override
public String call(Txn tx) throws Exception {
isNotEmpty.awaitTrue();
Node node = head.get();
head.set(node.next);
size.decrement();
if (size.get() == 0) {
isNotEmpty.set(false);
}
isNotFull.set(true);
return node.value;
}
});
}
}
class Node {
final String value;
final Node next;
Node(String value, Node next) {
this.value = value;
this.next = next;
}
}
class ConditionVariable {
final TxnBoolean ref;
ConditionVariable(boolean value) {
this.ref = newTxnBoolean(value);
}
void awaitTrue() {
ref.await(true);
}
void awaitFalse() {
ref.await(false);
}
void set(boolean value) {
ref.set(value);
}
}
}