public class Context extends Object
A Context object can be attached
to the Context.Storage
, which effectively
forms a scope for the context. The scope is bound to the current thread. Within a scope,
its Context is accessible even across API boundaries, through current()
. The scope is
later exited by detaching
the Context.
Context objects are immutable and inherit state from their parent. To add or overwrite the current state a new context object must be created and then attached, replacing the previously bound context. For example:
Context withCredential = Context.current().withValue(CRED_KEY, cred); withCredential.run(new Runnable() { public void run() { readUserRecords(userId, CRED_KEY.get()); } });
Contexts are also used to represent a scoped unit of work. When the unit of work is done the
context can be cancelled. This cancellation will also cascade to all descendant contexts. You can
add a Context.CancellationListener
to a context to be notified when it or one of its ancestors
has been cancelled. Cancellation does not release the state stored by a context and it's
perfectly valid to attach()
an already cancelled context to make it current. To cancel a
context (and its descendants) you first create a Context.CancellableContext
and when you need to
signal cancellation call Context.CancellableContext.cancel(java.lang.Throwable)
or Context.CancellableContext.detachAndCancel(io.grpc.Context, java.lang.Throwable)
. For example:
CancellableContext withCancellation = Context.current().withCancellation(); try { withCancellation.run(new Runnable() { public void run() { while (waitingForData() && !Context.current().isCancelled()) {} } }); doSomeWork(); } catch (Throwable t) { withCancellation.cancel(t); }
Contexts can also be created with a timeout relative to the system nano clock which will cause it to automatically cancel at the desired time.
Notes and cautions on use:
attach()
should have a detach()
in the same method. And every
CancellableContext should be cancelled at some point. Breaking these rules may lead to memory
leaks.
ROOT
for a non-null instance.
Modifier and Type | Class and Description |
---|---|
static class |
Context.CancellableContext
A context which inherits cancellation from its parent but which can also be independently
cancelled and which will propagate cancellation to its descendants.
|
static interface |
Context.CancellationListener
A listener notified on context cancellation.
|
static class |
Context.Key<T>
Key for indexing values stored in a context.
|
static class |
Context.Storage
Defines the mechanisms for attaching and detaching the "current" context.
|
Modifier and Type | Field and Description |
---|---|
static Context |
ROOT
The logical root context which is the ultimate ancestor of all contexts.
|
Modifier and Type | Method and Description |
---|---|
void |
addListener(Context.CancellationListener cancellationListener,
Executor executor)
Add a listener that will be notified when the context becomes cancelled.
|
Context |
attach()
Attach this context, thus enter a new scope within which this context is
current() . |
<V> V |
call(Callable<V> c)
|
Throwable |
cancellationCause()
If a context
isCancelled() then return the cause of the cancellation or
null if context was cancelled without a cause. |
static Context |
current()
Return the context associated with the current scope, will never return
null . |
static Executor |
currentContextExecutor(Executor e)
Create an executor that propagates the
current() context when Executor.execute(java.lang.Runnable)
is called as the current() context of the Runnable scheduled. |
void |
detach(Context toAttach)
Reverse an
attach() , restoring the previous context and exiting the current scope. |
Executor |
fixedContextExecutor(Executor e)
|
Context |
fork()
Create a new context which propagates the values of this context but does not cascade its
cancellation.
|
Deadline |
getDeadline()
A context may have an associated
Deadline at which it will be automatically cancelled. |
boolean |
isCancelled()
Is this context cancelled.
|
static <T> Context.Key<T> |
key(String name)
Create a
Context.Key with the given debug name. |
static <T> Context.Key<T> |
keyWithDefault(String name,
T defaultValue)
Create a
Context.Key with the given debug name and default value. |
void |
removeListener(Context.CancellationListener cancellationListener)
Remove a
Context.CancellationListener . |
void |
run(Runnable r)
|
Context.CancellableContext |
withCancellation()
Create a new context which is independently cancellable and also cascades cancellation from
its parent.
|
Context.CancellableContext |
withDeadline(Deadline deadline,
ScheduledExecutorService scheduler)
Create a new context which will cancel itself at the given
Deadline . |
Context.CancellableContext |
withDeadlineAfter(long duration,
TimeUnit unit,
ScheduledExecutorService scheduler)
Create a new context which will cancel itself after the given
duration from now. |
<V> Context |
withValue(Context.Key<V> k1,
V v1)
Create a new context with the given key value set.
|
<V1,V2> Context |
withValues(Context.Key<V1> k1,
V1 v1,
Context.Key<V2> k2,
V2 v2)
Create a new context with the given key value set.
|
<V1,V2,V3> Context |
withValues(Context.Key<V1> k1,
V1 v1,
Context.Key<V2> k2,
V2 v2,
Context.Key<V3> k3,
V3 v3)
Create a new context with the given key value set.
|
<V1,V2,V3,V4> |
withValues(Context.Key<V1> k1,
V1 v1,
Context.Key<V2> k2,
V2 v2,
Context.Key<V3> k3,
V3 v3,
Context.Key<V4> k4,
V4 v4)
Create a new context with the given key value set.
|
<C> Callable<C> |
wrap(Callable<C> c)
|
Runnable |
wrap(Runnable r)
|
public static final Context ROOT
Never assume this is the default context for new threads, because Context.Storage
may define
a default context that is different from ROOT.
public static <T> Context.Key<T> key(String name)
Context.Key
with the given debug name. Multiple different keys may have the same name;
the name is intended for debugging purposes and does not impact behavior.public static <T> Context.Key<T> keyWithDefault(String name, T defaultValue)
Context.Key
with the given debug name and default value. Multiple different keys may
have the same name; the name is intended for debugging purposes and does not impact behavior.public static Context current()
null
.
Will never return Context.CancellableContext
even if one is attached, instead a
Context
is returned with the same properties and lifetime. This is to avoid
code stealing the ability to cancel arbitrarily.
public Context.CancellableContext withCancellation()
Context.CancellableContext.cancel(Throwable)
or Context.CancellableContext.detachAndCancel(Context,
Throwable)
are called at a later point, in order to allow this context to be garbage
collected.
Sample usage:
Context.CancellableContext withCancellation = Context.current().withCancellation(); try { withCancellation.run(new Runnable() { public void run() { Context current = Context.current(); while (!current.isCancelled()) { keepWorking(); } } }); } finally { withCancellation.cancel(null); }
public Context.CancellableContext withDeadlineAfter(long duration, TimeUnit unit, ScheduledExecutorService scheduler)
duration
from now.
The returned context will cascade cancellation of its parent. Callers may explicitly cancel
the returned context prior to the deadline just as for withCancellation()
. If the unit
of work completes before the deadline, the context should be explicitly cancelled to allow
it to be garbage collected.
Sample usage:
Context.CancellableContext withDeadline = Context.current() .withDeadlineAfter(5, TimeUnit.SECONDS, scheduler); try { withDeadline.run(new Runnable() { public void run() { Context current = Context.current(); while (!current.isCancelled()) { keepWorking(); } } }); } finally { withDeadline.cancel(null); }
public Context.CancellableContext withDeadline(Deadline deadline, ScheduledExecutorService scheduler)
Deadline
.
The returned context will cascade cancellation of its parent. Callers may explicitly cancel
the returned context prior to the deadline just as for withCancellation()
. If the unit
of work completes before the deadline, the context should be explicitly cancelled to allow
it to be garbage collected.
Sample usage:
Context.CancellableContext withDeadline = Context.current() .withDeadline(someReceivedDeadline, scheduler); try { withDeadline.run(new Runnable() { public void run() { Context current = Context.current(); while (!current.isCancelled() && moreWorkToDo()) { keepWorking(); } } }); } finally { withDeadline.cancel(null); }
public <V> Context withValue(Context.Key<V> k1, V v1)
Context withCredential = Context.current().withValue(CRED_KEY, cred); withCredential.run(new Runnable() { public void run() { readUserRecords(userId, CRED_KEY.get()); } });
public <V1,V2> Context withValues(Context.Key<V1> k1, V1 v1, Context.Key<V2> k2, V2 v2)
public <V1,V2,V3> Context withValues(Context.Key<V1> k1, V1 v1, Context.Key<V2> k2, V2 v2, Context.Key<V3> k3, V3 v3)
public <V1,V2,V3,V4> Context withValues(Context.Key<V1> k1, V1 v1, Context.Key<V2> k2, V2 v2, Context.Key<V3> k3, V3 v3, Context.Key<V4> k4, V4 v4)
public Context fork()
public Context attach()
current()
. The
previously current context is returned. It is allowed to attach contexts where isCancelled()
is true
.
Instead of using attach()
and detach(Context)
most use-cases are better
served by using the run(Runnable)
or call(java.util.concurrent.Callable)
to
execute work immediately within a context's scope. If work needs to be done in other threads it
is recommended to use the 'wrap' methods or to use a propagating executor.
All calls to attach()
should have a corresponding detach(Context)
within
the same method:
Context previous = someContext.attach();
try {
// Do work
} finally {
someContext.detach(previous);
}
public void detach(Context toAttach)
attach()
, restoring the previous context and exiting the current scope.
This context should be the same context that was previously attached
. The
provided replacement should be what was returned by the same attach()
call. If
an attach()
and a detach()
meet above requirements, they match.
It is expected that between any pair of matching attach()
and detach()
, all
attach()
es and detach()
es are called in matching pairs. If this method finds
that this context is not current
, either you or some code in-between are not
detaching correctly, and a SEVERE message will be logged but the context to attach will still
be bound. Never use Context.current().detach()
, as this will
compromise this error-detecting mechanism.
public boolean isCancelled()
public Throwable cancellationCause()
isCancelled()
then return the cause of the cancellation or
null
if context was cancelled without a cause. If the context is not yet cancelled
will always return null
.
The cancellation cause is provided for informational purposes only and implementations should generally assume that it has already been handled and logged properly.
public Deadline getDeadline()
Deadline
at which it will be automatically cancelled.Deadline
or null
if no deadline is set.public void addListener(Context.CancellationListener cancellationListener, Executor executor)
public void removeListener(Context.CancellationListener cancellationListener)
Context.CancellationListener
.public Executor fixedContextExecutor(Executor e)
Executor
so that it always executes with this context as the current()
context. It is generally expected that currentContextExecutor(Executor)
would be
used more commonly than this method.
One scenario in which this executor may be useful is when a single thread is sharding work to multiple threads.
currentContextExecutor(Executor)
public static Executor currentContextExecutor(Executor e)
current()
context when Executor.execute(java.lang.Runnable)
is called as the current()
context of the Runnable
scheduled. Note that this
is a static method.fixedContextExecutor(Executor)