class SyncExecutionImpl<R> extends ExecutionImpl<R> implements SyncExecutionInternal<R> {
  // -- Cross-attempt state --

  // An optional Failsafe executor
  private final FailsafeExecutor<R> executor;
  // An optional Failsafe call
  private final CallImpl<R> call;
  // The outermost function that executions begin with
  private Function<SyncExecutionInternal<R>, ExecutionResult<R>> outerFn;
  // The interruptable execution thread
  private final Thread executionThread;
  // Whether the execution is currently interruptable
  private final AtomicBoolean interruptable;
  // Whether the execution has been internally interrupted
  private final AtomicBoolean interrupted;

  // -- Per-attempt state --

  // The delay time in nanoseconds
  private volatile long delayNanos;

  /**
   * Create a standalone sync execution for the {@code policies}.
   */
  SyncExecutionImpl(List<? extends Policy<R>> policies) {
    super(policies);
    executor = null;
    call = null;
    interruptable = new AtomicBoolean();
    interrupted = new AtomicBoolean();
    executionThread = Thread.currentThread();
    preExecute();
  }

  /**
   * Create a sync execution for the {@code executor}.
   */
  SyncExecutionImpl(FailsafeExecutor<R> executor, Scheduler scheduler, CallImpl<R> call,
    Function<SyncExecutionInternal<R>, ExecutionResult<R>> innerFn) {
    super(executor.policies);
    this.executor = executor;
    this.call = call;
    interruptable = new AtomicBoolean();
    interrupted = new AtomicBoolean();
    executionThread = Thread.currentThread();
    if (call != null)
      call.setExecution(this);

    outerFn = innerFn;
    for (PolicyExecutor<R> policyExecutor : policyExecutors)
      outerFn = policyExecutor.apply(outerFn, scheduler);
  }

  /**
   * Create a sync execution for a new attempt.
   */
  private SyncExecutionImpl(SyncExecutionImpl<R> execution) {
    super(execution);
    executor = execution.executor;
    call = execution.call;
    interruptable = execution.interruptable;
    interrupted = execution.interrupted;
    executionThread = execution.executionThread;
    if (call != null)
      call.setExecution(this);
  }

  @Override
  public void complete() {
    postExecute(ExecutionResult.none());
  }

  @Override
  public boolean isComplete() {
    return completed;
  }

  @Override
  public Duration getDelay() {
    return Duration.ofNanos(delayNanos);
  }

  @Override
  public void record(R result, Throwable exception) {
    preExecute();
    postExecute(new ExecutionResult<>(result, exception));
  }

  @Override
  public void recordResult(R result) {
    preExecute();
    postExecute(new ExecutionResult<>(result, null));
  }

  @Override
  public void recordException(Throwable exception) {
    preExecute();
    postExecute(new ExecutionResult<>(null, exception));
  }

  @Override
  public synchronized void preExecute() {<FILL_FUNCTION_BODY>}

  @Override
  synchronized ExecutionResult<R> postExecute(ExecutionResult<R> result) {
    result = super.postExecute(result);
    delayNanos = result.getDelay();
    return result;
  }

  @Override
  public boolean isInterrupted() {
    return interrupted.get();
  }

  @Override
  public void setInterruptable(boolean interruptable) {
    this.interruptable.set(interruptable);
  }

  @Override
  public void interrupt() {
    // Guard against race with the execution becoming uninterruptable
    synchronized (getLock()) {
      if (interruptable.get()) {
        interrupted.set(true);
        executionThread.interrupt();
      }
    }
  }

  private boolean isStandalone() {
    return executor == null;
  }

  @Override
  public SyncExecutionImpl<R> copy() {
    return isStandalone() ? this : new SyncExecutionImpl<>(this);
  }

  /**
   * Performs a synchronous execution.
   */
  R executeSync() {
    ExecutionResult<R> result = outerFn.apply(this);
    completed = result.isComplete();
    executor.completionHandler.accept(result, this);
    Throwable exception = result.getException();
    if (exception != null) {
      if (exception instanceof RuntimeException)
        throw (RuntimeException) exception;
      if (exception instanceof Error)
        throw (Error) exception;
      throw new FailsafeException(exception);
    }
    return result.getResult();
  }
}

    if (isStandalone()) {
      attemptRecorded = false;
      cancelledIndex = Integer.MIN_VALUE;
      interrupted.set(false);
    }
    super.preExecute();
    interruptable.set(true);
  
<methods>public boolean cancel() ,public void cancel(PolicyExecutor<R>) ,public int getAttemptCount() ,public java.time.Duration getElapsedAttemptTime() ,public java.time.Duration getElapsedTime() ,public int getExecutionCount() ,public T getLastException() ,public R getLastResult() ,public R getLastResult(R) ,public ExecutionInternal<R> getLatest() ,public java.lang.Object getLock() ,public ExecutionResult<R> getResult() ,public java.time.Instant getStartTime() ,public boolean isCancelled() ,public boolean isCancelled(PolicyExecutor<R>) ,public boolean isFirstAttempt() ,public boolean isPreExecuted() ,public boolean isRetry() ,public void onCancel(dev.failsafe.function.CheckedRunnable) ,public synchronized void preExecute() ,public synchronized void record(ExecutionResult<R>) ,public synchronized void recordAttempt() ,public java.lang.String toString() <variables>volatile boolean attemptRecorded,private volatile java.time.Instant attemptStartTime,private final non-sealed java.util.concurrent.atomic.AtomicInteger attempts,volatile dev.failsafe.function.CheckedRunnable cancelCallback,volatile int cancelledIndex,volatile boolean completed,private final non-sealed java.util.concurrent.atomic.AtomicInteger executions,private final non-sealed AtomicReference<ExecutionInternal<R>> latest,final non-sealed List<PolicyExecutor<R>> policyExecutors,private volatile boolean preExecuted,private final non-sealed ExecutionResult<R> previousResult,volatile ExecutionResult<R> result,private volatile java.time.Instant startTime