package org.corfudb.runtime.concurrent;
import lombok.extern.slf4j.Slf4j;
import org.corfudb.runtime.collections.SMRMap;
import org.corfudb.runtime.object.transactions.AbstractTransactionsTest;
import org.junit.Test;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* Created by dalia on 3/18/17.
*/
@Slf4j
public class MapsAsMQsTest extends AbstractTransactionsTest {
@Override
public void TXBegin() { OptimisticTXBegin(); }
protected int numIterations = PARAMETERS.NUM_ITERATIONS_MODERATE;
/**
* This test verifies commit atomicity against concurrent -read- activity,
* which constantly causes rollbacks and optimistic-rollbacks.
*
* @throws Exception
*/
@Test
public void useMapsAsMQs() throws Exception {
String mapName1 = "testMapA";
Map<Long, Long> testMap1 = instantiateCorfuObject(SMRMap.class, mapName1);
final int nThreads = 4;
CountDownLatch barrier = new CountDownLatch(nThreads-1);
ReentrantLock lock = new ReentrantLock();
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
// 1st thread: producer of new "trigger" values
scheduleConcurrently(t -> {
// wait for other threads to start
barrier.await();
log.debug("all started");
for (int i = 0; i < numIterations; i++) {
try {
lock.lock();
// place a value in the map
log.debug("- sending 1st trigger " + i);
testMap1.put(1L, (long) i);
// await for the consumer condition to circulate back
c2.await();
log.debug("- sending 2nd trigger " + i);
} finally {
//lock.unlock();
}
}
});
// 2nd thread: monitor map and wait for "trigger" values to show up, produce 1st signal
scheduleConcurrently(t -> {
// signal start
barrier.countDown();
int busyDelay = 1; // millisecs
for (int i = 0; i < numIterations; i++) {
while (testMap1.get(1L) == null || testMap1.get(1L) != (long) i) {
log.debug( "- wait for 1st trigger " + i);
Thread.sleep(busyDelay);
}
log.debug( "- received 1st trigger " + i);
// 1st producer signal through lock
try {
lock.lock();
// 1st producer signal
c1.signal();
} finally {
lock.unlock();
}
}
});
// 3rd thread: monitor 1st producer condition and produce a second "trigger"
scheduleConcurrently(t -> {
// signal start
barrier.countDown();
for (int i = 0; i < numIterations; i++) {
try {
TXBegin();
lock.lock();
// wait for 1st producer signal
c1.await();
log.debug( "- received 1st condition " + i);
// produce another tigger value
log.debug( "- sending 2nd trigger " + i);
testMap1.put(2L, (long) i);
TXEnd();
} finally {
lock.unlock();
}
}
});
// 4th thread: monitor map and wait for 2nd "trigger" values to show up, produce second signal
scheduleConcurrently(t -> {
// signal start
barrier.countDown();
int busyDelay = 1; // millisecs
for (int i = 0; i < numIterations; i++) {
while (testMap1.get(2L) == null || testMap1.get(2L) != (long) i)
Thread.sleep(busyDelay);
log.debug( "- received 2nd trigger " + i);
// 2nd producer signal through lock
try {
lock.lock();
// 2nd producer signal
log.debug( "- sending 2nd signal " + i);
c2.signal();
} finally {
lock.unlock();
}
}
});
executeScheduled(nThreads, PARAMETERS.TIMEOUT_LONG);
}
}