package org.multiverse.stms.gamma.integration.liveness; 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.LockMode; import org.multiverse.api.callables.TxnVoidCallable; import org.multiverse.stms.gamma.GammaStm; import org.multiverse.stms.gamma.transactionalobjects.GammaTxnLong; import org.multiverse.stms.gamma.transactions.GammaTxn; import static org.multiverse.TestUtils.*; import static org.multiverse.api.GlobalStmInstance.getGlobalStmInstance; import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn; public class DeadLockStressTest { enum Mode { Normal, Mix, PrivatizeReadLevelMode, PrivatizeWriteLevelMode } private volatile boolean stop; private int refCount = 100; private int threadCount = 10; private GammaTxnLong[] refs; private ChangeThread[] threads; private GammaStm stm; private Mode mode; @Before public void setUp() { clearThreadLocalTxn(); stop = false; stm = (GammaStm) getGlobalStmInstance(); } @Test public void whenNormal() { test(Mode.Normal); } @Test public void whenMix() { test(Mode.Mix); } @Test public void whenPessimisticReadLevel() { test(Mode.PrivatizeReadLevelMode); } @Test public void whenPessimisticWriteLevel() { test(Mode.PrivatizeWriteLevelMode); } public void test(Mode mode) { this.mode = mode; refs = new GammaTxnLong[refCount]; for (int k = 0; k < refCount; k++) { refs[k] = new GammaTxnLong(stm); } threads = new ChangeThread[threadCount]; for (int k = 0; k < threadCount; k++) { threads[k] = new ChangeThread(k); } startAll(threads); sleepMs(getStressTestDurationMs(60 * 1000)); stop = true; joinAll(threads); } public class ChangeThread extends TestThread { private final TxnExecutor normalBlock = stm.newTxnFactoryBuilder() .newTxnExecutor(); private final TxnExecutor pessimisticReadLevelBlock = stm.newTxnFactoryBuilder() .setReadLockMode(LockMode.Exclusive) .newTxnExecutor(); private final TxnExecutor pessimisticWriteLevelBlock = stm.newTxnFactoryBuilder() .setWriteLockMode(LockMode.Exclusive) .newTxnExecutor(); public ChangeThread(int id) { super("ChangeThread-" + id); } @Override public void doRun() throws Exception { int k = 0; while (!stop) { if (k % 100000 == 0) { System.out.printf("%s is at %s\n", getName(), k); } switch (mode) { case PrivatizeReadLevelMode: privatizeReadLevel(); break; case PrivatizeWriteLevelMode: privatizeWriteLevel(); break; case Normal: normal(); break; case Mix: switch (randomInt(3)) { case 0: privatizeReadLevel(); break; case 1: privatizeWriteLevel(); break; case 2: normal(); break; default: throw new IllegalStateException(); } break; default: throw new IllegalStateException(); } k++; } } public void normal() { normalBlock.execute(new TxnVoidCallable() { @Override public void call(Txn tx) throws Exception { doIt((GammaTxn) tx); } }); } public void privatizeReadLevel() { pessimisticReadLevelBlock.execute(new TxnVoidCallable() { @Override public void call(Txn tx) throws Exception { doIt((GammaTxn) tx); } }); } public void privatizeWriteLevel() { pessimisticWriteLevelBlock.execute(new TxnVoidCallable() { @Override public void call(Txn tx) throws Exception { doIt((GammaTxn) tx); } }); } public void doIt(GammaTxn tx) { for (int k = 0; k < refs.length; k++) { if (!randomOneOf(10)) { continue; } int index = randomInt(refs.length); GammaTxnLong ref = refs[index]; ref.getAndSet(tx, ref.get(tx) + 1); } } } }