package org.multiverse.stms.gamma.transactions.lean; import org.junit.Before; import org.junit.Test; import org.multiverse.api.LockMode; import org.multiverse.api.exceptions.DeadTxnException; import org.multiverse.api.exceptions.ReadWriteConflict; import org.multiverse.stms.gamma.GammaStm; import org.multiverse.stms.gamma.transactionalobjects.GammaTxnRef; import org.multiverse.stms.gamma.transactionalobjects.Tranlocal; import org.multiverse.stms.gamma.transactions.GammaTxn; import org.multiverse.stms.gamma.transactions.GammaTxnConfig; import org.multiverse.stms.gamma.transactions.fat.FatMonoGammaTxn; import org.multiverse.stms.gamma.transactions.fat.FatVariableLengthGammaTxn; import static org.junit.Assert.*; import static org.multiverse.TestUtils.*; import static org.multiverse.stms.gamma.GammaTestUtils.LOCKMODE_EXCLUSIVE; import static org.multiverse.stms.gamma.GammaTestUtils.LOCKMODE_NONE; import static org.multiverse.stms.gamma.GammaTestUtils.LOCKMODE_READ; import static org.multiverse.stms.gamma.GammaTestUtils.LOCKMODE_WRITE; import static org.multiverse.stms.gamma.GammaTestUtils.*; public abstract class LeanGammaTxn_prepareTest<T extends GammaTxn> { protected GammaStm stm; @Before public void setUp() { stm = new GammaStm(); } public abstract T newTransaction(); @Test public void conflict_whenArriveByOther() { String initialValue = "foo"; GammaTxnRef<String> ref = new GammaTxnRef<String>(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); String newValue = "bar"; 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 whenContainsRead() { long globalConflictCount = stm.getGlobalConflictCounter().count(); String initialValue = "foo"; GammaTxnRef<String> ref = new GammaTxnRef<String>(stm, initialValue); long initialVersion = ref.getVersion(); GammaTxn tx = stm.newDefaultTxn(); ref.openForRead(tx, LOCKMODE_NONE); tx.prepare(); assertIsPrepared(tx); assertFalse(tx.commitConflict); assertLockMode(ref, LOCKMODE_NONE); assertVersionAndValue(ref, initialVersion, initialValue); assertGlobalConflictCount(stm, globalConflictCount); } public void whenNonDirtyDirty_thenLockedForCommit() { long globalConflictCount = stm.getGlobalConflictCounter().count(); Long initialValue = 10L; GammaTxnRef<Long> ref = new GammaTxnRef<Long>(stm, initialValue); long initialVersion = ref.getVersion(); GammaTxn tx = newTransaction(); Tranlocal tranlocal = ref.openForWrite(tx, LOCKMODE_NONE); tx.prepare(); assertIsPrepared(tx); assertTrue(tranlocal.isDirty()); assertFalse(tx.commitConflict); assertEquals(LockMode.Exclusive.asInt(), tranlocal.getLockMode()); assertLockMode(ref, LockMode.Exclusive); assertVersionAndValue(ref, initialVersion, initialValue); assertGlobalConflictCount(stm, globalConflictCount); } public void whenDirtyDirty_thenLockedForCommit() { long globalConflictCount = stm.getGlobalConflictCounter().count(); Long initialValue = 10L; GammaTxnRef<Long> ref = new GammaTxnRef<Long>(stm, initialValue); long initialVersion = ref.getVersion(); GammaTxn tx = newTransaction(); Tranlocal tranlocal = ref.openForWrite(tx, LOCKMODE_NONE); 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.getGlobalConflictCounter().count(); Long initialValue = 10L; GammaTxnRef<Long> ref = new GammaTxnRef<Long>(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); Tranlocal tranlocal = ref.openForWrite(tx, LOCKMODE_NONE); tranlocal.long_value++; FatMonoGammaTxn otherTx = new FatMonoGammaTxn(stm); 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.getGlobalConflictCounter().count(); Long initialValue = 10L; GammaTxnRef<Long> ref = new GammaTxnRef<Long>(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); Tranlocal tranlocal = ref.openForWrite(tx, LOCKMODE_NONE); tranlocal.long_value++; FatMonoGammaTxn otherTx = new FatMonoGammaTxn(stm); 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.getGlobalConflictCounter().count(); Long initialValue = 10L; GammaTxnRef<Long> ref = new GammaTxnRef<Long>(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); Tranlocal tranlocal = ref.openForWrite(tx, LOCKMODE_NONE); tranlocal.long_value++; FatMonoGammaTxn otherTx = new FatMonoGammaTxn(stm); 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.getGlobalConflictCounter().count(); T tx = newTransaction(); tx.prepare(); tx.prepare(); assertIsPrepared(tx); assertFalse(tx.commitConflict); assertGlobalConflictCount(stm, globalConflictCount); } @Test public void whenAlreadyAborted_thenDeadTxnException() { long globalConflictCount = stm.getGlobalConflictCounter().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.getGlobalConflictCounter().count(); T tx = newTransaction(); tx.commit(); try { tx.prepare(); fail(); } catch (DeadTxnException expected) { } assertIsCommitted(tx); assertGlobalConflictCount(stm, globalConflictCount); } }