package org.multiverse.stms.gamma.transactions.lean; import org.multiverse.stms.gamma.GammaStm; import org.multiverse.stms.gamma.Listeners; import org.multiverse.stms.gamma.transactionalobjects.BaseGammaTxnRef; import org.multiverse.stms.gamma.transactionalobjects.Tranlocal; import org.multiverse.stms.gamma.transactions.GammaTxn; import org.multiverse.stms.gamma.transactions.GammaTxnConfig; import static org.multiverse.utils.Bugshaker.shakeBugs; /** * A Lean GammaTxn implementation that is optimized for dealing with only a single * transactional reference. */ public final class LeanMonoGammaTxn extends GammaTxn { public final Tranlocal tranlocal = new Tranlocal(); public LeanMonoGammaTxn(GammaStm stm) { this(new GammaTxnConfig(stm)); } public LeanMonoGammaTxn(GammaTxnConfig config) { super(config, TRANSACTIONTYPE_LEAN_MONO); } @Override public final Tranlocal locate(BaseGammaTxnRef o) { if (status != TX_ACTIVE) { throw abortLocateOnBadStatus(o); } if (o == null) { throw abortLocateOnNullArgument(); } return getRefTranlocal(o); } @Override public final void commit() { if (status == TX_COMMITTED) { return; } if (status != TX_ACTIVE && status != TX_PREPARED) { throw abortCommitOnBadStatus(); } final BaseGammaTxnRef owner = tranlocal.owner; if (owner == null) { status = TX_COMMITTED; return; } if (!hasWrites) { tranlocal.owner = null; tranlocal.ref_value = null; status = TX_COMMITTED; return; } final long version = tranlocal.version; //if the transaction still is active, we need to prepare the transaction. if (status == TX_ACTIVE) { if (owner.version != version) { throw abortOnReadWriteConflict(owner); } final int arriveStatus = owner.arriveAndExclusiveLock(64); if (arriveStatus == FAILURE) { throw abortOnReadWriteConflict(owner); } if (owner.version != version) { if ((arriveStatus & MASK_UNREGISTERED) == 0) { owner.departAfterFailureAndUnlock(); } else { owner.unlockByUnregistered(); } throw abortOnReadWriteConflict(owner); } if((arriveStatus & MASK_CONFLICT)!=0){ commitConflict = true; } } if (commitConflict) { config.globalConflictCounter.signalConflict(); } if(SHAKE_BUGS) shakeBugs(); owner.ref_value = tranlocal.ref_value; owner.version = version + 1; Listeners listeners = owner.listeners; if (listeners != null) { listeners = owner.___removeListenersAfterWrite(); } owner.departAfterUpdateAndUnlock(); tranlocal.owner = null; //we need to set them to null to prevent memory leaks. tranlocal.ref_value = null; tranlocal.ref_oldValue = null; if (listeners != null) { listeners.openAll(pool); } status = TX_COMMITTED; } @Override public final void abort() { if (status == TX_ABORTED) { return; } if (status == TX_COMMITTED) { throw failAbortOnAlreadyCommitted(); } status = TX_ABORTED; BaseGammaTxnRef owner = tranlocal.owner; if (owner != null) { owner.releaseAfterFailure(tranlocal, pool); } } @Override public final void prepare() { if (status == TX_PREPARED) { return; } if (status != TX_ACTIVE) { throw abortPrepareOnBadStatus(); } final BaseGammaTxnRef owner = tranlocal.owner; if (owner != null) { if (!owner.prepare(this, tranlocal)) { throw abortOnReadWriteConflict(owner); } } status = TX_PREPARED; } @Override public final Tranlocal getRefTranlocal(BaseGammaTxnRef ref) { //noinspection ObjectEquality return tranlocal.owner == ref ? tranlocal : null; } @Override public final void retry() { if (status != TX_ACTIVE) { throw abortRetryOnBadStatus(); } if (!config.isBlockingAllowed()) { throw abortRetryOnNoBlockingAllowed(); } if (tranlocal == null) { throw abortRetryOnNoRetryPossible(); } final BaseGammaTxnRef owner = tranlocal.owner; if (owner == null) { throw abortRetryOnNoRetryPossible(); } retryListener.reset(); final long listenerEra = retryListener.getEra(); boolean atLeastOneRegistration = false; switch (tranlocal.owner.registerChangeListener(retryListener, tranlocal, pool, listenerEra)) { case REGISTRATION_DONE: atLeastOneRegistration = true; break; case REGISTRATION_NOT_NEEDED: atLeastOneRegistration = true; break; case REGISTRATION_NONE: break; default: throw new IllegalStateException(); } owner.releaseAfterFailure(tranlocal, pool); status = TX_ABORTED; if (!atLeastOneRegistration) { throw abortRetryOnNoRetryPossible(); } throw newRetryError(); } @Override public final boolean softReset() { if (attempt >= config.getMaxRetries()) { return false; } commitConflict = false; status = TX_ACTIVE; hasWrites = false; attempt++; return true; } @Override public final void hardReset() { commitConflict = false; status = TX_ACTIVE; hasWrites = false; remainingTimeoutNs = config.timeoutNs; attempt = 1; } @Override public final boolean isReadConsistent(Tranlocal justAdded) { return true; } @Override public void initLocalConflictCounter() { //ignore } }