1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.atlassian.util.concurrent;
18
19 import java.util.concurrent.Callable;
20 import java.util.concurrent.CancellationException;
21 import java.util.concurrent.CountDownLatch;
22 import java.util.concurrent.ExecutionException;
23 import java.util.concurrent.Future;
24 import java.util.concurrent.FutureTask;
25 import java.util.concurrent.TimeUnit;
26 import java.util.concurrent.TimeoutException;
27 import java.util.concurrent.atomic.AtomicReference;
28
29 import net.jcip.annotations.ThreadSafe;
30
31
32
33
34
35
36
37
38
39
40
41 @ThreadSafe
42 public class SettableFuture<T> implements Future<T> {
43 private final AtomicReference<Value<T>> ref = new AtomicReference<Value<T>>();
44 private final CountDownLatch latch = new CountDownLatch(1);
45
46
47
48
49
50
51
52
53
54
55
56 public void set(final T value) {
57 setAndCheckValue(new ReferenceValue<T>(value));
58 }
59
60
61
62
63
64
65
66
67
68
69
70
71
72 public void setException(final Throwable throwable) {
73 setAndCheckValue(new ThrowableValue<T>(throwable));
74 }
75
76 public T get() throws InterruptedException, ExecutionException {
77 latch.await();
78 return ref.get().get();
79 }
80
81 public T get(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
82 if (!latch.await(timeout, unit)) {
83 throw new TimedOutException(timeout, unit);
84 }
85 return ref.get().get();
86 }
87
88 public boolean isDone() {
89 return ref.get() != null;
90 }
91
92 public boolean isCancelled() {
93 return isDone() && (ref.get() instanceof CancelledValue);
94 }
95
96 public boolean cancel(final boolean mayInterruptIfRunning) {
97 return setValue(new CancelledValue<T>()) == null;
98 }
99
100
101
102
103
104
105
106
107 private void setAndCheckValue(final Value<T> value) {
108 final Value<T> oldValue = setValue(value);
109 if ((oldValue != null) && !value.equals(oldValue)) {
110 throw new IllegalStateException("cannot change value after it has been set");
111 }
112 }
113
114
115
116
117
118
119
120 private Value<T> setValue(final Value<T> value) {
121 while (true) {
122 final Value<T> oldValue = ref.get();
123 if (oldValue != null) {
124 return oldValue;
125 }
126
127 if (!ref.compareAndSet(null, value)) {
128 continue;
129 }
130
131 latch.countDown();
132 return null;
133 }
134 }
135
136
137 private static interface Value<T> {
138 T get() throws ExecutionException;
139 }
140
141
142 private static class ReferenceValue<T> implements Value<T> {
143 private final T value;
144
145 ReferenceValue(final T value) {
146 this.value = value;
147 }
148
149 public T get() {
150 return value;
151 }
152
153 @Override
154 public boolean equals(final Object obj) {
155
156 if (!(obj instanceof ReferenceValue)) {
157 return false;
158 }
159 final ReferenceValue<?> other = (ReferenceValue<?>) obj;
160 return (value == null) ? (other.value == null) : value.equals(other.value);
161 }
162
163
164 @Override
165 public int hashCode() {
166 throw new UnsupportedOperationException();
167 }
168
169 }
170
171
172 private static class ThrowableValue<T> implements Value<T> {
173 private final Throwable throwable;
174
175 ThrowableValue(final Throwable throwable) {
176 this.throwable = throwable;
177 }
178
179 public T get() throws ExecutionException {
180 throw new ExecutionException(throwable);
181 }
182
183 @Override
184 public boolean equals(final Object obj) {
185
186 if (!(obj instanceof ThrowableValue)) {
187 return false;
188 }
189 return throwable.equals(((ThrowableValue<?>) obj).throwable);
190 }
191
192
193 @Override
194 public int hashCode() {
195 throw new UnsupportedOperationException();
196 }
197
198 }
199
200
201 private static class CancelledValue<T> implements Value<T> {
202 public T get() throws ExecutionException {
203 throw new CancellationException();
204 }
205 }
206 }