package org.multiverse.stms.gamma.integration.blocking;
import org.junit.Before;
import org.junit.Test;
import org.multiverse.TestThread;
import org.multiverse.api.TxnExecutor;
import org.multiverse.api.Txn;
import org.multiverse.api.callables.TxnBooleanCallable;
import org.multiverse.api.callables.TxnVoidCallable;
import org.multiverse.stms.gamma.GammaStm;
import org.multiverse.stms.gamma.LeanGammaTxnExecutor;
import org.multiverse.stms.gamma.transactionalobjects.GammaTxnLong;
import org.multiverse.stms.gamma.transactions.GammaTxnFactory;
import org.multiverse.stms.gamma.transactions.fat.FatFixedLengthGammaTxnFactory;
import org.multiverse.stms.gamma.transactions.fat.FatMonoGammaTxnFactory;
import org.multiverse.stms.gamma.transactions.fat.FatVariableLengthGammaTxnFactory;
import static java.lang.Math.abs;
import static org.junit.Assert.assertEquals;
import static org.multiverse.TestUtils.*;
import static org.multiverse.api.GlobalStmInstance.getGlobalStmInstance;
import static org.multiverse.api.StmUtils.retry;
import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn;
public class PingPongStressTest {
private volatile boolean stop = false;
private GammaTxnLong ref;
private GammaStm stm;
@Before
public void setUp() {
clearThreadLocalTxn();
stm = (GammaStm) getGlobalStmInstance();
ref = new GammaTxnLong(stm);
stop = false;
}
@Test
public void withMonoTransactionAnd2Threads() throws InterruptedException {
test(new FatMonoGammaTxnFactory(stm), 2);
}
@Test
public void withArrayTransactionAnd2Threads() throws InterruptedException {
test(new FatFixedLengthGammaTxnFactory(stm), 2);
}
@Test
public void withMapTransactionAnd2Threads() throws InterruptedException {
test(new FatVariableLengthGammaTxnFactory(stm), 2);
}
@Test
public void withMonoTransactionAnd10Threads() throws InterruptedException {
test(new FatMonoGammaTxnFactory(stm), 10);
}
@Test
public void withArrayTransactionAnd10Threads() throws InterruptedException {
test(new FatFixedLengthGammaTxnFactory(stm), 10);
}
@Test
public void withMapTransactionAnd10Threads() throws InterruptedException {
test(new FatVariableLengthGammaTxnFactory(stm), 10);
}
public void test(GammaTxnFactory transactionFactory, int threadCount) throws InterruptedException {
TxnExecutor executor = new LeanGammaTxnExecutor(transactionFactory);
PingPongThread[] threads = createThreads(executor, threadCount);
startAll(threads);
sleepMs(30 * 1000);
stop = true;
stm.getDefaultTxnExecutor().execute(new TxnVoidCallable() {
@Override
public void call(Txn tx) throws Exception {
ref.set(-abs(ref.get()));
}
});
System.out.println("Waiting for joining threads");
joinAll(threads);
assertEquals(sum(threads), -ref.atomicGet());
System.out.println(stm.getGlobalConflictCounter().count());
}
private PingPongThread[] createThreads(TxnExecutor executor, int threadCount) {
PingPongThread[] threads = new PingPongThread[threadCount];
for (int k = 0; k < threads.length; k++) {
threads[k] = new PingPongThread(k, executor, threadCount);
}
return threads;
}
private long sum(PingPongThread[] threads) {
long result = 0;
for (PingPongThread t : threads) {
result += t.count;
}
return result;
}
private class PingPongThread extends TestThread {
private final TxnExecutor executor;
private final int threadCount;
private final int id;
private long count;
public PingPongThread(int id, TxnExecutor executor, int threadCount) {
super("PingPongThread-" + id);
this.id = id;
this.executor = executor;
this.threadCount = threadCount;
}
@Override
public void doRun() {
TxnBooleanCallable callable = new TxnBooleanCallable() {
@Override
public boolean call(Txn tx) throws Exception {
if (ref.get() < 0) {
return false;
}
if (ref.get() % threadCount != id) {
retry();
}
ref.increment();
return true;
}
};
while (!stop) {
if (count % (20000) == 0) {
System.out.println(getName() + " " + count);
}
if (!executor.execute(callable)) {
break;
}
count++;
}
System.out.printf("%s finished\n", getName());
}
}
}