package org.multiverse.stms.gamma.integration.traditionalsynchronization;
import org.junit.Before;
import org.multiverse.TestThread;
import org.multiverse.api.Txn;
import org.multiverse.api.callables.TxnVoidCallable;
import org.multiverse.api.references.TxnInteger;
import org.multiverse.api.references.TxnRef;
import org.multiverse.stms.gamma.GammaTxnExecutor;
import org.multiverse.stms.gamma.GammaStm;
import static org.multiverse.TestUtils.*;
import static org.multiverse.api.GlobalStmInstance.getGlobalStmInstance;
import static org.multiverse.api.StmUtils.*;
import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn;
public abstract class ReentrantMutex_AbstractTest {
protected GammaStm stm;
private int threadCount = 10;
private ReentrantMutex mutex;
private volatile boolean stop;
@Before
public void setUp() {
stm = (GammaStm) getGlobalStmInstance();
clearThreadLocalTxn();
stop = false;
}
protected abstract GammaTxnExecutor newUnlockBlock();
protected abstract GammaTxnExecutor newLockBlock();
public void run() {
mutex = new ReentrantMutex();
StressThread[] threads = new StressThread[threadCount];
for (int k = 0; k < threads.length; 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) {
mutex.lock(this);
boolean nested = randomOneOf(3);
if (nested) {
mutex.lock(this);
}
sleepRandomMs(100);
mutex.unlock(this);
if (nested) {
mutex.unlock(this);
}
count++;
if (count % 10 == 0) {
System.out.printf("%s is at %s\n", getName(), count);
}
}
}
}
class ReentrantMutex {
private final TxnRef<Thread> owner = newTxnRef();
private final TxnInteger count = newTxnInteger();
private final GammaTxnExecutor lockBlock = newLockBlock();
private final GammaTxnExecutor unlockBlock = newUnlockBlock();
public void lock(final Thread thread) {
lockBlock.execute(new TxnVoidCallable() {
@Override
public void call(Txn tx) throws Exception {
if (owner.get() == null) {
owner.set(thread);
count.increment();
return;
}
if (owner.get() == thread) {
count.increment();
return;
}
retry();
}
});
}
public void unlock(final Thread thread) {
unlockBlock.execute(new TxnVoidCallable() {
@Override
public void call(Txn tx) throws Exception {
if (owner.get() != thread) {
throw new IllegalMonitorStateException();
}
count.decrement();
if (count.get() == 0) {
owner.set(null);
}
}
});
}
}
}