diff --git a/context/src/main/java/io/grpc/Context.java b/context/src/main/java/io/grpc/Context.java index 910233fae7..4734e85457 100644 --- a/context/src/main/java/io/grpc/Context.java +++ b/context/src/main/java/io/grpc/Context.java @@ -16,6 +16,7 @@ package io.grpc; +import java.io.Closeable; import java.util.ArrayList; import java.util.concurrent.Callable; import java.util.concurrent.Executor; @@ -657,8 +658,29 @@ public class Context { * cancelled and which will propagate cancellation to its descendants. To avoid leaking memory, * every CancellableContext must have a defined lifetime, after which it is guaranteed to be * cancelled. + * + *
This class must be cancelled by either calling {@link #close} or {@link #cancel}. + * {@link #close} is equivalent to calling {@code cancel(null)}. It is safe to call the methods + * more than once, but only the first call will have any effect. Because it's safe to call the + * methods multiple times, users are encouraged to always call {@link #close} at the end of + * the operation, and disregard whether {@link #cancel} was already called somewhere else. + * + *
Blocking code can use the try-with-resources idiom: + *
+ * try (CancellableContext c = Context.current()
+ * .withDeadlineAfter(100, TimeUnit.MILLISECONDS, executor)) {
+ * Context toRestore = c.attach();
+ * try {
+ * // do some blocking work
+ * } finally {
+ * c.detach(toRestore);
+ * }
+ * }
+ *
+ * Asynchronous code will have to manually track the end of the CancellableContext's lifetime, + * and cancel the context at the appropriate time. */ - public static final class CancellableContext extends Context { + public static final class CancellableContext extends Context implements Closeable { private final Deadline deadline; private final Context uncancellableSurrogate; @@ -739,7 +761,10 @@ public class Context { /** * Cancel this context and optionally provide a cause (can be {@code null}) for the - * cancellation. This will trigger notification of listeners. + * cancellation. This will trigger notification of listeners. It is safe to call this method + * multiple times. Only the first call will have any effect. + * + *
Calling {@code cancel(null)} is the same as calling {@link #close}.
*
* @return {@code true} if this context cancelled the context and notified listeners,
* {@code false} if the context was already cancelled.
@@ -811,6 +836,14 @@ public class Context {
boolean canBeCancelled() {
return true;
}
+
+ /**
+ * Cleans up this object by calling {@code cancel(null)}.
+ */
+ @Override
+ public void close() {
+ cancel(null);
+ }
}
/**
diff --git a/context/src/test/java/io/grpc/ContextTest.java b/context/src/test/java/io/grpc/ContextTest.java
index 93191bdf2f..4f8e082ab6 100644
--- a/context/src/test/java/io/grpc/ContextTest.java
+++ b/context/src/test/java/io/grpc/ContextTest.java
@@ -95,13 +95,14 @@ public class ContextTest {
@After
public void tearDown() throws Exception {
scheduler.shutdown();
+ assertEquals(Context.ROOT, Context.current());
}
@Test
public void defaultContext() throws Exception {
final SettableFuture