blob: 0f2e7fbf3c155cab19421dd0f605e470e02cc190 [file] [log] [blame]
Louis Ryanf6121162015-06-24 13:54:13 -07001/*
2 * Copyright 2015, Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32package io.grpc;
33
Louis Ryanf6121162015-06-24 13:54:13 -070034import java.util.ArrayList;
35import java.util.concurrent.Callable;
36import java.util.concurrent.Executor;
Louis Ryanf6121162015-06-24 13:54:13 -070037import java.util.concurrent.ScheduledExecutorService;
38import java.util.concurrent.ScheduledFuture;
39import java.util.concurrent.TimeUnit;
40import java.util.concurrent.TimeoutException;
41import java.util.logging.Level;
42import java.util.logging.Logger;
43
Louis Ryanf6121162015-06-24 13:54:13 -070044/**
Louis Ryan8dad7342016-02-18 16:30:06 -080045 * A context propagation mechanism which can carry scoped-values across API boundaries and between
46 * threads. Examples of state propagated via context include:
Louis Ryanf6121162015-06-24 13:54:13 -070047 * <ul>
Louis Ryanf6121162015-06-24 13:54:13 -070048 * <li>Security principals and credentials.</li>
Louis Ryan8dad7342016-02-18 16:30:06 -080049 * <li>Local and distributed tracing information.</li>
Louis Ryanf6121162015-06-24 13:54:13 -070050 * </ul>
51 *
Kun Zhang84862862016-12-02 09:47:58 -080052 * <p>A Context object can be {@link #attach attached} to the {@link Storage}, which effectively
53 * forms a <b>scope</b> for the context. The scope is bound to the current thread. Within a scope,
54 * its Context is accessible even across API boundaries, through {@link #current}. The scope is
55 * later exited by {@link #detach detaching} the Context.
56 *
57 * <p>Context objects are immutable and inherit state from their parent. To add or overwrite the
58 * current state a new context object must be created and then attached, replacing the previously
59 * bound context. For example:
60 *
Louis Ryanf6121162015-06-24 13:54:13 -070061 * <pre>
62 * Context withCredential = Context.current().withValue(CRED_KEY, cred);
63 * executorService.execute(withCredential.wrap(new Runnable() {
64 * public void run() {
65 * readUserRecords(userId, CRED_KEY.get());
66 * }
67 * }));
Louis Ryanf6121162015-06-24 13:54:13 -070068 * </pre>
69 *
Kun Zhang84862862016-12-02 09:47:58 -080070 * <p>Contexts are also used to represent a scoped unit of work. When the unit of work is done the
71 * context can be cancelled. This cancellation will also cascade to all descendant contexts. You can
72 * add a {@link CancellationListener} to a context to be notified when it or one of its ancestors
73 * has been cancelled. Cancellation does not release the state stored by a context and it's
74 * perfectly valid to {@link #attach()} an already cancelled context to make it current. To cancel a
75 * context (and its descendants) you first create a {@link CancellableContext} and when you need to
76 * signal cancellation call {@link CancellableContext#cancel} or {@link
77 * CancellableContext#detachAndCancel}. For example:
Louis Ryanf6121162015-06-24 13:54:13 -070078 * <pre>
79 * CancellableContext withCancellation = Context.current().withCancellation();
80 * try {
Louis Ryan49b51352015-11-06 17:01:26 -080081 * executorService.execute(withCancellation.wrap(new Runnable() {
Louis Ryanf6121162015-06-24 13:54:13 -070082 * public void run() {
83 * while (waitingForData() &amp;&amp; !Context.current().isCancelled()) {}
84 * }
85 * });
86 * doSomeWork();
87 * } catch (Throwable t) {
88 * withCancellation.cancel(t);
89 * }
90 * </pre>
91 *
Louis Ryan8dad7342016-02-18 16:30:06 -080092 * <p>Contexts can also be created with a timeout relative to the system nano clock which will
93 * cause it to automatically cancel at the desired time.
94 *
Louis Ryanf6121162015-06-24 13:54:13 -070095 *
96 * <p>Notes and cautions on use:
97 * <ul>
98 * <li>While Context objects are immutable they do not place such a restriction on the state
99 * they store.</li>
100 * <li>Context is not intended for passing optional parameters to an API and developers should
101 * take care to avoid excessive dependence on context when designing an API.</li>
102 * <li>If Context is being used in an environment that needs to support class unloading it is the
103 * responsibility of the application to ensure that all contexts are properly cancelled.</li>
104 * </ul>
105 */
106public class Context {
107
Eric Andersonc8648dc2016-08-04 11:08:36 -0700108 private static final Logger log = Logger.getLogger(Context.class.getName());
Louis Ryanf6121162015-06-24 13:54:13 -0700109
Louis Ryanf6121162015-06-24 13:54:13 -0700110 private static final Object[][] EMPTY_ENTRIES = new Object[0][2];
111
Louis Ryan8dad7342016-02-18 16:30:06 -0800112 private static final Key<Deadline> DEADLINE_KEY = new Key<Deadline>("deadline");
113
Louis Ryanf6121162015-06-24 13:54:13 -0700114 /**
Kun Zhang84862862016-12-02 09:47:58 -0800115 * The logical root context which is the ultimate ancestor of all contexts. This context
Louis Ryanf6121162015-06-24 13:54:13 -0700116 * is not cancellable and so will not cascade cancellation or retain listeners.
Kun Zhang84862862016-12-02 09:47:58 -0800117 *
118 * <p>Never assume this is the default context for new threads, because {@link Storage} may define
119 * a default context that is different from ROOT.
Louis Ryanf6121162015-06-24 13:54:13 -0700120 */
121 public static final Context ROOT = new Context(null);
122
Kun Zhangb34d07d2016-12-21 13:21:10 -0800123 // One and only one of them is non-null
124 private static final Storage storage;
125 private static final Exception storageInitError;
Kun Zhang84862862016-12-02 09:47:58 -0800126
Kun Zhangb34d07d2016-12-21 13:21:10 -0800127 static {
128 Storage newStorage = null;
129 Exception error = null;
Kun Zhang84862862016-12-02 09:47:58 -0800130 try {
131 Class<?> clazz = Class.forName("io.grpc.ContextStorageOverride");
Kun Zhangb34d07d2016-12-21 13:21:10 -0800132 newStorage = (Storage) clazz.getConstructor().newInstance();
Kun Zhang84862862016-12-02 09:47:58 -0800133 } catch (ClassNotFoundException e) {
134 log.log(Level.FINE, "Storage override doesn't exist. Using default.", e);
Kun Zhangb34d07d2016-12-21 13:21:10 -0800135 newStorage = new ThreadLocalContextStorage();
Eric Andersoncfb6bd02016-12-08 10:19:08 -0800136 } catch (Exception e) {
Kun Zhangb34d07d2016-12-21 13:21:10 -0800137 error = e;
Kun Zhang84862862016-12-02 09:47:58 -0800138 }
Kun Zhangb34d07d2016-12-21 13:21:10 -0800139 storage = newStorage;
140 storageInitError = error;
Kun Zhang84862862016-12-02 09:47:58 -0800141 }
142
143 // For testing
144 static Storage storage() {
145 if (storage == null) {
Kun Zhangb34d07d2016-12-21 13:21:10 -0800146 throw new RuntimeException("Storage override had failed to initialize", storageInitError);
Kun Zhang84862862016-12-02 09:47:58 -0800147 }
148 return storage;
149 }
Louis Ryan49b51352015-11-06 17:01:26 -0800150
151 /**
Eric Anderson4a427b72016-01-23 09:24:00 -0800152 * Create a {@link Key} with the given debug name. Multiple different keys may have the same name;
153 * the name is intended for debugging purposes and does not impact behavior.
Louis Ryanf6121162015-06-24 13:54:13 -0700154 */
155 public static <T> Key<T> key(String name) {
156 return new Key<T>(name);
157 }
158
159 /**
Eric Anderson4a427b72016-01-23 09:24:00 -0800160 * Create a {@link Key} with the given debug name and default value. Multiple different keys may
161 * have the same name; the name is intended for debugging purposes and does not impact behavior.
Louis Ryanf6121162015-06-24 13:54:13 -0700162 */
163 public static <T> Key<T> keyWithDefault(String name, T defaultValue) {
164 return new Key<T>(name, defaultValue);
165 }
166
167 /**
Kun Zhang84862862016-12-02 09:47:58 -0800168 * Return the context associated with the current scope, will never return {@code null}.
Louis Ryanf6121162015-06-24 13:54:13 -0700169 *
170 * <p>Will never return {@link CancellableContext} even if one is attached, instead a
171 * {@link Context} is returned with the same properties and lifetime. This is to avoid
172 * code stealing the ability to cancel arbitrarily.
173 */
174 public static Context current() {
Kun Zhang84862862016-12-02 09:47:58 -0800175 Context current = storage().current();
Louis Ryan49b51352015-11-06 17:01:26 -0800176 if (current == null) {
Louis Ryanf6121162015-06-24 13:54:13 -0700177 return ROOT;
178 }
Louis Ryan49b51352015-11-06 17:01:26 -0800179 return current;
Louis Ryanf6121162015-06-24 13:54:13 -0700180 }
181
182 private final Context parent;
183 private final Object[][] keyValueEntries;
184 private final boolean cascadesCancellation;
185 private ArrayList<ExecutableListener> listeners;
Louis Ryan73e2a232015-11-13 17:18:12 -0800186 private CancellationListener parentListener = new ParentListener();
187 private final boolean canBeCancelled;
Louis Ryanf6121162015-06-24 13:54:13 -0700188
189 /**
190 * Construct a context that cannot be cancelled and will not cascade cancellation from its parent.
191 */
192 private Context(Context parent) {
193 this.parent = parent;
Louis Ryan8dad7342016-02-18 16:30:06 -0800194 // Not inheriting cancellation implies not inheriting a deadline too.
195 keyValueEntries = new Object[][]{{DEADLINE_KEY, null}};
Louis Ryanf6121162015-06-24 13:54:13 -0700196 cascadesCancellation = false;
Louis Ryan73e2a232015-11-13 17:18:12 -0800197 canBeCancelled = false;
Louis Ryanf6121162015-06-24 13:54:13 -0700198 }
199
200 /**
201 * Construct a context that cannot be cancelled but will cascade cancellation from its parent if
202 * it is cancellable.
203 */
204 private Context(Context parent, Object[][] keyValueEntries) {
205 this.parent = parent;
206 this.keyValueEntries = keyValueEntries;
207 cascadesCancellation = true;
Louis Ryan73e2a232015-11-13 17:18:12 -0800208 canBeCancelled = this.parent != null && this.parent.canBeCancelled;
209 }
210
211 /**
212 * Construct a context that can be cancelled and will cascade cancellation from its parent if
213 * it is cancellable.
214 */
215 private Context(Context parent, Object[][] keyValueEntries, boolean isCancellable) {
216 this.parent = parent;
217 this.keyValueEntries = keyValueEntries;
218 cascadesCancellation = true;
219 canBeCancelled = isCancellable;
Louis Ryanf6121162015-06-24 13:54:13 -0700220 }
221
222 /**
223 * Create a new context which is independently cancellable and also cascades cancellation from
224 * its parent. Callers should ensure that either {@link CancellableContext#cancel(Throwable)}
Louis Ryan49b51352015-11-06 17:01:26 -0800225 * or {@link CancellableContext#detachAndCancel(Context, Throwable)} are called to notify
226 * listeners and release the resources associated with them.
Louis Ryanf6121162015-06-24 13:54:13 -0700227 *
228 * <p>Sample usage:
229 * <pre>
230 * Context.CancellableContext withCancellation = Context.current().withCancellation();
231 * try {
232 * executorService.execute(withCancellation.wrap(new Runnable() {
233 * public void run() {
234 * Context current = Context.current();
235 * while (!current.isCancelled()) {
236 * keepWorking();
237 * }
238 * }
239 * });
240 * doSomethingRelatedWork();
241 * } catch (Throwable t) {
242 * withCancellation.cancel(t);
243 * }
244 * </pre>
245 */
246 public CancellableContext withCancellation() {
247 return new CancellableContext(this);
248 }
249
250 /**
Louis Ryanf6121162015-06-24 13:54:13 -0700251 * Create a new context which will cancel itself after the given {@code duration} from now.
252 * The returned context will cascade cancellation of its parent. Callers may explicitly cancel
253 * the returned context prior to the deadline just as for {@link #withCancellation()},
254 *
255 * <p>Sample usage:
256 * <pre>
257 * Context.CancellableContext withDeadline = Context.current().withDeadlineAfter(5,
Eric Anderson6e94cf32016-01-23 09:32:41 -0800258 * TimeUnit.SECONDS, scheduler);
Louis Ryanf6121162015-06-24 13:54:13 -0700259 * executorService.execute(withDeadline.wrap(new Runnable() {
260 * public void run() {
261 * Context current = Context.current();
262 * while (!current.isCancelled()) {
263 * keepWorking();
264 * }
265 * }
266 * });
267 * </pre>
268 */
Eric Anderson6e94cf32016-01-23 09:32:41 -0800269 public CancellableContext withDeadlineAfter(long duration, TimeUnit unit,
Louis Ryan8dad7342016-02-18 16:30:06 -0800270 ScheduledExecutorService scheduler) {
271 return withDeadline(Deadline.after(duration, unit), scheduler);
272 }
273
274 /**
275 * Create a new context which will cancel itself at the given {@link Deadline}.
276 * The returned context will cascade cancellation of its parent. Callers may explicitly cancel
277 * the returned context prior to the deadline just as for {@link #withCancellation()},
278 *
279 * <p>Sample usage:
280 * <pre>
281 * Context.CancellableContext withDeadline = Context.current()
282 * .withDeadline(someReceivedDeadline);
283 * executorService.execute(withDeadline.wrap(new Runnable() {
284 * public void run() {
285 * Context current = Context.current();
286 * while (!current.isCancelled()) {
287 * keepWorking();
288 * }
289 * }
290 * });
291 * </pre>
292 */
293 public CancellableContext withDeadline(Deadline deadline,
Eric Anderson6e94cf32016-01-23 09:32:41 -0800294 ScheduledExecutorService scheduler) {
Kun Zhangc4f7f5c2016-09-01 16:00:43 -0700295 checkNotNull(deadline, "deadline");
296 checkNotNull(scheduler, "scheduler");
Louis Ryan8dad7342016-02-18 16:30:06 -0800297 return new CancellableContext(this, deadline, scheduler);
Louis Ryanf6121162015-06-24 13:54:13 -0700298 }
299
300 /**
301 * Create a new context with the given key value set. The new context will cascade cancellation
302 * from its parent.
303 *
304 <pre>
305 * Context withCredential = Context.current().withValue(CRED_KEY, cred);
306 * executorService.execute(withCredential.wrap(new Runnable() {
307 * public void run() {
308 * readUserRecords(userId, CRED_KEY.get());
309 * }
310 * }));
311 * </pre>
312 *
313 */
314 public <V> Context withValue(Key<V> k1, V v1) {
315 return new Context(this, new Object[][]{{k1, v1}});
316 }
317
318 /**
319 * Create a new context with the given key value set. The new context will cascade cancellation
320 * from its parent.
321 */
322 public <V1, V2> Context withValues(Key<V1> k1, V1 v1, Key<V2> k2, V2 v2) {
323 return new Context(this, new Object[][]{{k1, v1}, {k2, v2}});
324 }
325
326 /**
327 * Create a new context with the given key value set. The new context will cascade cancellation
328 * from its parent.
329 */
330 public <V1, V2, V3> Context withValues(Key<V1> k1, V1 v1, Key<V2> k2, V2 v2, Key<V3> k3, V3 v3) {
331 return new Context(this, new Object[][]{{k1, v1}, {k2, v2}, {k3, v3}});
332 }
333
334 /**
Kun Zhang97b92632016-12-13 14:03:23 -0800335 * Create a new context with the given key value set. The new context will cascade cancellation
336 * from its parent.
337 */
338 public <V1, V2, V3, V4> Context withValues(Key<V1> k1, V1 v1, Key<V2> k2, V2 v2,
339 Key<V3> k3, V3 v3, Key<V4> k4, V4 v4) {
340 return new Context(this, new Object[][]{{k1, v1}, {k2, v2}, {k3, v3}, {k4, v4}});
341 }
342
343 /**
Louis Ryan8dad7342016-02-18 16:30:06 -0800344 * Create a new context which propagates the values of this context but does not cascade its
Eric Andersonb5e6d422016-04-28 18:48:55 -0700345 * cancellation.
Louis Ryanf6121162015-06-24 13:54:13 -0700346 */
Eric Andersonb5e6d422016-04-28 18:48:55 -0700347 public Context fork() {
348 return new Context(this);
Louis Ryanf6121162015-06-24 13:54:13 -0700349 }
350
351 boolean canBeCancelled() {
352 // A context is cancellable if it cascades from its parent and its parent is
Louis Ryan73e2a232015-11-13 17:18:12 -0800353 // cancellable or is itself directly cancellable..
354 return canBeCancelled;
Louis Ryanf6121162015-06-24 13:54:13 -0700355 }
356
357 /**
Kun Zhang84862862016-12-02 09:47:58 -0800358 * Attach this context, thus enter a new scope within which this context is {@link #current}. The
359 * previously current context is returned. It is allowed to attach contexts where {@link
360 * #isCancelled()} is {@code true}.
Louis Ryan8dad7342016-02-18 16:30:06 -0800361 *
362 * <p>Instead of using {@link #attach()} & {@link #detach(Context)} most use-cases are better
Kun Zhang84862862016-12-02 09:47:58 -0800363 * served by using the {@link #run(Runnable)} or {@link #call(java.util.concurrent.Callable)} to
364 * execute work immediately within a context's scope. If work needs to be done in other threads it
365 * is recommended to use the 'wrap' methods or to use a propagating executor.
Louis Ryanf6121162015-06-24 13:54:13 -0700366 */
Louis Ryan49b51352015-11-06 17:01:26 -0800367 public Context attach() {
368 Context previous = current();
Kun Zhang84862862016-12-02 09:47:58 -0800369 storage().attach(this);
Louis Ryan49b51352015-11-06 17:01:26 -0800370 return previous;
371 }
372
373 /**
Kun Zhang84862862016-12-02 09:47:58 -0800374 * Detach the current context and attach the provided replacement which should be the context of
375 * the outer scope, thus exit the current scope. If this context is not {@link #current()} a
376 * SEVERE message will be logged but the context to attach will still be bound.
Louis Ryan49b51352015-11-06 17:01:26 -0800377 */
378 public void detach(Context toAttach) {
Kun Zhangc4f7f5c2016-09-01 16:00:43 -0700379 checkNotNull(toAttach, "toAttach");
Kun Zhang84862862016-12-02 09:47:58 -0800380 storage().detach(this, toAttach);
Louis Ryanf6121162015-06-24 13:54:13 -0700381 }
382
383 // Visible for testing
384 boolean isCurrent() {
385 return current() == this;
386 }
387
388 /**
Louis Ryanf6121162015-06-24 13:54:13 -0700389 * Is this context cancelled.
390 */
391 public boolean isCancelled() {
392 if (parent == null || !cascadesCancellation) {
393 return false;
394 } else {
395 return parent.isCancelled();
396 }
397 }
398
399 /**
400 * If a context {@link #isCancelled()} then return the cause of the cancellation or
401 * {@code null} if context was cancelled without a cause. If the context is not yet cancelled
402 * will always return {@code null}.
403 *
Louis Ryan8dad7342016-02-18 16:30:06 -0800404 * <p>The cancellation cause is provided for informational purposes only and implementations
405 * should generally assume that it has already been handled and logged properly.
Louis Ryanf6121162015-06-24 13:54:13 -0700406 */
Louis Ryan8dad7342016-02-18 16:30:06 -0800407 public Throwable cancellationCause() {
Louis Ryanf6121162015-06-24 13:54:13 -0700408 if (parent == null || !cascadesCancellation) {
409 return null;
410 } else {
Louis Ryan8dad7342016-02-18 16:30:06 -0800411 return parent.cancellationCause();
Louis Ryanf6121162015-06-24 13:54:13 -0700412 }
413 }
414
415 /**
Louis Ryan8dad7342016-02-18 16:30:06 -0800416 * A context may have an associated {@link Deadline} at which it will be automatically cancelled.
417 * @return A {@link io.grpc.Deadline} or {@code null} if no deadline is set.
418 */
Louis Ryan8dad7342016-02-18 16:30:06 -0800419 public Deadline getDeadline() {
420 return DEADLINE_KEY.get(this);
421 }
422
423 /**
Louis Ryanf6121162015-06-24 13:54:13 -0700424 * Add a listener that will be notified when the context becomes cancelled.
425 */
426 public void addListener(final CancellationListener cancellationListener,
427 final Executor executor) {
Kun Zhangc4f7f5c2016-09-01 16:00:43 -0700428 checkNotNull(cancellationListener, "cancellationListener");
429 checkNotNull(executor, "executor");
Louis Ryan73e2a232015-11-13 17:18:12 -0800430 if (canBeCancelled) {
Louis Ryanf6121162015-06-24 13:54:13 -0700431 ExecutableListener executableListener =
432 new ExecutableListener(executor, cancellationListener);
433 synchronized (this) {
434 if (isCancelled()) {
435 executableListener.deliver();
436 } else {
437 if (listeners == null) {
438 // Now that we have a listener we need to listen to our parent so
439 // we can cascade listener notification.
440 listeners = new ArrayList<ExecutableListener>();
441 listeners.add(executableListener);
Kun Zhangc4f7f5c2016-09-01 16:00:43 -0700442 parent.addListener(parentListener, DirectExecutor.INSTANCE);
Louis Ryanf6121162015-06-24 13:54:13 -0700443 } else {
444 listeners.add(executableListener);
445 }
446 }
447 }
Louis Ryanf6121162015-06-24 13:54:13 -0700448 }
449 }
450
451 /**
452 * Remove a {@link CancellationListener}.
453 */
454 public void removeListener(CancellationListener cancellationListener) {
Louis Ryan73e2a232015-11-13 17:18:12 -0800455 if (!canBeCancelled) {
456 return;
457 }
Louis Ryanf6121162015-06-24 13:54:13 -0700458 synchronized (this) {
459 if (listeners != null) {
460 for (int i = listeners.size() - 1; i >= 0; i--) {
461 if (listeners.get(i).listener == cancellationListener) {
462 listeners.remove(i);
Louis Ryan73e2a232015-11-13 17:18:12 -0800463 // Just remove the first matching listener, given that we allow duplicate
464 // adds we should allow for duplicates after remove.
Louis Ryanf6121162015-06-24 13:54:13 -0700465 break;
466 }
467 }
468 // We have no listeners so no need to listen to our parent
469 if (listeners.isEmpty()) {
470 parent.removeListener(parentListener);
471 listeners = null;
472 }
473 }
474 }
475 }
476
477 /**
478 * Notify all listeners that this context has been cancelled and immediately release
479 * any reference to them so that they may be garbage collected.
480 */
481 void notifyAndClearListeners() {
Louis Ryan73e2a232015-11-13 17:18:12 -0800482 if (!canBeCancelled) {
483 return;
484 }
Louis Ryanf6121162015-06-24 13:54:13 -0700485 ArrayList<ExecutableListener> tmpListeners;
486 synchronized (this) {
487 if (listeners == null) {
488 return;
489 }
490 tmpListeners = listeners;
491 listeners = null;
492 }
Louis Ryan73e2a232015-11-13 17:18:12 -0800493 // Deliver events to non-child context listeners before we notify child contexts. We do this
494 // to cancel higher level units of work before child units. This allows for a better error
495 // handling paradigm where the higher level unit of work knows it is cancelled and so can
496 // ignore errors that bubble up as a result of cancellation of lower level units.
Louis Ryanf6121162015-06-24 13:54:13 -0700497 for (int i = 0; i < tmpListeners.size(); i++) {
Louis Ryan73e2a232015-11-13 17:18:12 -0800498 if (!(tmpListeners.get(i).listener instanceof ParentListener)) {
499 tmpListeners.get(i).deliver();
500 }
501 }
502 for (int i = 0; i < tmpListeners.size(); i++) {
503 if (tmpListeners.get(i).listener instanceof ParentListener) {
504 tmpListeners.get(i).deliver();
505 }
Louis Ryanf6121162015-06-24 13:54:13 -0700506 }
507 parent.removeListener(parentListener);
508 }
509
Louis Ryan8dad7342016-02-18 16:30:06 -0800510 // Used in tests to ensure that listeners are defined and released when cancellation cascades.
511 // It's very important to ensure that we do not accidentally retain listeners.
Louis Ryanf6121162015-06-24 13:54:13 -0700512 int listenerCount() {
513 synchronized (this) {
514 return listeners == null ? 0 : listeners.size();
515 }
516 }
517
518 /**
Louis Ryan8dad7342016-02-18 16:30:06 -0800519 * Immediately run a {@link Runnable} with this context as the {@link #current} context.
520 * @param r {@link Runnable} to run.
521 */
522 public void run(Runnable r) {
523 Context previous = attach();
524 try {
525 r.run();
526 } finally {
527 detach(previous);
528 }
529 }
530
531 /**
532 * Immediately call a {@link Callable} with this context as the {@link #current} context.
533 * @param c {@link Callable} to call.
534 * @return result of call.
535 */
536 public <V> V call(Callable<V> c) throws Exception {
537 Context previous = attach();
538 try {
539 return c.call();
540 } finally {
541 detach(previous);
542 }
543 }
544
545 /**
Louis Ryanf6121162015-06-24 13:54:13 -0700546 * Wrap a {@link Runnable} so that it executes with this context as the {@link #current} context.
547 */
548 public Runnable wrap(final Runnable r) {
549 return new Runnable() {
550 @Override
551 public void run() {
Louis Ryan49b51352015-11-06 17:01:26 -0800552 Context previous = attach();
Louis Ryanf6121162015-06-24 13:54:13 -0700553 try {
554 r.run();
555 } finally {
Louis Ryan49b51352015-11-06 17:01:26 -0800556 detach(previous);
Louis Ryanf6121162015-06-24 13:54:13 -0700557 }
558 }
559 };
560 }
561
562 /**
563 * Wrap a {@link Callable} so that it executes with this context as the {@link #current} context.
564 */
565 public <C> Callable<C> wrap(final Callable<C> c) {
566 return new Callable<C>() {
567 @Override
568 public C call() throws Exception {
Louis Ryan49b51352015-11-06 17:01:26 -0800569 Context previous = attach();
Louis Ryanf6121162015-06-24 13:54:13 -0700570 try {
571 return c.call();
572 } finally {
Louis Ryan49b51352015-11-06 17:01:26 -0800573 detach(previous);
Louis Ryanf6121162015-06-24 13:54:13 -0700574 }
575 }
576 };
577 }
578
579 /**
Louis Ryan8dad7342016-02-18 16:30:06 -0800580 * Wrap an {@link Executor} so that it always executes with this context as the {@link #current}
581 * context. It is generally expected that {@link #currentContextExecutor(Executor)} would be
582 * used more commonly than this method.
Eric Andersona5d9e342015-07-22 16:54:08 -0700583 *
Louis Ryan8dad7342016-02-18 16:30:06 -0800584 * <p>One scenario in which this executor may be useful is when a single thread is sharding work
585 * to multiple threads.
586 *
587 * @see #currentContextExecutor(Executor)
Eric Andersona5d9e342015-07-22 16:54:08 -0700588 */
Louis Ryan8dad7342016-02-18 16:30:06 -0800589 public Executor fixedContextExecutor(final Executor e) {
590 class FixedContextExecutor implements Executor {
Eric Andersona5d9e342015-07-22 16:54:08 -0700591 @Override
592 public void execute(Runnable r) {
593 e.execute(wrap(r));
594 }
595 }
596
Louis Ryan8dad7342016-02-18 16:30:06 -0800597 return new FixedContextExecutor();
Eric Andersona5d9e342015-07-22 16:54:08 -0700598 }
599
600 /**
601 * Create an executor that propagates the {@link #current} context when {@link Executor#execute}
Louis Ryan8dad7342016-02-18 16:30:06 -0800602 * is called as the {@link #current} context of the {@code Runnable} scheduled. <em>Note that this
Eric Andersona5d9e342015-07-22 16:54:08 -0700603 * is a static method.</em>
604 *
Louis Ryan8dad7342016-02-18 16:30:06 -0800605 * @see #fixedContextExecutor(Executor)
Eric Andersona5d9e342015-07-22 16:54:08 -0700606 */
Louis Ryan8dad7342016-02-18 16:30:06 -0800607 public static Executor currentContextExecutor(final Executor e) {
608 class CurrentContextExecutor implements Executor {
Eric Andersona5d9e342015-07-22 16:54:08 -0700609 @Override
610 public void execute(Runnable r) {
611 e.execute(Context.current().wrap(r));
612 }
613 }
614
Louis Ryan8dad7342016-02-18 16:30:06 -0800615 return new CurrentContextExecutor();
Eric Andersona5d9e342015-07-22 16:54:08 -0700616 }
617
618 /**
Louis Ryanf6121162015-06-24 13:54:13 -0700619 * Lookup the value for a key in the context inheritance chain.
620 */
Eric Andersoneba12fb2015-07-23 13:24:04 -0700621 private Object lookup(Key<?> key) {
Louis Ryanf6121162015-06-24 13:54:13 -0700622 for (int i = 0; i < keyValueEntries.length; i++) {
623 if (key.equals(keyValueEntries[i][0])) {
624 return keyValueEntries[i][1];
625 }
626 }
627 if (parent == null) {
628 return null;
629 }
630 return parent.lookup(key);
631 }
632
633 /**
634 * A context which inherits cancellation from its parent but which can also be independently
635 * cancelled and which will propagate cancellation to its descendants.
636 */
637 public static final class CancellableContext extends Context {
638
639 private boolean cancelled;
Louis Ryan8dad7342016-02-18 16:30:06 -0800640 private Throwable cancellationCause;
641 private final Context uncancellableSurrogate;
642 private ScheduledFuture<?> pendingDeadline;
643
644 /**
645 * If the parent deadline is before the given deadline there is no need to install the value
646 * or listen for its expiration as the parent context will already be listening for it.
647 */
648 private static Object[][] deriveDeadline(Context parent, Deadline deadline) {
649 Deadline parentDeadline = DEADLINE_KEY.get(parent);
650 return parentDeadline == null || deadline.isBefore(parentDeadline)
651 ? new Object[][]{{ DEADLINE_KEY, deadline}} :
652 EMPTY_ENTRIES;
653 }
Louis Ryanf6121162015-06-24 13:54:13 -0700654
655 /**
656 * Create a cancellable context that does not have a deadline.
657 */
658 private CancellableContext(Context parent) {
Louis Ryan73e2a232015-11-13 17:18:12 -0800659 super(parent, EMPTY_ENTRIES, true);
Louis Ryan8dad7342016-02-18 16:30:06 -0800660 // Create a surrogate that inherits from this to attach so that you cannot retrieve a
Louis Ryanf6121162015-06-24 13:54:13 -0700661 // cancellable context from Context.current()
Louis Ryan8dad7342016-02-18 16:30:06 -0800662 uncancellableSurrogate = new Context(this, EMPTY_ENTRIES);
Louis Ryanf6121162015-06-24 13:54:13 -0700663 }
664
665 /**
666 * Create a cancellable context that has a deadline.
667 */
Louis Ryan8dad7342016-02-18 16:30:06 -0800668 private CancellableContext(Context parent, Deadline deadline,
Eric Anderson6e94cf32016-01-23 09:32:41 -0800669 ScheduledExecutorService scheduler) {
Louis Ryan8dad7342016-02-18 16:30:06 -0800670 super(parent, deriveDeadline(parent, deadline), true);
671 if (DEADLINE_KEY.get(this) == deadline) {
buchgrfd8fd512016-03-23 21:08:44 +0100672 final TimeoutException cause = new TimeoutException("context timed out");
673 if (!deadline.isExpired()) {
674 // The parent deadline was after the new deadline so we need to install a listener
675 // on the new earlier deadline to trigger expiration for this context.
676 pendingDeadline = deadline.runOnExpiration(new Runnable() {
677 @Override
678 public void run() {
Eric Andersonc8648dc2016-08-04 11:08:36 -0700679 try {
680 cancel(cause);
681 } catch (Throwable t) {
682 log.log(Level.SEVERE, "Cancel threw an exception, which should not happen", t);
683 }
buchgrfd8fd512016-03-23 21:08:44 +0100684 }
685 }, scheduler);
686 } else {
687 // Cancel immediately if the deadline is already expired.
688 cancel(cause);
689 }
Louis Ryan8dad7342016-02-18 16:30:06 -0800690 }
691 uncancellableSurrogate = new Context(this, EMPTY_ENTRIES);
Louis Ryanf6121162015-06-24 13:54:13 -0700692 }
693
694
695 @Override
Louis Ryan49b51352015-11-06 17:01:26 -0800696 public Context attach() {
Louis Ryan8dad7342016-02-18 16:30:06 -0800697 return uncancellableSurrogate.attach();
Louis Ryanf6121162015-06-24 13:54:13 -0700698 }
699
700 @Override
Louis Ryan49b51352015-11-06 17:01:26 -0800701 public void detach(Context toAttach) {
Louis Ryan8dad7342016-02-18 16:30:06 -0800702 uncancellableSurrogate.detach(toAttach);
Louis Ryanf6121162015-06-24 13:54:13 -0700703 }
704
705 @Override
706 public boolean isCurrent() {
Louis Ryan8dad7342016-02-18 16:30:06 -0800707 return uncancellableSurrogate.isCurrent();
Louis Ryanf6121162015-06-24 13:54:13 -0700708 }
709
710 /**
Kun Zhangc4f7f5c2016-09-01 16:00:43 -0700711 * Cancel this context and optionally provide a cause (can be {@code null}) for the
712 * cancellation. This will trigger notification of listeners.
Louis Ryanf6121162015-06-24 13:54:13 -0700713 *
714 * @return {@code true} if this context cancelled the context and notified listeners,
715 * {@code false} if the context was already cancelled.
716 */
Kun Zhangc4f7f5c2016-09-01 16:00:43 -0700717 public boolean cancel(Throwable cause) {
Louis Ryanf6121162015-06-24 13:54:13 -0700718 boolean triggeredCancel = false;
719 synchronized (this) {
720 if (!cancelled) {
721 cancelled = true;
Louis Ryan8dad7342016-02-18 16:30:06 -0800722 if (pendingDeadline != null) {
Louis Ryanf6121162015-06-24 13:54:13 -0700723 // If we have a scheduled cancellation pending attempt to cancel it.
Louis Ryan8dad7342016-02-18 16:30:06 -0800724 pendingDeadline.cancel(false);
725 pendingDeadline = null;
Louis Ryanf6121162015-06-24 13:54:13 -0700726 }
Louis Ryan8dad7342016-02-18 16:30:06 -0800727 this.cancellationCause = cause;
Louis Ryanf6121162015-06-24 13:54:13 -0700728 triggeredCancel = true;
729 }
730 }
731 if (triggeredCancel) {
732 notifyAndClearListeners();
733 }
734 return triggeredCancel;
735 }
736
737 /**
Kun Zhang84862862016-12-02 09:47:58 -0800738 * Cancel this context and detach it as the current context.
Louis Ryanf6121162015-06-24 13:54:13 -0700739 *
Louis Ryan49b51352015-11-06 17:01:26 -0800740 * @param toAttach context to make current.
741 * @param cause of cancellation, can be {@code null}.
Louis Ryanf6121162015-06-24 13:54:13 -0700742 */
Kun Zhangc4f7f5c2016-09-01 16:00:43 -0700743 public void detachAndCancel(Context toAttach, Throwable cause) {
Louis Ryanf6121162015-06-24 13:54:13 -0700744 try {
Louis Ryan49b51352015-11-06 17:01:26 -0800745 detach(toAttach);
Louis Ryanf6121162015-06-24 13:54:13 -0700746 } finally {
747 cancel(cause);
748 }
749 }
750
751 @Override
Louis Ryanf6121162015-06-24 13:54:13 -0700752 public boolean isCancelled() {
753 synchronized (this) {
754 if (cancelled) {
755 return true;
756 }
757 }
758 // Detect cancellation of parent in the case where we have no listeners and
759 // record it.
760 if (super.isCancelled()) {
Louis Ryan8dad7342016-02-18 16:30:06 -0800761 cancel(super.cancellationCause());
Louis Ryanf6121162015-06-24 13:54:13 -0700762 return true;
763 }
764 return false;
765 }
766
Louis Ryanf6121162015-06-24 13:54:13 -0700767 @Override
Louis Ryan8dad7342016-02-18 16:30:06 -0800768 public Throwable cancellationCause() {
Louis Ryanf6121162015-06-24 13:54:13 -0700769 if (isCancelled()) {
Louis Ryan8dad7342016-02-18 16:30:06 -0800770 return cancellationCause;
Louis Ryanf6121162015-06-24 13:54:13 -0700771 }
772 return null;
773 }
774 }
775
776 /**
777 * A listener notified on context cancellation.
778 */
779 public interface CancellationListener {
780 /**
781 * @param context the newly cancelled context.
782 */
783 public void cancelled(Context context);
784 }
785
786 /**
787 * Key for indexing values stored in a context.
788 */
789 public static class Key<T> {
790
791 private final String name;
792 private final T defaultValue;
793
794 Key(String name) {
795 this(name, null);
796 }
797
798 Key(String name, T defaultValue) {
Kun Zhangc4f7f5c2016-09-01 16:00:43 -0700799 this.name = checkNotNull(name, "name");
Louis Ryanf6121162015-06-24 13:54:13 -0700800 this.defaultValue = defaultValue;
801 }
802
803 /**
804 * Get the value from the {@link #current()} context for this key.
805 */
806 @SuppressWarnings("unchecked")
807 public T get() {
808 return get(Context.current());
809 }
810
811 /**
812 * Get the value from the specified context for this key.
813 */
814 @SuppressWarnings("unchecked")
815 public T get(Context context) {
816 T value = (T) context.lookup(this);
817 return value == null ? defaultValue : value;
818 }
819
820 @Override
Louis Ryanbe600862015-11-06 13:46:13 -0800821 public String toString() {
822 return name;
823 }
Louis Ryanf6121162015-06-24 13:54:13 -0700824 }
825
826 /**
Kun Zhang84862862016-12-02 09:47:58 -0800827 * Defines the mechanisms for attaching and detaching the "current" context.
828 *
829 * <p>The default implementation will put the current context in a {@link ThreadLocal}. If an
830 * alternative implementation named {@code io.grpc.ContextStorageOverride} exists in the
831 * classpath, it will be used instead of the default implementation.
832 *
833 * <p>This API is <a href="https://github.com/grpc/grpc-java/issues/2462">experimental</a> and
834 * subject to change.
835 */
836 public abstract static class Storage {
837 /**
838 * Implements {@link io.grpc.Context#attach}.
839 *
840 * @param toAttach the context to be attached
841 */
842 public abstract void attach(Context toAttach);
843
844 /**
845 * Implements {@link io.grpc.Context#detach}
846 *
847 * @param toDetach the context to be detached. Should be, or be equivalent to, the current
848 * context of the current scope
849 * @param toRestore the context to be the current. Should be, or be equivalent to, the context
850 * of the outer scope
851 */
852 public abstract void detach(Context toDetach, Context toRestore);
853
854 /**
855 * Implements {@link io.grpc.Context#current}. Returns the context of the current scope.
856 */
857 public abstract Context current();
858 }
859
860 /**
861 * Stores listener and executor pair.
Louis Ryanf6121162015-06-24 13:54:13 -0700862 */
863 private class ExecutableListener implements Runnable {
864 private final Executor executor;
865 private final CancellationListener listener;
866
867 private ExecutableListener(Executor executor, CancellationListener listener) {
868 this.executor = executor;
869 this.listener = listener;
870 }
871
872 private void deliver() {
Eric Anderson63f14822015-10-26 17:03:30 -0700873 try {
874 executor.execute(this);
875 } catch (Throwable t) {
Eric Andersonc8648dc2016-08-04 11:08:36 -0700876 log.log(Level.INFO, "Exception notifying context listener", t);
Eric Anderson63f14822015-10-26 17:03:30 -0700877 }
Louis Ryanf6121162015-06-24 13:54:13 -0700878 }
879
880 @Override
881 public void run() {
882 listener.cancelled(Context.this);
883 }
884 }
Louis Ryan73e2a232015-11-13 17:18:12 -0800885
886 private class ParentListener implements CancellationListener {
887 @Override
888 public void cancelled(Context context) {
889 if (Context.this instanceof CancellableContext) {
Louis Ryan8dad7342016-02-18 16:30:06 -0800890 // Record cancellation with its cancellationCause.
891 ((CancellableContext) Context.this).cancel(context.cancellationCause());
Louis Ryan73e2a232015-11-13 17:18:12 -0800892 } else {
893 notifyAndClearListeners();
894 }
895 }
896 }
Kun Zhangc4f7f5c2016-09-01 16:00:43 -0700897
898 private static <T> T checkNotNull(T reference, Object errorMessage) {
899 if (reference == null) {
900 throw new NullPointerException(String.valueOf(errorMessage));
901 }
902 return reference;
903 }
904
905 private enum DirectExecutor implements Executor {
906 INSTANCE;
907
908 @Override
909 public void execute(Runnable command) {
910 command.run();
911 }
912
913 @Override
914 public String toString() {
915 return "Context.DirectExecutor";
916 }
917 }
Louis Ryanf6121162015-06-24 13:54:13 -0700918}