1 /*
2  * Copyright (C) 2012 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 
21 import javax.annotation.Nullable;
22 
23 /**
24  * A settable future that can be set asynchronously via {@link #setFuture}.
25  * A similar effect could be accomplished by adding a listener to the delegate
26  * future that sets a normal settable future after the delegate is complete.
27  * This approach gains us the ability to keep track of whether a delegate has
28  * been set (i.e. so that we can prevent collisions from setting it twice and
29  * can know before the computation is done whether it has been set), as well
30  * as improved cancellation semantics (i.e. if either future is cancelled,
31  * then the other one is too).  This class is thread-safe.
32  *
33  * @param <V> The result type returned by the Future's {@code get} method.
34  *
35  * @author Stephen Hicks
36  */
37 final class AsyncSettableFuture<V> extends ForwardingListenableFuture<V> {
38 
39   /** Creates a new asynchronously-settable future. */
create()40   public static <V> AsyncSettableFuture<V> create() {
41     return new AsyncSettableFuture<V>();
42   }
43 
44   private final NestedFuture<V> nested = new NestedFuture<V>();
45   private final ListenableFuture<V> dereferenced = Futures.dereference(nested);
46 
AsyncSettableFuture()47   private AsyncSettableFuture() {}
48 
delegate()49   @Override protected ListenableFuture<V> delegate() {
50     return dereferenced;
51   }
52 
53   /**
54    * Sets this future to forward to the given future.  Returns {@code true}
55    * if the future was able to be set (i.e. it hasn't been set already).
56    */
setFuture(ListenableFuture<? extends V> future)57   public boolean setFuture(ListenableFuture<? extends V> future) {
58     return nested.setFuture(checkNotNull(future));
59   }
60 
61   /**
62    * Convenience method that calls {@link #setFuture} on a {@link
63    * Futures#immediateFuture}.  Returns {@code true} if the future
64    * was able to be set (i.e. it hasn't been set already).
65    */
setValue(@ullable V value)66   public boolean setValue(@Nullable V value) {
67     return setFuture(Futures.immediateFuture(value));
68   }
69 
70   /**
71    * Convenience method that calls {@link #setFuture} on a {@link
72    * Futures#immediateFailedFuture}.  Returns {@code true} if the
73    * future was able to be set (i.e. it hasn't been set already).
74    */
setException(Throwable exception)75   public boolean setException(Throwable exception) {
76     return setFuture(Futures.<V>immediateFailedFuture(exception));
77   }
78 
79   /**
80    * Returns {@code true} if this future has been (possibly asynchronously) set.
81    * Note that a {@code false} result in no way gaurantees that a later call
82    * to, e.g., {@link #setFuture} will succeed, since another thread could
83    * make the call in between.  This is somewhat analogous to {@link #isDone},
84    * but since setting and completing are not the same event, it is useful to
85    * have this method broken out.
86    */
isSet()87   public boolean isSet() {
88     return nested.isDone();
89   }
90 
91   private static final class NestedFuture<V> extends AbstractFuture<ListenableFuture<? extends V>> {
setFuture(ListenableFuture<? extends V> value)92     boolean setFuture(ListenableFuture<? extends V> value) {
93       boolean result = set(value);
94       if (isCancelled()) {
95         value.cancel(wasInterrupted());
96       }
97       return result;
98     }
99   }
100 }
101