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.FatVariableLengthGammaTxn; import static org.junit.Assert.*; import static org.junit.Assume.assumeTrue; import static org.multiverse.TestUtils.assertIsAborted; import static org.multiverse.TestUtils.assertIsCommitted; import static org.multiverse.stms.gamma.GammaTestUtils.*; public abstract class LeanGammaTxn_commitTest<T extends GammaTxn> { public GammaStm stm; @Before public void setUp() { stm = new GammaStm(); } public abstract T newTransaction(); public abstract int getMaximumLength(); public abstract void assertClearedAfterCommit(); public abstract void assertClearedAfterAbort(); @Test public void conflict_whenReadByOther(){ String initialValue = null; 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.commit(); assertGlobalConflictCount(stm, globalConflictCount+1); assertVersionAndValue(ref, initialVersion+1, newValue); } @Test public void whenUnused() { T tx = newTransaction(); long globalConflictCount = stm.globalConflictCounter.count(); tx.commit(); assertIsCommitted(); assertClearedAfterCommit(); assertGlobalConflictCount(stm, globalConflictCount); } @Test public void whenMultipleDirtyWrites() { assumeTrue(getMaximumLength() > 1); long globalConflictCount = stm.globalConflictCounter.count(); String initialValue1 = "foo1"; String updateValue1 = "bar1"; GammaTxnRef<String> ref1 = new GammaTxnRef<String>(stm, initialValue1); long initialVersion1 = ref1.getVersion(); String initialValue2 = "foo2"; String updateValue2 = "bar1"; GammaTxnRef<String> ref2 = new GammaTxnRef<String>(stm, initialValue2); long initialVersion2 = ref2.getVersion(); T tx = newTransaction(); Tranlocal tranlocal1 = ref1.openForWrite(tx, LOCKMODE_NONE); tranlocal1.ref_value = updateValue1; Tranlocal tranlocal2 = ref2.openForWrite(tx, LOCKMODE_NONE); tranlocal2.ref_value = updateValue2; tx.commit(); assertIsCommitted(tx); assertRefHasNoLocks(ref1); assertSurplus(ref1, 0); assertVersionAndValue(ref1, initialVersion1 + 1, updateValue1); assertWriteBiased(ref1); assertNull(tranlocal1.owner); assertNull(tranlocal1.ref_value); assertFalse(tranlocal1.hasDepartObligation); assertRefHasNoLocks(ref2); assertSurplus(ref2, 0); assertVersionAndValue(ref2, initialVersion2 + 1, updateValue2); assertWriteBiased(ref2); assertNull(tranlocal2.owner); assertNull(tranlocal2.ref_value); assertFalse(tranlocal2.hasDepartObligation); assertGlobalConflictCount(stm, globalConflictCount); } @Test public void whenMultipleNonDirtyWrites() { assumeTrue(getMaximumLength() > 1); long globalConflictCount = stm.globalConflictCounter.count(); String initialValue1 = "foo1"; GammaTxnRef<String> ref1 = new GammaTxnRef<String>(stm, initialValue1); long initialVersion1 = ref1.getVersion(); String initialValue2 = "foo2"; GammaTxnRef<String> ref2 = new GammaTxnRef<String>(stm, initialValue2); long initialVersion2 = ref2.getVersion(); T tx = newTransaction(); Tranlocal tranlocal1 = ref1.openForWrite(tx, LOCKMODE_NONE); Tranlocal tranlocal2 = ref2.openForWrite(tx, LOCKMODE_NONE); tx.commit(); assertIsCommitted(tx); assertRefHasNoLocks(ref1); assertSurplus(ref1, 0); assertVersionAndValue(ref1, initialVersion1 + 1, initialValue1); assertWriteBiased(ref1); assertNull(tranlocal1.owner); assertNull(tranlocal1.ref_value); assertFalse(tranlocal1.hasDepartObligation); assertRefHasNoLocks(ref2); assertSurplus(ref2, 0); assertVersionAndValue(ref2, initialVersion2 + 1, initialValue2); assertWriteBiased(ref2); assertNull(tranlocal2.owner); assertNull(tranlocal2.ref_value); assertFalse(tranlocal2.hasDepartObligation); assertGlobalConflictCount(stm, globalConflictCount); } @Test public void whenNonDirtyUpdate() { long globalConflictCount = stm.globalConflictCounter.count(); String initialValue = "foo"; GammaTxnRef<String> ref = new GammaTxnRef<String>(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); Tranlocal tranlocal = ref.openForWrite(tx, LOCKMODE_NONE); tranlocal.ref_value = initialValue; tx.commit(); assertNull(tranlocal.owner); assertNull(tranlocal.ref_value); assertNull(tranlocal.ref_oldValue); assertIsCommitted(tx); assertSurplus(ref, 0); assertReadonlyCount(ref, 0); assertWriteBiased(ref); assertIsCommitted(tx); assertRefHasNoLocks(ref); assertVersionAndValue(ref, initialVersion + 1, initialValue); assertIsCommitted(tx); assertGlobalConflictCount(stm, globalConflictCount); } @Test public void whenDirtyUpdate() { long globalConflictCount = stm.globalConflictCounter.count(); String initialValue = "foo"; String newValue = "bar"; GammaTxnRef<String> ref = new GammaTxnRef<String>(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); Tranlocal tranlocal = ref.openForWrite(tx, LOCKMODE_NONE); tranlocal.ref_value = newValue; tx.commit(); assertNull(tranlocal.owner); assertNull(tranlocal.ref_value); assertNull(tranlocal.ref_oldValue); assertIsCommitted(tx); assertSurplus(ref, 0); assertReadonlyCount(ref, 0); assertWriteBiased(ref); assertIsCommitted(tx); assertRefHasNoLocks(ref); assertVersionAndValue(ref, initialVersion + 1, newValue); assertIsCommitted(tx); assertGlobalConflictCount(stm, globalConflictCount); } @Test public void whenLockedByOtherAndWrite() { whenLockedByOtherAndWrite(LockMode.Read); whenLockedByOtherAndWrite(LockMode.Write); whenLockedByOtherAndWrite(LockMode.Exclusive); } protected void whenLockedByOtherAndWrite(LockMode lockMode) { long globalConflictCount = stm.globalConflictCounter.count(); String initialValue = "foo"; GammaTxnRef<String> ref = new GammaTxnRef<String>(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); Tranlocal tranlocal = ref.openForWrite(tx, LOCKMODE_NONE); GammaTxn otherTx = stm.newDefaultTxn(); ref.getLock().acquire(otherTx, lockMode); try { tx.commit(); fail(); } catch (ReadWriteConflict expected) { } assertIsAborted(tx); assertWriteBiased(ref); assertReadonlyCount(ref, 0); assertSurplus(ref, 1); assertNull(tranlocal.owner); assertNull(tranlocal.ref_value); assertNull(tranlocal.ref_oldValue); assertVersionAndValue(ref, initialVersion, initialValue); assertRefHasLockMode(ref, otherTx, lockMode.asInt()); assertGlobalConflictCount(stm, globalConflictCount); } @Test public void whenNormalRead() { long globalConflictCount = stm.globalConflictCounter.count(); String initialValue = "foo"; GammaTxnRef<String> ref = new GammaTxnRef<String>(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); Tranlocal tranlocal = ref.openForRead(tx, LOCKMODE_NONE); tx.commit(); assertIsCommitted(tx); assertNull(tranlocal.owner); assertNull(tranlocal.ref_value); assertNull(tranlocal.ref_oldValue); assertRefHasNoLocks(ref); assertSurplus(ref, 0); assertReadonlyCount(ref, 0); assertWriteBiased(ref); assertVersionAndValue(ref, initialVersion, initialValue); assertGlobalConflictCount(stm, globalConflictCount); } @Test public void whenAlreadyPreparedAndUnused() { long globalConflictCount = stm.globalConflictCounter.count(); T tx = newTransaction(); tx.prepare(); tx.commit(); assertIsCommitted(tx); assertGlobalConflictCount(stm, globalConflictCount); } @Test public void whenAlreadyCommitted() { long globalConflictCount = stm.globalConflictCounter.count(); String initialValue = "foo"; GammaTxnRef<String> ref = new GammaTxnRef<String>(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); tx.commit(); tx.commit(); assertIsCommitted(tx); assertRefHasNoLocks(ref); assertVersionAndValue(ref, initialVersion, initialValue); assertGlobalConflictCount(stm, globalConflictCount); } @Test public void whenAlreadyAborted() { long globalConflictCount = stm.globalConflictCounter.count(); T tx = newTransaction(); tx.abort(); try { tx.commit(); fail(); } catch (DeadTxnException expected) { } assertIsAborted(tx); assertGlobalConflictCount(stm, globalConflictCount); } }