package org.multiverse.stms.gamma.transactions; import org.multiverse.api.BackoffPolicy; import org.multiverse.api.IsolationLevel; import org.multiverse.api.LockMode; import org.multiverse.api.PropagationLevel; import org.multiverse.api.TraceLevel; import org.multiverse.api.TxnConfig; import org.multiverse.api.exceptions.IllegalTxnFactoryException; import org.multiverse.api.lifecycle.TxnListener; import org.multiverse.stms.gamma.GammaConstants; import org.multiverse.stms.gamma.GammaStm; import org.multiverse.stms.gamma.GammaStmConfig; import org.multiverse.stms.gamma.GlobalConflictCounter; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import static java.lang.String.format; import static java.util.Collections.EMPTY_LIST; import static java.util.Collections.unmodifiableList; /** * A configuration object that contains the configuration for a GammaTxn. * <p/> * GammaTxnConfig object is considered to be immutable. The only mutable part if the speculative * configuration that can get upgraded if enabled and speculations failed. * * @author Peter Veentjer. */ @SuppressWarnings({"OverlyComplexClass", "ClassWithTooManyFields"}) public final class GammaTxnConfig implements TxnConfig, GammaConstants { public final static AtomicLong idGenerator = new AtomicLong(); public final AtomicReference<SpeculativeGammaConfiguration> speculativeConfiguration = new AtomicReference<SpeculativeGammaConfiguration>(); public final GammaStm stm; public final GlobalConflictCounter globalConflictCounter; public PropagationLevel propagationLevel; public IsolationLevel isolationLevel; public boolean writeSkewAllowed; public boolean inconsistentReadAllowed; public LockMode readLockMode; public LockMode writeLockMode; public int readLockModeAsInt; public int writeLockModeAsInt; public String familyName; public boolean isAnonymous; public boolean interruptible; public boolean readonly; public int spinCount; public boolean dirtyCheck; public int minimalArrayTreeSize; public boolean trackReads; public boolean blockingAllowed; public int maxRetries; public boolean speculative; public int maxFixedLengthTransactionSize; public BackoffPolicy backoffPolicy; public long timeoutNs; public TraceLevel traceLevel; public boolean controlFlowErrorsReused; public boolean isFat; public int maximumPoorMansConflictScanLength; public ArrayList<TxnListener> permanentListeners; public boolean unrepeatableReadAllowed; public GammaTxnConfig(GammaStm stm) { this(stm, new GammaStmConfig()); } public GammaTxnConfig(GammaStm stm, GammaStmConfig config) { this.stm = stm; this.globalConflictCounter = stm.getGlobalConflictCounter(); this.interruptible = config.interruptible; this.readonly = config.readonly; this.spinCount = config.spinCount; this.readLockMode = config.readLockMode; this.readLockModeAsInt = config.readLockMode.asInt(); this.writeLockMode = config.writeLockMode; this.writeLockModeAsInt = config.writeLockMode.asInt(); this.dirtyCheck = config.dirtyCheck; this.minimalArrayTreeSize = config.minimalVariableLengthTransactionSize; this.trackReads = config.trackReads; this.blockingAllowed = config.blockingAllowed; this.maxRetries = config.maxRetries; this.speculative = config.speculativeConfigEnabled; this.maxFixedLengthTransactionSize = config.maxFixedLengthTransactionSize; this.backoffPolicy = config.backoffPolicy; this.timeoutNs = config.timeoutNs; this.traceLevel = config.traceLevel; this.isolationLevel = config.isolationLevel; this.writeSkewAllowed = isolationLevel.doesAllowWriteSkew(); this.inconsistentReadAllowed = isolationLevel.doesAllowInconsistentRead(); this.unrepeatableReadAllowed = isolationLevel.doesAllowUnrepeatableRead(); this.propagationLevel = config.propagationLevel; this.controlFlowErrorsReused = config.controlFlowErrorsReused; this.familyName = "anonymoustransaction-" + idGenerator.incrementAndGet(); this.isAnonymous = true; this.maximumPoorMansConflictScanLength = config.maximumPoorMansConflictScanLength; this.isFat = config.isFat; if (config.permanentListeners.isEmpty()) { this.permanentListeners = null; } else { this.permanentListeners = new ArrayList<TxnListener>(config.permanentListeners); } } /** * Makes a clone of the given GammaTxnConfig. * * @param config the GammaTxnConfig to clone. */ private GammaTxnConfig(GammaTxnConfig config) { this.stm = config.stm; this.globalConflictCounter = config.globalConflictCounter; this.propagationLevel = config.propagationLevel; this.isolationLevel = config.isolationLevel; this.writeSkewAllowed = config.writeSkewAllowed; this.inconsistentReadAllowed = config.inconsistentReadAllowed; this.readLockMode = config.readLockMode; this.readLockModeAsInt = config.readLockModeAsInt; this.writeLockMode = config.writeLockMode; this.writeLockModeAsInt = config.writeLockModeAsInt; this.familyName = config.familyName; this.isAnonymous = config.isAnonymous; this.interruptible = config.interruptible; this.readonly = config.readonly; this.spinCount = config.spinCount; this.dirtyCheck = config.dirtyCheck; this.minimalArrayTreeSize = config.minimalArrayTreeSize; this.trackReads = config.trackReads; this.blockingAllowed = config.blockingAllowed; this.maxRetries = config.maxRetries; this.speculative = config.speculative; this.maxFixedLengthTransactionSize = config.maxFixedLengthTransactionSize; this.backoffPolicy = config.backoffPolicy; this.timeoutNs = config.timeoutNs; this.traceLevel = config.traceLevel; this.controlFlowErrorsReused = config.controlFlowErrorsReused; this.isFat = config.isFat; this.maximumPoorMansConflictScanLength = config.maximumPoorMansConflictScanLength; this.permanentListeners = config.permanentListeners; } public GammaTxnConfig(GammaStm stm, int maxFixedLengthTransactionSize) { this(stm); this.maxFixedLengthTransactionSize = maxFixedLengthTransactionSize; } @Override public LockMode getReadLockMode() { return readLockMode; } @Override public LockMode getWriteLockMode() { return writeLockMode; } @Override public IsolationLevel getIsolationLevel() { return isolationLevel; } @Override public boolean isControlFlowErrorsReused() { return controlFlowErrorsReused; } public SpeculativeGammaConfiguration getSpeculativeConfiguration() { return speculativeConfiguration.get(); } @Override public long getTimeoutNs() { return timeoutNs; } @Override public TraceLevel getTraceLevel() { return traceLevel; } @Override public boolean isInterruptible() { return interruptible; } @Override public BackoffPolicy getBackoffPolicy() { return backoffPolicy; } @Override public boolean isSpeculative() { return speculative; } @Override public String getFamilyName() { return familyName; } @Override public boolean isReadonly() { return readonly; } @Override public int getSpinCount() { return spinCount; } @Override public boolean isDirtyCheckEnabled() { return dirtyCheck; } @Override public GammaStm getStm() { return stm; } public GlobalConflictCounter getGlobalConflictCounter() { return globalConflictCounter; } @Override public boolean isReadTrackingEnabled() { return trackReads; } @Override public boolean isBlockingAllowed() { return blockingAllowed; } @Override public int getMaxRetries() { return maxRetries; } @Override public PropagationLevel getPropagationLevel() { return propagationLevel; } @Override public List<TxnListener> getPermanentListeners() { if (permanentListeners == null) { return EMPTY_LIST; } return unmodifiableList(permanentListeners); } public void updateSpeculativeConfigurationToUseNonRefType() { while (true) { SpeculativeGammaConfiguration current = speculativeConfiguration.get(); SpeculativeGammaConfiguration update = current.newWithNonRefType(); if (speculativeConfiguration.compareAndSet(current, update)) { return; } } } public void updateSpeculativeConfigurationToUseListeners() { while (true) { SpeculativeGammaConfiguration current = speculativeConfiguration.get(); SpeculativeGammaConfiguration update = current.newWithListeners(); if (speculativeConfiguration.compareAndSet(current, update)) { return; } } } public void updateSpeculativeConfigureToUseAbortOnly() { while (true) { SpeculativeGammaConfiguration current = speculativeConfiguration.get(); SpeculativeGammaConfiguration update = current.newWithAbortOnly(); if (speculativeConfiguration.compareAndSet(current, update)) { return; } } } public void updateSpeculativeConfigurationToUseCommute() { while (true) { SpeculativeGammaConfiguration current = speculativeConfiguration.get(); SpeculativeGammaConfiguration update = current.newWithCommute(); if (speculativeConfiguration.compareAndSet(current, update)) { return; } } } public void updateSpeculativeConfigurationToUseExplicitLocking() { while (true) { SpeculativeGammaConfiguration current = speculativeConfiguration.get(); SpeculativeGammaConfiguration update = current.newWithLocks(); if (speculativeConfiguration.compareAndSet(current, update)) { return; } } } public void updateSpeculativeConfigurationToUseConstructedObjects() { while (true) { SpeculativeGammaConfiguration current = speculativeConfiguration.get(); SpeculativeGammaConfiguration next = current.newWithConstructedObjects(); if (speculativeConfiguration.compareAndSet(current, next)) { return; } } } public void updateSpeculativeConfigurationToUseRichMansConflictScan() { while (true) { SpeculativeGammaConfiguration current = speculativeConfiguration.get(); SpeculativeGammaConfiguration next = current.newWithRichMansConflictScan(); if (speculativeConfiguration.compareAndSet(current, next)) { return; } } } public void updateSpeculativeConfigurationToUseMinimalTransactionLength(int newLength) { while (true) { SpeculativeGammaConfiguration current = speculativeConfiguration.get(); SpeculativeGammaConfiguration next = current.newWithMinimalLength(newLength); if (speculativeConfiguration.compareAndSet(current, next)) { return; } } } public void updateSpeculativeConfigurationToUseEnsure() { while (true) { SpeculativeGammaConfiguration current = speculativeConfiguration.get(); SpeculativeGammaConfiguration next = current.newWithEnsure(); if (speculativeConfiguration.compareAndSet(current, next)) { return; } } } public GammaTxnConfig init() { if (!writeSkewAllowed && !trackReads && !readonly) { String msg = format("'[%s] If no writeskew is allowed, read tracking should be enabled", familyName); throw new IllegalTxnFactoryException(msg); } if (blockingAllowed && !trackReads) { String msg = format("[%s] If blocking is allowed, read tracking should be enabled", familyName); throw new IllegalTxnFactoryException(msg); } if (readLockModeAsInt > writeLockModeAsInt) { String msg = format("[%s] The used write LockMode [%s] should be equal or higher than the read LockMode [%s]", familyName, readLockMode, writeLockMode); throw new IllegalTxnFactoryException(msg); } if (readLockMode != LockMode.None && !trackReads) { String msg = format("[%s] If readLockMode is [%s] , read tracking should be enabled", familyName, readLockMode); throw new IllegalTxnFactoryException(msg); } if (speculativeConfiguration.get() == null) { SpeculativeGammaConfiguration newSpeculativeConfiguration; if (speculative) { newSpeculativeConfiguration = new SpeculativeGammaConfiguration( isFat(), false, false, false, false, false, false, false, false, false, 1); } else { newSpeculativeConfiguration = new SpeculativeGammaConfiguration( true, true, true, true, true, true, true, true, true, true, Integer.MAX_VALUE); } if (maximumPoorMansConflictScanLength == 0) { newSpeculativeConfiguration = newSpeculativeConfiguration.newWithRichMansConflictScan(); } speculativeConfiguration.compareAndSet(null, newSpeculativeConfiguration); } return this; } private boolean isFat() { if (isFat) { return true; } if (isolationLevel != IsolationLevel.Snapshot) { return true; } if (permanentListeners != null) { return true; } if (readLockMode != LockMode.None) { return true; } if (writeLockMode != LockMode.None) { return true; } if (dirtyCheck) { return true; } if (readonly) { return true; } return false; } public GammaTxnConfig setTimeoutNs(long timeoutNs) { if (timeoutNs < 0) { throw new IllegalArgumentException("timeoutNs can't be smaller than 0"); } GammaTxnConfig config = new GammaTxnConfig(this); config.timeoutNs = timeoutNs; return config; } public GammaTxnConfig setFamilyName(String familyName) { if (familyName == null) { throw new NullPointerException("familyName can't be null"); } GammaTxnConfig config = new GammaTxnConfig(this); config.isAnonymous = false; config.familyName = familyName; return config; } public GammaTxnConfig setMaxRetries(int maxRetries) { if (maxRetries < 0) { throw new IllegalArgumentException("maxRetries can't be smaller than 0"); } GammaTxnConfig config = new GammaTxnConfig(this); config.maxRetries = maxRetries; return config; } public GammaTxnConfig setMaximumPoorMansConflictScanLength(int maximumPoorMansConflictScanLength) { if (maximumPoorMansConflictScanLength < 0) { throw new IllegalStateException(); } GammaTxnConfig config = new GammaTxnConfig(this); config.maximumPoorMansConflictScanLength = maximumPoorMansConflictScanLength; return config; } public GammaTxnConfig setReadTrackingEnabled(boolean trackReads) { GammaTxnConfig config = new GammaTxnConfig(this); config.trackReads = trackReads; return config; } public GammaTxnConfig setSpeculative(boolean speculativeConfigEnabled) { GammaTxnConfig config = new GammaTxnConfig(this); config.speculative = speculativeConfigEnabled; return config; } public GammaTxnConfig setReadonly(boolean readonly) { GammaTxnConfig config = new GammaTxnConfig(this); config.readonly = readonly; return config; } public GammaTxnConfig setDirtyCheckEnabled(boolean dirtyCheck) { GammaTxnConfig config = new GammaTxnConfig(this); config.dirtyCheck = dirtyCheck; return config; } public GammaTxnConfig setBlockingAllowed(boolean blockingAllowed) { GammaTxnConfig config = new GammaTxnConfig(this); config.blockingAllowed = blockingAllowed; return config; } public GammaTxnConfig setInterruptible(boolean interruptible) { GammaTxnConfig config = new GammaTxnConfig(this); config.interruptible = interruptible; return config; } public GammaTxnConfig setControlFlowErrorsReused(boolean controlFlowErrorsReused) { GammaTxnConfig config = new GammaTxnConfig(this); config.controlFlowErrorsReused = controlFlowErrorsReused; return config; } public GammaTxnConfig setSpinCount(int spinCount) { if (spinCount < 0) { throw new IllegalArgumentException("spinCount can't be smaller than 0"); } GammaTxnConfig config = new GammaTxnConfig(this); config.spinCount = spinCount; return config; } public GammaTxnConfig setBackoffPolicy(BackoffPolicy backoffPolicy) { if (backoffPolicy == null) { throw new NullPointerException("backoffPolicy can't be null"); } GammaTxnConfig config = new GammaTxnConfig(this); config.backoffPolicy = backoffPolicy; return config; } public GammaTxnConfig setTraceLevel(TraceLevel traceLevel) { if (traceLevel == null) { throw new NullPointerException("traceLevel can't be null"); } GammaTxnConfig config = new GammaTxnConfig(this); config.traceLevel = traceLevel; return config; } public GammaTxnConfig setPropagationLevel(PropagationLevel propagationLevel) { if (propagationLevel == null) { throw new NullPointerException(); } GammaTxnConfig config = new GammaTxnConfig(this); config.propagationLevel = propagationLevel; return config; } public GammaTxnConfig setIsolationLevel(IsolationLevel isolationLevel) { if (isolationLevel == null) { throw new NullPointerException(); } GammaTxnConfig config = new GammaTxnConfig(this); config.isolationLevel = isolationLevel; config.writeSkewAllowed = isolationLevel.doesAllowWriteSkew(); config.inconsistentReadAllowed = isolationLevel.doesAllowInconsistentRead(); config.unrepeatableReadAllowed = isolationLevel.doesAllowUnrepeatableRead(); return config; } public GammaTxnConfig setWriteLockMode(LockMode writeLockMode) { if (writeLockMode == null) { throw new NullPointerException(); } GammaTxnConfig config = new GammaTxnConfig(this); config.writeLockMode = writeLockMode; config.writeLockModeAsInt = writeLockMode.asInt(); return config; } public GammaTxnConfig setReadLockMode(LockMode readLockMode) { if (readLockMode == null) { throw new NullPointerException(); } GammaTxnConfig config = new GammaTxnConfig(this); config.readLockMode = readLockMode; config.readLockModeAsInt = readLockMode.asInt(); if (config.readLockModeAsInt > config.writeLockModeAsInt) { config.writeLockMode = config.readLockMode; config.writeLockModeAsInt = config.readLockModeAsInt; } return config; } public GammaTxnConfig setFat() { GammaTxnConfig config = new GammaTxnConfig(this); config.isFat = true; return config; } public GammaTxnConfig addPermanentListener(TxnListener listener) { if (listener == null) { throw new NullPointerException(); } //we need to clone the list since the GammaTxnConfig is considered to be immutable ArrayList<TxnListener> newPermanentListeners = new ArrayList<TxnListener>(); if (permanentListeners != null) { newPermanentListeners.addAll(permanentListeners); } newPermanentListeners.add(listener); GammaTxnConfig config = new GammaTxnConfig(this); config.permanentListeners = newPermanentListeners; return config; } @Override public String toString() { return "GammaTxnConfig{" + "speculativeConfiguration=" + speculativeConfiguration + ", globalConflictCounter=" + globalConflictCounter + ", propagationLevel=" + propagationLevel + ", isolationLevel=" + isolationLevel + ", writeSkewAllowed=" + writeSkewAllowed + ", inconsistentReadAllowed=" + inconsistentReadAllowed + ", readLockMode=" + readLockMode + ", writeLockMode=" + writeLockMode + ", readLockModeAsInt=" + readLockModeAsInt + ", writeLockModeAsInt=" + writeLockModeAsInt + ", familyName='" + familyName + '\'' + ", isAnonymous=" + isAnonymous + ", interruptible=" + interruptible + ", readonly=" + readonly + ", spinCount=" + spinCount + ", dirtyCheck=" + dirtyCheck + ", minimalArrayTreeSize=" + minimalArrayTreeSize + ", trackReads=" + trackReads + ", blockingAllowed=" + blockingAllowed + ", maxRetries=" + maxRetries + ", speculativeConfigEnabled=" + speculative + ", maxFixedLengthTransactionSize=" + maxFixedLengthTransactionSize + ", backoffPolicy=" + backoffPolicy + ", timeoutNs=" + timeoutNs + ", traceLevel=" + traceLevel + ", controlFlowErrorsReused=" + controlFlowErrorsReused + ", isFat=" + isFat + ", maximumPoorMansConflictScanLength=" + maximumPoorMansConflictScanLength + ", permanentListeners=" + permanentListeners + '}'; } }