| /* |
| * Copyright (C) 2007 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. |
| */ |
| |
| /** |
| * Some portions of this class have been modified to make it functional in this |
| * package. |
| */ |
| package org.apache.hadoop.ozone.container.common.volume; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.common.util.concurrent.ListenableFuture; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import javax.annotation.Nullable; |
| import java.util.concurrent.Future; |
| import java.util.concurrent.ScheduledExecutorService; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.TimeoutException; |
| |
| /** |
| * Implementation of {@code Futures#withTimeout}. |
| * <p> |
| * <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. |
| */ |
| final class TimeoutFuture<V> extends AbstractFuture.TrustedFuture<V> { |
| public static final Logger LOG = LoggerFactory.getLogger( |
| TimeoutFuture.class); |
| |
| static <V> ListenableFuture<V> create( |
| ListenableFuture<V> delegate, |
| long time, |
| TimeUnit unit, |
| ScheduledExecutorService scheduledExecutor) { |
| TimeoutFuture<V> result = new TimeoutFuture<V>(delegate); |
| TimeoutFuture.Fire<V> fire = new TimeoutFuture.Fire<V>(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). |
| */ |
| |
| @Nullable private ListenableFuture<V> delegateRef; |
| @Nullable private Future<?> 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 |
| private 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 { |
| timeoutFuture.setException( |
| new TimeoutException("Future timed out: " + delegate)); |
| } finally { |
| delegate.cancel(true); |
| } |
| } |
| } |
| } |
| |
| @Override |
| protected void afterDone() { |
| maybePropagateCancellation(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; |
| } |
| } |