package org.multiverse.stms.gamma.transactions.lean; import org.junit.Before; import org.junit.Test; import org.multiverse.api.LockMode; import org.multiverse.api.TxnStatus; import org.multiverse.api.exceptions.DeadTxnException; import org.multiverse.api.exceptions.PreparedTxnException; import org.multiverse.api.exceptions.ReadWriteConflict; import org.multiverse.api.exceptions.SpeculativeConfigurationError; import org.multiverse.stms.gamma.GammaConstants; import org.multiverse.stms.gamma.GammaStm; import org.multiverse.stms.gamma.transactionalobjects.*; import org.multiverse.stms.gamma.transactions.GammaTxn; import static org.junit.Assert.*; import static org.junit.Assume.assumeTrue; import static org.multiverse.TestUtils.*; import static org.multiverse.stms.gamma.GammaTestUtils.*; public abstract class LeanGammaTxn_openForReadTest<T extends GammaTxn> implements GammaConstants { protected GammaStm stm; public abstract T newTransaction(); public abstract int getMaximumLength(); @Before public void setUp() { stm = new GammaStm(); } @Test public void whenMultipleWrites() { assumeTrue(getMaximumLength() > 1); String initialValue1 = "foo1"; GammaTxnRef<String> ref1 = new GammaTxnRef<String>(stm, initialValue1); String initialValue2 = "foo2"; GammaTxnRef<String> ref2 = new GammaTxnRef<String>(stm, initialValue2); GammaTxn tx = newTransaction(); Tranlocal tranlocal1 = ref1.openForRead(tx, LOCKMODE_NONE); Tranlocal tranlocal2 = ref2.openForRead(tx, LOCKMODE_NONE); assertIsActive(tx); assertRefHasNoLocks(ref1); assertSurplus(ref1, 0); assertSame(ref1, tranlocal1.owner); assertEquals(TRANLOCAL_READ, tranlocal1.mode); assertSame(initialValue1, tranlocal1.ref_value); assertRefHasNoLocks(ref2); assertSurplus(ref2, 0); assertSame(ref2, tranlocal2.owner); assertEquals(TRANLOCAL_READ, tranlocal2.mode); assertSame(initialValue2, tranlocal2.ref_value); } @Test public void whenExplicitLocking_thenSpeculativeConfigurationFailure() { whenExplicitLocking(LockMode.Read); whenExplicitLocking(LockMode.Write); whenExplicitLocking(LockMode.Exclusive); } public void whenExplicitLocking(LockMode lockMode) { String initialValue = "foo"; GammaTxnRef<String> ref = new GammaTxnRef<String>(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); try { ref.openForRead(tx, lockMode.asInt()); fail(); } catch (SpeculativeConfigurationError expected) { } assertIsAborted(tx); assertRefHasNoLocks(ref); assertSurplus(ref, 0); assertReadonlyCount(ref, 0); assertWriteBiased(ref); assertVersionAndValue(ref, initialVersion, initialValue); assertTrue(tx.config.speculativeConfiguration.get().locksDetected); } @Test public void whenIntRef_thenSpeculativeConfigurationError() { int initialValue = 10; GammaTxnInteger ref = new GammaTxnInteger(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); try { ref.openForRead(tx, LOCKMODE_NONE); fail(); } catch (SpeculativeConfigurationError expected) { } assertIsAborted(tx); assertRefHasNoLocks(ref); assertVersionAndValue(ref, initialVersion, initialValue); assertSpeculativeConfigurationNonRefTypeRequired(tx); } @Test public void whenBooleanRef_thenSpeculativeConfigurationError() { boolean initialValue = true; GammaTxnBoolean ref = new GammaTxnBoolean(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); try { ref.openForRead(tx, LOCKMODE_NONE); fail(); } catch (SpeculativeConfigurationError expected) { } assertIsAborted(tx); assertRefHasNoLocks(ref); assertVersionAndValue(ref, initialVersion, initialValue); assertSpeculativeConfigurationNonRefTypeRequired(tx); } @Test public void whenTxnDouble_thenSpeculativeConfigurationError() { double initialValue = 10; GammaTxnDouble ref = new GammaTxnDouble(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); try { ref.openForRead(tx, LOCKMODE_NONE); fail(); } catch (SpeculativeConfigurationError expected) { } assertIsAborted(tx); assertRefHasNoLocks(ref); assertVersionAndValue(ref, initialVersion, initialValue); assertSpeculativeConfigurationNonRefTypeRequired(tx); } @Test public void whenLongRef_thenSpeculativeConfigurationError() { long initialValue = 10; GammaTxnLong ref = new GammaTxnLong(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); try { ref.openForRead(tx, LOCKMODE_NONE); fail(); } catch (SpeculativeConfigurationError expected) { } assertIsAborted(tx); assertRefHasNoLocks(ref); assertVersionAndValue(ref, initialVersion, initialValue); assertSpeculativeConfigurationNonRefTypeRequired(tx); } @Test public void whenExclusiveLockObtainedByOthers() { String initialValue = "foo"; GammaTxnRef<String> ref = new GammaTxnRef<String>(stm, initialValue); long initialVersion = ref.getVersion(); GammaTxn otherTx = stm.newDefaultTxn(); ref.getLock().acquire(otherTx, LockMode.Exclusive); T tx = newTransaction(); try { ref.openForRead(tx, LOCKMODE_NONE); fail(); } catch (ReadWriteConflict expected) { } assertIsAborted(tx); assertRefHasExclusiveLock(ref, otherTx); assertSurplus(ref, 1); assertWriteBiased(ref); assertReadonlyCount(ref, 0); assertVersionAndValue(ref, initialVersion, initialValue); } @Test public void whenNonExclusiveLockAcquiredByOthers() { whenNonExclusiveLockAcquiredByOthers(LockMode.Read); whenNonExclusiveLockAcquiredByOthers(LockMode.Write); } public void whenNonExclusiveLockAcquiredByOthers(LockMode lockMode) { String initialValue = "foo"; GammaTxnRef<String> ref = new GammaTxnRef<String>(stm, initialValue); long initialVersion = ref.getVersion(); GammaTxn otherTx = stm.newDefaultTxn(); ref.getLock().acquire(otherTx, lockMode); T tx = newTransaction(); Tranlocal tranlocal = ref.openForRead(tx, LOCKMODE_NONE); assertIsActive(tx); assertNotNull(tranlocal); assertSame(ref, tranlocal.owner); assertSame(initialValue, tranlocal.ref_value); assertNull(tranlocal.ref_oldValue); assertEquals(LOCKMODE_NONE, tranlocal.lockMode); assertEquals(TRANLOCAL_READ, tranlocal.mode); assertFalse(tranlocal.hasDepartObligation); assertEquals(initialVersion, tranlocal.version); assertRefHasLockMode(ref, otherTx, lockMode.asInt()); assertSurplus(ref, 1); assertWriteBiased(ref); assertReadonlyCount(ref, 0); assertVersionAndValue(ref, initialVersion, initialValue); } @Test public void whenNotOpenedBefore() { String initialValue = "foo"; GammaTxnRef<String> ref = new GammaTxnRef<String>(stm, initialValue); long initialVersion = ref.getVersion(); GammaTxn tx = newTransaction(); Tranlocal tranlocal = ref.openForRead(tx, LOCKMODE_NONE); assertNotNull(tranlocal); assertSame(ref, tranlocal.owner); assertEquals(LOCKMODE_NONE, tranlocal.lockMode); assertEquals(TRANLOCAL_READ, tranlocal.mode); assertSame(initialValue, tranlocal.ref_value); assertNull(tranlocal.ref_oldValue); assertEquals(initialVersion, tranlocal.version); assertRefHasNoLocks(ref); assertVersionAndValue(ref, initialVersion, initialValue); } @Test public void whenAlreadyOpenedForRead() { String initialValue = "foo"; GammaTxnRef<String> ref = new GammaTxnRef<String>(stm, initialValue); long initialVersion = ref.getVersion(); GammaTxn tx = newTransaction(); Tranlocal first = ref.openForRead(tx, LOCKMODE_NONE); Tranlocal tranlocal = ref.openForRead(tx, LOCKMODE_NONE); assertSame(first, tranlocal); assertNotNull(tranlocal); assertSame(ref, tranlocal.owner); assertEquals(LOCKMODE_NONE, tranlocal.lockMode); assertEquals(TRANLOCAL_READ, tranlocal.mode); assertSame(initialValue, tranlocal.ref_value); assertNull(tranlocal.ref_oldValue); assertEquals(initialVersion, tranlocal.version); assertRefHasNoLocks(ref); assertVersionAndValue(ref, initialVersion, initialValue); } @Test public void whenMultipleReadsAndConsistent() { assumeTrue(getMaximumLength() > 1); GammaTxnRef<String> ref1 = new GammaTxnRef<String>(stm); GammaTxnRef<String> ref2 = new GammaTxnRef<String>(stm); GammaTxn tx = newTransaction(); ref1.openForRead(tx, LOCKMODE_NONE); ref2.openForRead(tx, LOCKMODE_NONE); tx.commit(); assertIsCommitted(tx); assertRefHasNoLocks(ref1); assertSurplus(ref1, 0); assertRefHasNoLocks(ref2); assertSurplus(ref2, 0); } @Test public void whenMultipleReadsAndInconsistent() { assumeTrue(getMaximumLength() > 1); GammaTxnRef<String> ref1 = new GammaTxnRef<String>(stm); GammaTxnRef<String> ref2 = new GammaTxnRef<String>(stm); GammaTxn tx = newTransaction(); ref1.openForRead(tx, LOCKMODE_NONE); ref1.atomicSet("foo"); try { ref2.openForRead(tx, LOCKMODE_NONE); fail(); } catch (ReadWriteConflict expected) { } assertIsAborted(tx); assertRefHasNoLocks(ref1); assertSurplus(ref1, 0); assertRefHasNoLocks(ref2); assertSurplus(ref2, 0); } @Test public void whenMultipleReadsAndPreviousReadLockedExclusively() { assumeTrue(getMaximumLength() > 1); GammaTxnRef<String> ref1 = new GammaTxnRef<String>(stm); GammaTxnRef<String> ref2 = new GammaTxnRef<String>(stm); GammaTxn tx = newTransaction(); ref1.openForRead(tx, LOCKMODE_NONE); GammaTxn otherTx = stm.newDefaultTxn(); ref1.getLock().acquire(otherTx, LockMode.Exclusive); try { ref2.openForRead(tx, LOCKMODE_NONE); fail(); } catch (ReadWriteConflict expected) { } assertIsAborted(tx); assertRefHasExclusiveLock(ref1, otherTx); assertSurplus(ref1, 1); assertRefHasNoLocks(ref2); assertSurplus(ref2, 0); } @Test public void whenAlreadyOpenedForWrite() { String initialValue = "foo"; GammaTxnRef<String> ref = new GammaTxnRef<String>(stm, initialValue); long initialVersion = ref.getVersion(); GammaTxn tx = newTransaction(); Tranlocal first = ref.openForWrite(tx, LOCKMODE_NONE); Tranlocal tranlocal = ref.openForRead(tx, LOCKMODE_NONE); assertSame(first, tranlocal); assertNotNull(tranlocal); assertSame(ref, tranlocal.owner); assertEquals(LOCKMODE_NONE, tranlocal.lockMode); assertEquals(TRANLOCAL_WRITE, tranlocal.mode); assertSame(initialValue, tranlocal.ref_value); assertNull(tranlocal.ref_oldValue); assertEquals(initialVersion, tranlocal.version); assertRefHasNoLocks(ref); assertVersionAndValue(ref, initialVersion, initialValue); } @Test public void whenAlreadyPreparedAndunused() { String initialValue = "foo"; GammaTxnRef<String> ref = new GammaTxnRef<String>(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); tx.prepare(); try { ref.openForRead(tx, LOCKMODE_NONE); fail(); } catch (PreparedTxnException expected) { } assertIsAborted(tx); assertRefHasNoLocks(ref); assertIsAborted(tx); assertVersionAndValue(ref, initialVersion, initialValue); } @Test public void whenAlreadyCommitted() { String initialValue = "foo"; GammaTxnRef<String> ref = new GammaTxnRef<String>(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); tx.commit(); try { ref.openForRead(tx, LOCKMODE_NONE); fail(); } catch (DeadTxnException expected) { } assertIsCommitted(tx); assertRefHasNoLocks(ref); assertVersionAndValue(ref, initialVersion, initialValue); } @Test public void whenAlreadyAborted() { String initialValue = "foo"; GammaTxnRef<String> ref = new GammaTxnRef<String>(stm, initialValue); long initialVersion = ref.getVersion(); T tx = newTransaction(); tx.abort(); try { ref.openForRead(tx, LOCKMODE_NONE); fail(); } catch (DeadTxnException expected) { } assertIsAborted(tx); assertRefHasNoLocks(ref); assertIsAborted(tx); assertVersionAndValue(ref, initialVersion, initialValue); } @Test public void whenOverflowing() { int maxCapacity = getMaximumLength(); assumeTrue(maxCapacity < Integer.MAX_VALUE); GammaTxn tx = newTransaction(); for (int k = 0; k < maxCapacity; k++) { GammaTxnRef ref = new GammaTxnRef(stm, 0); ref.openForRead(tx, LOCKMODE_NONE); } GammaTxnRef ref = new GammaTxnRef(stm, 0); try { ref.openForRead(tx, LOCKMODE_NONE); fail(); } catch (SpeculativeConfigurationError expected) { } assertEquals(TxnStatus.Aborted, tx.getStatus()); assertEquals(maxCapacity + 1, tx.getConfig().getSpeculativeConfiguration().minimalLength); } }