package org.multiverse.stms.gamma.integration.isolation;
import org.junit.After;
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 org.multiverse.stms.gamma.transactions.GammaTxn;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.junit.Assert.assertFalse;
import static org.multiverse.TestUtils.*;
import static org.multiverse.api.GlobalStmInstance.getGlobalStmInstance;
import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn;
/**
* Question: could the problem be in the quick release mechanism?
* <p/>
* Problem?
* if a writing transaction has done n updates (and has released the updates) and has m to go.
* If a reading transaction reads the n updates, there is no reason for the updating transaction to cause
* a conflict since they are no conflicting arrives on the part if has already completes. If the reading transactions
* hits the n+1 update, it is allowed to see a different value than it already has read...
* problem.. the n updates it has read, already contains the new values, so reading another new value is no problem.
*/
public abstract class RefReadConsistency_AbstractTest {
private GammaTxnRef<String>[] refs;
private final AtomicBoolean inconsistencyDetected = new AtomicBoolean();
private int readerCount = 10;
private int writerCount = 2;
private int durationMs = 1 * 60 * 1000;
private volatile boolean stop;
protected GammaStm stm;
@Before
public void setUp() {
clearThreadLocalTxn();
stop = false;
stm = (GammaStm) getGlobalStmInstance();
inconsistencyDetected.set(false);
}
@After
public void tearDown() {
System.out.println("Stm.GlobalConflictCount: " + stm.getGlobalConflictCounter().count());
for (GammaTxnRef ref : refs) {
System.out.println(ref.toDebugString());
}
}
protected abstract TxnExecutor createReadBlock();
protected abstract TxnExecutor createWriteBlock();
public void run(int refCount) {
refs = new GammaTxnRef[refCount];
for (int k = 0; k < refs.length; k++) {
refs[k] = new GammaTxnRef<String>(stm);
}
ReadThread[] readerThreads = new ReadThread[readerCount];
for (int k = 0; k < readerThreads.length; k++) {
readerThreads[k] = new ReadThread(k);
}
WriterThread[] writerThreads = new WriterThread[writerCount];
for (int k = 0; k < writerThreads.length; k++) {
writerThreads[k] = new WriterThread(k);
}
startAll(readerThreads);
startAll(writerThreads);
System.out.printf("Running for %s milliseconds\n", durationMs);
sleepMs(getStressTestDurationMs(durationMs));
stop = true;
joinAll(readerThreads);
joinAll(writerThreads);
assertFalse("Inconsistency detected", inconsistencyDetected.get());
}
public class WriterThread extends TestThread {
public WriterThread(int id) {
super("WriterThread-" + id);
}
@Override
public void doRun() throws Exception {
final String value = getName();
TxnExecutor executor = createWriteBlock();
TxnVoidCallable callable = new TxnVoidCallable() {
@Override
public void call(Txn tx) throws Exception {
GammaTxn btx = (GammaTxn) tx;
//String initial = refs[0].get(btx);
for (int k = 0; k < refs.length; k++) {
refs[k].openForWrite(btx, LOCKMODE_NONE).ref_value = value;
//String s = refs[k].getAndSet(tx, value);
//assertSame("failed at " + k, initial, s);
}
}
};
int mod = 1;
int k = 0;
while (!stop) {
executor.execute(callable);
sleepRandomUs(100);
k++;
if (k % mod == 0) {
mod = mod * 2;
System.out.printf("%s is at %s\n", getName(), k);
}
}
}
}
public class ReadThread extends TestThread {
public ReadThread(int id) {
super("ReadThread-" + id);
}
@Override
public void doRun() throws Exception {
TxnExecutor executor = createReadBlock();
TxnVoidCallable callable = new TxnVoidCallable() {
@Override
public void call(Txn tx) throws Exception {
GammaTxn btx = (GammaTxn) tx;
String initial = (String) refs[0].openForRead(btx, LOCKMODE_NONE).ref_value;
for (int k = 1; k < refs.length; k++) {
String s = (String) refs[k].openForRead(btx, LOCKMODE_NONE).ref_value;
if (s != initial) {
System.out.printf("Inconsistency detected at index %s!!!\n",k);
inconsistencyDetected.set(true);
stop = true;
}
}
}
};
int mod = 1;
int k = 0;
while (!stop) {
executor.execute(callable);
k++;
if (k % mod == 0) {
mod = mod * 2;
System.out.printf("%s is at %s\n", getName(), k);
}
}
}
}
}