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 java.util.concurrent.atomic.AtomicLong;
import static org.junit.Assert.fail;
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 checks if a the Semaphore; a traditional synchronization structure can be build
* using an STM.
*/
public abstract class Semaphore_AbstractTest {
protected GammaStm stm;
private volatile boolean stop;
private int threadCount = 10;
private int resourceCount = 5;
private Semaphore semaphore;
@Before
public void setUp() {
clearThreadLocalTxn();
stm = (GammaStm) getGlobalStmInstance();
stop = false;
}
protected abstract TxnExecutor newDownBlock();
protected abstract TxnExecutor newUpBlock();
public void run() {
semaphore = new Semaphore(resourceCount);
WorkerThread[] workers = new WorkerThread[threadCount];
for (int k = 0; k < threadCount; k++) {
workers[k] = new WorkerThread(k);
}
startAll(workers);
sleepMs(TestUtils.getStressTestDurationMs(30 * 1000));
System.out.println("Terminating");
stop = true;
System.out.println(semaphore.ref.toDebugString());
joinAll(workers);
}
class WorkerThread extends TestThread {
long count;
public WorkerThread(int id) {
super("Producer-" + id);
}
@Override
public void doRun() throws Exception {
while (!stop) {
semaphore.down();
semaphore.up();
count++;
if (count % 1000000 == 0) {
System.out.printf("%s is at %s\n", getName(), count);
}
}
}
}
class Semaphore {
private GammaTxnRef<Long> ref;
private AtomicLong users = new AtomicLong();
private TxnExecutor upBlock = newUpBlock();
private TxnExecutor downBlock = newDownBlock();
public Semaphore(int initial) {
ref = new GammaTxnRef<Long>(stm, new Long(initial));
}
public void up() {
upBlock.execute(new TxnVoidCallable() {
@Override
public void call(Txn tx) throws Exception {
ref.set(ref.get() + 1);
}
});
users.incrementAndGet();
if (users.get() > resourceCount) {
fail();
}
}
public void down() {
users.decrementAndGet();
downBlock.execute(new TxnVoidCallable() {
@Override
public void call(Txn tx) throws Exception {
if (ref.get() == 0) {
retry();
}
ref.set(ref.get() - 1);
}
});
}
}
}