package org.multiverse.stms.gamma.transactionalobjects.refs;
import org.junit.Before;
import org.junit.Test;
import org.multiverse.api.LockMode;
import org.multiverse.api.blocking.DefaultRetryLatch;
import org.multiverse.api.blocking.RetryLatch;
import org.multiverse.api.functions.LongFunction;
import org.multiverse.stms.gamma.GammaConstants;
import org.multiverse.stms.gamma.GammaObjectPool;
import org.multiverse.stms.gamma.GammaStm;
import org.multiverse.stms.gamma.Listeners;
import org.multiverse.stms.gamma.transactionalobjects.GammaTxnLong;
import org.multiverse.stms.gamma.transactionalobjects.Tranlocal;
import org.multiverse.stms.gamma.transactions.GammaTxn;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.multiverse.TestUtils.getField;
import static org.multiverse.api.TxnThreadLocal.clearThreadLocalTxn;
import static org.multiverse.stms.gamma.GammaTestUtils.*;
public class RegisterChangeListenerTest implements GammaConstants {
private GammaStm stm;
private GammaObjectPool pool;
@Before
public void setUp() {
stm = new GammaStm();
pool = new GammaObjectPool();
clearThreadLocalTxn();
}
@Test
public void whenCommuting() {
GammaTxnLong ref = new GammaTxnLong(stm, 0);
LongFunction function = mock(LongFunction.class);
GammaTxn tx = stm.newDefaultTxn();
ref.commute(tx, function);
RetryLatch latch = new DefaultRetryLatch();
long listenerEra = latch.getEra();
Tranlocal tranlocal = tx.getRefTranlocal(ref);
int result = ref.registerChangeListener(latch, tranlocal, pool, listenerEra);
assertEquals(REGISTRATION_NONE, result);
assertNull(getField(ref, "listeners"));
assertFalse(latch.isOpen());
verifyZeroInteractions(function);
}
@Test
public void whenInterestingWriteAlreadyHappened_thenLatchOpenedAndNoRegistration() {
GammaTxnLong ref = new GammaTxnLong(stm);
GammaTxn tx = stm.newDefaultTxn();
Tranlocal read = ref.openForRead(tx, LOCKMODE_NONE);
GammaTxn otherTx = stm.newDefaultTxn();
Tranlocal write = ref.openForWrite(otherTx, LOCKMODE_NONE);
write.long_value++;
otherTx.commit();
RetryLatch latch = new DefaultRetryLatch();
long listenerEra = latch.getEra();
int result = ref.registerChangeListener(latch, read, pool, listenerEra);
assertEquals(REGISTRATION_NOT_NEEDED, result);
assertNull(getField(ref, "listeners"));
assertTrue(latch.isOpen());
}
@Test
public void whenExclusiveLockAndNoConflict_thenRegistered() {
GammaTxnLong ref = new GammaTxnLong(stm, 10);
long version = ref.getVersion();
GammaTxn tx = stm.newDefaultTxn();
Tranlocal read = ref.openForRead(tx, LOCKMODE_NONE);
GammaTxn otherTx = stm.newDefaultTxn();
ref.getLock().acquire(otherTx, LockMode.Exclusive);
RetryLatch latch = new DefaultRetryLatch();
long listenerEra = latch.getEra();
int result = ref.registerChangeListener(latch, read, pool, listenerEra);
assertEquals(REGISTRATION_DONE, result);
assertSurplus(ref, 1);
assertHasListeners(ref, latch);
assertRefHasExclusiveLock(ref, otherTx);
assertVersionAndValue(ref, version, 10);
assertFalse(latch.isOpen());
}
@Test
public void whenWriteLockAndNoConflict_thenRegistered() {
GammaTxnLong ref = new GammaTxnLong(stm, 10);
long version = ref.getVersion();
GammaTxn tx = stm.newDefaultTxn();
Tranlocal read = ref.openForRead(tx, LOCKMODE_NONE);
GammaTxn otherTx = stm.newDefaultTxn();
ref.getLock().acquire(otherTx, LockMode.Write);
RetryLatch latch = new DefaultRetryLatch();
long listenerEra = latch.getEra();
int result = ref.registerChangeListener(latch, read, pool, listenerEra);
assertEquals(REGISTRATION_DONE, result);
assertSurplus(ref, 1);
assertHasListeners(ref, latch);
assertRefHasWriteLock(ref, otherTx);
assertVersionAndValue(ref, version, 10);
assertFalse(latch.isOpen());
}
@Test
public void whenExclusiveLockAndInterestingChangeAlreadyHappened() {
GammaTxnLong ref = new GammaTxnLong(stm);
GammaTxn tx = stm.newDefaultTxn();
Tranlocal read = ref.openForRead(tx, LOCKMODE_NONE);
ref.atomicIncrementAndGet(1);
long version = ref.getVersion();
GammaTxn otherTx = stm.newDefaultTxn();
ref.getLock().acquire(otherTx, LockMode.Exclusive);
RetryLatch latch = new DefaultRetryLatch();
long listenerEra = latch.getEra();
int result = ref.registerChangeListener(latch, read, pool, listenerEra);
assertEquals(REGISTRATION_NOT_NEEDED, result);
assertNull(getField(ref, "listeners"));
assertTrue(latch.isOpen());
assertSurplus(ref, 1);
assertHasNoListeners(ref);
assertRefHasExclusiveLock(ref, otherTx);
assertVersionAndValue(ref, version, 1);
}
@Test
public void wheWriteLockAndInterestingChangeAlreadyHappened() {
GammaTxnLong ref = new GammaTxnLong(stm);
GammaTxn tx = stm.newDefaultTxn();
Tranlocal read = ref.openForRead(tx, LOCKMODE_NONE);
ref.atomicIncrementAndGet(1);
long version = ref.getVersion();
GammaTxn otherTx = stm.newDefaultTxn();
ref.getLock().acquire(otherTx, LockMode.Write);
RetryLatch latch = new DefaultRetryLatch();
long listenerEra = latch.getEra();
int result = ref.registerChangeListener(latch, read, pool, listenerEra);
assertEquals(REGISTRATION_NOT_NEEDED, result);
assertNull(getField(ref, "listeners"));
assertTrue(latch.isOpen());
assertSurplus(ref, 1);
assertHasNoListeners(ref);
assertRefHasWriteLock(ref, otherTx);
assertVersionAndValue(ref, version, 1);
}
@Test
public void whenConstructed_thenNoRegistration() {
GammaTxn tx = stm.newDefaultTxn();
GammaTxnLong ref = new GammaTxnLong(tx);
Tranlocal read = tx.locate(ref);
RetryLatch latch = new DefaultRetryLatch();
long listenerEra = latch.getEra();
int result = ref.registerChangeListener(latch, read, pool, listenerEra);
assertEquals(REGISTRATION_NONE, result);
assertHasNoListeners(ref);
assertFalse(latch.isOpen());
}
@Test
public void whenFirstOne_thenRegistrationSuccessful() {
GammaTxnLong ref = new GammaTxnLong(stm);
GammaTxn tx = stm.newDefaultTxn();
Tranlocal read = ref.openForRead(tx, LOCKMODE_NONE);
RetryLatch latch = new DefaultRetryLatch();
long listenerEra = latch.getEra();
int result = ref.registerChangeListener(latch, read, pool, listenerEra);
assertEquals(REGISTRATION_DONE, result);
Listeners listeners = (Listeners) getField(ref, "listeners");
assertNotNull(listeners);
assertSame(latch, listeners.listener);
assertNull(listeners.next);
assertEquals(listenerEra, listeners.listenerEra);
assertFalse(latch.isOpen());
}
@Test
public void whenSecondOne_thenListenerAddedToChain() {
GammaTxnLong ref = new GammaTxnLong(stm);
GammaTxn tx1 = stm.newDefaultTxn();
Tranlocal read1 = ref.openForRead(tx1, LOCKMODE_NONE);
RetryLatch latch1 = new DefaultRetryLatch();
long listenerEra1 = latch1.getEra();
ref.registerChangeListener(latch1, read1, pool, listenerEra1);
GammaTxn tx2 = stm.newDefaultTxn();
Tranlocal read2 = ref.openForRead(tx2, LOCKMODE_NONE);
RetryLatch latch2 = new DefaultRetryLatch();
long listenerEra2 = latch2.getEra();
int result = ref.registerChangeListener(latch2, read2, pool, listenerEra2);
assertEquals(REGISTRATION_DONE, result);
Listeners listeners = (Listeners) getField(ref, "listeners");
assertNotNull(listeners);
assertSame(latch2, listeners.listener);
assertEquals(listenerEra2, listeners.listenerEra);
assertNotNull(listeners.next);
assertSame(latch1, listeners.next.listener);
assertEquals(listenerEra1, listeners.next.listenerEra);
assertFalse(latch1.isOpen());
}
}