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