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.Functions; import org.multiverse.api.functions.IntFunction; import org.multiverse.api.predicates.IntPredicate; import org.multiverse.api.references.TxnInteger; 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.stms.gamma.GammaStmUtils.*; import static org.multiverse.stms.gamma.ThreadLocalGammaObjectPool.getThreadLocalGammaObjectPool; /** * @author Peter Veentjer. */ @SuppressWarnings({"OverlyComplexClass"}) public class GammaTxnInteger extends BaseGammaTxnRef implements TxnInteger { public GammaTxnInteger(int value) { this((GammaStm) getGlobalStmInstance(), value); } public GammaTxnInteger(final GammaTxn tx) { this(tx, 0); } public GammaTxnInteger(final GammaTxn tx, final int value) { super(tx.getConfig().stm, TYPE_INT); arriveAndLock(1, LOCKMODE_EXCLUSIVE); Tranlocal tranlocal = openForConstruction(tx); tranlocal.long_value = value; } public GammaTxnInteger(final GammaStm stm) { this(stm, 0); } public GammaTxnInteger(final GammaStm stm, final int value) { super(stm, TYPE_INT); this.long_value = value; //noinspection PointlessArithmeticExpression this.version = VERSION_UNCOMMITTED + 1; } @Override public final int get() { return get(getRequiredThreadLocalGammaTxn()); } @Override public final int get(final Txn tx) { return get(asGammaTxn(tx)); } public final int get(final GammaTxn tx) { return (int) openForRead(tx, LOCKMODE_NONE).long_value; } @Override public int getAndLock(final LockMode lockMode) { return getAndLock(getRequiredThreadLocalGammaTxn(), lockMode); } @Override public final int getAndLock(final Txn tx, final LockMode lockMode) { return getAndLock(asGammaTxn(tx), lockMode); } public final int getAndLock(final GammaTxn tx, final LockMode lockMode) { return (int) getLong(tx, lockMode); } @Override public final int set(final int value) { return set(getRequiredThreadLocalGammaTxn(), value); } @Override public final int set(final Txn tx, final int value) { return set(asGammaTxn(tx), value); } public final int set(final GammaTxn tx, final int value) { Tranlocal tranlocal = openForWrite(tx, LOCKMODE_NONE); tranlocal.long_value = value; return value; } @Override public final int setAndLock(final int value, final LockMode lockMode) { return setAndLock(getRequiredThreadLocalGammaTxn(), value, lockMode); } @Override public final int setAndLock(final Txn tx, final int value, final LockMode lockMode) { return setAndLock(asGammaTxn(tx), value, lockMode); } public final int setAndLock(final GammaTxn tx, final int value, final LockMode lockMode) { return (int) longAsDouble(setLong(tx, lockMode, value, false)); } @Override public final int getAndSet(final int value) { return getAndSet(getRequiredThreadLocalGammaTxn(), value); } @Override public final int getAndSet(final Txn tx, final int value) { return getAndSet(asGammaTxn(tx), value); } public final int getAndSet(final GammaTxn tx, final int value) { Tranlocal tranlocal = openForWrite(tx, LOCKMODE_NONE); int oldValue = (int) tranlocal.long_value; tranlocal.long_value = value; return oldValue; } @Override public final int getAndSetAndLock(final int value, final LockMode lockMode) { return getAndSetLock(getRequiredThreadLocalGammaTxn(), value, lockMode); } @Override public final int getAndSetAndLock(final Txn tx, final int value, final LockMode lockMode) { return getAndSetLock(asGammaTxn(tx), value, lockMode); } public final int getAndSetLock(final GammaTxn tx, final int value, final LockMode lockMode) { return (int) setLong(tx, lockMode, value, true); } @Override public final int atomicGet() { return (int) atomicGetLong(); } @Override public final int atomicWeakGet() { return (int) long_value; } @Override public final int atomicSet(final int newValue) { return (int) atomicSetLong(newValue, false); } @Override public final int atomicGetAndSet(final int newValue) { return (int) atomicSetLong(newValue, true); } @Override public final void commute(final IntFunction function) { commute(getRequiredThreadLocalGammaTxn(), function); } @Override public final void commute(final Txn tx, final IntFunction function) { commute(asGammaTxn(tx), function); } public final void commute(final GammaTxn tx, final IntFunction function) { openForCommute(tx, function); } @Override public final int atomicAlterAndGet(final IntFunction function) { return atomicAlter(function, false); } @Override public final int atomicGetAndAlter(final IntFunction function) { return atomicAlter(function, true); } private int atomicAlter(final IntFunction 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 int oldValue = (int) long_value; int 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(); } long_value = newValue; //noinspection NonAtomicOperationOnVolatileField version++; final Listeners listeners = ___removeListenersAfterWrite(); departAfterUpdateAndUnlock(); if (listeners != null) { listeners.openAll(getThreadLocalGammaObjectPool()); } return returnOld ? oldValue : newValue; } @Override public final int alterAndGet(final IntFunction function) { return alterAndGet(getRequiredThreadLocalGammaTxn(), function); } @Override public final int alterAndGet(final Txn tx, final IntFunction function) { return alterAndGet(asGammaTxn(tx), function); } public final int alterAndGet(final GammaTxn tx, final IntFunction function) { return alter(tx, function, false); } @Override public final int getAndAlter(final IntFunction function) { return getAndAlter(getRequiredThreadLocalGammaTxn(), function); } @Override public final int getAndAlter(final Txn tx, final IntFunction function) { return getAndAlter(asGammaTxn(tx), function); } public final int getAndAlter(final GammaTxn tx, final IntFunction function) { return alter(tx, function, true); } private int alter(final GammaTxn tx, final IntFunction 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 { int oldValue = (int) write.long_value; write.long_value = function.call(oldValue); abort = false; return returnOld ? oldValue : (int) write.long_value; } finally { if (abort) { tx.abort(); } } } @Override public final boolean atomicCompareAndSet(final int expectedValue, final int newValue) { return atomicCompareAndSetLong(expectedValue, newValue); } @Override public final int atomicGetAndIncrement(final int amount) { return atomicIncrement(amount, true); } @Override public final int atomicIncrementAndGet(final int amount) { return atomicIncrement(amount, false); } private int atomicIncrement(final int amount, boolean returnOld) { final int arriveStatus = arriveAndExclusiveLockOrBackoff(); if (arriveStatus == FAILURE) { throw new LockedException(); } final int oldValue = (int) long_value; if (amount == 0) { if ((arriveStatus & MASK_UNREGISTERED) != 0) { unlockByUnregistered(); } else { departAfterReadingAndUnlock(); } return oldValue; } if ((arriveStatus & MASK_CONFLICT) != 0) { stm.globalConflictCounter.signalConflict(); } final int newValue = oldValue + amount; long_value = newValue; //noinspection NonAtomicOperationOnVolatileField version++; final Listeners listeners = ___removeListenersAfterWrite(); departAfterUpdateAndUnlock(); if (listeners != null) { listeners.openAll(getThreadLocalGammaObjectPool()); } return returnOld ? oldValue : newValue; } @Override public final int getAndIncrement(final int amount) { return getAndIncrement(getRequiredThreadLocalGammaTxn(), amount); } @Override public final int getAndIncrement(final Txn tx, final int amount) { return getAndIncrement(asGammaTxn(tx), amount); } public final int getAndIncrement(final GammaTxn tx, final int amount) { return increment(tx, amount, true); } @Override public final int incrementAndGet(final int amount) { return incrementAndGet(getRequiredThreadLocalGammaTxn(), amount); } @Override public final int incrementAndGet(final Txn tx, final int amount) { return incrementAndGet(asGammaTxn(tx), amount); } public final int incrementAndGet(final GammaTxn tx, final int amount) { return increment(tx, amount, false); } private int increment(final GammaTxn tx, final int amount, final boolean returnOld) { Tranlocal tranlocal = openForWrite(tx, LOCKMODE_NONE); int oldValue = (int) tranlocal.long_value; tranlocal.long_value += amount; return returnOld ? oldValue : (int) tranlocal.long_value; } @Override public final void increment() { increment(getRequiredThreadLocalGammaTxn(), 1); } @Override public final void increment(final Txn tx) { increment(asGammaTxn(tx), 1); } @Override public final void increment(final int amount) { increment(getRequiredThreadLocalGammaTxn(), amount); } @Override public final void increment(final Txn tx, final int amount) { increment(asGammaTxn(tx), amount); } public final void increment(final GammaTxn tx, final int amount) { commute(tx, Functions.incIntFunction(amount)); } @Override public final void decrement() { increment(getRequiredThreadLocalGammaTxn(), -1); } @Override public final void decrement(Txn tx) { increment(asGammaTxn(tx), -1); } @Override public final void decrement(final int amount) { increment(getRequiredThreadLocalGammaTxn(), -amount); } @Override public final void decrement(final Txn tx, final int amount) { increment(asGammaTxn(tx), -amount); } @Override public final void await(final int value) { await(getRequiredThreadLocalGammaTxn(), value); } @Override public final void await(final Txn tx, final int value) { await(asGammaTxn(tx), value); } public final void await(final GammaTxn tx, final int value) { if (get(tx) != value) { tx.retry(); } } @Override public final void await(final IntPredicate predicate) { await(getRequiredThreadLocalGammaTxn(), predicate); } @Override public final void await(final Txn tx, final IntPredicate predicate) { await(asGammaTxn(tx), predicate); } public final void await(final GammaTxn tx, final IntPredicate predicate) { final Tranlocal tranlocal = openForRead(tx, LOCKMODE_NONE); boolean abort = true; try { if (!predicate.evaluate((int) tranlocal.long_value)) { tx.retry(); } abort = false; } finally { if (abort) { tx.abort(); } } } @Override public final String toDebugString() { return String.format("GammaTxnInteger{orec=%s, version=%s, value=%s, hasListeners=%s)", ___toOrecString(), version, long_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) { return Integer.toString(get(tx)); } @Override public final String atomicToString() { return Integer.toString(atomicGet()); } }