package org.multiverse.stms.gamma.integration.traditionalsynchronization;
import org.junit.Before;
import org.multiverse.TestThread;
import org.multiverse.TestUtils;
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 static org.junit.Assert.assertEquals;
import static org.multiverse.TestUtils.*;
import static org.multiverse.api.GlobalStmInstance.getGlobalStmInstance;
import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn;
/**
* A stresstest that checks if the NonReentrantMutex; a traditional synchronization structure, can be build
* using stm. It isn't meant as a replacement for Mutex, but just to see if the system behaves like it should.
*
* @author Peter Veentjer.
*/
public abstract class NonReentrantMutex_AbstractTest {
private volatile boolean stop;
private int accountCount = 50;
private int threadCount = processorCount() * 4;
private ProtectedIntValue[] intValues;
protected GammaStm stm;
@Before
public void setUp() {
clearThreadLocalTxn();
stm = (GammaStm) getGlobalStmInstance();
stop = false;
}
protected abstract TxnExecutor newUnlockBlock();
protected abstract TxnExecutor newLockBlock();
public void run() {
intValues = new ProtectedIntValue[accountCount];
for (int k = 0; k < accountCount; k++) {
intValues[k] = new ProtectedIntValue();
}
IncThread[] threads = new IncThread[threadCount];
for (int k = 0; k < threads.length; k++) {
threads[k] = new IncThread(k);
}
startAll(threads);
sleepMs(TestUtils.getStressTestDurationMs(60 * 1000));
stop = true;
joinAll(threads);
assertEquals(sum(threads), sum(intValues));
System.out.println("total increments: " + sum(threads));
}
int sum(IncThread[] threads) {
int result = 0;
for (IncThread thread : threads) {
result += thread.count;
}
return result;
}
int sum(ProtectedIntValue[] intValues) {
int result = 0;
for (ProtectedIntValue intValue : intValues) {
result += intValue.balance;
}
return result;
}
class IncThread extends TestThread {
private int count;
public IncThread(int id) {
super("IncThread-" + id);
}
@Override
public void doRun() throws Exception {
while (!stop) {
ProtectedIntValue intValue = intValues[TestUtils.randomInt(accountCount)];
intValue.inc();
if (count % 500000 == 0) {
System.out.printf("%s is at %s\n", getName(), count);
}
count++;
}
}
}
class ProtectedIntValue {
final NonReentrantMutex mutex = new NonReentrantMutex();
int balance;
public void inc() {
mutex.lock();
balance++;
mutex.unlock();
}
}
class NonReentrantMutex {
final GammaTxnRef locked = new GammaTxnRef(stm, null);
final TxnExecutor lockBlock = newLockBlock();
final TxnExecutor unlockBlock = newUnlockBlock();
final TxnVoidCallable lockCallable = new TxnVoidCallable() {
@Override
public void call(Txn tx) throws Exception {
locked.awaitNull(tx);
locked.set(tx, this);
}
};
final TxnVoidCallable unlockCallable = new TxnVoidCallable() {
@Override
public void call(Txn tx) throws Exception {
if (locked.isNull(tx)) {
throw new IllegalStateException();
}
locked.set(tx, null);
}
};
public void lock() {
lockBlock.execute(lockCallable);
}
public void unlock() {
unlockBlock.execute(unlockCallable);
}
}
}