package org.multiverse.stms.gamma.integration.isolation.writeskew;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.multiverse.api.IsolationLevel;
import org.multiverse.api.LockMode;
import org.multiverse.api.exceptions.ReadWriteConflict;
import org.multiverse.stms.gamma.GammaStm;
import org.multiverse.stms.gamma.transactionalobjects.GammaTxnLong;
import org.multiverse.stms.gamma.transactions.GammaTxn;
import org.multiverse.stms.gamma.transactions.GammaTxnConfig;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.multiverse.api.GlobalStmInstance.getGlobalStmInstance;
import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn;
public abstract class WriteSkew_AbstractTest<T extends GammaTxn> {
protected GammaStm stm;
@Before
public void setUp() {
clearThreadLocalTxn();
stm = (GammaStm) getGlobalStmInstance();
}
public abstract T newTransaction(GammaTxnConfig config);
@After
public void tearDown() {
clearThreadLocalTxn();
}
@Test
public void whenWriteSkewAllowed_thenNotDetected() {
GammaTxnLong ref1 = new GammaTxnLong(stm);
GammaTxnLong ref2 = new GammaTxnLong(stm);
GammaTxnConfig config = new GammaTxnConfig(stm)
.setSpeculative(false)
.setIsolationLevel(IsolationLevel.Snapshot);
GammaTxn tx = newTransaction(config);
ref1.incrementAndGet(tx, 1);
ref2.get(tx);
ref2.atomicIncrementAndGet(1);
tx.commit();
assertEquals(1, ref1.atomicGet());
}
@Test
public void whenWritesLocked_thenWriteSkewNotDetected() {
whenWritesLocked_thenWriteSkewNotDetected(LockMode.None);
whenWritesLocked_thenWriteSkewNotDetected(LockMode.Read);
whenWritesLocked_thenWriteSkewNotDetected(LockMode.Write);
whenWritesLocked_thenWriteSkewNotDetected(LockMode.Exclusive);
}
public void whenWritesLocked_thenWriteSkewNotDetected(LockMode writeLockMode) {
GammaTxnLong ref1 = new GammaTxnLong(stm);
GammaTxnLong ref2 = new GammaTxnLong(stm);
GammaTxnConfig config = new GammaTxnConfig(stm)
.setSpeculative(false)
.setIsolationLevel(IsolationLevel.Snapshot)
.setWriteLockMode(writeLockMode);
GammaTxn tx = newTransaction(config);
ref1.incrementAndGet(tx, 1);
ref2.get(tx);
ref2.atomicIncrementAndGet(1);
tx.commit();
assertEquals(1, ref1.atomicGet());
}
@Test
public void whenReadsLocked_thenWriteSkewNotPossible() {
whenReadsLocked_thenWriteSkewNotPossible(LockMode.Read);
whenReadsLocked_thenWriteSkewNotPossible(LockMode.Write);
whenReadsLocked_thenWriteSkewNotPossible(LockMode.Exclusive);
}
public void whenReadsLocked_thenWriteSkewNotPossible(LockMode readLockMode) {
GammaTxnLong ref1 = new GammaTxnLong(stm);
GammaTxnLong ref2 = new GammaTxnLong(stm);
GammaTxnConfig config = new GammaTxnConfig(stm)
.setSpeculative(false)
.setIsolationLevel(IsolationLevel.Snapshot)
.setControlFlowErrorsReused(false);
GammaTxn tx = newTransaction(config);
ref1.incrementAndGet(tx, 1);
ref2.get(tx);
GammaTxn otherTx = stm.newDefaultTxn();
ref2.incrementAndGet(otherTx, 1);
ref2.getLock().acquire(tx, readLockMode);
try {
otherTx.commit();
fail();
} catch (ReadWriteConflict ignored) {
}
tx.commit();
}
@Test
public void whenLocked_thenWriteSkewNotPossible() {
whenLocked_thenWriteSkewNotPossible(LockMode.Read);
whenLocked_thenWriteSkewNotPossible(LockMode.Write);
whenLocked_thenWriteSkewNotPossible(LockMode.Exclusive);
}
public void whenLocked_thenWriteSkewNotPossible(LockMode lockMode) {
GammaTxnLong ref1 = new GammaTxnLong(stm);
GammaTxnLong ref2 = new GammaTxnLong(stm);
GammaTxnConfig config = new GammaTxnConfig(stm)
.setSpeculative(false)
.setIsolationLevel(IsolationLevel.Snapshot);
GammaTxn tx = newTransaction(config);
ref1.incrementAndGet(tx, 1);
GammaTxn otherTx = stm.newDefaultTxn();
ref2.incrementAndGet(otherTx, 1);
ref2.getLock().acquire(tx, lockMode);
ref2.get(tx);
try {
otherTx.commit();
fail();
} catch (ReadWriteConflict expected) {
}
tx.commit();
}
@Test
public void whenEnsured_thenWriteSkewNotPossible() {
GammaTxnLong ref1 = new GammaTxnLong(stm);
GammaTxnLong ref2 = new GammaTxnLong(stm);
GammaTxnConfig config = new GammaTxnConfig(stm)
.setSpeculative(false)
.setIsolationLevel(IsolationLevel.Snapshot);
GammaTxn tx = newTransaction(config);
ref1.incrementAndGet(tx, 1);
ref2.ensure(tx);
ref2.atomicIncrementAndGet(1);
try {
tx.commit();
fail();
} catch (ReadWriteConflict expected) {
}
}
@Test
public void whenSerializedIsolationLevel_thenWriteSkewNotPossible() {
GammaTxnLong ref1 = new GammaTxnLong(stm);
GammaTxnLong ref2 = new GammaTxnLong(stm);
GammaTxnConfig config = new GammaTxnConfig(stm)
.setSpeculative(false)
.setIsolationLevel(IsolationLevel.Serializable);
GammaTxn tx = newTransaction(config);
ref1.incrementAndGet(tx, 1);
ref2.get(tx);
ref2.atomicIncrementAndGet(1);
try {
tx.commit();
fail();
} catch (ReadWriteConflict expected) {
}
}
}