Roman Elizarov | f16fd27 | 2017-02-07 11:26:00 +0300 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2016-2017 JetBrains s.r.o. |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 17 | package kotlinx.coroutines.experimental |
| 18 | |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 19 | import kotlinx.coroutines.experimental.internal.* |
| 20 | import kotlinx.coroutines.experimental.internalAnnotations.* |
| 21 | import kotlin.coroutines.experimental.* |
| 22 | import kotlin.coroutines.experimental.intrinsics.* |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 23 | |
| 24 | // --------------- cancellable continuations --------------- |
| 25 | |
| 26 | /** |
Vsevolod Tolstopyatov | f6430f4 | 2018-04-17 17:56:32 +0300 | [diff] [blame] | 27 | * Cancellable continuation. It is _completed_ when it is resumed or cancelled. |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 28 | * When [cancel] function is explicitly invoked, this continuation immediately resumes with [CancellationException] or |
Roman Elizarov | b7c46de | 2017-02-08 12:35:24 +0300 | [diff] [blame] | 29 | * with the specified cancel cause. |
| 30 | * |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 31 | * Cancellable continuation has three states (as subset of [Job] states): |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 32 | * |
| 33 | * | **State** | [isActive] | [isCompleted] | [isCancelled] | |
Roman Elizarov | 7886ef6 | 2017-02-13 14:00:18 +0300 | [diff] [blame] | 34 | * | ----------------------------------- | ---------- | ------------- | ------------- | |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 35 | * | _Active_ (initial state) | `true` | `false` | `false` | |
| 36 | * | _Resumed_ (final _completed_ state) | `false` | `true` | `false` | |
| 37 | * | _Canceled_ (final _completed_ state)| `false` | `true` | `true` | |
Roman Elizarov | b7c46de | 2017-02-08 12:35:24 +0300 | [diff] [blame] | 38 | * |
| 39 | * Invocation of [cancel] transitions this continuation from _active_ to _cancelled_ state, while |
| 40 | * invocation of [resume] or [resumeWithException] transitions it from _active_ to _resumed_ state. |
| 41 | * |
Roman Elizarov | 29affbb | 2017-07-21 13:47:41 +0300 | [diff] [blame] | 42 | * A [cancelled][isCancelled] continuation implies that it is [completed][isCompleted]. |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 43 | * |
Roman Elizarov | b7c46de | 2017-02-08 12:35:24 +0300 | [diff] [blame] | 44 | * Invocation of [resume] or [resumeWithException] in _resumed_ state produces [IllegalStateException] |
| 45 | * but is ignored in _cancelled_ state. |
Roman Elizarov | 29affbb | 2017-07-21 13:47:41 +0300 | [diff] [blame] | 46 | * |
| 47 | * ``` |
| 48 | * +-----------+ resume +---------+ |
| 49 | * | Active | ----------> | Resumed | |
| 50 | * +-----------+ +---------+ |
| 51 | * | |
| 52 | * | cancel |
| 53 | * V |
| 54 | * +-----------+ |
| 55 | * | Cancelled | |
| 56 | * +-----------+ |
| 57 | * |
| 58 | * ``` |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 59 | */ |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 60 | public interface CancellableContinuation<in T> : Continuation<T> { |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 61 | /** |
| 62 | * Returns `true` when this continuation is active -- it has not completed or cancelled yet. |
| 63 | */ |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 64 | public val isActive: Boolean |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 65 | |
| 66 | /** |
| 67 | * Returns `true` when this continuation has completed for any reason. A continuation |
| 68 | * that was cancelled is also considered complete. |
| 69 | */ |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 70 | public val isCompleted: Boolean |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 71 | |
Roman Elizarov | 834af46 | 2017-01-23 20:50:29 +0300 | [diff] [blame] | 72 | /** |
Roman Elizarov | 32d9532 | 2017-02-09 15:57:31 +0300 | [diff] [blame] | 73 | * Returns `true` if this continuation was [cancelled][cancel]. |
| 74 | * |
| 75 | * It implies that [isActive] is `false` and [isCompleted] is `true`. |
Roman Elizarov | 834af46 | 2017-01-23 20:50:29 +0300 | [diff] [blame] | 76 | */ |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 77 | public val isCancelled: Boolean |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 78 | |
| 79 | /** |
Roman Elizarov | 7b2d8b0 | 2017-02-02 20:09:14 +0300 | [diff] [blame] | 80 | * Tries to resume this continuation with a given value and returns non-null object token if it was successful, |
| 81 | * or `null` otherwise (it was already resumed or cancelled). When non-null object was returned, |
| 82 | * [completeResume] must be invoked with it. |
Roman Elizarov | 1216e91 | 2017-02-22 09:57:06 +0300 | [diff] [blame] | 83 | * |
| 84 | * When [idempotent] is not `null`, this function performs _idempotent_ operation, so that |
| 85 | * further invocations with the same non-null reference produce the same result. |
| 86 | * |
| 87 | * @suppress **This is unstable API and it is subject to change.** |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 88 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 89 | public fun tryResume(value: T, idempotent: Any? = null): Any? |
Roman Elizarov | 7b2d8b0 | 2017-02-02 20:09:14 +0300 | [diff] [blame] | 90 | |
| 91 | /** |
Roman Elizarov | f6fed2a | 2017-02-03 19:12:09 +0300 | [diff] [blame] | 92 | * Tries to resume this continuation with a given exception and returns non-null object token if it was successful, |
| 93 | * or `null` otherwise (it was already resumed or cancelled). When non-null object was returned, |
| 94 | * [completeResume] must be invoked with it. |
Roman Elizarov | 1216e91 | 2017-02-22 09:57:06 +0300 | [diff] [blame] | 95 | * |
| 96 | * @suppress **This is unstable API and it is subject to change.** |
Roman Elizarov | f6fed2a | 2017-02-03 19:12:09 +0300 | [diff] [blame] | 97 | */ |
Roman Elizarov | a198bad | 2017-02-10 13:30:38 +0300 | [diff] [blame] | 98 | public fun tryResumeWithException(exception: Throwable): Any? |
Roman Elizarov | f6fed2a | 2017-02-03 19:12:09 +0300 | [diff] [blame] | 99 | |
| 100 | /** |
| 101 | * Completes the execution of [tryResume] or [tryResumeWithException] on its non-null result. |
Roman Elizarov | 1216e91 | 2017-02-22 09:57:06 +0300 | [diff] [blame] | 102 | * |
| 103 | * @suppress **This is unstable API and it is subject to change.** |
Roman Elizarov | 7b2d8b0 | 2017-02-02 20:09:14 +0300 | [diff] [blame] | 104 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 105 | public fun completeResume(token: Any) |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 106 | |
| 107 | /** |
| 108 | * Makes this continuation cancellable. Use it with `holdCancellability` optional parameter to |
| 109 | * [suspendCancellableCoroutine] function. It throws [IllegalStateException] if invoked more than once. |
| 110 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 111 | public fun initCancellability() |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 112 | |
| 113 | /** |
| 114 | * Cancels this continuation with an optional cancellation [cause]. The result is `true` if this continuation was |
| 115 | * cancelled as a result of this invocation and `false` otherwise. |
| 116 | */ |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 117 | public fun cancel(cause: Throwable? = null): Boolean |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 118 | |
| 119 | /** |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 120 | * Registers handler that is **synchronously** invoked once on cancellation (both regular and exceptional) of this continuation. |
| 121 | * When the continuation is already cancelled, then the handler is immediately invoked |
| 122 | * with cancellation exception. Otherwise, the handler will be invoked once on cancellation if this |
| 123 | * continuation is cancelled. |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 124 | * |
| 125 | * Installed [handler] should not throw any exceptions. If it does, they will get caught, |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 126 | * wrapped into [CompletionHandlerException], and rethrown, potentially causing the crash of unrelated code. |
| 127 | * |
| 128 | * At most one [handler] can be installed on one continuation |
Vsevolod Tolstopyatov | bb19a26 | 2018-05-25 19:14:59 +0300 | [diff] [blame] | 129 | * |
| 130 | * @param invokeImmediately not used |
| 131 | * @param onCancelling not used |
| 132 | * @return not used |
Roman Elizarov | e1c0b65 | 2017-12-01 14:02:57 +0300 | [diff] [blame] | 133 | */ |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 134 | @Deprecated( |
| 135 | message = "Disposable handlers on regular completion are no longer supported", |
| 136 | replaceWith = ReplaceWith("invokeOnCancellation(handler)"), |
Vsevolod Tolstopyatov | 2cd27fb | 2018-06-07 13:22:17 +0300 | [diff] [blame] | 137 | level = DeprecationLevel.WARNING |
Vsevolod Tolstopyatov | bb19a26 | 2018-05-25 19:14:59 +0300 | [diff] [blame] | 138 | ) |
| 139 | public fun invokeOnCompletion( |
| 140 | onCancelling: Boolean = false, |
| 141 | invokeImmediately: Boolean = true, |
| 142 | handler: CompletionHandler): DisposableHandle |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 143 | |
| 144 | /** |
| 145 | * Registers handler that is **synchronously** invoked once on cancellation (both regular and exceptional) of this continuation. |
| 146 | * When the continuation is already cancelled, then the handler is immediately invoked |
| 147 | * with cancellation exception. Otherwise, the handler will be invoked once on cancellation if this |
| 148 | * continuation is cancelled. |
| 149 | * |
| 150 | * Installed [handler] should not throw any exceptions. If it does, they will get caught, |
| 151 | * wrapped into [CompletionHandlerException], and rethrown, potentially causing the crash of unrelated code. |
| 152 | * |
Roman Elizarov | 58c5fdc | 2018-06-08 14:01:12 +0300 | [diff] [blame] | 153 | * At most one [handler] can be installed on one continuation. |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 154 | */ |
| 155 | public fun invokeOnCancellation(handler: CompletionHandler) |
Roman Elizarov | a198bad | 2017-02-10 13:30:38 +0300 | [diff] [blame] | 156 | |
| 157 | /** |
| 158 | * Resumes this continuation with a given [value] in the invoker thread without going though |
| 159 | * [dispatch][CoroutineDispatcher.dispatch] function of the [CoroutineDispatcher] in the [context]. |
| 160 | * This function is designed to be used only by the [CoroutineDispatcher] implementations themselves. |
| 161 | * **It should not be used in general code**. |
Roman Elizarov | a198bad | 2017-02-10 13:30:38 +0300 | [diff] [blame] | 162 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 163 | public fun CoroutineDispatcher.resumeUndispatched(value: T) |
Roman Elizarov | a198bad | 2017-02-10 13:30:38 +0300 | [diff] [blame] | 164 | |
| 165 | /** |
| 166 | * Resumes this continuation with a given [exception] in the invoker thread without going though |
| 167 | * [dispatch][CoroutineDispatcher.dispatch] function of the [CoroutineDispatcher] in the [context]. |
| 168 | * This function is designed to be used only by the [CoroutineDispatcher] implementations themselves. |
| 169 | * **It should not be used in general code**. |
Roman Elizarov | a198bad | 2017-02-10 13:30:38 +0300 | [diff] [blame] | 170 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 171 | public fun CoroutineDispatcher.resumeUndispatchedWithException(exception: Throwable) |
Roman Elizarov | 834af46 | 2017-01-23 20:50:29 +0300 | [diff] [blame] | 172 | } |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 173 | |
| 174 | /** |
Roman Elizarov | a74eb5f | 2017-05-11 20:15:18 +0300 | [diff] [blame] | 175 | * Suspends coroutine similar to [suspendCoroutine], but provide an implementation of [CancellableContinuation] to |
Roman Elizarov | d82b3a9 | 2017-06-23 21:52:08 +0300 | [diff] [blame] | 176 | * the [block]. This function throws [CancellationException] if the coroutine is cancelled or completed while suspended. |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 177 | * |
| 178 | * If [holdCancellability] optional parameter is `true`, then the coroutine is suspended, but it is not |
| 179 | * cancellable until [CancellableContinuation.initCancellability] is invoked. |
Roman Elizarov | a74eb5f | 2017-05-11 20:15:18 +0300 | [diff] [blame] | 180 | * |
| 181 | * See [suspendAtomicCancellableCoroutine] for suspending functions that need *atomic cancellation*. |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 182 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 183 | public suspend inline fun <T> suspendCancellableCoroutine( |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 184 | holdCancellability: Boolean = false, |
| 185 | crossinline block: (CancellableContinuation<T>) -> Unit |
| 186 | ): T = |
Roman Elizarov | 58a7add | 2017-01-20 12:19:52 +0300 | [diff] [blame] | 187 | suspendCoroutineOrReturn { cont -> |
Roman Elizarov | 2b12d58 | 2017-06-22 20:12:19 +0300 | [diff] [blame] | 188 | val cancellable = CancellableContinuationImpl(cont, resumeMode = MODE_CANCELLABLE) |
Roman Elizarov | ee7c0eb | 2017-02-16 15:29:28 +0300 | [diff] [blame] | 189 | if (!holdCancellability) cancellable.initCancellability() |
| 190 | block(cancellable) |
| 191 | cancellable.getResult() |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 192 | } |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 193 | |
Roman Elizarov | a74eb5f | 2017-05-11 20:15:18 +0300 | [diff] [blame] | 194 | /** |
| 195 | * Suspends coroutine similar to [suspendCancellableCoroutine], but with *atomic cancellation*. |
| 196 | * |
| 197 | * When suspended function throws [CancellationException] it means that the continuation was not resumed. |
| 198 | * As a side-effect of atomic cancellation, a thread-bound coroutine (to some UI thread, for example) may |
| 199 | * continue to execute even after it was cancelled from the same thread in the case when the continuation |
| 200 | * was already resumed and was posted for execution to the thread's queue. |
| 201 | */ |
Roman Elizarov | aa461cf | 2018-04-11 13:20:29 +0300 | [diff] [blame] | 202 | public suspend inline fun <T> suspendAtomicCancellableCoroutine( |
Roman Elizarov | a74eb5f | 2017-05-11 20:15:18 +0300 | [diff] [blame] | 203 | holdCancellability: Boolean = false, |
| 204 | crossinline block: (CancellableContinuation<T>) -> Unit |
| 205 | ): T = |
| 206 | suspendCoroutineOrReturn { cont -> |
Roman Elizarov | 2b12d58 | 2017-06-22 20:12:19 +0300 | [diff] [blame] | 207 | val cancellable = CancellableContinuationImpl(cont, resumeMode = MODE_ATOMIC_DEFAULT) |
Roman Elizarov | a74eb5f | 2017-05-11 20:15:18 +0300 | [diff] [blame] | 208 | if (!holdCancellability) cancellable.initCancellability() |
| 209 | block(cancellable) |
| 210 | cancellable.getResult() |
| 211 | } |
Roman Elizarov | 0a78839 | 2017-02-15 17:52:12 +0300 | [diff] [blame] | 212 | |
| 213 | /** |
| 214 | * Removes a given node on cancellation. |
| 215 | * @suppress **This is unstable API and it is subject to change.** |
| 216 | */ |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 217 | @Deprecated( |
| 218 | message = "Disposable handlers on cancellation are no longer supported", |
| 219 | replaceWith = ReplaceWith("removeOnCancellation(handler)"), |
Vsevolod Tolstopyatov | 2cd27fb | 2018-06-07 13:22:17 +0300 | [diff] [blame] | 220 | level = DeprecationLevel.WARNING) |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 221 | public fun CancellableContinuation<*>.removeOnCancel(node: LockFreeLinkedListNode): DisposableHandle { |
Vsevolod Tolstopyatov | f6430f4 | 2018-04-17 17:56:32 +0300 | [diff] [blame] | 222 | removeOnCancellation(node) |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 223 | return NonDisposableHandle |
| 224 | } |
| 225 | |
| 226 | /** |
| 227 | * Removes a given node on cancellation. |
| 228 | * @suppress **This is unstable API and it is subject to change.** |
| 229 | */ |
Roman Elizarov | dbd9e1c | 2018-04-28 15:14:18 +0300 | [diff] [blame] | 230 | public fun CancellableContinuation<*>.removeOnCancellation(node: LockFreeLinkedListNode) = |
| 231 | invokeOnCancellation(handler = RemoveOnCancel(node).asHandler) |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 232 | |
Vsevolod Tolstopyatov | 80a2947 | 2018-04-17 16:02:02 +0300 | [diff] [blame] | 233 | /** |
| 234 | * Disposes a specified [handle] when this continuation is cancelled. |
| 235 | * |
| 236 | * This is a shortcut for the following code with slightly more efficient implementation (one fewer object created). |
| 237 | * ``` |
| 238 | * invokeOnCancellation { handle.dispose() } |
| 239 | * ``` |
| 240 | */ |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 241 | @Deprecated( |
| 242 | message = "Disposable handlers on regular completion are no longer supported", |
| 243 | replaceWith = ReplaceWith("disposeOnCancellation(handler)"), |
Vsevolod Tolstopyatov | 2cd27fb | 2018-06-07 13:22:17 +0300 | [diff] [blame] | 244 | level = DeprecationLevel.WARNING) |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 245 | public fun CancellableContinuation<*>.disposeOnCompletion(handle: DisposableHandle): DisposableHandle { |
Vsevolod Tolstopyatov | f6430f4 | 2018-04-17 17:56:32 +0300 | [diff] [blame] | 246 | disposeOnCancellation(handle) |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 247 | return NonDisposableHandle |
| 248 | } |
| 249 | |
Vsevolod Tolstopyatov | 80a2947 | 2018-04-17 16:02:02 +0300 | [diff] [blame] | 250 | /** |
| 251 | * Disposes a specified [handle] when this continuation is cancelled. |
| 252 | * |
| 253 | * This is a shortcut for the following code with slightly more efficient implementation (one fewer object created). |
| 254 | * ``` |
| 255 | * invokeOnCancellation { handle.dispose() } |
| 256 | * ``` |
| 257 | */ |
Roman Elizarov | dbd9e1c | 2018-04-28 15:14:18 +0300 | [diff] [blame] | 258 | public fun CancellableContinuation<*>.disposeOnCancellation(handle: DisposableHandle) = |
| 259 | invokeOnCancellation(handler = DisposeOnCancel(handle).asHandler) |
Roman Elizarov | 0a78839 | 2017-02-15 17:52:12 +0300 | [diff] [blame] | 260 | |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 261 | // --------------- implementation details --------------- |
| 262 | |
Roman Elizarov | dbd9e1c | 2018-04-28 15:14:18 +0300 | [diff] [blame] | 263 | private class RemoveOnCancel(private val node: LockFreeLinkedListNode) : CancelHandler() { |
| 264 | override fun invoke(cause: Throwable?) { node.remove() } |
Roman Elizarov | 0a78839 | 2017-02-15 17:52:12 +0300 | [diff] [blame] | 265 | override fun toString() = "RemoveOnCancel[$node]" |
| 266 | } |
| 267 | |
Roman Elizarov | dbd9e1c | 2018-04-28 15:14:18 +0300 | [diff] [blame] | 268 | private class DisposeOnCancel(private val handle: DisposableHandle) : CancelHandler() { |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 269 | override fun invoke(cause: Throwable?) = handle.dispose() |
Roman Elizarov | dbd9e1c | 2018-04-28 15:14:18 +0300 | [diff] [blame] | 270 | override fun toString(): String = "DisposeOnCancel[$handle]" |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 271 | } |
| 272 | |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 273 | @PublishedApi |
Roman Elizarov | e22f14a | 2017-06-22 19:04:52 +0300 | [diff] [blame] | 274 | internal class CancellableContinuationImpl<in T>( |
Roman Elizarov | bcdd8e1 | 2017-10-20 16:42:06 +0800 | [diff] [blame] | 275 | delegate: Continuation<T>, |
Roman Elizarov | 2b12d58 | 2017-06-22 20:12:19 +0300 | [diff] [blame] | 276 | resumeMode: Int |
Roman Elizarov | ac48070 | 2017-12-28 01:09:43 +0300 | [diff] [blame] | 277 | ) : AbstractContinuation<T>(delegate, resumeMode), CancellableContinuation<T>, Runnable { |
Roman Elizarov | 2b12d58 | 2017-06-22 20:12:19 +0300 | [diff] [blame] | 278 | |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 279 | public override val context: CoroutineContext = delegate.context |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 280 | |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 281 | override fun initCancellability() { |
Roman Elizarov | 6640b2b | 2018-01-17 19:08:55 +0300 | [diff] [blame] | 282 | initParentJobInternal(delegate.context[Job]) |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 283 | } |
Roman Elizarov | 58a7add | 2017-01-20 12:19:52 +0300 | [diff] [blame] | 284 | |
Vsevolod Tolstopyatov | bb19a26 | 2018-05-25 19:14:59 +0300 | [diff] [blame] | 285 | override fun invokeOnCompletion(onCancelling: Boolean, |
| 286 | invokeImmediately: Boolean, handler: CompletionHandler): DisposableHandle { |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 287 | invokeOnCancellation(handler) |
| 288 | return NonDisposableHandle |
| 289 | } |
Roman Elizarov | 4d626de | 2018-01-11 22:57:28 +0300 | [diff] [blame] | 290 | |
Roman Elizarov | 1216e91 | 2017-02-22 09:57:06 +0300 | [diff] [blame] | 291 | override fun tryResume(value: T, idempotent: Any?): Any? { |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 292 | loopOnState { state -> |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 293 | when (state) { |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 294 | is NotCompleted -> { |
Roman Elizarov | 932e860 | 2017-06-21 17:21:37 +0300 | [diff] [blame] | 295 | val update: Any? = if (idempotent == null) value else |
| 296 | CompletedIdempotentResult(idempotent, value, state) |
Vsevolod Tolstopyatov | f6430f4 | 2018-04-17 17:56:32 +0300 | [diff] [blame] | 297 | if (tryUpdateStateToFinal(state, update)) return state |
Roman Elizarov | 1216e91 | 2017-02-22 09:57:06 +0300 | [diff] [blame] | 298 | } |
| 299 | is CompletedIdempotentResult -> { |
| 300 | if (state.idempotentResume === idempotent) { |
| 301 | check(state.result === value) { "Non-idempotent resume" } |
| 302 | return state.token |
| 303 | } else |
| 304 | return null |
| 305 | } |
Roman Elizarov | 7b2d8b0 | 2017-02-02 20:09:14 +0300 | [diff] [blame] | 306 | else -> return null // cannot resume -- not active anymore |
Roman Elizarov | 187eace | 2017-01-31 09:39:58 +0300 | [diff] [blame] | 307 | } |
| 308 | } |
| 309 | } |
| 310 | |
Roman Elizarov | f6fed2a | 2017-02-03 19:12:09 +0300 | [diff] [blame] | 311 | override fun tryResumeWithException(exception: Throwable): Any? { |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 312 | loopOnState { state -> |
| 313 | when (state) { |
| 314 | is NotCompleted -> { |
Vsevolod Tolstopyatov | f6430f4 | 2018-04-17 17:56:32 +0300 | [diff] [blame] | 315 | if (tryUpdateStateToFinal(state, CompletedExceptionally(exception))) return state |
Roman Elizarov | 1216e91 | 2017-02-22 09:57:06 +0300 | [diff] [blame] | 316 | } |
Roman Elizarov | f6fed2a | 2017-02-03 19:12:09 +0300 | [diff] [blame] | 317 | else -> return null // cannot resume -- not active anymore |
| 318 | } |
| 319 | } |
| 320 | } |
| 321 | |
Vsevolod Tolstopyatov | f6430f4 | 2018-04-17 17:56:32 +0300 | [diff] [blame] | 322 | override fun completeResume(token: Any) = completeStateUpdate(token as NotCompleted, state, resumeMode) |
Roman Elizarov | 7b2d8b0 | 2017-02-02 20:09:14 +0300 | [diff] [blame] | 323 | |
Roman Elizarov | a198bad | 2017-02-10 13:30:38 +0300 | [diff] [blame] | 324 | override fun CoroutineDispatcher.resumeUndispatched(value: T) { |
Roman Elizarov | 8552f0e | 2017-09-28 13:03:44 +0300 | [diff] [blame] | 325 | val dc = delegate as? DispatchedContinuation |
| 326 | resumeImpl(value, if (dc?.dispatcher === this) MODE_UNDISPATCHED else resumeMode) |
Roman Elizarov | a198bad | 2017-02-10 13:30:38 +0300 | [diff] [blame] | 327 | } |
| 328 | |
| 329 | override fun CoroutineDispatcher.resumeUndispatchedWithException(exception: Throwable) { |
Roman Elizarov | 8552f0e | 2017-09-28 13:03:44 +0300 | [diff] [blame] | 330 | val dc = delegate as? DispatchedContinuation |
Roman Elizarov | bcdd8e1 | 2017-10-20 16:42:06 +0800 | [diff] [blame] | 331 | resumeImpl(CompletedExceptionally(exception), if (dc?.dispatcher === this) MODE_UNDISPATCHED else resumeMode) |
Roman Elizarov | 1216e91 | 2017-02-22 09:57:06 +0300 | [diff] [blame] | 332 | } |
Roman Elizarov | 69d9c85 | 2017-07-12 15:22:21 +0300 | [diff] [blame] | 333 | |
Roman Elizarov | bcdd8e1 | 2017-10-20 16:42:06 +0800 | [diff] [blame] | 334 | @Suppress("UNCHECKED_CAST") |
| 335 | override fun <T> getSuccessfulResult(state: Any?): T = |
| 336 | if (state is CompletedIdempotentResult) state.result as T else state as T |
| 337 | |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 338 | protected override fun nameString(): String = |
Roman Elizarov | 8b38fa2 | 2017-09-27 17:44:31 +0300 | [diff] [blame] | 339 | "CancellableContinuation(${delegate.toDebugString()})" |
Roman Elizarov | 3754f95 | 2017-01-18 20:47:54 +0300 | [diff] [blame] | 340 | } |
Roman Elizarov | e22f14a | 2017-06-22 19:04:52 +0300 | [diff] [blame] | 341 | |
| 342 | private class CompletedIdempotentResult( |
| 343 | @JvmField val idempotentResume: Any?, |
| 344 | @JvmField val result: Any?, |
Vsevolod Tolstopyatov | f3a5013 | 2018-04-16 19:41:20 +0300 | [diff] [blame] | 345 | @JvmField val token: NotCompleted |
Roman Elizarov | e22f14a | 2017-06-22 19:04:52 +0300 | [diff] [blame] | 346 | ) { |
| 347 | override fun toString(): String = "CompletedIdempotentResult[$result]" |
| 348 | } |