package org.multiverse.stms.gamma.transactions.fat;
import org.multiverse.api.lifecycle.TxnEvent;
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;
public final class FatMonoGammaTxn extends GammaTxn {
public final Tranlocal tranlocal = new Tranlocal();
public FatMonoGammaTxn(GammaStm stm) {
this(new GammaTxnConfig(stm));
}
public FatMonoGammaTxn(GammaTxnConfig config) {
super(config, TRANSACTIONTYPE_FAT_MONO);
richmansMansConflictScan = false;
}
@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();
}
if (abortOnly) {
throw abortCommitOnAbortOnly();
}
if (status == TX_ACTIVE) {
notifyListeners(TxnEvent.PrePrepare);
}
final BaseGammaTxnRef owner = tranlocal.owner;
if (owner != null) {
if (hasWrites) {
if (status == TX_ACTIVE) {
if (!skipPrepare()) {
if (!owner.prepare(this, tranlocal)) {
throw abortOnReadWriteConflict(owner);
}
}
}
if (commitConflict) {
config.globalConflictCounter.signalConflict();
}
Listeners listeners = owner.commit(tranlocal, pool);
if (listeners != null) {
listeners.openAll(pool);
}
} else {
owner.releaseAfterReading(tranlocal, pool);
}
}
tranlocal.owner = null;
status = TX_COMMITTED;
notifyListeners(TxnEvent.PostCommit);
}
@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);
}
notifyListeners(TxnEvent.PostAbort);
}
@Override
public final void prepare() {
if (status == TX_PREPARED) {
return;
}
if (status != TX_ACTIVE) {
throw abortPrepareOnBadStatus();
}
if (abortOnly) {
throw abortPrepareOnAbortOnly();
}
notifyListeners(TxnEvent.PrePrepare);
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;
}
if (listeners != null) {
listeners.clear();
pool.putArrayList(listeners);
listeners = null;
}
status = TX_ACTIVE;
hasWrites = false;
attempt++;
abortOnly = false;
commitConflict = false;
evaluatingCommute = false;
return true;
}
@Override
public final void hardReset() {
if (listeners != null) {
listeners.clear();
pool.putArrayList(listeners);
listeners = null;
}
status = TX_ACTIVE;
hasWrites = false;
remainingTimeoutNs = config.timeoutNs;
attempt = 1;
abortOnly = false;
commitConflict = false;
evaluatingCommute = false;
}
@Override
public final boolean isReadConsistent(Tranlocal justAdded) {
return true;
}
@Override
public void initLocalConflictCounter() {
//ignore
}
}