package org.multiverse.stms.gamma.integration.traditionalsynchronization; import org.junit.Before; import org.multiverse.TestThread; import org.multiverse.api.Txn; import org.multiverse.api.TxnExecutor; import org.multiverse.api.callables.TxnVoidCallable; import org.multiverse.stms.gamma.GammaStm; import org.multiverse.stms.gamma.transactionalobjects.GammaTxnRef; import java.util.concurrent.atomic.AtomicLong; 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; /** * A Stresstest that sees if the stm can be used to create a readwritelock that is not reentrant. * * @author Peter Veentjer. */ public abstract class NonReentrantReadWriteLock_AbstractTest { protected GammaStm stm; private int threadCount = 10; private ReadWriteLock readWriteLock; private volatile boolean stop; @Before public void setUp() { stm = (GammaStm) getGlobalStmInstance(); clearThreadLocalTxn(); stop = false; } protected abstract TxnExecutor newReleaseWriteLockBlock(); protected abstract TxnExecutor newAcquireWriteLockBlock(); protected abstract TxnExecutor newReleaseReadLockBlock(); protected abstract TxnExecutor newAcquireReadLockBlock(); public void run() { readWriteLock = new ReadWriteLock(); StressThread[] threads = new StressThread[threadCount]; for (int k = 0; k < threadCount; k++) { threads[k] = new StressThread(k); } startAll(threads); sleepMs(30000); stop = true; joinAll(threads); } class StressThread extends TestThread { public StressThread(int id) { super("StressThread-" + id); } @Override public void doRun() throws Exception { long count = 0; while (!stop) { if (randomOneOf(5)) { readWriteLock.acquireWriteLock(); sleepMs(100); readWriteLock.releaseWriteLock(); } else { readWriteLock.acquireReadLock(); sleepMs(100); readWriteLock.releaseReadLock(); } count++; if (count % 10 == 0) { System.out.printf("%s is at %s\n", getName(), count); } } } } class ReadWriteLock { final GammaTxnRef<Long> lock = new GammaTxnRef<Long>(stm, 0L); final AtomicLong readers = new AtomicLong(); final AtomicLong writers = new AtomicLong(); final TxnExecutor acquireReadLockBlock = newAcquireReadLockBlock(); final TxnExecutor releaseReadLockBlock = newReleaseReadLockBlock(); final TxnExecutor acquireWriteLockBlock = newAcquireWriteLockBlock(); final TxnExecutor releaseWriteLockBlock = newReleaseWriteLockBlock(); public void acquireReadLock() { acquireReadLockBlock.execute(new TxnVoidCallable() { @Override public void call(Txn tx) throws Exception { if (lock.get() < 0) { retry(); } lock.set(lock.get() + 1); } }); readers.incrementAndGet(); assertEquals(0, writers.get()); } public void releaseReadLock() { readers.decrementAndGet(); assertEquals(0, writers.get()); releaseReadLockBlock.execute(new TxnVoidCallable() { @Override public void call(Txn tx) throws Exception { if (lock.get() <= 0) { throw new IllegalMonitorStateException(); } lock.set(lock.get() - 1); } }); } public void acquireWriteLock() { acquireWriteLockBlock.execute(new TxnVoidCallable() { @Override public void call(Txn tx) throws Exception { if (lock.get() != 0) { retry(); } lock.set(-1L); } }); writers.incrementAndGet(); assertEquals(0, readers.get()); } public void releaseWriteLock() { writers.decrementAndGet(); assertEquals(0, readers.get()); releaseWriteLockBlock.execute(new TxnVoidCallable() { @Override public void call(Txn tx) throws Exception { if (lock.get() != -1) { throw new IllegalMonitorStateException(); } lock.set(0L); } }); } } }