package org.multiverse.stms.gamma.transactions.fat;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.multiverse.SomeUncheckedException;
import org.multiverse.api.LockMode;
import org.multiverse.api.exceptions.AbortOnlyException;
import org.multiverse.api.exceptions.DeadTxnException;
import org.multiverse.api.exceptions.ReadWriteConflict;
import org.multiverse.api.functions.Functions;
import org.multiverse.api.functions.LongFunction;
import org.multiverse.api.lifecycle.TxnEvent;
import org.multiverse.api.lifecycle.TxnListener;
import org.multiverse.stms.gamma.GammaConstants;
import org.multiverse.stms.gamma.GammaStm;
import org.multiverse.stms.gamma.transactionalobjects.GammaTxnLong;
import org.multiverse.stms.gamma.transactionalobjects.Tranlocal;
import org.multiverse.stms.gamma.transactions.GammaTxn;
import org.multiverse.stms.gamma.transactions.GammaTxnConfig;
import static org.junit.Assert.*;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.*;
import static org.multiverse.TestUtils.*;
import static org.multiverse.stms.gamma.GammaTestUtils.*;
public abstract class FatGammaTxn_prepareTest<T extends GammaTxn> implements GammaConstants {
public GammaStm stm;
@Before
public void setUp() {
stm = new GammaStm();
}
protected abstract T newTransaction();
protected abstract T newTransaction(GammaTxnConfig config);
@Test
public void listener_whenNormalListenerAvailable() {
T tx = newTransaction();
TxnListener listener = mock(TxnListener.class);
tx.register(listener);
tx.prepare();
assertIsPrepared(tx);
//verify(listener).notify(tx, TxnEvent.PrePrepare);
verify(listener).notify(tx, TxnEvent.PrePrepare);
}
@Test
public void listener_whenPermanentListenerAvailable() {
TxnListener listener = mock(TxnListener.class);
GammaTxnConfig config = new GammaTxnConfig(stm)
.addPermanentListener(listener);
T tx = newTransaction(config);
tx.prepare();
assertIsPrepared(tx);
//verify(listener).notify(tx, TxnEvent.PrePrepare);
verify(listener).notify(tx, TxnEvent.PrePrepare);
}
@Test
public void conflict_whenArriveByOther() {
long initialValue = 10;
GammaTxnLong ref = new GammaTxnLong(stm, initialValue);
long initialVersion = ref.getVersion();
T tx = newTransaction();
long newValue = 1;
ref.set(tx, newValue);
GammaTxnConfig config = new GammaTxnConfig(stm)
.setMaximumPoorMansConflictScanLength(0);
FatVariableLengthGammaTxn otherTx = new FatVariableLengthGammaTxn(config);
ref.get(otherTx);
long globalConflictCount = stm.globalConflictCounter.count();
tx.prepare();
assertIsPrepared(tx);
assertTrue(tx.commitConflict);
assertGlobalConflictCount(stm, globalConflictCount);
assertVersionAndValue(ref, initialVersion, initialValue);
assertLockMode(ref, LOCKMODE_EXCLUSIVE);
assertSurplus(ref, 2);
}
@Test
public void whenAbortOnly() {
long globalConflictCount = stm.globalConflictCounter.count();
T tx = newTransaction();
tx.setAbortOnly();
try {
tx.prepare();
fail();
} catch (AbortOnlyException expected) {
}
assertIsAborted(tx);
assertGlobalConflictCount(stm, globalConflictCount);
}
@Test
public void whenContainsRead() {
whenContainsRead(LockMode.None);
whenContainsRead(LockMode.Read);
whenContainsRead(LockMode.Write);
whenContainsRead(LockMode.Exclusive);
}
public void whenContainsRead(LockMode readLockMode) {
long globalConflictCount = stm.globalConflictCounter.count();
long initialValue = 10;
GammaTxnLong ref = new GammaTxnLong(stm, initialValue);
long initialVersion = ref.getVersion();
GammaTxn tx = stm.newDefaultTxn();
ref.openForRead(tx, readLockMode.asInt());
tx.prepare();
assertIsPrepared(tx);
assertFalse(tx.commitConflict);
assertLockMode(ref, readLockMode);
assertVersionAndValue(ref, initialVersion, initialValue);
assertGlobalConflictCount(stm, globalConflictCount);
}
@Test
@Ignore
public void writeSkew() {
}
@Test
public void whenContainsCommute() {
int initialValue = 10;
GammaTxnLong ref = new GammaTxnLong(stm, initialValue);
long initialVersion = ref.getVersion();
long globalConflictCount = stm.globalConflictCounter.count();
LongFunction function = Functions.incLongFunction();
GammaTxn tx = newTransaction();
ref.commute(tx, function);
Tranlocal tranlocal = tx.locate(ref);
tx.prepare();
assertIsPrepared(tx);
assertRefHasExclusiveLock(ref, tx);
assertTrue(tranlocal.isDirty);
assertEquals(LOCKMODE_EXCLUSIVE, tranlocal.lockMode);
assertEquals(initialValue + 1, tranlocal.long_value);
assertTrue(tranlocal.hasDepartObligation);
assertGlobalConflictCount(stm, globalConflictCount);
assertVersionAndValue(ref, initialVersion, initialValue);
}
@Test
public void whenContainsMultipleCommutes() {
int initialValue = 10;
GammaTxnLong ref = new GammaTxnLong(stm, initialValue);
long initialVersion = ref.getVersion();
long globalConflictCount = stm.globalConflictCounter.count();
LongFunction function1 = Functions.incLongFunction();
LongFunction function2 = Functions.incLongFunction();
LongFunction function3 = Functions.incLongFunction();
GammaTxn tx = newTransaction();
ref.commute(tx, function1);
ref.commute(tx, function2);
ref.commute(tx, function3);
Tranlocal tranlocal = tx.locate(ref);
tx.prepare();
assertIsPrepared(tx);
assertRefHasExclusiveLock(ref, tx);
assertTrue(tranlocal.isDirty);
assertEquals(LOCKMODE_EXCLUSIVE, tranlocal.lockMode);
assertEquals(initialValue + 3, tranlocal.long_value);
assertTrue(tranlocal.hasDepartObligation);
assertGlobalConflictCount(stm, globalConflictCount);
assertVersionAndValue(ref, initialVersion, initialValue);
}
@Test
public void whenContainsCommuteThatCausesProblems() {
int initialValue = 10;
GammaTxnLong ref = new GammaTxnLong(stm, initialValue);
long initialVersion = ref.getVersion();
long globalConflictCount = stm.globalConflictCounter.count();
LongFunction function = mock(LongFunction.class);
when(function.call(anyLong())).thenThrow(new SomeUncheckedException());
GammaTxn tx = newTransaction();
ref.commute(tx, function);
Tranlocal tranlocal = tx.locate(ref);
try {
tx.prepare();
fail();
} catch (SomeUncheckedException expected) {
}
assertIsAborted(tx);
assertRefHasNoLocks(ref);
assertEquals(LOCKMODE_NONE, tranlocal.lockMode);
assertGlobalConflictCount(stm, globalConflictCount);
assertVersionAndValue(ref, initialVersion, initialValue);
}
@Test
public void whenContainsCommuteThatIsLocked() {
whenContainsCommuteThatIsLocked(LockMode.Read);
whenContainsCommuteThatIsLocked(LockMode.Write);
whenContainsCommuteThatIsLocked(LockMode.Exclusive);
}
public void whenContainsCommuteThatIsLocked(LockMode lockMode) {
int initialValue = 10;
GammaTxnLong ref = new GammaTxnLong(stm, initialValue);
long initialVersion = ref.getVersion();
long globalConflictCount = stm.globalConflictCounter.count();
LongFunction function = mock(LongFunction.class);
when(function.call(anyLong())).thenThrow(new SomeUncheckedException());
GammaTxn tx = newTransaction();
ref.commute(tx, function);
Tranlocal tranlocal = tx.locate(ref);
GammaTxn otherTx = newTransaction();
ref.getLock().acquire(otherTx, lockMode);
try {
tx.prepare();
fail();
} catch (ReadWriteConflict expected) {
}
assertIsAborted(tx);
assertRefHasLockMode(ref, otherTx, lockMode.asInt());
assertEquals(LOCKMODE_NONE, tranlocal.lockMode);
assertGlobalConflictCount(stm, globalConflictCount);
assertVersionAndValue(ref, initialVersion, initialValue);
}
@Test
public void whenContainsConstructed() {
long globalConflictCount = stm.globalConflictCounter.count();
GammaTxn tx = newTransaction();
int initialValue = 10;
GammaTxnLong ref = new GammaTxnLong(tx, initialValue);
Tranlocal tranlocal = tx.locate(ref);
tx.prepare();
assertIsPrepared(tx);
assertRefHasExclusiveLock(ref, tx);
assertTrue(tranlocal.isDirty);
assertEquals(LOCKMODE_EXCLUSIVE, tranlocal.lockMode);
assertTrue(tranlocal.hasDepartObligation);
assertGlobalConflictCount(stm, globalConflictCount);
assertVersionAndValue(ref, GammaConstants.VERSION_UNCOMMITTED, 0);
}
// =============================== dirty check =================================
@Test
public void dirtyCheckDisabled_whenNotDirty_thenLockedForCommit() {
dirtyCheckDisabled_whenNotDirty_thenLockedForCommit(LockMode.None);
dirtyCheckDisabled_whenNotDirty_thenLockedForCommit(LockMode.Read);
dirtyCheckDisabled_whenNotDirty_thenLockedForCommit(LockMode.Write);
dirtyCheckDisabled_whenNotDirty_thenLockedForCommit(LockMode.None);
}
public void dirtyCheckDisabled_whenNotDirty_thenLockedForCommit(LockMode writeLockMode) {
long globalConflictCount = stm.globalConflictCounter.count();
long initialValue = 10;
GammaTxnLong ref = new GammaTxnLong(stm, initialValue);
long initialVersion = ref.getVersion();
GammaTxnConfig config = new GammaTxnConfig(stm)
.setDirtyCheckEnabled(false);
GammaTxn tx = newTransaction(config);
Tranlocal tranlocal = ref.openForWrite(tx, writeLockMode.asInt());
tx.prepare();
assertIsPrepared(tx);
assertFalse(tx.commitConflict);
assertTrue(tranlocal.isDirty());
assertEquals(LockMode.Exclusive.asInt(), tranlocal.getLockMode());
assertLockMode(ref, LockMode.Exclusive);
assertVersionAndValue(ref, initialVersion, initialValue);
assertGlobalConflictCount(stm, globalConflictCount);
}
@Test
public void dirtyCheckDisabled_whenDirty_thenLockedForCommit() {
dirtyCheckDisabled_whenDirty_thenLockedForCommit(LockMode.None);
dirtyCheckDisabled_whenDirty_thenLockedForCommit(LockMode.Read);
dirtyCheckDisabled_whenDirty_thenLockedForCommit(LockMode.Write);
dirtyCheckDisabled_whenDirty_thenLockedForCommit(LockMode.None);
}
public void dirtyCheckDisabled_whenDirty_thenLockedForCommit(LockMode writeLockMode) {
long globalConflictCount = stm.globalConflictCounter.count();
long initialValue = 10;
GammaTxnLong ref = new GammaTxnLong(stm, initialValue);
long initialVersion = ref.getVersion();
GammaTxnConfig config = new GammaTxnConfig(stm)
.setDirtyCheckEnabled(false);
GammaTxn tx = newTransaction(config);
Tranlocal tranlocal = ref.openForWrite(tx, writeLockMode.asInt());
tranlocal.long_value++;
tx.prepare();
assertIsPrepared(tx);
assertFalse(tx.commitConflict);
assertTrue(tranlocal.isDirty());
assertEquals(LockMode.Exclusive.asInt(), tranlocal.getLockMode());
assertLockMode(ref, LockMode.Exclusive);
assertVersionAndValue(ref, initialVersion, initialValue);
assertGlobalConflictCount(stm, globalConflictCount);
}
@Test
public void dirtyCheckEnabled_whenNotDirty_thenNothingHappens() {
dirtyCheckDisabled_whenNotDirty_thenLockedForCommit(LockMode.None);
dirtyCheckDisabled_whenNotDirty_thenLockedForCommit(LockMode.Read);
dirtyCheckDisabled_whenNotDirty_thenLockedForCommit(LockMode.Write);
dirtyCheckDisabled_whenNotDirty_thenLockedForCommit(LockMode.Exclusive);
}
public void dirtyCheckEnabled_whenNotDirty_nothingHappens(LockMode writeLockMode) {
long globalConflictCount = stm.globalConflictCounter.count();
long initialValue = 10;
GammaTxnLong ref = new GammaTxnLong(stm, initialValue);
long initialVersion = ref.getVersion();
GammaTxnConfig config = new GammaTxnConfig(stm)
.setDirtyCheckEnabled(true);
GammaTxn tx = newTransaction(config);
Tranlocal tranlocal = ref.openForWrite(tx, writeLockMode.asInt());
tx.prepare();
assertIsPrepared(tx);
assertFalse(tx.commitConflict);
assertFalse(tranlocal.isDirty());
assertEquals(writeLockMode.asInt(), tranlocal.getLockMode());
assertLockMode(ref, writeLockMode);
assertVersionAndValue(ref, initialVersion, initialValue);
assertGlobalConflictCount(stm, globalConflictCount);
}
@Test
public void dirtyCheckEnabled_whenDirty_thenLockedForCommit() {
dirtyCheckEnabled_whenDirty_thenLockedForCommit(LockMode.None);
dirtyCheckEnabled_whenDirty_thenLockedForCommit(LockMode.Read);
dirtyCheckEnabled_whenDirty_thenLockedForCommit(LockMode.Write);
dirtyCheckEnabled_whenDirty_thenLockedForCommit(LockMode.Exclusive);
}
public void dirtyCheckEnabled_whenDirty_thenLockedForCommit(LockMode writeLockMode) {
long globalConflictCount = stm.globalConflictCounter.count();
long initialValue = 10;
GammaTxnLong ref = new GammaTxnLong(stm, initialValue);
long initialVersion = ref.getVersion();
GammaTxnConfig config = new GammaTxnConfig(stm)
.setDirtyCheckEnabled(true);
GammaTxn tx = newTransaction(config);
Tranlocal tranlocal = ref.openForWrite(tx, writeLockMode.asInt());
tranlocal.long_value++;
tx.prepare();
assertIsPrepared(tx);
assertFalse(tx.commitConflict);
assertTrue(tranlocal.isDirty());
assertEquals(LockMode.Exclusive.asInt(), tranlocal.getLockMode());
assertLockMode(ref, LockMode.Exclusive);
assertVersionAndValue(ref, initialVersion, initialValue);
assertGlobalConflictCount(stm, globalConflictCount);
}
// =============================== locked by other =============================
@Test
public void conflict_dirty_whenReadLockedByOther() {
long globalConflictCount = stm.globalConflictCounter.count();
long initialValue = 10;
GammaTxnLong ref = new GammaTxnLong(stm, initialValue);
long initialVersion = ref.getVersion();
T tx = newTransaction();
Tranlocal tranlocal = ref.openForWrite(tx, LOCKMODE_NONE);
tranlocal.long_value++;
T otherTx = newTransaction();
ref.openForRead(otherTx, LOCKMODE_READ);
try {
tx.prepare();
fail();
} catch (ReadWriteConflict expected) {
}
assertIsAborted(tx);
assertVersionAndValue(ref, initialVersion, initialValue);
assertRefHasReadLock(ref, otherTx);
assertReadLockCount(ref, 1);
assertGlobalConflictCount(stm, globalConflictCount);
}
@Test
public void conflict_dirty_whenWriteLockedByOther() {
long globalConflictCount = stm.globalConflictCounter.count();
long initialValue = 10;
GammaTxnLong ref = new GammaTxnLong(stm, initialValue);
long initialVersion = ref.getVersion();
T tx = newTransaction();
Tranlocal tranlocal = ref.openForWrite(tx, LOCKMODE_NONE);
tranlocal.long_value++;
T otherTx = newTransaction();
ref.openForRead(otherTx, LOCKMODE_WRITE);
try {
tx.prepare();
fail();
} catch (ReadWriteConflict expected) {
}
assertIsAborted(tx);
assertVersionAndValue(ref, initialVersion, initialValue);
assertRefHasWriteLock(ref, otherTx);
assertGlobalConflictCount(stm, globalConflictCount);
}
@Test
public void conflict_dirty_whenExclusiveLockedByOther() {
long globalConflictCount = stm.globalConflictCounter.count();
long initialValue = 10;
GammaTxnLong ref = new GammaTxnLong(stm, initialValue);
long initialVersion = ref.getVersion();
T tx = newTransaction();
Tranlocal tranlocal = ref.openForWrite(tx, LOCKMODE_NONE);
tranlocal.long_value++;
T otherTx = newTransaction();
ref.openForRead(otherTx, LOCKMODE_EXCLUSIVE);
try {
tx.prepare();
fail();
} catch (ReadWriteConflict expected) {
}
assertIsAborted(tx);
assertVersionAndValue(ref, initialVersion, initialValue);
assertRefHasExclusiveLock(ref, otherTx);
assertGlobalConflictCount(stm, globalConflictCount);
}
// ================================ states =====================================
@Test
public void whenPreparedAndUnused() {
long globalConflictCount = stm.globalConflictCounter.count();
T tx = newTransaction();
tx.prepare();
tx.prepare();
assertIsPrepared(tx);
assertFalse(tx.commitConflict);
assertGlobalConflictCount(stm, globalConflictCount);
}
@Test
public void whenAlreadyAborted_thenDeadTxnException() {
long globalConflictCount = stm.globalConflictCounter.count();
T tx = newTransaction();
tx.abort();
try {
tx.prepare();
fail();
} catch (DeadTxnException expected) {
}
assertIsAborted(tx);
assertGlobalConflictCount(stm, globalConflictCount);
}
@Test
public void whenAlreadyCommitted_thenDeadTxnException() {
long globalConflictCount = stm.globalConflictCounter.count();
T tx = newTransaction();
tx.commit();
try {
tx.prepare();
fail();
} catch (DeadTxnException expected) {
}
assertIsCommitted(tx);
assertGlobalConflictCount(stm, globalConflictCount);
}
}