package org.multiverse.stms.gamma.integration.classic; import org.junit.Before; import org.multiverse.TestThread; import org.multiverse.api.TxnExecutor; import org.multiverse.api.Txn; import org.multiverse.api.callables.TxnVoidCallable; import org.multiverse.api.references.TxnLong; import org.multiverse.stms.gamma.GammaStm; import java.util.concurrent.atomic.AtomicLong; 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.StmUtils.newTxnLong; import static org.multiverse.api.StmUtils.retry; import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn; /** * http://en.wikipedia.org/wiki/Readers-writers_problem */ public abstract class ReadersWritersProblem_AbstractTest { private long count = 3000; private int readerThreadCount = 10; private int writerThreadCount = 5; private ReadersWritersLock readWriteLock; private AtomicLong currentReaderCount = new AtomicLong(); private AtomicLong currentWriterCount = new AtomicLong(); protected GammaStm stm; protected abstract TxnExecutor newAcquiredBlock(); protected abstract TxnExecutor newAcquireBlock(); @Before public void setUp() { clearThreadLocalTxn(); stm = (GammaStm) getGlobalStmInstance(); } public void run() { readWriteLock = new ReadersWritersLock(); ReaderThread[] readers = createReaderThreads(); WriterThread[] writers = createWriterThreads(); startAll(writers); startAll(readers); joinAll(1000, writers); joinAll(1000, readers); assertEquals(0, currentReaderCount.get()); assertEquals(0, currentWriterCount.get()); } private ReaderThread[] createReaderThreads() { ReaderThread[] readers = new ReaderThread[readerThreadCount]; for (int k = 0; k < readerThreadCount; k++) { readers[k] = new ReaderThread(k); } return readers; } private WriterThread[] createWriterThreads() { WriterThread[] writers = new WriterThread[writerThreadCount]; for (int k = 0; k < writerThreadCount; k++) { writers[k] = new WriterThread(k); } return writers; } public class ReaderThread extends TestThread { public ReaderThread(int id) { super("ReaderThread-" + id); } @Override public void doRun() throws Exception { for (int k = 0; k < count; k++) { readWriteLock.acquireReadLock(); try { assertNoWriters(); currentReaderCount.incrementAndGet(); sleepRandomMs(2); currentReaderCount.decrementAndGet(); assertNoWriters(); if (k % 100 == 0) { System.out.printf("%s is at count %s\n", getName(), k); } } finally { readWriteLock.releaseReadLock(); } sleepRandomMs(5); } } } private void assertNoWriters() { if (currentWriterCount.get() > 0) { fail(); } } private void assertNoReaders() { if (currentReaderCount.get() > 0) { fail(); } } public class WriterThread extends TestThread { public WriterThread(int id) { super("WriterThread-" + id); } @Override public void doRun() throws Exception { for (int k = 0; k < count; k++) { readWriteLock.acquireWriteLock(); try { assertNoReaders(); assertNoWriters(); currentWriterCount.incrementAndGet(); sleepRandomMs(20); currentWriterCount.decrementAndGet(); assertNoWriters(); assertNoReaders(); if (k % 100 == 0) { System.out.printf("%s is at count %s\n", getName(), k); } } finally { readWriteLock.releaseWriteLock(); } } } } class ReadersWritersLock { //-1 is write lock, 0 = free, positive number is readLock count. private final TxnLong readerCount = newTxnLong(); private TxnExecutor acquireReadLockBlock; private TxnExecutor acquireWriteLockBlock; public ReadersWritersLock() { acquireReadLockBlock = newAcquireBlock(); acquireWriteLockBlock = newAcquiredBlock(); } public void acquireReadLock() { acquireReadLockBlock.execute(new TxnVoidCallable() { @Override public void call(Txn tx) throws Exception { if (readerCount.get() == -1) { retry(); } readerCount.increment(); } }); } public void acquireWriteLock() { acquireWriteLockBlock.execute(new TxnVoidCallable() { @Override public void call(Txn tx) throws Exception { if (readerCount.get() != 0) { retry(); } readerCount.decrement(); } }); } public void releaseWriteLock() { readerCount.atomicSet(0); } public void releaseReadLock() { readerCount.atomicIncrementAndGet(-1); } } }