package org.multiverse.stms.gamma.integration.failureatomicity;
import org.junit.Before;
import org.junit.Test;
import org.multiverse.TestThread;
import org.multiverse.api.Txn;
import org.multiverse.api.TxnExecutor;
import org.multiverse.api.callables.TxnVoidCallable;
import org.multiverse.api.exceptions.DeadTxnException;
import org.multiverse.stms.gamma.GammaStm;
import org.multiverse.stms.gamma.transactionalobjects.GammaTxnLong;
import org.multiverse.stms.gamma.transactions.GammaTxn;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.multiverse.TestUtils.*;
import static org.multiverse.api.GlobalStmInstance.getGlobalStmInstance;
import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn;
public class FailureAtomicityStressTest {
private int modifyThreadCount = 10;
private boolean stop;
private GammaTxnLong ref;
private GammaStm stm;
@Before
public void setUp() {
clearThreadLocalTxn();
stm = (GammaStm) getGlobalStmInstance();
ref = new GammaTxnLong(stm);
stop = false;
}
@Test
public void test() {
ModifyThread[] modifyThreads = new ModifyThread[modifyThreadCount];
for (int k = 0; k < modifyThreadCount; k++) {
modifyThreads[k] = new ModifyThread(k);
}
startAll(modifyThreads);
sleepMs(getStressTestDurationMs(30 * 1000));
stop = true;
joinAll(modifyThreads);
//since half of the transactions are going to be aborted we need to divide it by 2
assertEquals(sum(modifyThreads), ref.atomicGet());
}
public long sum(ModifyThread[] threads) {
long result = 0;
for (ModifyThread thread : threads) {
result += thread.writeCount;
}
return result;
}
public class ModifyThread extends TestThread {
long writeCount;
final TxnExecutor txnExecutor = stm.newTxnFactoryBuilder()
.newTxnExecutor();
public ModifyThread(int id) {
super("ModifyThread-" + id);
}
@Override
public void doRun() throws Exception {
while (!stop) {
if (writeCount % 500000 == 0) {
System.out.printf("%s is at %s\n", getName(), writeCount);
}
boolean abort = randomOneOf(10);
if (abort) {
try {
modifyButAbort();
fail();
} catch (DeadTxnException ignore) {
}
} else {
writeCount++;
modify();
}
}
}
private void modify() {
txnExecutor.execute(new TxnVoidCallable() {
@Override
public void call(Txn tx) throws Exception {
GammaTxn btx = (GammaTxn) tx;
long value = ref.get(btx);
ref.getAndSet(btx, value + 1);
}
});
}
private void modifyButAbort() {
txnExecutor.execute(new TxnVoidCallable() {
@Override
public void call(Txn tx) throws Exception {
GammaTxn btx = (GammaTxn) tx;
long value = ref.get(btx);
ref.getAndSet(btx, value + 1);
btx.abort();
}
});
}
}
}