package org.corfudb.runtime.object.transactions;
import com.google.common.reflect.TypeToken;
import org.corfudb.runtime.CorfuRuntime;
import org.corfudb.runtime.collections.ISMRMap;
import org.corfudb.runtime.collections.SMRMap;
import org.corfudb.runtime.exceptions.TransactionAbortedException;
import org.junit.Test;
import java.util.concurrent.Semaphore;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Created by dalia on 1/6/17.
*/
public class TXsFromTwoRuntimesTest extends AbstractTransactionsTest {
@Override
public void TXBegin() { OptimisticTXBegin(); }
@Test
public void staggeredTXsConflict() throws Exception {
final int nTXs = 5;
final Semaphore sem0 = new Semaphore(0),
sem1 = new Semaphore(0);
// create two parallel worlds, with separate runtimes.
// both instantiate the same shared map
//
final Thread thread1 = new Thread( () -> {
CorfuRuntime myruntime = new CorfuRuntime(getDefaultEndpoint());
myruntime.connect();
ISMRMap<Integer, Integer> mymap =
myruntime.getObjectsView()
.build()
.setStreamName("nonidepmpotentmaptest") // stream name
.setTypeToken(new TypeToken<SMRMap<Integer, Integer>>() {}) // object TokenType class
.open() ;
assertThat(mymap.get("world1"))
.isEqualTo(null);
for (int t = 0; t < nTXs; t++) {
myruntime.getObjectsView().TXBegin();
mymap.put(nTXs+t, t);
myruntime.getObjectsView().TXEnd();
}
// expect to see nTXS entries in this map
assertThat(mymap.size())
.isEqualTo(nTXs);
// now allow for thread0 to commit its own transaction
sem0.release();
// next, wait for the commit to have completed
try {
sem1.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
// expect to (still) see nTXS entries in this map
assertThat(mymap.size())
.isEqualTo(nTXs);
} );
final Thread thread0 = new Thread( () -> {
CorfuRuntime myruntime = new CorfuRuntime(getDefaultEndpoint());
myruntime.connect();
SMRMap<Integer, Integer> mymap =
myruntime.getObjectsView()
.build()
.setStreamName("nonidepmpotentmaptest") // stream name
.setTypeToken(new TypeToken<SMRMap<Integer, Integer>>() {}) // object TokenType class
.open();
// start a transaction and then hand over to thread 1
myruntime.getObjectsView().TXBegin();
assertThat(mymap.get(nTXs))
.isEqualTo(null);
mymap.put(0, mymap.size());
// enable thread1: it will do nTXS increments on the stream
thread1.start();
boolean isAbort = false;
try {
// wait for thread 1 to do its work;
// completion is indicated thru sem0
sem0.acquire();
myruntime.getObjectsView().TXEnd();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (TransactionAbortedException te) {
isAbort = true;
}
// release thread1 to complete
sem1.release();
// expect to abort
assertThat(isAbort)
.isTrue();
// expect to see nTXs entries on the stream
assertThat(mymap.size())
.isEqualTo(nTXs);
});
thread0.start();
final long WAIT_TIME = 10000;
try {
thread0.join(WAIT_TIME);
thread1.join(WAIT_TIME);
} catch (InterruptedException ie) {
throw new RuntimeException();
}
}
@Test
public void staggeredTXsNoConflict() throws Exception {
final int nTXs = 5;
final Semaphore sem0 = new Semaphore(0),
sem1 = new Semaphore(0);
// create two parallel worlds, with separate runtimes.
// both instantiate the same shared map
//
final Thread thread1 = new Thread( () -> {
CorfuRuntime myruntime = new CorfuRuntime(getDefaultEndpoint());
myruntime.connect();
ISMRMap<Integer, Integer> mymap =
myruntime.getObjectsView()
.build()
.setStreamName("nonidepmpotentmaptest") // stream name
.setTypeToken(new TypeToken<SMRMap<Integer, Integer>>() {}) // object TokenType class
.open() ;
assertThat(mymap.get("world1"))
.isEqualTo(null);
for (int t = 0; t < nTXs; t++) {
myruntime.getObjectsView().TXBegin();
mymap.put(nTXs+t, t);
myruntime.getObjectsView().TXEnd();
}
// expect to see nTXS entries in this map
assertThat(mymap.size())
.isEqualTo(nTXs);
// now allow for thread0 to commit its own transaction
sem0.release();
// next, wait for the commit to have completed
try {
sem1.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
// expect to (still) see nTXS+1 entries in this map
assertThat(mymap.size())
.isEqualTo(nTXs+1);
assertThat(mymap.get(0))
.isEqualTo(0);
} );
final Thread thread0 = new Thread( () -> {
CorfuRuntime myruntime = new CorfuRuntime(getDefaultEndpoint()) ;
myruntime.connect();
ISMRMap<Integer, Integer> mymap =
myruntime.getObjectsView()
.build()
.setStreamName("nonidepmpotentmaptest") // stream name
.setTypeToken(new TypeToken<SMRMap<Integer, Integer>>() {}) // object TokenType class
.open();
// start a transaction and then hand over to thread 1
myruntime.getObjectsView().TXBegin();
assertThat(mymap.get(0))
.isEqualTo(null);
mymap.computeIfAbsent(0, (K) -> {
// should be computed deterministically, does it?
return mymap.size();
});
// enable thread1: it will do nTXS increments on the stream
thread1.start();
boolean isAbort = false;
try {
// wait for thread 1 to do its work;
// completion is indicated thru sem0
sem0.acquire();
myruntime.getObjectsView().TXEnd();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (TransactionAbortedException te) {
isAbort = true;
}
// release thread1 to complete
sem1.release();
// expect to abort
assertThat(isAbort)
.isFalse();
// this currently fails, due to incorrect sync on commit in VersionLockedObject
// expect to see nTXs+1 entries on the stream
assertThat(mymap.size())
.isEqualTo(nTXs+1);
assertThat(mymap.get(0))
.isEqualTo(0);
});
thread0.start();
final long WAIT_TIME = 10000;
try {
thread0.join(WAIT_TIME);
thread1.join(WAIT_TIME);
} catch (InterruptedException ie) {
throw new RuntimeException();
}
}
}