aboutsummaryrefslogtreecommitdiff
path: root/guava/src/com/google/common/util/concurrent/TimeoutFuture.java
diff options
context:
space:
mode:
Diffstat (limited to 'guava/src/com/google/common/util/concurrent/TimeoutFuture.java')
-rw-r--r--guava/src/com/google/common/util/concurrent/TimeoutFuture.java181
1 files changed, 0 insertions, 181 deletions
diff --git a/guava/src/com/google/common/util/concurrent/TimeoutFuture.java b/guava/src/com/google/common/util/concurrent/TimeoutFuture.java
deleted file mode 100644
index d09b24e1c..000000000
--- a/guava/src/com/google/common/util/concurrent/TimeoutFuture.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * 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 com.google.common.util.concurrent;
-
-import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
-
-import com.google.common.annotations.GwtIncompatible;
-import com.google.common.base.Preconditions;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import org.checkerframework.checker.nullness.qual.Nullable;
-
-/**
- * Implementation of {@code Futures#withTimeout}.
- *
- * <p>Future that delegates to another but will finish early (via a {@link TimeoutException} wrapped
- * in an {@link ExecutionException}) if the specified duration expires. The delegate future is
- * interrupted and cancelled if it times out.
- */
-@GwtIncompatible
-final class TimeoutFuture<V> extends FluentFuture.TrustedFuture<V> {
- static <V> ListenableFuture<V> create(
- ListenableFuture<V> delegate,
- long time,
- TimeUnit unit,
- ScheduledExecutorService scheduledExecutor) {
- TimeoutFuture<V> result = new TimeoutFuture<>(delegate);
- Fire<V> fire = new Fire<>(result);
- result.timer = scheduledExecutor.schedule(fire, time, unit);
- delegate.addListener(fire, directExecutor());
- return result;
- }
-
- /*
- * Memory visibility of these fields. There are two cases to consider.
- *
- * 1. visibility of the writes to these fields to Fire.run:
- *
- * The initial write to delegateRef is made definitely visible via the semantics of
- * addListener/SES.schedule. The later racy write in cancel() is not guaranteed to be observed,
- * however that is fine since the correctness is based on the atomic state in our base class. The
- * initial write to timer is never definitely visible to Fire.run since it is assigned after
- * SES.schedule is called. Therefore Fire.run has to check for null. However, it should be visible
- * if Fire.run is called by delegate.addListener since addListener is called after the assignment
- * to timer, and importantly this is the main situation in which we need to be able to see the
- * write.
- *
- * 2. visibility of the writes to an afterDone() call triggered by cancel():
- *
- * Since these fields are non-final that means that TimeoutFuture is not being 'safely published',
- * thus a motivated caller may be able to expose the reference to another thread that would then
- * call cancel() and be unable to cancel the delegate.
- * There are a number of ways to solve this, none of which are very pretty, and it is currently
- * believed to be a purely theoretical problem (since the other actions should supply sufficient
- * write-barriers).
- */
-
- private @Nullable ListenableFuture<V> delegateRef;
- private @Nullable ScheduledFuture<?> timer;
-
- private TimeoutFuture(ListenableFuture<V> delegate) {
- this.delegateRef = Preconditions.checkNotNull(delegate);
- }
-
- /** A runnable that is called when the delegate or the timer completes. */
- private static final class Fire<V> implements Runnable {
- @Nullable TimeoutFuture<V> timeoutFutureRef;
-
- Fire(TimeoutFuture<V> timeoutFuture) {
- this.timeoutFutureRef = timeoutFuture;
- }
-
- @Override
- public void run() {
- // If either of these reads return null then we must be after a successful cancel or another
- // call to this method.
- TimeoutFuture<V> timeoutFuture = timeoutFutureRef;
- if (timeoutFuture == null) {
- return;
- }
- ListenableFuture<V> delegate = timeoutFuture.delegateRef;
- if (delegate == null) {
- return;
- }
-
- /*
- * If we're about to complete the TimeoutFuture, we want to release our reference to it.
- * Otherwise, we'll pin it (and its result) in memory until the timeout task is GCed. (The
- * need to clear our reference to the TimeoutFuture is the reason we use a *static* nested
- * class with a manual reference back to the "containing" class.)
- *
- * This has the nice-ish side effect of limiting reentrancy: run() calls
- * timeoutFuture.setException() calls run(). That reentrancy would already be harmless, since
- * timeoutFuture can be set (and delegate cancelled) only once. (And "set only once" is
- * important for other reasons: run() can still be invoked concurrently in different threads,
- * even with the above null checks.)
- */
- timeoutFutureRef = null;
- if (delegate.isDone()) {
- timeoutFuture.setFuture(delegate);
- } else {
- try {
- ScheduledFuture<?> timer = timeoutFuture.timer;
- String message = "Timed out";
- if (timer != null) {
- long overDelayMs = Math.abs(timer.getDelay(TimeUnit.MILLISECONDS));
- if (overDelayMs > 10) { // Not all timing drift is worth reporting
- message += " (timeout delayed by " + overDelayMs + " ms after scheduled time)";
- }
- }
- timeoutFuture.timer = null; // Don't include already elapsed delay in delegate.toString()
- timeoutFuture.setException(new TimeoutFutureException(message + ": " + delegate));
- } finally {
- delegate.cancel(true);
- }
- }
- }
- }
-
- private static final class TimeoutFutureException extends TimeoutException {
- private TimeoutFutureException(String message) {
- super(message);
- }
-
- @Override
- public synchronized Throwable fillInStackTrace() {
- setStackTrace(new StackTraceElement[0]);
- return this; // no stack trace, wouldn't be useful anyway
- }
- }
-
- @Override
- protected String pendingToString() {
- ListenableFuture<? extends V> localInputFuture = delegateRef;
- ScheduledFuture<?> localTimer = timer;
- if (localInputFuture != null) {
- String message = "inputFuture=[" + localInputFuture + "]";
- if (localTimer != null) {
- final long delay = localTimer.getDelay(TimeUnit.MILLISECONDS);
- // Negative delays look confusing in an error message
- if (delay > 0) {
- message += ", remaining delay=[" + delay + " ms]";
- }
- }
- return message;
- }
- return null;
- }
-
- @Override
- protected void afterDone() {
- maybePropagateCancellationTo(delegateRef);
-
- Future<?> localTimer = timer;
- // Try to cancel the timer as an optimization.
- // timer may be null if this call to run was by the timer task since there is no happens-before
- // edge between the assignment to timer and an execution of the timer task.
- if (localTimer != null) {
- localTimer.cancel(false);
- }
-
- delegateRef = null;
- timer = null;
- }
-}