All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dev.mccue.guava.concurrent.AbstractCatchingFuture Maven / Gradle / Ivy

There is a newer version: 33.2.0
Show newest version
/*
 * Copyright (C) 2006 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package dev.mccue.guava.concurrent;

import static dev.mccue.guava.base.Preconditions.checkNotNull;
import static dev.mccue.guava.concurrent.Futures.getDone;
import static dev.mccue.guava.concurrent.MoreExecutors.rejectionPropagatingExecutor;
import static dev.mccue.guava.concurrent.NullnessCasts.uncheckedCastNullableTToT;
import static dev.mccue.guava.concurrent.Platform.isInstanceOfThrowableClass;
import static dev.mccue.guava.concurrent.Platform.restoreInterruptIfIsInterruptedException;

import dev.mccue.guava.base.Function;
import dev.mccue.guava.concurrent.internal.InternalFutureFailureAccess;
import dev.mccue.guava.concurrent.internal.InternalFutures;
import com.google.errorprone.annotations.ForOverride;
import com.google.errorprone.annotations.concurrent.LazyInit;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import dev.mccue.jsr305.CheckForNull;
import org.checkerframework.checker.nullness.qual.Nullable;

/** Implementations of {@code Futures.catching*}. */
@ElementTypesAreNonnullByDefault
@SuppressWarnings("nullness") // TODO(b/147136275): Remove once our checker understands & and |.
abstract class AbstractCatchingFuture<
        V extends @Nullable Object, X extends Throwable, F, T extends @Nullable Object>
    extends FluentFuture.TrustedFuture implements Runnable {
  static  ListenableFuture create(
      ListenableFuture input,
      Class exceptionType,
      Function fallback,
      Executor executor) {
    CatchingFuture future = new CatchingFuture<>(input, exceptionType, fallback);
    input.addListener(future, rejectionPropagatingExecutor(executor, future));
    return future;
  }

  static  ListenableFuture create(
      ListenableFuture input,
      Class exceptionType,
      AsyncFunction fallback,
      Executor executor) {
    AsyncCatchingFuture future = new AsyncCatchingFuture<>(input, exceptionType, fallback);
    input.addListener(future, rejectionPropagatingExecutor(executor, future));
    return future;
  }

  /*
   * In certain circumstances, this field might theoretically not be visible to an afterDone() call
   * triggered by cancel(). For details, see the comments on the fields of TimeoutFuture.
   */
  @CheckForNull @LazyInit ListenableFuture inputFuture;
  @CheckForNull @LazyInit Class exceptionType;
  @CheckForNull @LazyInit F fallback;

  AbstractCatchingFuture(
      ListenableFuture inputFuture, Class exceptionType, F fallback) {
    this.inputFuture = checkNotNull(inputFuture);
    this.exceptionType = checkNotNull(exceptionType);
    this.fallback = checkNotNull(fallback);
  }

  @Override
  public final void run() {
    ListenableFuture localInputFuture = inputFuture;
    Class localExceptionType = exceptionType;
    F localFallback = fallback;
    if (localInputFuture == null | localExceptionType == null | localFallback == null
        // This check, unlike all the others, is a volatile read
        || isCancelled()) {
      return;
    }
    inputFuture = null;

    // For an explanation of the cases here, see the comments on AbstractTransformFuture.run.
    V sourceResult = null;
    Throwable throwable = null;
    try {
      if (localInputFuture instanceof InternalFutureFailureAccess) {
        throwable =
            InternalFutures.tryInternalFastPathGetFailure(
                (InternalFutureFailureAccess) localInputFuture);
      }
      if (throwable == null) {
        sourceResult = getDone(localInputFuture);
      }
    } catch (ExecutionException e) {
      throwable = e.getCause();
      if (throwable == null) {
        throwable =
            new NullPointerException(
                "Future type "
                    + localInputFuture.getClass()
                    + " threw "
                    + e.getClass()
                    + " without a cause");
      }
    } catch (Throwable t) { // this includes CancellationException and sneaky checked exception
      throwable = t;
    }

    if (throwable == null) {
      /*
       * The cast is safe: There was no exception, so the assignment from getDone must have
       * succeeded.
       */
      set(uncheckedCastNullableTToT(sourceResult));
      return;
    }

    if (!isInstanceOfThrowableClass(throwable, localExceptionType)) {
      setFuture(localInputFuture);
      // TODO(cpovirk): Test that fallback is not run in this case.
      return;
    }

    @SuppressWarnings("unchecked") // verified safe by isInstanceOfThrowableClass
    X castThrowable = (X) throwable;
    T fallbackResult;
    try {
      fallbackResult = doFallback(localFallback, castThrowable);
    } catch (Throwable t) {
      restoreInterruptIfIsInterruptedException(t);
      setException(t);
      return;
    } finally {
      exceptionType = null;
      fallback = null;
    }

    setResult(fallbackResult);
  }

  @Override
  @CheckForNull
  protected String pendingToString() {
    ListenableFuture localInputFuture = inputFuture;
    Class localExceptionType = exceptionType;
    F localFallback = fallback;
    String superString = super.pendingToString();
    String resultString = "";
    if (localInputFuture != null) {
      resultString = "inputFuture=[" + localInputFuture + "], ";
    }
    if (localExceptionType != null && localFallback != null) {
      return resultString
          + "exceptionType=["
          + localExceptionType
          + "], fallback=["
          + localFallback
          + "]";
    } else if (superString != null) {
      return resultString + superString;
    }
    return null;
  }

  /** Template method for subtypes to actually run the fallback. */
  @ForOverride
  @ParametricNullness
  abstract T doFallback(F fallback, X throwable) throws Exception;

  /** Template method for subtypes to actually set the result. */
  @ForOverride
  abstract void setResult(@ParametricNullness T result);

  @Override
  protected final void afterDone() {
    maybePropagateCancellationTo(inputFuture);
    this.inputFuture = null;
    this.exceptionType = null;
    this.fallback = null;
  }

  /**
   * An {@code AbstractCatchingFuture} that delegates to an {@code AsyncFunction} and {@code
   * #setFuture(ListenableFuture)}.
   */
  private static final class AsyncCatchingFuture
      extends AbstractCatchingFuture<
          V, X, AsyncFunction, ListenableFuture> {
    AsyncCatchingFuture(
        ListenableFuture input,
        Class exceptionType,
        AsyncFunction fallback) {
      super(input, exceptionType, fallback);
    }

    @Override
    ListenableFuture doFallback(
        AsyncFunction fallback, X cause) throws Exception {
      ListenableFuture replacement = fallback.apply(cause);
      checkNotNull(
          replacement,
          "AsyncFunction.apply returned null instead of a Future. "
              + "Did you mean to return immediateFuture(null)? %s",
          fallback);
      return replacement;
    }

    @Override
    void setResult(ListenableFuture result) {
      setFuture(result);
    }
  }

  /**
   * An {@code AbstractCatchingFuture} that delegates to a {@code Function} and {@code
   * #set(Object)}.
   */
  private static final class CatchingFuture
      extends AbstractCatchingFuture, V> {
    CatchingFuture(
        ListenableFuture input,
        Class exceptionType,
        Function fallback) {
      super(input, exceptionType, fallback);
    }

    @Override
    @ParametricNullness
    V doFallback(Function fallback, X cause) throws Exception {
      return fallback.apply(cause);
    }

    @Override
    void setResult(@ParametricNullness V result) {
      set(result);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy