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; } }); } } }