package org.multiverse.stms.gamma.integration.classic;
import org.junit.Before;
import org.multiverse.TestThread;
import org.multiverse.api.Txn;
import org.multiverse.api.TxnExecutor;
import org.multiverse.api.callables.TxnVoidCallable;
import org.multiverse.api.references.TxnBoolean;
import org.multiverse.api.references.TxnRefFactory;
import org.multiverse.stms.gamma.GammaConstants;
import org.multiverse.stms.gamma.GammaStm;
import org.multiverse.stms.gamma.GammaStmConfig;
import static org.junit.Assert.assertFalse;
import static org.multiverse.TestUtils.*;
import static org.multiverse.api.StmUtils.retry;
import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn;
/**
* http://en.wikipedia.org/wiki/Dining_philosophers_problem
*/
public abstract class DiningPhilosophers_AbstractTest implements GammaConstants {
private int philosopherCount = 10;
private volatile boolean stop;
private TxnBoolean[] forks;
protected GammaStm stm;
private TxnRefFactory refFactory;
@Before
public void setUp() {
clearThreadLocalTxn();
GammaStmConfig config = new GammaStmConfig();
//config.backoffPolicy = new SpinningBackoffPolicy();
stm = new GammaStm(config);
refFactory = stm.getTxRefFactoryBuilder().build();
stop = false;
}
protected abstract TxnExecutor newTakeForksBlock();
protected abstract TxnExecutor newReleaseForksBlock();
public void run() {
createForks();
PhilosopherThread[] philosopherThreads = createPhilosopherThreads();
startAll(philosopherThreads);
sleepMs(getStressTestDurationMs(30 * 1000));
stop = true;
joinAll(philosopherThreads);
assertAllForksHaveReturned();
for (PhilosopherThread philosopherThread : philosopherThreads) {
System.out.printf("%s ate %s times\n",
philosopherThread.getName(), philosopherThread.eatCount);
}
}
public void assertAllForksHaveReturned() {
for (TxnBoolean fork : forks) {
assertFalse(fork.atomicGet());
}
}
public PhilosopherThread[] createPhilosopherThreads() {
PhilosopherThread[] threads = new PhilosopherThread[philosopherCount];
for (int k = 0; k < philosopherCount; k++) {
TxnBoolean leftFork = forks[k];
TxnBoolean rightFork = k == philosopherCount - 1 ? forks[0] : forks[k + 1];
threads[k] = new PhilosopherThread(k, leftFork, rightFork);
}
return threads;
}
public void createForks() {
forks = new TxnBoolean[philosopherCount];
for (int k = 0; k < forks.length; k++) {
forks[k] = refFactory.newTxnBoolean(false);
}
}
class PhilosopherThread extends TestThread {
private int eatCount = 0;
private final TxnBoolean leftFork;
private final TxnBoolean rightFork;
private final TxnExecutor releaseForksBlock = newReleaseForksBlock();
private final TxnExecutor takeForksBlock = newTakeForksBlock();
PhilosopherThread(int id, TxnBoolean leftFork, TxnBoolean rightFork) {
super("PhilosopherThread-" + id);
this.leftFork = leftFork;
this.rightFork = rightFork;
}
@Override
public void doRun() {
while (!stop) {
eatCount++;
if (eatCount % 100 == 0) {
System.out.printf("%s at %s\n", getName(), eatCount);
}
eat();
// sleepMs(5);
}
}
public void eat() {
takeForks();
stuffHole();
releaseForks();
}
private void stuffHole() {
//simulate the eating
sleepRandomMs(50);
}
public void releaseForks() {
releaseForksBlock.execute(new TxnVoidCallable() {
@Override
public void call(Txn tx) throws Exception {
leftFork.set(false);
rightFork.set(false);
}
});
}
public void takeForks() {
takeForksBlock.execute(new TxnVoidCallable() {
@Override
public void call(Txn tx) throws Exception {
if (leftFork.get() || rightFork.get()) {
retry();
}
leftFork.set(true);
rightFork.set(true);
}
});
}
}
}