@@ -19,7 +19,7 @@ private const val RESUMED = 2
19
19
*/
20
20
@PublishedApi
21
21
internal open class CancellableContinuationImpl <in T >(
22
- public final override val delegate : Continuation <T >,
22
+ final override val delegate : Continuation <T >,
23
23
resumeMode : Int
24
24
) : DispatchedTask<T>(resumeMode), CancellableContinuation<T>, CoroutineStackFrame {
25
25
public override val context: CoroutineContext = delegate.context
@@ -102,6 +102,14 @@ internal open class CancellableContinuationImpl<in T>(
102
102
103
103
override fun takeState (): Any? = state
104
104
105
+ override fun cancelResult (state : Any? , cause : Throwable ) {
106
+ if (state is CompletedWithCancellation ) {
107
+ invokeHandlerSafely {
108
+ state.onCancellation(cause)
109
+ }
110
+ }
111
+ }
112
+
105
113
public override fun cancel (cause : Throwable ? ): Boolean {
106
114
_state .loop { state ->
107
115
if (state !is NotCompleted ) return false // false if already complete or cancelling
@@ -165,8 +173,19 @@ internal open class CancellableContinuationImpl<in T>(
165
173
return getSuccessfulResult(state)
166
174
}
167
175
168
- override fun resumeWith (result : Result <T >) =
176
+ override fun resumeWith (result : Result <T >) {
169
177
resumeImpl(result.toState(), resumeMode)
178
+ }
179
+
180
+ override fun resume (value : T , onCancellation : (cause: Throwable ) -> Unit ) {
181
+ val cancelled = resumeImpl(CompletedWithCancellation (value, onCancellation), resumeMode)
182
+ if (cancelled != null ) {
183
+ // too late to resume (was cancelled) -- call handler
184
+ invokeHandlerSafely {
185
+ onCancellation(cancelled.cause)
186
+ }
187
+ }
188
+ }
170
189
171
190
internal fun resumeWithExceptionMode (exception : Throwable , mode : Int ) =
172
191
resumeImpl(CompletedExceptionally (exception), mode)
@@ -219,22 +238,23 @@ internal open class CancellableContinuationImpl<in T>(
219
238
dispatch(mode)
220
239
}
221
240
222
- private fun resumeImpl (proposedUpdate : Any? , resumeMode : Int ) {
241
+ // returns null when successfully dispatched resumed, CancelledContinuation if too late (was already cancelled)
242
+ private fun resumeImpl (proposedUpdate : Any? , resumeMode : Int ): CancelledContinuation ? {
223
243
_state .loop { state ->
224
244
when (state) {
225
245
is NotCompleted -> {
226
246
if (! _state .compareAndSet(state, proposedUpdate)) return @loop // retry on cas failure
227
247
disposeParentHandle()
228
248
dispatchResume(resumeMode)
229
- return
249
+ return null
230
250
}
231
251
is CancelledContinuation -> {
232
252
/*
233
253
* If continuation was cancelled, then resume attempt must be ignored,
234
254
* because cancellation is asynchronous and may race with resume.
235
255
* Racy exceptions will be lost, too.
236
256
*/
237
- if (state.makeResumed()) return // ok -- resumed just once
257
+ if (state.makeResumed()) return state // tried to resume just once, but was cancelled
238
258
}
239
259
}
240
260
alreadyResumedError(proposedUpdate) // otherwise -- an error (second resume attempt)
@@ -307,7 +327,11 @@ internal open class CancellableContinuationImpl<in T>(
307
327
308
328
@Suppress(" UNCHECKED_CAST" )
309
329
override fun <T > getSuccessfulResult (state : Any? ): T =
310
- if (state is CompletedIdempotentResult ) state.result as T else state as T
330
+ when (state) {
331
+ is CompletedIdempotentResult -> state.result as T
332
+ is CompletedWithCancellation -> state.result as T
333
+ else -> state as T
334
+ }
311
335
312
336
// For nicer debugging
313
337
public override fun toString (): String =
@@ -344,3 +368,11 @@ private class CompletedIdempotentResult(
344
368
) {
345
369
override fun toString (): String = " CompletedIdempotentResult[$result ]"
346
370
}
371
+
372
+ private class CompletedWithCancellation (
373
+ @JvmField val result : Any? ,
374
+ @JvmField val onCancellation : (cause: Throwable ) -> Unit
375
+ ) {
376
+ override fun toString (): String = " CompletedWithCancellation[$result ]"
377
+ }
378
+
0 commit comments