blob: 8f2a5305b4cfa6808b39cc437343ac9a440f51bb [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
34import com.google.common.base.Preconditions;
35import com.google.common.util.concurrent.MoreExecutors;
Kun Zhangd2929cd2015-08-04 16:32:32 -070036
Louis Ryanf6121162015-06-24 13:54:13 -070037import java.io.Closeable;
38import java.io.IOException;
Louis Ryanf6121162015-06-24 13:54:13 -070039import java.util.ArrayList;
40import java.util.concurrent.Callable;
41import java.util.concurrent.Executor;
Louis Ryanf6121162015-06-24 13:54:13 -070042import java.util.concurrent.ScheduledExecutorService;
43import java.util.concurrent.ScheduledFuture;
44import java.util.concurrent.TimeUnit;
45import java.util.concurrent.TimeoutException;
46import java.util.logging.Level;
47import java.util.logging.Logger;
48
49import javax.annotation.Nullable;
50
Louis Ryanf6121162015-06-24 13:54:13 -070051/**
52 * A context propagation mechanism which carries deadlines, cancellation signals,
53 * and other scoped values across API boundaries and between threads. Examples of functionality
54 * propagated via context include:
55 * <ul>
56 * <li>Deadlines for a local operation or remote call.</li>
57 * <li>Security principals and credentials.</li>
58 * <li>Local and distributed tracing context.</li>
59 * </ul>
60 *
61 * <p>Context objects make their state available by being attached to the executing thread using
62 * a {@link ThreadLocal}. The context object bound to a thread is considered {@link #current()}.
63 * Context objects are immutable and inherit state from their parent. To add or overwrite the
64 * current state a new context object must be created and then attached to the thread replacing the
65 * previously bound context. For example:
66 * <pre>
67 * Context withCredential = Context.current().withValue(CRED_KEY, cred);
68 * executorService.execute(withCredential.wrap(new Runnable() {
69 * public void run() {
70 * readUserRecords(userId, CRED_KEY.get());
71 * }
72 * }));
73
74 * </pre>
75 *
76 * <p>Context objects will cascade cancellation from their parent and propagate it to their
77 * children. You can add a {@link CancellationListener} to a context to be notified when it or
78 * one of its ancestors has been cancelled. Cancellation does not release the state stored by
79 * a context and it's perfectly valid to {@link #attach()} an already cancelled context to a
80 * thread to make it current. To cancel a context (and its descendants) you first create a
81 * {@link CancellableContext} and when you need to signal cancellation call
82 * {@link CancellableContext#cancel} or {@link CancellableContext#detachAndCancel}. For example:
83 * <pre>
84 * CancellableContext withCancellation = Context.current().withCancellation();
85 * try {
Louis Ryan49b51352015-11-06 17:01:26 -080086 * executorService.execute(withCancellation.wrap(new Runnable() {
Louis Ryanf6121162015-06-24 13:54:13 -070087 * public void run() {
88 * while (waitingForData() &amp;&amp; !Context.current().isCancelled()) {}
89 * }
90 * });
91 * doSomeWork();
92 * } catch (Throwable t) {
93 * withCancellation.cancel(t);
94 * }
95 * </pre>
96 *
97 *
98 * <p>Notes and cautions on use:
99 * <ul>
100 * <li>While Context objects are immutable they do not place such a restriction on the state
101 * they store.</li>
102 * <li>Context is not intended for passing optional parameters to an API and developers should
103 * take care to avoid excessive dependence on context when designing an API.</li>
104 * <li>If Context is being used in an environment that needs to support class unloading it is the
105 * responsibility of the application to ensure that all contexts are properly cancelled.</li>
106 * </ul>
107 */
Carl Mastrangelo4221f5a2015-08-26 13:40:11 -0700108@ExperimentalApi("https://github.com/grpc/grpc-java/issues/262")
Louis Ryanf6121162015-06-24 13:54:13 -0700109public class Context {
110
111 private static final Logger LOG = Logger.getLogger(Context.class.getName());
112
Louis Ryanf6121162015-06-24 13:54:13 -0700113 private static final Object[][] EMPTY_ENTRIES = new Object[0][2];
114
115 /**
116 * The logical root context which is {@link #current()} if no other context is bound. This context
117 * is not cancellable and so will not cascade cancellation or retain listeners.
118 */
119 public static final Context ROOT = new Context(null);
120
121 /**
Louis Ryan49b51352015-11-06 17:01:26 -0800122 * Currently bound context.
123 */
124 private static final ThreadLocal<Context> localContext = new ThreadLocal<Context>() {
125 @Override
126 protected Context initialValue() {
127 return ROOT;
128 }
129 };
130
131 /**
Eric Anderson4a427b72016-01-23 09:24:00 -0800132 * Create a {@link Key} with the given debug name. Multiple different keys may have the same name;
133 * the name is intended for debugging purposes and does not impact behavior.
Louis Ryanf6121162015-06-24 13:54:13 -0700134 */
135 public static <T> Key<T> key(String name) {
136 return new Key<T>(name);
137 }
138
139 /**
Eric Anderson4a427b72016-01-23 09:24:00 -0800140 * Create a {@link Key} with the given debug name and default value. Multiple different keys may
141 * have the same name; the name is intended for debugging purposes and does not impact behavior.
Louis Ryanf6121162015-06-24 13:54:13 -0700142 */
143 public static <T> Key<T> keyWithDefault(String name, T defaultValue) {
144 return new Key<T>(name, defaultValue);
145 }
146
147 /**
148 * Return the context associated with the current thread, will never return {@code null} as
149 * the {@link #ROOT} context is implicitly associated with all threads.
150 *
151 * <p>Will never return {@link CancellableContext} even if one is attached, instead a
152 * {@link Context} is returned with the same properties and lifetime. This is to avoid
153 * code stealing the ability to cancel arbitrarily.
154 */
155 public static Context current() {
Louis Ryan49b51352015-11-06 17:01:26 -0800156 Context current = localContext.get();
157 if (current == null) {
Louis Ryanf6121162015-06-24 13:54:13 -0700158 return ROOT;
159 }
Louis Ryan49b51352015-11-06 17:01:26 -0800160 return current;
Louis Ryanf6121162015-06-24 13:54:13 -0700161 }
162
163 private final Context parent;
164 private final Object[][] keyValueEntries;
165 private final boolean cascadesCancellation;
166 private ArrayList<ExecutableListener> listeners;
Louis Ryan73e2a232015-11-13 17:18:12 -0800167 private CancellationListener parentListener = new ParentListener();
168 private final boolean canBeCancelled;
Louis Ryanf6121162015-06-24 13:54:13 -0700169
170 /**
171 * Construct a context that cannot be cancelled and will not cascade cancellation from its parent.
172 */
173 private Context(Context parent) {
174 this.parent = parent;
175 keyValueEntries = EMPTY_ENTRIES;
176 cascadesCancellation = false;
Louis Ryan73e2a232015-11-13 17:18:12 -0800177 canBeCancelled = false;
Louis Ryanf6121162015-06-24 13:54:13 -0700178 }
179
180 /**
181 * Construct a context that cannot be cancelled but will cascade cancellation from its parent if
182 * it is cancellable.
183 */
184 private Context(Context parent, Object[][] keyValueEntries) {
185 this.parent = parent;
186 this.keyValueEntries = keyValueEntries;
187 cascadesCancellation = true;
Louis Ryan73e2a232015-11-13 17:18:12 -0800188 canBeCancelled = this.parent != null && this.parent.canBeCancelled;
189 }
190
191 /**
192 * Construct a context that can be cancelled and will cascade cancellation from its parent if
193 * it is cancellable.
194 */
195 private Context(Context parent, Object[][] keyValueEntries, boolean isCancellable) {
196 this.parent = parent;
197 this.keyValueEntries = keyValueEntries;
198 cascadesCancellation = true;
199 canBeCancelled = isCancellable;
Louis Ryanf6121162015-06-24 13:54:13 -0700200 }
201
202 /**
203 * Create a new context which is independently cancellable and also cascades cancellation from
204 * its parent. Callers should ensure that either {@link CancellableContext#cancel(Throwable)}
Louis Ryan49b51352015-11-06 17:01:26 -0800205 * or {@link CancellableContext#detachAndCancel(Context, Throwable)} are called to notify
206 * listeners and release the resources associated with them.
Louis Ryanf6121162015-06-24 13:54:13 -0700207 *
208 * <p>Sample usage:
209 * <pre>
210 * Context.CancellableContext withCancellation = Context.current().withCancellation();
211 * try {
212 * executorService.execute(withCancellation.wrap(new Runnable() {
213 * public void run() {
214 * Context current = Context.current();
215 * while (!current.isCancelled()) {
216 * keepWorking();
217 * }
218 * }
219 * });
220 * doSomethingRelatedWork();
221 * } catch (Throwable t) {
222 * withCancellation.cancel(t);
223 * }
224 * </pre>
225 */
226 public CancellableContext withCancellation() {
227 return new CancellableContext(this);
228 }
229
230 /**
231 * Create a new context which will cancel itself after an absolute deadline expressed as
232 * nanoseconds in the {@link System#nanoTime()} clock. The returned context will cascade
233 * cancellation of its parent. Callers may explicitly cancel the returned context prior to
234 * the deadline just as for {@link #withCancellation()},
235 *
236 * <p>It is recommended that callers only use this method when propagating a derivative of
237 * a received existing deadline. When establishing a new deadline, {@link #withDeadlineAfter}
238 * is the better mechanism.
239 */
Eric Anderson6e94cf32016-01-23 09:32:41 -0800240 public CancellableContext withDeadlineNanoTime(long deadlineNanoTime,
241 ScheduledExecutorService scheduler) {
242 return withDeadlineAfter(deadlineNanoTime - System.nanoTime(), TimeUnit.NANOSECONDS, scheduler);
Louis Ryanf6121162015-06-24 13:54:13 -0700243 }
244
245 /**
246 * Create a new context which will cancel itself after the given {@code duration} from now.
247 * The returned context will cascade cancellation of its parent. Callers may explicitly cancel
248 * the returned context prior to the deadline just as for {@link #withCancellation()},
249 *
250 * <p>Sample usage:
251 * <pre>
252 * Context.CancellableContext withDeadline = Context.current().withDeadlineAfter(5,
Eric Anderson6e94cf32016-01-23 09:32:41 -0800253 * TimeUnit.SECONDS, scheduler);
Louis Ryanf6121162015-06-24 13:54:13 -0700254 * executorService.execute(withDeadline.wrap(new Runnable() {
255 * public void run() {
256 * Context current = Context.current();
257 * while (!current.isCancelled()) {
258 * keepWorking();
259 * }
260 * }
261 * });
262 * </pre>
263 */
Eric Anderson6e94cf32016-01-23 09:32:41 -0800264 public CancellableContext withDeadlineAfter(long duration, TimeUnit unit,
265 ScheduledExecutorService scheduler) {
Louis Ryanf6121162015-06-24 13:54:13 -0700266 Preconditions.checkArgument(duration >= 0, "duration must be greater than or equal to 0");
267 Preconditions.checkNotNull(unit, "unit");
Eric Anderson6e94cf32016-01-23 09:32:41 -0800268 Preconditions.checkNotNull(scheduler, "scheduler");
269 return new CancellableContext(this, unit.toNanos(duration), scheduler);
Louis Ryanf6121162015-06-24 13:54:13 -0700270 }
271
272 /**
273 * Create a new context with the given key value set. The new context will cascade cancellation
274 * from its parent.
275 *
276 <pre>
277 * Context withCredential = Context.current().withValue(CRED_KEY, cred);
278 * executorService.execute(withCredential.wrap(new Runnable() {
279 * public void run() {
280 * readUserRecords(userId, CRED_KEY.get());
281 * }
282 * }));
283 * </pre>
284 *
285 */
286 public <V> Context withValue(Key<V> k1, V v1) {
287 return new Context(this, new Object[][]{{k1, v1}});
288 }
289
290 /**
291 * Create a new context with the given key value set. The new context will cascade cancellation
292 * from its parent.
293 */
294 public <V1, V2> Context withValues(Key<V1> k1, V1 v1, Key<V2> k2, V2 v2) {
295 return new Context(this, new Object[][]{{k1, v1}, {k2, v2}});
296 }
297
298 /**
299 * Create a new context with the given key value set. The new context will cascade cancellation
300 * from its parent.
301 */
302 public <V1, V2, V3> Context withValues(Key<V1> k1, V1 v1, Key<V2> k2, V2 v2, Key<V3> k3, V3 v3) {
303 return new Context(this, new Object[][]{{k1, v1}, {k2, v2}, {k3, v3}});
304 }
305
306 /**
307 * Create a new context which copies the values of this context but does not propagate its
308 * cancellation and is its own independent root for cancellation.
309 */
310 public CancellableContext fork() {
311 return new Context(this).withCancellation();
312 }
313
314 boolean canBeCancelled() {
315 // A context is cancellable if it cascades from its parent and its parent is
Louis Ryan73e2a232015-11-13 17:18:12 -0800316 // cancellable or is itself directly cancellable..
317 return canBeCancelled;
Louis Ryanf6121162015-06-24 13:54:13 -0700318 }
319
320 /**
321 * Attach this context to the thread and make it {@link #current}, the previously current context
Louis Ryan49b51352015-11-06 17:01:26 -0800322 * is returned. It is allowed to attach contexts where {@link #isCancelled()} is {@code true}.
Louis Ryanf6121162015-06-24 13:54:13 -0700323 */
Louis Ryan49b51352015-11-06 17:01:26 -0800324 public Context attach() {
325 Context previous = current();
326 localContext.set(this);
327 return previous;
328 }
329
330 /**
331 * Detach the current context from the thread and attach the provided replacement. If this
332 * context is not {@link #current()} a SEVERE message will be logged but the context to attach
333 * will still be bound.
334 */
335 public void detach(Context toAttach) {
336 Preconditions.checkNotNull(toAttach);
337 Context previous = current();
338 if (previous != this) {
339 // Log a severe message instead of throwing an exception as the context to attach is assumed
340 // to be the correct one and the unbalanced state represents a coding mistake in a lower
341 // layer in the stack that cannot be recovered from here.
342 LOG.log(Level.SEVERE, "Context was not attached when detaching",
343 new Throwable().fillInStackTrace());
344 }
345 localContext.set(toAttach);
Louis Ryanf6121162015-06-24 13:54:13 -0700346 }
347
348 // Visible for testing
349 boolean isCurrent() {
350 return current() == this;
351 }
352
353 /**
Louis Ryanf6121162015-06-24 13:54:13 -0700354 * Is this context cancelled.
355 */
356 public boolean isCancelled() {
357 if (parent == null || !cascadesCancellation) {
358 return false;
359 } else {
360 return parent.isCancelled();
361 }
362 }
363
364 /**
365 * If a context {@link #isCancelled()} then return the cause of the cancellation or
366 * {@code null} if context was cancelled without a cause. If the context is not yet cancelled
367 * will always return {@code null}.
368 *
369 * <p>The cause is provided for informational purposes only and implementations should generally
370 * assume that it has already been handled and logged properly.
371 */
372 @Nullable
373 public Throwable cause() {
374 if (parent == null || !cascadesCancellation) {
375 return null;
376 } else {
377 return parent.cause();
378 }
379 }
380
381 /**
382 * Add a listener that will be notified when the context becomes cancelled.
383 */
384 public void addListener(final CancellationListener cancellationListener,
385 final Executor executor) {
386 Preconditions.checkNotNull(cancellationListener);
387 Preconditions.checkNotNull(executor);
Louis Ryan73e2a232015-11-13 17:18:12 -0800388 if (canBeCancelled) {
Louis Ryanf6121162015-06-24 13:54:13 -0700389 ExecutableListener executableListener =
390 new ExecutableListener(executor, cancellationListener);
391 synchronized (this) {
392 if (isCancelled()) {
393 executableListener.deliver();
394 } else {
395 if (listeners == null) {
396 // Now that we have a listener we need to listen to our parent so
397 // we can cascade listener notification.
398 listeners = new ArrayList<ExecutableListener>();
399 listeners.add(executableListener);
400 parent.addListener(parentListener, MoreExecutors.directExecutor());
401 } else {
402 listeners.add(executableListener);
403 }
404 }
405 }
Louis Ryanf6121162015-06-24 13:54:13 -0700406 }
407 }
408
409 /**
410 * Remove a {@link CancellationListener}.
411 */
412 public void removeListener(CancellationListener cancellationListener) {
Louis Ryan73e2a232015-11-13 17:18:12 -0800413 if (!canBeCancelled) {
414 return;
415 }
Louis Ryanf6121162015-06-24 13:54:13 -0700416 synchronized (this) {
417 if (listeners != null) {
418 for (int i = listeners.size() - 1; i >= 0; i--) {
419 if (listeners.get(i).listener == cancellationListener) {
420 listeners.remove(i);
Louis Ryan73e2a232015-11-13 17:18:12 -0800421 // Just remove the first matching listener, given that we allow duplicate
422 // adds we should allow for duplicates after remove.
Louis Ryanf6121162015-06-24 13:54:13 -0700423 break;
424 }
425 }
426 // We have no listeners so no need to listen to our parent
427 if (listeners.isEmpty()) {
428 parent.removeListener(parentListener);
429 listeners = null;
430 }
431 }
432 }
433 }
434
435 /**
436 * Notify all listeners that this context has been cancelled and immediately release
437 * any reference to them so that they may be garbage collected.
438 */
439 void notifyAndClearListeners() {
Louis Ryan73e2a232015-11-13 17:18:12 -0800440 if (!canBeCancelled) {
441 return;
442 }
Louis Ryanf6121162015-06-24 13:54:13 -0700443 ArrayList<ExecutableListener> tmpListeners;
444 synchronized (this) {
445 if (listeners == null) {
446 return;
447 }
448 tmpListeners = listeners;
449 listeners = null;
450 }
Louis Ryan73e2a232015-11-13 17:18:12 -0800451 // Deliver events to non-child context listeners before we notify child contexts. We do this
452 // to cancel higher level units of work before child units. This allows for a better error
453 // handling paradigm where the higher level unit of work knows it is cancelled and so can
454 // ignore errors that bubble up as a result of cancellation of lower level units.
Louis Ryanf6121162015-06-24 13:54:13 -0700455 for (int i = 0; i < tmpListeners.size(); i++) {
Louis Ryan73e2a232015-11-13 17:18:12 -0800456 if (!(tmpListeners.get(i).listener instanceof ParentListener)) {
457 tmpListeners.get(i).deliver();
458 }
459 }
460 for (int i = 0; i < tmpListeners.size(); i++) {
461 if (tmpListeners.get(i).listener instanceof ParentListener) {
462 tmpListeners.get(i).deliver();
463 }
Louis Ryanf6121162015-06-24 13:54:13 -0700464 }
465 parent.removeListener(parentListener);
466 }
467
468 // Used in tests to ensure that listeners are defined and released based on
469 // cancellation propagation. It's very important to ensure that we do not
470 // accidentally retain listeners.
471 int listenerCount() {
472 synchronized (this) {
473 return listeners == null ? 0 : listeners.size();
474 }
475 }
476
477 /**
478 * Wrap a {@link Runnable} so that it executes with this context as the {@link #current} context.
479 */
480 public Runnable wrap(final Runnable r) {
481 return new Runnable() {
482 @Override
483 public void run() {
Louis Ryan49b51352015-11-06 17:01:26 -0800484 Context previous = attach();
Louis Ryanf6121162015-06-24 13:54:13 -0700485 try {
486 r.run();
487 } finally {
Louis Ryan49b51352015-11-06 17:01:26 -0800488 detach(previous);
Louis Ryanf6121162015-06-24 13:54:13 -0700489 }
490 }
491 };
492 }
493
494 /**
495 * Wrap a {@link Callable} so that it executes with this context as the {@link #current} context.
496 */
497 public <C> Callable<C> wrap(final Callable<C> c) {
498 return new Callable<C>() {
499 @Override
500 public C call() throws Exception {
Louis Ryan49b51352015-11-06 17:01:26 -0800501 Context previous = attach();
Louis Ryanf6121162015-06-24 13:54:13 -0700502 try {
503 return c.call();
504 } finally {
Louis Ryan49b51352015-11-06 17:01:26 -0800505 detach(previous);
Louis Ryanf6121162015-06-24 13:54:13 -0700506 }
507 }
508 };
509 }
510
511 /**
Eric Andersona5d9e342015-07-22 16:54:08 -0700512 * Wrap an {@link Executor} so that it executes with this context as the {@link #current} context.
513 * It is generally expected that {@link #propagate(Executor)} would be used more commonly than
514 * this method.
515 *
516 * @see #propagate(Executor)
517 */
518 public Executor wrap(final Executor e) {
519 class WrappingExecutor implements Executor {
520 @Override
521 public void execute(Runnable r) {
522 e.execute(wrap(r));
523 }
524 }
525
526 return new WrappingExecutor();
527 }
528
529 /**
530 * Create an executor that propagates the {@link #current} context when {@link Executor#execute}
531 * is called to the {@link #current} context of the {@code Runnable} scheduled. <em>Note that this
532 * is a static method.</em>
533 *
534 * @see #wrap(Executor)
535 */
536 public static Executor propagate(final Executor e) {
537 class PropagatingExecutor implements Executor {
538 @Override
539 public void execute(Runnable r) {
540 e.execute(Context.current().wrap(r));
541 }
542 }
543
544 return new PropagatingExecutor();
545 }
546
547 /**
Louis Ryanf6121162015-06-24 13:54:13 -0700548 * Lookup the value for a key in the context inheritance chain.
549 */
Eric Andersoneba12fb2015-07-23 13:24:04 -0700550 private Object lookup(Key<?> key) {
Louis Ryanf6121162015-06-24 13:54:13 -0700551 for (int i = 0; i < keyValueEntries.length; i++) {
552 if (key.equals(keyValueEntries[i][0])) {
553 return keyValueEntries[i][1];
554 }
555 }
556 if (parent == null) {
557 return null;
558 }
559 return parent.lookup(key);
560 }
561
562 /**
563 * A context which inherits cancellation from its parent but which can also be independently
564 * cancelled and which will propagate cancellation to its descendants.
565 */
566 public static final class CancellableContext extends Context {
567
568 private boolean cancelled;
569 private Throwable cause;
570 private final Context dummy;
571 private ScheduledFuture<?> scheduledFuture;
572
573 /**
574 * Create a cancellable context that does not have a deadline.
575 */
576 private CancellableContext(Context parent) {
Louis Ryan73e2a232015-11-13 17:18:12 -0800577 super(parent, EMPTY_ENTRIES, true);
Louis Ryan49b51352015-11-06 17:01:26 -0800578 // Create a dummy that inherits from this to attach so that you cannot retrieve a
Louis Ryanf6121162015-06-24 13:54:13 -0700579 // cancellable context from Context.current()
580 dummy = new Context(this, EMPTY_ENTRIES);
581 }
582
583 /**
584 * Create a cancellable context that has a deadline.
585 */
Eric Anderson6e94cf32016-01-23 09:32:41 -0800586 private CancellableContext(Context parent, long delayNanos,
587 ScheduledExecutorService scheduler) {
Louis Ryanf6121162015-06-24 13:54:13 -0700588 this(parent);
Eric Anderson63f14822015-10-26 17:03:30 -0700589 scheduledFuture = scheduler.schedule(new Runnable() {
Louis Ryanf6121162015-06-24 13:54:13 -0700590 @Override
591 public void run() {
Eric Anderson6e94cf32016-01-23 09:32:41 -0800592 cancel(new TimeoutException("context timed out"));
Louis Ryanf6121162015-06-24 13:54:13 -0700593 }
594 }, delayNanos, TimeUnit.NANOSECONDS);
595 }
596
597
598 @Override
Louis Ryan49b51352015-11-06 17:01:26 -0800599 public Context attach() {
600 return dummy.attach();
Louis Ryanf6121162015-06-24 13:54:13 -0700601 }
602
603 @Override
Louis Ryan49b51352015-11-06 17:01:26 -0800604 public void detach(Context toAttach) {
605 dummy.detach(toAttach);
Louis Ryanf6121162015-06-24 13:54:13 -0700606 }
607
608 @Override
609 public boolean isCurrent() {
610 return dummy.isCurrent();
611 }
612
613 /**
614 * Attach this context to the thread and return a {@link AutoCloseable} that can be
Louis Ryan49b51352015-11-06 17:01:26 -0800615 * used with try-with-resource statements to properly attach the previously bound context
616 * when {@link AutoCloseable#close()} is called.
Louis Ryanf6121162015-06-24 13:54:13 -0700617 *
618 * @return a {@link java.io.Closeable} which can be used with try-with-resource blocks.
619 */
620 public Closeable attachAsCloseable() {
Louis Ryan49b51352015-11-06 17:01:26 -0800621 final Context previous = attach();
Louis Ryanf6121162015-06-24 13:54:13 -0700622 return new Closeable() {
623 @Override
624 public void close() throws IOException {
Louis Ryan49b51352015-11-06 17:01:26 -0800625 detachAndCancel(previous, null);
Louis Ryanf6121162015-06-24 13:54:13 -0700626 }
627 };
628 }
629
630 /**
631 * Cancel this context and optionally provide a cause for the cancellation. This
632 * will trigger notification of listeners.
633 *
634 * @return {@code true} if this context cancelled the context and notified listeners,
635 * {@code false} if the context was already cancelled.
636 */
637 public boolean cancel(@Nullable Throwable cause) {
638 boolean triggeredCancel = false;
639 synchronized (this) {
640 if (!cancelled) {
641 cancelled = true;
642 if (scheduledFuture != null) {
643 // If we have a scheduled cancellation pending attempt to cancel it.
644 scheduledFuture.cancel(false);
645 scheduledFuture = null;
646 }
647 this.cause = cause;
648 triggeredCancel = true;
649 }
650 }
651 if (triggeredCancel) {
652 notifyAndClearListeners();
653 }
654 return triggeredCancel;
655 }
656
657 /**
Louis Ryan49b51352015-11-06 17:01:26 -0800658 * Cancel this context and detach it as the current context from the thread.
Louis Ryanf6121162015-06-24 13:54:13 -0700659 *
Louis Ryan49b51352015-11-06 17:01:26 -0800660 * @param toAttach context to make current.
661 * @param cause of cancellation, can be {@code null}.
Louis Ryanf6121162015-06-24 13:54:13 -0700662 */
Louis Ryan49b51352015-11-06 17:01:26 -0800663 public void detachAndCancel(Context toAttach, @Nullable Throwable cause) {
Louis Ryanf6121162015-06-24 13:54:13 -0700664 try {
Louis Ryan49b51352015-11-06 17:01:26 -0800665 detach(toAttach);
Louis Ryanf6121162015-06-24 13:54:13 -0700666 } finally {
667 cancel(cause);
668 }
669 }
670
671 @Override
Louis Ryanf6121162015-06-24 13:54:13 -0700672 public boolean isCancelled() {
673 synchronized (this) {
674 if (cancelled) {
675 return true;
676 }
677 }
678 // Detect cancellation of parent in the case where we have no listeners and
679 // record it.
680 if (super.isCancelled()) {
681 cancel(super.cause());
682 return true;
683 }
684 return false;
685 }
686
687 @Nullable
688 @Override
689 public Throwable cause() {
690 if (isCancelled()) {
691 return cause;
692 }
693 return null;
694 }
695 }
696
697 /**
698 * A listener notified on context cancellation.
699 */
700 public interface CancellationListener {
701 /**
702 * @param context the newly cancelled context.
703 */
704 public void cancelled(Context context);
705 }
706
707 /**
708 * Key for indexing values stored in a context.
709 */
710 public static class Key<T> {
711
712 private final String name;
713 private final T defaultValue;
714
715 Key(String name) {
716 this(name, null);
717 }
718
719 Key(String name, T defaultValue) {
720 this.name = Preconditions.checkNotNull(name);
721 this.defaultValue = defaultValue;
722 }
723
724 /**
725 * Get the value from the {@link #current()} context for this key.
726 */
727 @SuppressWarnings("unchecked")
728 public T get() {
729 return get(Context.current());
730 }
731
732 /**
733 * Get the value from the specified context for this key.
734 */
735 @SuppressWarnings("unchecked")
736 public T get(Context context) {
737 T value = (T) context.lookup(this);
738 return value == null ? defaultValue : value;
739 }
740
741 @Override
Louis Ryanbe600862015-11-06 13:46:13 -0800742 public String toString() {
743 return name;
744 }
Louis Ryanf6121162015-06-24 13:54:13 -0700745 }
746
747 /**
748 * Stores listener & executor pair.
749 */
750 private class ExecutableListener implements Runnable {
751 private final Executor executor;
752 private final CancellationListener listener;
753
754 private ExecutableListener(Executor executor, CancellationListener listener) {
755 this.executor = executor;
756 this.listener = listener;
757 }
758
759 private void deliver() {
Eric Anderson63f14822015-10-26 17:03:30 -0700760 try {
761 executor.execute(this);
762 } catch (Throwable t) {
763 LOG.log(Level.INFO, "Exception notifying context listener", t);
764 }
Louis Ryanf6121162015-06-24 13:54:13 -0700765 }
766
767 @Override
768 public void run() {
769 listener.cancelled(Context.this);
770 }
771 }
Louis Ryan73e2a232015-11-13 17:18:12 -0800772
773 private class ParentListener implements CancellationListener {
774 @Override
775 public void cancelled(Context context) {
776 if (Context.this instanceof CancellableContext) {
777 // Record cancellation with its cause.
778 ((CancellableContext) Context.this).cancel(context.cause());
779 } else {
780 notifyAndClearListeners();
781 }
782 }
783 }
Louis Ryanf6121162015-06-24 13:54:13 -0700784}