@@ -4,31 +4,99 @@ import kotlinx.coroutines.internal.*
4
4
import kotlin.coroutines.*
5
5
import kotlin.coroutines.intrinsics.*
6
6
7
- // --------------- cancellable continuations ---------------
8
-
9
7
/* *
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
12
15
* the specified cancel cause.
13
16
*
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
15
70
*
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:
17
86
*
18
87
* | **State** | [isActive] | [isCompleted] | [isCancelled] |
19
88
* | ----------------------------------- | ---------- | ------------- | ------------- |
20
89
* | _Active_ (initial state) | `true` | `false` | `false` |
21
90
* | _Resumed_ (final _completed_ state) | `false` | `true` | `false` |
22
91
* | _Canceled_ (final _completed_ state)| `false` | `true` | `true` |
23
92
*
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.
28
94
*
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.
31
98
*
99
+ * Possible state transitions diagram:
32
100
* ```
33
101
* +-----------+ resume +---------+
34
102
* | Active | ----------> | Resumed |
@@ -45,18 +113,24 @@ import kotlin.coroutines.intrinsics.*
45
113
@SubclassOptInRequired(InternalForInheritanceCoroutinesApi ::class )
46
114
public interface CancellableContinuation <in T > : Continuation <T > {
47
115
/* *
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].
49
121
*/
50
122
public val isActive: Boolean
51
123
52
124
/* *
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`.
55
129
*/
56
130
public val isCompleted: Boolean
57
131
58
132
/* *
59
- * Returns `true` if this continuation was [cancelled][cancel].
133
+ * Returns `true` if this continuation was [cancelled][CancellableContinuation. cancel].
60
134
*
61
135
* It implies that [isActive] is `false` and [isCompleted] is `true`.
62
136
*/
@@ -124,6 +198,7 @@ public interface CancellableContinuation<in T> : Continuation<T> {
124
198
/* *
125
199
* Cancels this continuation with an optional cancellation `cause`. The result is `true` if this continuation was
126
200
* 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].
127
202
*/
128
203
public fun cancel (cause : Throwable ? = null): Boolean
129
204
@@ -243,7 +318,7 @@ internal fun <T> CancellableContinuation<T>.invokeOnCancellation(handler: Cancel
243
318
/* *
244
319
* Suspends the coroutine like [suspendCoroutine], but providing a [CancellableContinuation] to
245
320
* 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 .
247
322
*
248
323
* A typical use of this function is to suspend a coroutine while waiting for a result
249
324
* from a single-shot callback API and to return the result to the caller.
0 commit comments