@@ -4,31 +4,99 @@ import kotlinx.coroutines.internal.*
44import kotlin.coroutines.*
55import kotlin.coroutines.intrinsics.*
66
7- // --------------- cancellable continuations ---------------
8-
97/* *
10- * Cancellable continuation. It is _completed_ when resumed or cancelled.
11- * When the [cancel] function is explicitly invoked, this continuation immediately resumes with a [CancellationException] or
8+ * Cancellable [continuation][Continuation] is a thread-safe continuation primitive with the support of
9+ * an asynchronous cancellation.
10+ *
11+ * Cancellable continuation can be [resumed][Continuation.resumeWith], but unlike regular [Continuation],
12+ * it also might be [cancelled][CancellableContinuation.cancel] explicitly or [implicitly][Job.cancel] via a parent [job][Job].
13+ *
14+ * If the continuation is cancelled successfully, it resumes with a [CancellationException] or
1215 * the specified cancel cause.
1316 *
14- * An instance of `CancellableContinuation` is created by the [suspendCancellableCoroutine] function.
17+ * ### Usage
18+ *
19+ * An instance of `CancellableContinuation` can only be obtained by the [suspendCancellableCoroutine] function.
20+ * The interface itself is public for use and private for implementation.
21+ *
22+ * A typical usages of this function is to suspend a coroutine while waiting for a result
23+ * from a callback or an external source of values that optionally supports cancellation:
24+ *
25+ * ```
26+ * suspend fun <T> CompletableFuture<T>.await(): T = suspendCancellableCoroutine { c ->
27+ * val future = this
28+ * future.whenComplete { result, throwable ->
29+ * if (throwable != null) {
30+ * // Resume continuation with an exception if an external source failed
31+ * c.resumeWithException(throwable)
32+ * } else {
33+ * // Resume continuation with a value if it was computed
34+ * c.resume(result)
35+ * }
36+ * }
37+ * // Cancel the computation if the continuation itself was cancelled because a caller of 'await' is cancelled
38+ * c.invokeOnCancellation { future.cancel(true) }
39+ * }
40+ * ```
41+ *
42+ * ### Thread-safety
43+ *
44+ * Instances of [CancellableContinuation] are thread-safe and can be safely shared across multiple threads.
45+ * [CancellableContinuation] allows concurrent invocations of the [cancel] and [resume] pair, guaranteeing
46+ * that only one of these operations will succeed.
47+ * Concurrent invocations of [resume] methods lead to a [IllegalStateException] and are considered a programmatic error.
48+ * Concurrent invocations of [cancel] methods is permitted, and at most one of them succeeds.
49+ *
50+ * ### Prompt cancellation guarantee
51+ *
52+ * A cancellable continuation provides a **prompt cancellation guarantee**.
53+ *
54+ * If the [Job] of the coroutine that obtained a cancellable continuation was cancelled while this continuation was suspended it will not resume
55+ * successfully, even if [CancellableContinuation.resume] was already invoked but not yet executed.
56+ *
57+ * The cancellation of the coroutine's job is generally asynchronous with respect to the suspended coroutine.
58+ * The suspended coroutine is resumed with a call to its [Continuation.resumeWith] member function or to the
59+ * [resume][Continuation.resume] extension function.
60+ * However, when the coroutine is resumed, it does not immediately start executing but is passed to its
61+ * [CoroutineDispatcher] to schedule its execution when the dispatcher's resources become available for execution.
62+ * The job's cancellation can happen before, after, and concurrently with the call to `resume`. In any
63+ * case, prompt cancellation guarantees that the coroutine will not resume its code successfully.
64+ *
65+ * If the coroutine was resumed with an exception (for example, using the [Continuation.resumeWithException] extension
66+ * function) and cancelled, then the exception thrown by the `suspendCancellableCoroutine` function is determined
67+ * by what happened first: exceptional resume or cancellation.
68+ *
69+ * ### Resuming with a closeable resource
1570 *
16- * Cancellable continuation has three states (as subset of [Job] states):
71+ * [CancellableContinuation] provides the capability to work with values that represent a resource that should be
72+ * closed. For that, it provides `resume(value: R, onCancellation: ((cause: Throwable, value: R, context: CoroutineContext) -> Unit)`
73+ * function that guarantees that either the given `value` will be successfully returned from the corresponding
74+ * `suspend` function or that `onCancellation` will be invoked with the supplied value:
75+ *
76+ * ```
77+ * continuation.resume(resourceToResumeWith) { _, resourceToClose, _
78+ * // Will be invoked if the continuation is cancelled while being dispatched
79+ * resourceToClose.close()
80+ * }
81+ * ```
82+ *
83+ * #### Continuation states
84+ *
85+ * A cancellable continuation has three observable states:
1786 *
1887 * | **State** | [isActive] | [isCompleted] | [isCancelled] |
1988 * | ----------------------------------- | ---------- | ------------- | ------------- |
2089 * | _Active_ (initial state) | `true` | `false` | `false` |
2190 * | _Resumed_ (final _completed_ state) | `false` | `true` | `false` |
2291 * | _Canceled_ (final _completed_ state)| `false` | `true` | `true` |
2392 *
24- * Invocation of [cancel] transitions this continuation from _active_ to _cancelled_ state, while
25- * invocation of [Continuation.resume] or [Continuation.resumeWithException] transitions it from _active_ to _resumed_ state.
26- *
27- * A [cancelled][isCancelled] continuation implies that it is [completed][isCompleted].
93+ * For a detailed description of each state, see the corresponding properties' documentation.
2894 *
29- * Invocation of [Continuation.resume] or [Continuation.resumeWithException] in _resumed_ state produces an [IllegalStateException],
30- * but is ignored in _cancelled_ state.
95+ * A successful invocation of [cancel] transitions the continuation from an _active_ to a _cancelled_ state, while
96+ * an invocation of [Continuation.resume] or [Continuation.resumeWithException] transitions it from
97+ * an _active_ to _resumed_ state.
3198 *
99+ * Possible state transitions diagram:
32100 * ```
33101 * +-----------+ resume +---------+
34102 * | Active | ----------> | Resumed |
@@ -45,18 +113,24 @@ import kotlin.coroutines.intrinsics.*
45113@SubclassOptInRequired(InternalForInheritanceCoroutinesApi ::class )
46114public interface CancellableContinuation <in T > : Continuation <T > {
47115 /* *
48- * Returns `true` when this continuation is active -- it has not completed or cancelled yet.
116+ * Returns `true` when this continuation is active -- it was created,
117+ * but not yet [resumed][Continuation.resumeWith] or [cancelled][CancellableContinuation.cancel].
118+ *
119+ * This state implies that [isCompleted] and [isCancelled] are `false`,
120+ * but this can change immediately after the invocation because of parallel calls to [cancel] and [resume].
49121 */
50122 public val isActive: Boolean
51123
52124 /* *
53- * Returns `true` when this continuation has completed for any reason. A cancelled continuation
54- * is also considered complete.
125+ * Returns `true` when this continuation was completed -- [resumed][Continuation.resumeWith] or
126+ * [cancelled][CancellableContinuation.cancel].
127+ *
128+ * This state implies that [isActive] is `false`.
55129 */
56130 public val isCompleted: Boolean
57131
58132 /* *
59- * Returns `true` if this continuation was [cancelled][cancel].
133+ * Returns `true` if this continuation was [cancelled][CancellableContinuation. cancel].
60134 *
61135 * It implies that [isActive] is `false` and [isCompleted] is `true`.
62136 */
@@ -124,6 +198,7 @@ public interface CancellableContinuation<in T> : Continuation<T> {
124198 /* *
125199 * Cancels this continuation with an optional cancellation `cause`. The result is `true` if this continuation was
126200 * cancelled as a result of this invocation, and `false` otherwise.
201+ * [cancel] might return `false` when the continuation was either [resumed][resume] or already [cancelled][cancel].
127202 */
128203 public fun cancel (cause : Throwable ? = null): Boolean
129204
@@ -243,7 +318,7 @@ internal fun <T> CancellableContinuation<T>.invokeOnCancellation(handler: Cancel
243318/* *
244319 * Suspends the coroutine like [suspendCoroutine], but providing a [CancellableContinuation] to
245320 * the [block]. This function throws a [CancellationException] if the [Job] of the coroutine is
246- * cancelled or completed while it is suspended.
321+ * cancelled or completed while it is suspended, or if [CancellableContinuation.cancel] is invoked .
247322 *
248323 * A typical use of this function is to suspend a coroutine while waiting for a result
249324 * from a single-shot callback API and to return the result to the caller.
0 commit comments