package org.multiverse.stms.gamma.integration.isolation; 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.fail; import static org.multiverse.TestUtils.*; import static org.multiverse.api.GlobalStmInstance.getGlobalStmInstance; import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn; /** * A Stresstest that check if the it is possible that a dirty read is done (this is not allowed). * * @author Peter Veentjer. */ public class ReadCommittedStressTest { private GammaTxnLong ref; private int readThreadCount = 10; private int modifyThreadCount = 2; private volatile boolean stop; private GammaStm stm; @Before public void setUp() { clearThreadLocalTxn(); stm = (GammaStm) getGlobalStmInstance(); ref = new GammaTxnLong(stm); stop = false; } @Test public void test() { FailingModifyThread[] modifyThreads = new FailingModifyThread[modifyThreadCount]; for (int k = 0; k < modifyThreadCount; k++) { modifyThreads[k] = new FailingModifyThread(k); } ReadThread[] readerThread = new ReadThread[readThreadCount]; for (int k = 0; k < readThreadCount; k++) { readerThread[k] = new ReadThread(k); } startAll(modifyThreads); startAll(readerThread); sleepMs(getStressTestDurationMs(30 * 1000)); stop = true; joinAll(modifyThreads); joinAll(readerThread); } class FailingModifyThread extends TestThread { public FailingModifyThread(int threadId) { super("FailingModifyThread-" + threadId); } @Override public void doRun() { TxnExecutor executor = stm.getDefaultTxnExecutor(); TxnVoidCallable callable = new TxnVoidCallable() { @Override public void call(Txn tx) throws Exception { GammaTxn btx = (GammaTxn) tx; ref.getAndSet(btx, ref.get(btx)); btx.abort(); } }; while (!stop) { try { executor.execute(callable); fail(); } catch (DeadTxnException ignore) { } sleepRandomMs(10); } } } class ReadThread extends TestThread { public ReadThread(int threadId) { super("ReadThread-" + threadId); } @Override public void doRun() { TxnVoidCallable callable = new TxnVoidCallable() { @Override public void call(Txn tx) throws Exception { GammaTxn btx = (GammaTxn) tx; if (ref.get(btx) % 2 != 0) { fail(); } } }; TxnExecutor readonlyReadtrackingBlock = stm.newTxnFactoryBuilder() .setReadonly(true) .setReadTrackingEnabled(true) .newTxnExecutor(); TxnExecutor updateReadtrackingBlock = stm.newTxnFactoryBuilder() .setReadonly(false) .setReadTrackingEnabled(true) .newTxnExecutor(); int k = 0; while (!stop) { switch (k % 2) { case 0: readonlyReadtrackingBlock.execute(callable); break; case 1: case 3: updateReadtrackingBlock.execute(callable); break; default: throw new IllegalStateException(); } k++; sleepRandomMs(5); } } } }