• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.util.concurrent;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 import static com.google.common.base.Strings.isNullOrEmpty;
21 import static com.google.common.util.concurrent.Futures.getDone;
22 import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
23 
24 import com.google.common.util.concurrent.internal.InternalFutureFailureAccess;
25 import com.google.errorprone.annotations.CanIgnoreReturnValue;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.concurrent.CancellationException;
29 import java.util.concurrent.ExecutionException;
30 import java.util.concurrent.Executor;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.TimeUnit;
33 import java.util.concurrent.TimeoutException;
34 import java.util.logging.Level;
35 import java.util.logging.Logger;
36 import org.checkerframework.checker.nullness.qual.Nullable;
37 
38 /** Emulation for AbstractFuture in GWT. */
39 @SuppressWarnings("nullness") // TODO(b/147136275): Remove once our checker understands & and |.
40 @ElementTypesAreNonnullByDefault
41 public abstract class AbstractFuture<V extends @Nullable Object> extends InternalFutureFailureAccess
42     implements ListenableFuture<V> {
43 
44   static final boolean GENERATE_CANCELLATION_CAUSES = false;
45 
46   /**
47    * Tag interface marking trusted subclasses. This enables some optimizations. The implementation
48    * of this interface must also be an AbstractFuture and must not override or expose for overriding
49    * any of the public methods of ListenableFuture.
50    */
51   interface Trusted<V extends @Nullable Object> extends ListenableFuture<V> {}
52 
53   abstract static class TrustedFuture<V extends @Nullable Object> extends AbstractFuture<V>
54       implements Trusted<V> {
55     @CanIgnoreReturnValue
56     @Override
get()57     public final V get() throws InterruptedException, ExecutionException {
58       return super.get();
59     }
60 
61     @CanIgnoreReturnValue
62     @Override
get(long timeout, TimeUnit unit)63     public final V get(long timeout, TimeUnit unit)
64         throws InterruptedException, ExecutionException, TimeoutException {
65       return super.get(timeout, unit);
66     }
67 
68     @Override
isDone()69     public final boolean isDone() {
70       return super.isDone();
71     }
72 
73     @Override
isCancelled()74     public final boolean isCancelled() {
75       return super.isCancelled();
76     }
77 
78     @Override
addListener(Runnable listener, Executor executor)79     public final void addListener(Runnable listener, Executor executor) {
80       super.addListener(listener, executor);
81     }
82 
83     @CanIgnoreReturnValue
84     @Override
cancel(boolean mayInterruptIfRunning)85     public final boolean cancel(boolean mayInterruptIfRunning) {
86       return super.cancel(mayInterruptIfRunning);
87     }
88   }
89 
90   private static final Logger log = Logger.getLogger(AbstractFuture.class.getName());
91 
92   private State state;
93   private V value;
94   private @Nullable Future<? extends V> delegate;
95   private @Nullable Throwable throwable;
96   private boolean mayInterruptIfRunning;
97   private List<Listener> listeners;
98 
AbstractFuture()99   protected AbstractFuture() {
100     state = State.PENDING;
101     listeners = new ArrayList<Listener>();
102   }
103 
104   @CanIgnoreReturnValue
105   @Override
cancel(boolean mayInterruptIfRunning)106   public boolean cancel(boolean mayInterruptIfRunning) {
107     if (!state.permitsPublicUserToTransitionTo(State.CANCELLED)) {
108       return false;
109     }
110 
111     this.mayInterruptIfRunning = mayInterruptIfRunning;
112     state = State.CANCELLED;
113     notifyAndClearListeners();
114 
115     if (delegate != null) {
116       // TODO(lukes): consider adding the StackOverflowError protection from the server version
117       delegate.cancel(mayInterruptIfRunning);
118     }
119 
120     return true;
121   }
122 
interruptTask()123   protected void interruptTask() {}
124 
125   @Override
isCancelled()126   public boolean isCancelled() {
127     return state.isCancelled();
128   }
129 
130   @Override
isDone()131   public boolean isDone() {
132     return state.isDone();
133   }
134 
135   /*
136    * ForwardingFluentFuture needs to override those methods, so they are not final.
137    */
138   @CanIgnoreReturnValue
139   @Override
get()140   public V get() throws InterruptedException, ExecutionException {
141     state.maybeThrowOnGet(throwable);
142     return value;
143   }
144 
145   @CanIgnoreReturnValue
146   @Override
get(long timeout, TimeUnit unit)147   public V get(long timeout, TimeUnit unit)
148       throws InterruptedException, ExecutionException, TimeoutException {
149     checkNotNull(unit);
150     return get();
151   }
152 
153   @Override
addListener(Runnable runnable, Executor executor)154   public void addListener(Runnable runnable, Executor executor) {
155     Listener listener = new Listener(runnable, executor);
156     if (isDone()) {
157       listener.execute();
158     } else {
159       listeners.add(listener);
160     }
161   }
162 
163   @CanIgnoreReturnValue
setException(Throwable throwable)164   protected boolean setException(Throwable throwable) {
165     checkNotNull(throwable);
166     if (!state.permitsPublicUserToTransitionTo(State.FAILURE)) {
167       return false;
168     }
169 
170     forceSetException(throwable);
171     return true;
172   }
173 
forceSetException(Throwable throwable)174   private void forceSetException(Throwable throwable) {
175     this.throwable = throwable;
176     this.state = State.FAILURE;
177     notifyAndClearListeners();
178   }
179 
180   @CanIgnoreReturnValue
set(V value)181   protected boolean set(V value) {
182     if (!state.permitsPublicUserToTransitionTo(State.VALUE)) {
183       return false;
184     }
185 
186     forceSet(value);
187     return true;
188   }
189 
forceSet(V value)190   private void forceSet(V value) {
191     this.value = value;
192     this.state = State.VALUE;
193     notifyAndClearListeners();
194   }
195 
196   @CanIgnoreReturnValue
setFuture(ListenableFuture<? extends V> future)197   protected boolean setFuture(ListenableFuture<? extends V> future) {
198     checkNotNull(future);
199 
200     // If this future is already cancelled, cancel the delegate.
201     // TODO(cpovirk): Should we do this at the end of the method, as in the server version?
202     // TODO(cpovirk): Use maybePropagateCancellationTo?
203     if (isCancelled()) {
204       future.cancel(mayInterruptIfRunning);
205     }
206 
207     if (!state.permitsPublicUserToTransitionTo(State.DELEGATED)) {
208       return false;
209     }
210 
211     state = State.DELEGATED;
212     this.delegate = future;
213 
214     future.addListener(new SetFuture(future), directExecutor());
215     return true;
216   }
217 
wasInterrupted()218   protected final boolean wasInterrupted() {
219     return mayInterruptIfRunning;
220   }
221 
notifyAndClearListeners()222   private void notifyAndClearListeners() {
223     afterDone();
224     // TODO(lukes): consider adding the StackOverflowError protection from the server version
225     // TODO(cpovirk): consider clearing this.delegate
226     for (Listener listener : listeners) {
227       listener.execute();
228     }
229     listeners = null;
230   }
231 
afterDone()232   protected void afterDone() {}
233 
234   @Override
tryInternalFastPathGetFailure()235   protected final Throwable tryInternalFastPathGetFailure() {
236     if (this instanceof Trusted) {
237       return state == State.FAILURE ? throwable : null;
238     }
239     return null;
240   }
241 
maybePropagateCancellationTo(@ullable Future<?> related)242   final void maybePropagateCancellationTo(@Nullable Future<?> related) {
243     if (related != null & isCancelled()) {
244       related.cancel(wasInterrupted());
245     }
246   }
247 
248   @Override
toString()249   public String toString() {
250     StringBuilder builder = new StringBuilder().append(super.toString()).append("[status=");
251     if (isCancelled()) {
252       builder.append("CANCELLED");
253     } else if (isDone()) {
254       addDoneString(builder);
255     } else {
256       String pendingDescription;
257       try {
258         pendingDescription = pendingToString();
259       } catch (RuntimeException e) {
260         // Don't call getMessage or toString() on the exception, in case the exception thrown by the
261         // subclass is implemented with bugs similar to the subclass.
262         pendingDescription = "Exception thrown from implementation: " + e.getClass();
263       }
264       // The future may complete during or before the call to getPendingToString, so we use null
265       // as a signal that we should try checking if the future is done again.
266       if (!isNullOrEmpty(pendingDescription)) {
267         builder.append("PENDING, info=[").append(pendingDescription).append("]");
268       } else if (isDone()) {
269         addDoneString(builder);
270       } else {
271         builder.append("PENDING");
272       }
273     }
274     return builder.append("]").toString();
275   }
276 
277   /**
278    * Provide a human-readable explanation of why this future has not yet completed.
279    *
280    * @return null if an explanation cannot be provided because the future is done.
281    */
282   @Nullable
pendingToString()283   String pendingToString() {
284     if (state == State.DELEGATED) {
285       return "setFuture=[" + delegate + "]";
286     }
287     return null;
288   }
289 
addDoneString(StringBuilder builder)290   private void addDoneString(StringBuilder builder) {
291     try {
292       V value = getDone(this);
293       builder.append("SUCCESS, result=[").append(value).append("]");
294     } catch (ExecutionException e) {
295       builder.append("FAILURE, cause=[").append(e.getCause()).append("]");
296     } catch (CancellationException e) {
297       builder.append("CANCELLED");
298     } catch (RuntimeException e) {
299       builder.append("UNKNOWN, cause=[").append(e.getClass()).append(" thrown from get()]");
300     }
301   }
302 
303   private enum State {
304     PENDING {
305       @Override
isDone()306       boolean isDone() {
307         return false;
308       }
309 
310       @Override
maybeThrowOnGet(@ullable Throwable cause)311       void maybeThrowOnGet(@Nullable Throwable cause) throws ExecutionException {
312         throw new IllegalStateException("Cannot get() on a pending future.");
313       }
314 
315       @Override
permitsPublicUserToTransitionTo(State state)316       boolean permitsPublicUserToTransitionTo(State state) {
317         return !state.equals(PENDING);
318       }
319     },
320     DELEGATED {
321       @Override
isDone()322       boolean isDone() {
323         return false;
324       }
325 
326       @Override
maybeThrowOnGet(@ullable Throwable cause)327       void maybeThrowOnGet(@Nullable Throwable cause) throws ExecutionException {
328         throw new IllegalStateException("Cannot get() on a pending future.");
329       }
330 
permitsPublicUserToTransitionTo(State state)331       boolean permitsPublicUserToTransitionTo(State state) {
332         return state.equals(CANCELLED);
333       }
334     },
335     VALUE,
336     FAILURE {
337       @Override
maybeThrowOnGet(@ullable Throwable cause)338       void maybeThrowOnGet(@Nullable Throwable cause) throws ExecutionException {
339         throw new ExecutionException(cause);
340       }
341     },
342     CANCELLED {
343       @Override
isCancelled()344       boolean isCancelled() {
345         return true;
346       }
347 
348       @Override
maybeThrowOnGet(@ullable Throwable cause)349       void maybeThrowOnGet(@Nullable Throwable cause) throws ExecutionException {
350         // TODO(cpovirk): chain in a CancellationException created at the cancel() call?
351         throw new CancellationException();
352       }
353     };
354 
isDone()355     boolean isDone() {
356       return true;
357     }
358 
isCancelled()359     boolean isCancelled() {
360       return false;
361     }
362 
maybeThrowOnGet(@ullable Throwable cause)363     void maybeThrowOnGet(@Nullable Throwable cause) throws ExecutionException {}
364 
permitsPublicUserToTransitionTo(State state)365     boolean permitsPublicUserToTransitionTo(State state) {
366       return false;
367     }
368   }
369 
370   private static final class Listener {
371     final Runnable command;
372     final Executor executor;
373 
Listener(Runnable command, Executor executor)374     Listener(Runnable command, Executor executor) {
375       this.command = checkNotNull(command);
376       this.executor = checkNotNull(executor);
377     }
378 
execute()379     void execute() {
380       try {
381         executor.execute(command);
382       } catch (RuntimeException e) {
383         log.log(
384             Level.SEVERE,
385             "RuntimeException while executing runnable " + command + " with executor " + executor,
386             e);
387       }
388     }
389   }
390 
391   private final class SetFuture implements Runnable {
392     final ListenableFuture<? extends V> delegate;
393 
SetFuture(ListenableFuture<? extends V> delegate)394     SetFuture(ListenableFuture<? extends V> delegate) {
395       this.delegate = delegate;
396     }
397 
398     @Override
run()399     public void run() {
400       if (isCancelled()) {
401         return;
402       }
403 
404       if (delegate instanceof AbstractFuture) {
405         AbstractFuture<? extends V> other = (AbstractFuture<? extends V>) delegate;
406         value = other.value;
407         throwable = other.throwable;
408         // don't copy the mayInterruptIfRunning bit, for consistency with the server, to ensure that
409         // interruptTask() is called if and only if the bit is true and because we cannot infer the
410         // interrupt status from non AbstractFuture futures.
411         state = other.state;
412 
413         notifyAndClearListeners();
414         return;
415       }
416 
417       /*
418        * Almost everything in GWT is an AbstractFuture (which is as good as TrustedFuture under
419        * GWT). But ImmediateFuture and UncheckedThrowingFuture aren't, so we still need this case.
420        */
421       try {
422         forceSet(getDone(delegate));
423       } catch (ExecutionException exception) {
424         forceSetException(exception.getCause());
425       } catch (CancellationException cancellation) {
426         cancel(false);
427       } catch (Throwable t) {
428         forceSetException(t);
429       }
430     }
431   }
432 }
433