package org.multiverse.api.exceptions; /** * An {@link Error} thrown to regulate control flow inside multiverse {@link org.multiverse.api.TxnExecutor}. Normally * it would be a very bad thing to regulate control flow using an exception/error, but to make seamless integration in the Java * language possible, there is no better alternative. So these exceptions should not catch unless you really know know what you * are doing. So catching all Throwable instances (including Error) is a bad practice. * <p/> * There current are 3 different types of ControlFlowErrors: * <ol> * <li>{@link ReadWriteConflict}: used to indicate read and write conflicts</li> * <li>{@link RetryError}: used for blocking</li> * <li>{@link SpeculativeConfigurationError}: used for guessing the most optimal configuration for transactions</li> * </ol> * * <h3>Why it is an Error</h3> * * <p>It is an Error instead of a RuntimeException, to prevent users trying to catch the error inside a * try/catch(RuntimeException) block and consuming important events like a ReadWriteConflict. In most cases these events can * be solved by retrying the transaction. * * <p>It is an Error instead of a Throwable, because a Throwable needs to be propagated, so making the code a lot more * awkward to work with. * * <h3>Instance Caching</h3> * * <p>Normally ControlFlowErrors are cached to be reused because they can be thrown very often to be caught by the TxnExecutor * and discarded. Especially the stacktrace is very expensive to create. By default all ControlFlowErrors are reused * but with the {@link org.multiverse.api.TxnFactoryBuilder#setControlFlowErrorsReused(boolean)} this behavior * can be changed. It also can be configured on the Stm level, depending on the Stm implementation. For the * {@link org.multiverse.stms.gamma.GammaStm} you need to look at the * {@link org.multiverse.stms.gamma.GammaStmConfig#controlFlowErrorsReused}. * * <p>The constructors also expose configuration options to have the StackTrace filled. Especially for an Error that is * reused, not filling the stacktrace is very important because else the developer is looking at code where the exception * very likely didn't happen. * * @author Peter Veentjer */ public abstract class ControlFlowError extends Error { private static final long serialVersionUID = 0; private final boolean fillStackTrace; /** * Creates a new ControlFlowError. * * @param fillStackTrace true if the StackTrace should be filled, false otherwise. */ public ControlFlowError(boolean fillStackTrace) { this(fillStackTrace, null, null); } /** * Creates a new ControlFlowError with the provided message. * * @param fillStackTrace true if the StackTrace should be filled, false otherwise. * @param message the message of the exception. */ public ControlFlowError(boolean fillStackTrace, String message) { this(fillStackTrace, message, null); } /** * Creates a new ControlFlowError with the provided message and cause. * * @param fillStackTrace true if the StackTrace should be filled, false otherwise. * @param message the message of the exception. * @param cause the cause of the exception. */ public ControlFlowError(boolean fillStackTrace, String message, Throwable cause) { super(message, cause); this.fillStackTrace = fillStackTrace; } @Override public StackTraceElement[] getStackTrace() { if (fillStackTrace) { return super.getStackTrace(); } else { return new StackTraceElement[0]; } } }