• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.coroutines
6 
7 import kotlinx.coroutines.internal.*
8 import kotlin.coroutines.*
9 import kotlin.coroutines.intrinsics.*
10 
11 // --------------- cancellable continuations ---------------
12 
13 /**
14  * Cancellable continuation. It is _completed_ when it is resumed or cancelled.
15  * When [cancel] function is explicitly invoked, this continuation immediately resumes with [CancellationException] or
16  * with the specified cancel cause.
17  *
18  * Cancellable continuation has three states (as subset of [Job] states):
19  *
20  * | **State**                           | [isActive] | [isCompleted] | [isCancelled] |
21  * | ----------------------------------- | ---------- | ------------- | ------------- |
22  * | _Active_ (initial state)            | `true`     | `false`       | `false`       |
23  * | _Resumed_ (final _completed_ state) | `false`    | `true`        | `false`       |
24  * | _Canceled_ (final _completed_ state)| `false`    | `true`        | `true`        |
25  *
26  * Invocation of [cancel] transitions this continuation from _active_ to _cancelled_ state, while
27  * invocation of [resume] or [resumeWithException] transitions it from _active_ to _resumed_ state.
28  *
29  * A [cancelled][isCancelled] continuation implies that it is [completed][isCompleted].
30  *
31  * Invocation of [resume] or [resumeWithException] in _resumed_ state produces [IllegalStateException].
32  * Invocation of [resume] in _cancelled_ state is ignored (it is a trivial race between resume from the continuation owner and
33  * outer job cancellation and cancellation wins).
34  * Invocation of [resumeWithException] in _cancelled_ state triggers exception handling of passed exception.
35  *
36  * ```
37  *    +-----------+   resume    +---------+
38  *    |  Active   | ----------> | Resumed |
39  *    +-----------+             +---------+
40  *          |
41  *          | cancel
42  *          V
43  *    +-----------+
44  *    | Cancelled |
45  *    +-----------+
46  *
47  * ```
48  */
49 public interface CancellableContinuation<in T> : Continuation<T> {
50     /**
51      * Returns `true` when this continuation is active -- it has not completed or cancelled yet.
52      */
53     public val isActive: Boolean
54 
55     /**
56      * Returns `true` when this continuation has completed for any reason. A continuation
57      * that was cancelled is also considered complete.
58      */
59     public val isCompleted: Boolean
60 
61     /**
62      * Returns `true` if this continuation was [cancelled][cancel].
63      *
64      * It implies that [isActive] is `false` and [isCompleted] is `true`.
65      */
66     public val isCancelled: Boolean
67 
68     /**
69      * Tries to resume this continuation with a given value and returns non-null object token if it was successful,
70      * or `null` otherwise (it was already resumed or cancelled). When non-null object was returned,
71      * [completeResume] must be invoked with it.
72      *
73      * When [idempotent] is not `null`, this function performs _idempotent_ operation, so that
74      * further invocations with the same non-null reference produce the same result.
75      *
76      * @suppress **This is unstable API and it is subject to change.**
77      */
78     @InternalCoroutinesApi
79     public fun tryResume(value: T, idempotent: Any? = null): Any?
80 
81     /**
82      * Tries to resume this continuation with a given exception and returns non-null object token if it was successful,
83      * or `null` otherwise (it was already resumed or cancelled). When non-null object was returned,
84      * [completeResume] must be invoked with it.
85      *
86      * @suppress **This is unstable API and it is subject to change.**
87      */
88     @InternalCoroutinesApi
89     public fun tryResumeWithException(exception: Throwable): Any?
90 
91     /**
92      * Completes the execution of [tryResume] or [tryResumeWithException] on its non-null result.
93      *
94      * @suppress **This is unstable API and it is subject to change.**
95      */
96     @InternalCoroutinesApi
97     public fun completeResume(token: Any)
98 
99     /**
100      * Legacy function that turned on cancellation behavior in [suspendCancellableCoroutine] before kotlinx.coroutines 1.1.0.
101      * This function does nothing and is left only for binary compatibility with old compiled code.
102      *
103      * @suppress **Deprecated**: This function is no longer used.
104      *   It is left for binary compatibility with code compiled before kotlinx.coroutines 1.1.0.
105      */
106     @Deprecated(
107         level = DeprecationLevel.HIDDEN,
108         message = "This function is no longer used. " +
109             "It is left for binary compatibility with code compiled before kotlinx.coroutines 1.1.0. "
110     )
111     @InternalCoroutinesApi
112     public fun initCancellability()
113 
114     /**
115      * Cancels this continuation with an optional cancellation [cause]. The result is `true` if this continuation was
116      * cancelled as a result of this invocation and `false` otherwise.
117      */
118     public fun cancel(cause: Throwable? = null): Boolean
119 
120     /**
121      * Registers handler that is **synchronously** invoked once on cancellation (both regular and exceptional) of this continuation.
122      * When the continuation is already cancelled, then the handler is immediately invoked
123      * with cancellation exception. Otherwise, the handler will be invoked once on cancellation if this
124      * continuation is cancelled.
125      *
126      * Installed [handler] should not throw any exceptions.
127      * If it does, they will get caught, wrapped into [CompletionHandlerException] and
128      * processed as uncaught exception in the context of the current coroutine
129      * (see [CoroutineExceptionHandler]).
130      *
131      * At most one [handler] can be installed on one continuation.
132      *
133      * **Note**: Implementation of `CompletionHandler` must be fast, non-blocking, and thread-safe.
134      * This handler can be invoked concurrently with the surrounding code.
135      * There is no guarantee on the execution context in which the [handler] is invoked.
136      */
137     public fun invokeOnCancellation(handler: CompletionHandler)
138 
139     /**
140      * Resumes this continuation with a given [value] in the invoker thread without going though
141      * [dispatch][CoroutineDispatcher.dispatch] function of the [CoroutineDispatcher] in the [context].
142      * This function is designed to be used only by the [CoroutineDispatcher] implementations themselves.
143      * **It should not be used in general code**.
144      *
145      * **Note: This function is experimental.** Its signature general code may be changed in the future.
146      */
147     @ExperimentalCoroutinesApi
148     public fun CoroutineDispatcher.resumeUndispatched(value: T)
149 
150     /**
151      * Resumes this continuation with a given [exception] in the invoker thread without going though
152      * [dispatch][CoroutineDispatcher.dispatch] function of the [CoroutineDispatcher] in the [context].
153      * This function is designed to be used only by the [CoroutineDispatcher] implementations themselves.
154      * **It should not be used in general code**.
155      *
156      * **Note: This function is experimental.** Its signature general code may be changed in the future.
157      */
158     @ExperimentalCoroutinesApi
159     public fun CoroutineDispatcher.resumeUndispatchedWithException(exception: Throwable)
160 
161     /**
162      * Resumes this continuation with a given [value] and calls the specified [onCancellation]
163      * handler when resumed too late (when continuation was already cancelled) or when resumed
164      * successfully (before cancellation), but coroutine's job was cancelled before it had a
165      * chance to run in its dispatcher, so that suspended function threw an exception
166      * instead of returning this value.
167      *
168      * Installed [onCancellation] handler should not throw any exceptions.
169      * If it does, they will get caught, wrapped into [CompletionHandlerException] and
170      * processed as uncaught exception in the context of the current coroutine
171      * (see [CoroutineExceptionHandler]).
172      *
173      * This function shall be used when resuming with a resource that must be closed by the
174      * code that had called the corresponding suspending function, e.g.:
175      *
176      * ```
177      * continuation.resume(resource) {
178      *     resource.close()
179      * }
180      * ```
181      *
182      * **Note**: Implementation of [onCancellation] handler must be fast, non-blocking, and thread-safe.
183      * This handler can be invoked concurrently with the surrounding code.
184      * There is no guarantee on the execution context in which the [onCancellation] handler is invoked.
185      */
186     @ExperimentalCoroutinesApi // since 1.2.0, tentatively graduates in 1.3.0
187     public fun resume(value: T, onCancellation: (cause: Throwable) -> Unit)
188 }
189 
190 /**
191  * Suspends coroutine similar to [suspendCoroutine], but provide an implementation of [CancellableContinuation] to
192  * the [block]. This function throws [CancellationException] if the coroutine is cancelled or completed while suspended.
193  */
suspendCancellableCoroutinenull194 public suspend inline fun <T> suspendCancellableCoroutine(
195     crossinline block: (CancellableContinuation<T>) -> Unit
196 ): T =
197     suspendCoroutineUninterceptedOrReturn { uCont ->
198         val cancellable = CancellableContinuationImpl(uCont.intercepted(), resumeMode = MODE_CANCELLABLE)
199         // NOTE: Before version 1.1.0 the following invocation was inlined here, so invocation of this
200         // method indicates that the code was compiled by kotlinx.coroutines < 1.1.0
201         // cancellable.initCancellability()
202         block(cancellable)
203         cancellable.getResult()
204     }
205 
206 /**
207  * Suspends coroutine similar to [suspendCancellableCoroutine], but with *atomic cancellation*.
208  *
209  * When suspended function throws [CancellationException] it means that the continuation was not resumed.
210  * As a side-effect of atomic cancellation, a thread-bound coroutine (to some UI thread, for example) may
211  * continue to execute even after it was cancelled from the same thread in the case when the continuation
212  * was already resumed and was posted for execution to the thread's queue.
213  *
214  * @suppress **This an internal API and should not be used from general code.**
215  */
216 @InternalCoroutinesApi
suspendAtomicCancellableCoroutinenull217 public suspend inline fun <T> suspendAtomicCancellableCoroutine(
218     crossinline block: (CancellableContinuation<T>) -> Unit
219 ): T =
220     suspendCoroutineUninterceptedOrReturn { uCont ->
221         val cancellable = CancellableContinuationImpl(uCont.intercepted(), resumeMode = MODE_ATOMIC_DEFAULT)
222         block(cancellable)
223         cancellable.getResult()
224     }
225 
226 /**
227  * @suppress **Deprecated**
228  */
229 @Deprecated(
230     message = "holdCancellability parameter is deprecated and is no longer used",
231     replaceWith = ReplaceWith("suspendAtomicCancellableCoroutine(block)")
232 )
233 @InternalCoroutinesApi
suspendAtomicCancellableCoroutinenull234 public suspend inline fun <T> suspendAtomicCancellableCoroutine(
235     holdCancellability: Boolean = false,
236     crossinline block: (CancellableContinuation<T>) -> Unit
237 ): T =
238     suspendAtomicCancellableCoroutine(block)
239 
240 /**
241  * Removes a given node on cancellation.
242  */
243 internal fun CancellableContinuation<*>.removeOnCancellation(node: LockFreeLinkedListNode) =
244     invokeOnCancellation(handler = RemoveOnCancel(node).asHandler)
245 
246 /**
247  * Disposes a specified [handle] when this continuation is cancelled.
248  *
249  * This is a shortcut for the following code with slightly more efficient implementation (one fewer object created).
250  * ```
251  * invokeOnCancellation { handle.dispose() }
252  * ```
253  *
254  * @suppress **This an internal API and should not be used from general code.**
255  */
256 @InternalCoroutinesApi
257 public fun CancellableContinuation<*>.disposeOnCancellation(handle: DisposableHandle) =
258     invokeOnCancellation(handler = DisposeOnCancel(handle).asHandler)
259 
260 // --------------- implementation details ---------------
261 
262 private class RemoveOnCancel(private val node: LockFreeLinkedListNode) : CancelHandler() {
263     override fun invoke(cause: Throwable?) { node.remove() }
264     override fun toString() = "RemoveOnCancel[$node]"
265 }
266 
267 private class DisposeOnCancel(private val handle: DisposableHandle) : CancelHandler() {
invokenull268     override fun invoke(cause: Throwable?) = handle.dispose()
269     override fun toString(): String = "DisposeOnCancel[$handle]"
270 }
271