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