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