package org.multiverse.stms.gamma.transactionalobjects; import org.multiverse.api.LockMode; import org.multiverse.api.Txn; import org.multiverse.api.exceptions.LockedException; import org.multiverse.api.functions.Function; import org.multiverse.api.predicates.Predicate; import org.multiverse.api.references.TxnRef; import org.multiverse.stms.gamma.GammaStm; import org.multiverse.stms.gamma.Listeners; import org.multiverse.stms.gamma.transactions.GammaTxn; import static org.multiverse.api.GlobalStmInstance.getGlobalStmInstance; import static org.multiverse.api.TxnThreadLocal.getRequiredThreadLocalTxn; import static org.multiverse.stms.gamma.GammaStmUtils.asGammaTxn; import static org.multiverse.stms.gamma.GammaStmUtils.getRequiredThreadLocalGammaTxn; import static org.multiverse.stms.gamma.ThreadLocalGammaObjectPool.getThreadLocalGammaObjectPool; /** * A {@link org.multiverse.api.references.TxnRef} tailored for the {@link GammaStm}. * * @param <E> * @author Peter Veentjer. */ @SuppressWarnings({"OverlyComplexClass"}) public class GammaTxnRef<E> extends BaseGammaTxnRef implements TxnRef<E> { public GammaTxnRef(E value) { this((GammaStm) getGlobalStmInstance(), value); } public GammaTxnRef(final GammaTxn tx) { this(tx, null); } public GammaTxnRef(final GammaTxn tx, final E value) { super(tx.getConfig().stm, TYPE_REF); arriveAndLock(1, LOCKMODE_EXCLUSIVE); Tranlocal tranlocal = openForConstruction(tx); tranlocal.ref_value = value; } public GammaTxnRef(final GammaStm stm) { this(stm, null); } public GammaTxnRef(final GammaStm stm, final E value) { super(stm, TYPE_REF); this.ref_value = value; //noinspection PointlessArithmeticExpression this.version = VERSION_UNCOMMITTED + 1; } @Override public final E get() { return get(getRequiredThreadLocalGammaTxn()); } @Override public final E get(final Txn tx) { return get(asGammaTxn(tx)); } public final E get(final GammaTxn tx) { return (E) openForRead(tx, LOCKMODE_NONE).ref_value; } @Override public final E getAndLock(final LockMode lockMode) { return getAndLock(getRequiredThreadLocalGammaTxn(), lockMode); } @Override public final E getAndLock(final Txn tx, final LockMode lockMode) { return getAndLock(asGammaTxn(tx), lockMode); } public final E getAndLock(final GammaTxn tx, final LockMode lockMode) { return (E) getObject(tx, lockMode); } @Override public final E set(final E value) { return set(getRequiredThreadLocalTxn(), value); } @Override public final E set(final Txn tx, final E value) { return set(asGammaTxn(tx), value); } public final E set(final GammaTxn tx, final E value) { final Tranlocal tranlocal = openForWrite(tx, LOCKMODE_NONE); tranlocal.ref_value = value; return value; } @Override public final E setAndLock(final E value, final LockMode lockMode) { return setAndLock(getRequiredThreadLocalGammaTxn(), value, lockMode); } @Override public final E setAndLock(final Txn tx, final E value, final LockMode lockMode) { return setAndLock(asGammaTxn(tx), value, lockMode); } public final E setAndLock(final GammaTxn tx, final E value, final LockMode lockMode) { return (E) setObject(tx, lockMode, value, false); } @Override public final E getAndSet(final E value) { return getAndSet(getRequiredThreadLocalTxn(), value); } @Override public final E getAndSet(final Txn tx, final E value) { return getAndSet(asGammaTxn(tx), value); } public final E getAndSet(final GammaTxn tx, final E value) { Tranlocal tranlocal = openForWrite(tx, LOCKMODE_NONE); E oldValue = (E) tranlocal.ref_value; tranlocal.ref_value = value; return oldValue; } @Override public final E getAndSetAndLock(final E value, final LockMode lockMode) { return getAndSetAndLock(getRequiredThreadLocalGammaTxn(), value, lockMode); } @Override public final E getAndSetAndLock(final Txn tx, final E value, final LockMode lockMode) { return getAndSetAndLock(asGammaTxn(tx), value, lockMode); } public final E getAndSetAndLock(final GammaTxn tx, final E value, final LockMode lockMode) { return (E) setObject(tx, lockMode, value, true); } @Override public final E atomicGet() { return (E) atomicObjectGet(); } @Override public final E atomicWeakGet() { return (E) ref_value; } @Override public final E atomicSet(final E newValue) { return (E) atomicSetObject(newValue, false); } @Override public final E atomicGetAndSet(final E newValue) { return (E) atomicSetObject(newValue, true); } @Override public final void commute(final Function<E> function) { commute(getRequiredThreadLocalTxn(), function); } @Override public final void commute(final Txn tx, final Function<E> function) { commute(asGammaTxn(tx), function); } public final void commute(final GammaTxn tx, final Function<E> function) { openForCommute(tx, function); } @Override public final E atomicAlterAndGet(final Function<E> function) { return atomicAlter(function, false); } @Override public final E atomicGetAndAlter(final Function<E> function) { return atomicAlter(function, true); } private E atomicAlter(final Function<E> function, final boolean returnOld) { if (function == null) { throw new NullPointerException("Function can't be null"); } final int arriveStatus = arriveAndExclusiveLockOrBackoff(); if (arriveStatus == FAILURE) { throw new LockedException(); } final E oldValue = (E) ref_value; E newValue; boolean abort = true; try { newValue = function.call(oldValue); abort = false; } finally { if (abort) { departAfterFailureAndUnlock(); } } if (oldValue == newValue) { if ((arriveStatus & MASK_UNREGISTERED) != 0) { unlockByUnregistered(); } else { departAfterReadingAndUnlock(); } return oldValue; } if ((arriveStatus & MASK_CONFLICT) != 0) { stm.globalConflictCounter.signalConflict(); } ref_value = newValue; //noinspection NonAtomicOperationOnVolatileField version++; final Listeners listeners = ___removeListenersAfterWrite(); departAfterUpdateAndUnlock(); if (listeners != null) { listeners.openAll(getThreadLocalGammaObjectPool()); } return returnOld ? oldValue : newValue; } @Override public final E alterAndGet(final Function<E> function) { return alterAndGet(getRequiredThreadLocalTxn(), function); } @Override public final E alterAndGet(final Txn tx, final Function<E> function) { return alterAndGet(asGammaTxn(tx), function); } public final E alterAndGet(final GammaTxn tx, final Function<E> function) { return alter(tx, function, false); } @Override public final E getAndAlter(final Function<E> function) { return getAndAlter(getRequiredThreadLocalTxn(), function); } @Override public final E getAndAlter(final Txn tx, final Function<E> function) { return getAndAlter(asGammaTxn(tx), function); } public final E getAndAlter(final GammaTxn tx, final Function<E> function) { return alter(tx, function, true); } private E alter(final GammaTxn tx, final Function<E> function, final boolean returnOld) { if (tx == null) { throw new NullPointerException(); } if (function == null) { tx.abort(); throw new NullPointerException("Function can't be null"); } final Tranlocal write = openForWrite(tx, LOCKMODE_NONE); boolean abort = true; try { E oldValue = (E) write.ref_value; write.ref_value = function.call(oldValue); abort = false; return returnOld ? oldValue : (E) write.ref_value; } finally { if (abort) { tx.abort(); } } } @Override public final boolean atomicCompareAndSet(final E expectedValue, final E newValue) { final int arriveStatus = arriveAndExclusiveLockOrBackoff(); if (arriveStatus == FAILURE) { throw new LockedException(); } final E currentValue = (E) ref_value; if (currentValue != expectedValue) { departAfterFailureAndUnlock(); return false; } if (expectedValue == newValue) { if ((arriveStatus & MASK_UNREGISTERED) != 0) { unlockByUnregistered(); } else { departAfterReadingAndUnlock(); } return true; } if ((arriveStatus & MASK_CONFLICT) != 0) { stm.globalConflictCounter.signalConflict(); } ref_value = newValue; //noinspection NonAtomicOperationOnVolatileField version++; final Listeners listeners = ___removeListenersAfterWrite(); departAfterUpdateAndUnlock(); if (listeners != null) { listeners.openAll(getThreadLocalGammaObjectPool()); } return true; } @Override public final boolean isNull() { return isNull(getRequiredThreadLocalGammaTxn()); } @Override public final boolean isNull(final Txn tx) { return isNull(asGammaTxn(tx)); } public final boolean isNull(final GammaTxn tx) { return openForRead(tx, LOCKMODE_NONE).ref_value == null; } @Override public final boolean atomicIsNull() { return atomicGet() == null; } @Override public final E awaitNotNullAndGet() { return awaitNotNullAndGet(getRequiredThreadLocalGammaTxn()); } @Override public final E awaitNotNullAndGet(final Txn tx) { return awaitNotNullAndGet(asGammaTxn(tx)); } public final E awaitNotNullAndGet(final GammaTxn tx) { final Tranlocal tranlocal = openForRead(tx, LOCKMODE_NONE); if (tranlocal.ref_value == null) { tx.retry(); } return (E) tranlocal.ref_value; } @Override public final void awaitNull() { await(getRequiredThreadLocalGammaTxn(), (E) null); } @Override public final void awaitNull(final Txn tx) { await(asGammaTxn(tx), (E) null); } public final void awaitNull(final GammaTxn tx) { await(tx, (E) null); } @Override public final void await(final E value) { await(getRequiredThreadLocalTxn(), value); } @Override public final void await(final Txn tx, final E value) { await(asGammaTxn(tx), value); } public final void await(final GammaTxn tx, final E value) { //noinspection ObjectEquality if (openForRead(tx, LOCKMODE_NONE).ref_value != value) { tx.retry(); } } @Override public final void await(final Predicate<E> predicate) { await(getRequiredThreadLocalTxn(), predicate); } @Override public final void await(final Txn tx, final Predicate<E> predicate) { await(asGammaTxn(tx), predicate); } public final void await(final GammaTxn tx, final Predicate<E> predicate) { final Tranlocal tranlocal = openForRead(tx, LOCKMODE_NONE); boolean abort = true; try { if (!predicate.evaluate((E) tranlocal.ref_value)) { tx.retry(); } abort = false; } finally { if (abort) { tx.abort(); } } } @Override public final String toDebugString() { return String.format("GammaTxnRef{orec=%s, version=%s, value=%s, hasListeners=%s)", ___toOrecString(), version, ref_value, listeners != null); } @Override public final String toString() { return toString(getRequiredThreadLocalGammaTxn()); } @Override public final String toString(Txn tx) { return toString(asGammaTxn(tx)); } public final String toString(GammaTxn tx) { final E value = get(tx); return value == null ? "null" : value.toString(); } @Override public final String atomicToString() { final E value = atomicGet(); return value == null ? "null" : value.toString(); } }