package org.multiverse.stms.gamma.integration.classic;
import org.junit.Before;
import org.multiverse.TestThread;
import org.multiverse.TestUtils;
import org.multiverse.api.Txn;
import org.multiverse.api.TxnExecutor;
import org.multiverse.api.callables.TxnBooleanCallable;
import org.multiverse.api.callables.TxnVoidCallable;
import org.multiverse.api.references.TxnBoolean;
import org.multiverse.api.references.TxnRef;
import org.multiverse.stms.gamma.GammaStm;
import static org.junit.Assert.assertEquals;
import static org.multiverse.TestUtils.*;
import static org.multiverse.api.GlobalStmInstance.getGlobalStmInstance;
import static org.multiverse.api.StmUtils.*;
import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn;
/**
* http://en.wikipedia.org/wiki/Cigarette_smokers_problem
*/
public abstract class CigaretteSmokersProblem_AbstractTest {
private static final int SMOKE_TIME_SECONDS = 10;
private TxnBoolean tobaccoAvailable;
private TxnBoolean paperAvailable;
private TxnBoolean matchesAvailable;
private TxnRef<Thread> notifier;
private ArbiterThread arbiterThread;
private SmokerThread paperProvider;
private SmokerThread matchProvider;
private SmokerThread tobaccoProvider;
private volatile boolean stop;
private TxnExecutor executor;
protected GammaStm stm;
protected abstract TxnExecutor newBlock();
@Before
public void setUp() {
clearThreadLocalTxn();
stm = (GammaStm) getGlobalStmInstance();
tobaccoAvailable = newTxnBoolean();
paperAvailable = newTxnBoolean();
matchesAvailable = newTxnBoolean();
notifier = newTxnRef();
arbiterThread = new ArbiterThread();
paperProvider = new SmokerThread("PaperProviderThread", tobaccoAvailable, matchesAvailable);
matchProvider = new SmokerThread("MatchProvidedThread", tobaccoAvailable, paperAvailable);
tobaccoProvider = new SmokerThread("TobaccoProviderThread", paperAvailable, matchesAvailable);
stop = false;
}
public void run() {
executor = newBlock();
startAll(arbiterThread, paperProvider, matchProvider, tobaccoProvider);
sleepMs(60000);
System.out.println("Stopping threads");
stop = true;
joinAll(arbiterThread, paperProvider, matchProvider, tobaccoProvider);
System.out.println("MatchesAvailable: " + matchesAvailable.atomicGet());
System.out.println("PaperAvailable: " + paperAvailable.atomicGet());
System.out.println("TobaccoAvailable: " + tobaccoAvailable.atomicGet());
assertEquals(arbiterThread.count,
paperProvider.count + matchProvider.count + tobaccoProvider.count);
}
class ArbiterThread extends TestThread {
private int count;
public ArbiterThread() {
super("Arbiter");
}
@Override
public void doRun() throws Exception {
while (!stop) {
count++;
switch (TestUtils.randomInt(3)) {
case 0:
executor.execute(new TxnVoidCallable() {
@Override
public void call(Txn tx) {
if (notifier.get() != null) {
retry();
}
tobaccoAvailable.set(true);
paperAvailable.set(true);
notifier.set(matchProvider);
}
});
break;
case 1:
executor.execute(new TxnVoidCallable() {
@Override
public void call(Txn tx) {
if (notifier.get() != null) {
retry();
}
tobaccoAvailable.set(true);
matchesAvailable.set(true);
notifier.set(paperProvider);
}
});
break;
case 2:
executor.execute(new TxnVoidCallable() {
@Override
public void call(Txn tx) {
if (notifier.get() != null) {
retry();
}
matchesAvailable.set(true);
paperAvailable.set(true);
notifier.set(tobaccoProvider);
}
});
break;
default:
throw new RuntimeException();
}
}
executor.execute(new TxnVoidCallable() {
@Override
public void call(Txn tx) throws Exception {
notifier.awaitNull();
notifier.set(arbiterThread);
}
});
}
}
class SmokerThread extends TestThread {
private int count;
private TxnBoolean item1;
private TxnBoolean item2;
public SmokerThread(String name, TxnBoolean item1, TxnBoolean item2) {
super(name);
this.item1 = item1;
this.item2 = item2;
}
@Override
public void doRun() throws Exception {
while (makeCigarette()) {
sleepRandomMs(SMOKE_TIME_SECONDS);
count++;
if (count % 100 == 0) {
System.out.printf("%s is at %s\n", getName(), count);
}
}
}
private boolean makeCigarette() {
return executor.execute(new TxnBooleanCallable() {
@Override
public boolean call(Txn tx) throws Exception {
if (notifier.get() != SmokerThread.this) {
if (notifier.get() == arbiterThread) {
return false;
}
retry();
}
item1.set(false);
item2.set(false);
notifier.set(null);
return true;
}
});
}
}
}