package org.corfudb.runtime.object.transactions; import com.google.common.reflect.TypeToken; import org.corfudb.runtime.collections.SMRMap; import org.junit.Test; import java.util.HashSet; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import static org.assertj.core.api.Assertions.assertThat; /** * Created by dmalkhi on 12/13/16. */ public class WriteWriteTXConcurrencyTest extends TXConflictScenariosTest { @Override public void TXBegin() { WWTXBegin(); } @Test public void simpleWWTest() { //Instantiate a Corfu Stream named "A" dedicated to an SMRmap object. SMRMap<String, Integer> map = ( SMRMap<String, Integer>) instantiateCorfuObject( new TypeToken<SMRMap<String, Integer> >() { }, "A" ); AtomicInteger valA = new AtomicInteger(0), valB = new AtomicInteger(0); t(0, () -> WWTXBegin()); t(0, () -> map.put("a", 1)); t(0, () -> map.put("b", 1)); t(1, () -> WWTXBegin()); t(1, () -> { Integer ga = map.get("a"); if (ga != null) valA.set(ga); } ); t(1, () -> { Integer gb = map.get("b"); if (gb != null) valB.set(gb); } ); t(1, () -> TXEnd()); t(0, () -> TXEnd()); assertThat(valA.get()).isEqualTo(valB.get()); } @Test public void testOpacityInterleaved() throws Exception { // invoke the interleaving engine testOpacityWW(true); } @Test public void testOpacityThreaded() throws Exception { // invoke the threaded engine testOpacityWW(false); } public void testOpacityWW(boolean testInterleaved) throws Exception { testOpacity(testInterleaved); // verfiy that all aborts are justified for (int task_num = 0; task_num < numTasks; task_num++) { if (commitStatus.get(task_num) != COMMITVALUE) { assertThat(sharedCounters.get((task_num + 1) % numTasks).getValue()) .isNotEqualTo(snapStatus.get(task_num)); assertThat(commitStatus.get((task_num + 1) % numTasks)) .isEqualTo(COMMITVALUE); } } } /** * If task k aborts, then either task (k-1), or (k+1), or both, must have committed * (wrapping around for tasks n-1 and 0, respectively). */ public void testRWConflictWW(boolean testInterleaved) throws Exception { testRWConflicts(testInterleaved); // verfiy that all aborts are justified for (int task_num = 0; task_num < numTasks; task_num++) { if (commitStatus.get(task_num) != COMMITVALUE) assertThat(commitStatus.get((task_num + 1) % numTasks) == COMMITVALUE || commitStatus.get((task_num - 1) % numTasks) == COMMITVALUE) .isTrue(); } } @Test public void testRWConflictWWInterleaved() throws Exception { testRWConflictWW(true); } @Test public void testRWConflictWWThreaded() throws Exception { testRWConflictWW(false); } @Test public void testNoWriteConflictSimpleWW() throws Exception { testNoWriteConflictSimple(); // both transactions should commit, no WW conflict assertThat(commitStatus.get(0)) .isEqualTo(COMMITVALUE); assertThat(commitStatus.get(1)) .isEqualTo(COMMITVALUE); } /** * This test uses the concurrentAbortTest scenario. * The test invokes numTasks tasks, each one writes exclusively to a map entry, * and reads a few entries arbitrarily at random. * * Unlike optimistic TXs, in write-write conflict mode, there should be --no-- conflicts, * unless the conflict-parameters of different tasks happen to collide in hashCode(). * * @param testInterleaved * @throws Exception */ public void testAbortWW(boolean testInterleaved) throws Exception { concurrentAbortTest(testInterleaved); // calculate how many false abort might happen due to collisions in hashCode() int falseaborts = 0; Set<Integer> falsecollisions = new HashSet<>(); for (int i = 0; i < numTasks; i++ ) if (! falsecollisions.add(Integer.toString(i).hashCode()) ) falseaborts++; int aborts = 0; for (int i = 0; i < numTasks; i++) if (commitStatus.get(i) != COMMITVALUE) aborts++; assertThat(aborts) .isEqualTo(falseaborts); } @Test public void testAbortWWInterleaved() throws Exception { testAbortWW(true); } @Test public void testAbortWWThreaded() throws Exception { testAbortWW(false); } }