• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright 2016-2021 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.atomicfu.*
8 import kotlinx.coroutines.internal.*
9 import kotlin.coroutines.*
10 import kotlin.coroutines.intrinsics.*
11 import kotlin.jvm.*
12 
13 private const val UNDECIDED = 0
14 private const val SUSPENDED = 1
15 private const val RESUMED = 2
16 
17 private const val DECISION_SHIFT = 29
18 private const val INDEX_MASK = (1 shl DECISION_SHIFT) - 1
19 private const val NO_INDEX = INDEX_MASK
20 
21 private inline val Int.decision get() = this shr DECISION_SHIFT
22 private inline val Int.index get() = this and INDEX_MASK
23 @Suppress("NOTHING_TO_INLINE")
24 private inline fun decisionAndIndex(decision: Int, index: Int) = (decision shl DECISION_SHIFT) + index
25 
26 @JvmField
27 internal val RESUME_TOKEN = Symbol("RESUME_TOKEN")
28 
29 /**
30  * @suppress **This is unstable API and it is subject to change.**
31  */
32 @PublishedApi
33 internal open class CancellableContinuationImpl<in T>(
34     final override val delegate: Continuation<T>,
35     resumeMode: Int
36 ) : DispatchedTask<T>(resumeMode), CancellableContinuation<T>, CoroutineStackFrame, Waiter {
37     init {
38         assert { resumeMode != MODE_UNINITIALIZED } // invalid mode for CancellableContinuationImpl
39     }
40 
41     public override val context: CoroutineContext = delegate.context
42 
43     /*
44      * Implementation notes
45      *
46      * CancellableContinuationImpl is a subset of Job with following limitations:
47      * 1) It can have only cancellation listener (no "on cancelling")
48      * 2) It always invokes cancellation listener if it's cancelled (no 'invokeImmediately')
49      * 3) It can have at most one cancellation listener
50      * 4) Its cancellation listeners cannot be deregistered
51      * As a consequence it has much simpler state machine, more lightweight machinery and
52      * less dependencies.
53      */
54 
55     /** decision state machine
56 
57         +-----------+   trySuspend   +-----------+
58         | UNDECIDED | -------------> | SUSPENDED |
59         +-----------+                +-----------+
60               |
61               | tryResume
62               V
63         +-----------+
64         |  RESUMED  |
65         +-----------+
66 
67         Note: both tryResume and trySuspend can be invoked at most once, first invocation wins.
68         If the cancellation handler is specified via a [Segment] instance and the index in it
69         (so [Segment.onCancellation] should be called), the [_decisionAndIndex] field may store
70         this index additionally to the "decision" value.
71      */
72     private val _decisionAndIndex = atomic(decisionAndIndex(UNDECIDED, NO_INDEX))
73 
74     /*
75        === Internal states ===
76        name        state class          public state    description
77        ------      ------------         ------------    -----------
78        ACTIVE      Active               : Active        active, no listeners
79        SINGLE_A    CancelHandler        : Active        active, one cancellation listener
80        CANCELLED   CancelledContinuation: Cancelled     cancelled (final state)
81        COMPLETED   any                  : Completed     produced some result or threw an exception (final state)
82      */
83     private val _state = atomic<Any?>(Active)
84 
85     /*
86      * This field has a concurrent rendezvous in the following scenario:
87      *
88      * - installParentHandle publishes this instance on T1
89      *
90      * T1 writes:
91      * * handle = installed; right after the installation
92      * * Shortly after: if (isComplete) handle = NonDisposableHandle
93      *
94      * Any other T writes if the parent job is cancelled in detachChild:
95      * * handle = NonDisposableHandle
96      *
97      * We want to preserve a strict invariant on parentHandle transition, allowing only three of them:
98      * null -> anyHandle
99      * anyHandle -> NonDisposableHandle
100      * null -> NonDisposableHandle
101      *
102      * With a guarantee that after disposal the only state handle may end up in is NonDisposableHandle
103      */
104     private val _parentHandle = atomic<DisposableHandle?>(null)
105     private val parentHandle: DisposableHandle?
106         get() = _parentHandle.value
107 
108     internal val state: Any? get() = _state.value
109 
110     public override val isActive: Boolean get() = state is NotCompleted
111 
112     public override val isCompleted: Boolean get() = state !is NotCompleted
113 
114     public override val isCancelled: Boolean get() = state is CancelledContinuation
115 
116     // We cannot invoke `state.toString()` since it may cause a circular dependency
117     private val stateDebugRepresentation get() = when(state) {
118         is NotCompleted -> "Active"
119         is CancelledContinuation -> "Cancelled"
120         else -> "Completed"
121     }
122 
123     public override fun initCancellability() {
124         /*
125         * Invariant: at the moment of invocation, `this` has not yet
126         * leaked to user code and no one is able to invoke `resume` or `cancel`
127         * on it yet. Also, this function is not invoked for reusable continuations.
128         */
129         val handle = installParentHandle()
130             ?: return // fast path -- don't do anything without parent
131         // now check our state _after_ registering, could have completed while we were registering,
132         // but only if parent was cancelled. Parent could be in a "cancelling" state for a while,
133         // so we are helping it and cleaning the node ourselves
134         if (isCompleted) {
135             // Can be invoked concurrently in 'parentCancelled', no problems here
136             handle.dispose()
137             _parentHandle.value = NonDisposableHandle
138         }
139     }
140 
141     private fun isReusable(): Boolean = resumeMode.isReusableMode && (delegate as DispatchedContinuation<*>).isReusable()
142 
143     /**
144      * Resets cancellability state in order to [suspendCancellableCoroutineReusable] to work.
145      * Invariant: used only by [suspendCancellableCoroutineReusable] in [REUSABLE_CLAIMED] state.
146      */
147     @JvmName("resetStateReusable") // Prettier stack traces
148     internal fun resetStateReusable(): Boolean {
149         assert { resumeMode == MODE_CANCELLABLE_REUSABLE }
150         assert { parentHandle !== NonDisposableHandle }
151         val state = _state.value
152         assert { state !is NotCompleted }
153         if (state is CompletedContinuation && state.idempotentResume != null) {
154             // Cannot reuse continuation that was resumed with idempotent marker
155             detachChild()
156             return false
157         }
158         _decisionAndIndex.value = decisionAndIndex(UNDECIDED, NO_INDEX)
159         _state.value = Active
160         return true
161     }
162 
163     public override val callerFrame: CoroutineStackFrame?
164         get() = delegate as? CoroutineStackFrame
165 
166     public override fun getStackTraceElement(): StackTraceElement? = null
167 
168     override fun takeState(): Any? = state
169 
170     // Note: takeState does not clear the state so we don't use takenState
171     // and we use the actual current state where in CAS-loop
172     override fun cancelCompletedResult(takenState: Any?, cause: Throwable): Unit = _state.loop { state ->
173         when (state) {
174             is NotCompleted -> error("Not completed")
175             is CompletedExceptionally -> return // already completed exception or cancelled, nothing to do
176             is CompletedContinuation -> {
177                 check(!state.cancelled) { "Must be called at most once" }
178                 val update = state.copy(cancelCause = cause)
179                 if (_state.compareAndSet(state, update)) {
180                     state.invokeHandlers(this, cause)
181                     return // done
182                 }
183             }
184             else -> {
185                 // completed normally without marker class, promote to CompletedContinuation in case
186                 // if invokeOnCancellation if called later
187                 if (_state.compareAndSet(state, CompletedContinuation(state, cancelCause = cause))) {
188                     return // done
189                 }
190             }
191         }
192     }
193 
194     /*
195      * Attempt to postpone cancellation for reusable cancellable continuation
196      */
197     private fun cancelLater(cause: Throwable): Boolean {
198         // Ensure that we are postponing cancellation to the right reusable instance
199         if (!isReusable()) return false
200         val dispatched = delegate as DispatchedContinuation<*>
201         return dispatched.postponeCancellation(cause)
202     }
203 
204     public override fun cancel(cause: Throwable?): Boolean {
205         _state.loop { state ->
206             if (state !is NotCompleted) return false // false if already complete or cancelling
207             // Active -- update to final state
208             val update = CancelledContinuation(this, cause, handled = state is CancelHandler || state is Segment<*>)
209             if (!_state.compareAndSet(state, update)) return@loop // retry on cas failure
210             // Invoke cancel handler if it was present
211             when (state) {
212                 is CancelHandler -> callCancelHandler(state, cause)
213                 is Segment<*> -> callSegmentOnCancellation(state, cause)
214             }
215             // Complete state update
216             detachChildIfNonResuable()
217             dispatchResume(resumeMode) // no need for additional cancellation checks
218             return true
219         }
220     }
221 
222     internal fun parentCancelled(cause: Throwable) {
223         if (cancelLater(cause)) return
224         cancel(cause)
225         // Even if cancellation has failed, we should detach child to avoid potential leak
226         detachChildIfNonResuable()
227     }
228 
229     private inline fun callCancelHandlerSafely(block: () -> Unit) {
230         try {
231             block()
232         } catch (ex: Throwable) {
233             // Handler should never fail, if it does -- it is an unhandled exception
234             handleCoroutineException(
235                 context,
236                 CompletionHandlerException("Exception in invokeOnCancellation handler for $this", ex)
237             )
238         }
239     }
240 
241     private fun callCancelHandler(handler: CompletionHandler, cause: Throwable?) =
242         /*
243         * :KLUDGE: We have to invoke a handler in platform-specific way via `invokeIt` extension,
244         * because we play type tricks on Kotlin/JS and handler is not necessarily a function there
245         */
246         callCancelHandlerSafely { handler.invokeIt(cause) }
247 
248     fun callCancelHandler(handler: CancelHandler, cause: Throwable?) =
249         callCancelHandlerSafely { handler.invoke(cause) }
250 
251     private fun callSegmentOnCancellation(segment: Segment<*>, cause: Throwable?) {
252         val index = _decisionAndIndex.value.index
253         check(index != NO_INDEX) { "The index for Segment.onCancellation(..) is broken" }
254         callCancelHandlerSafely { segment.onCancellation(index, cause, context) }
255     }
256 
257     fun callOnCancellation(onCancellation: (cause: Throwable) -> Unit, cause: Throwable) {
258         try {
259             onCancellation.invoke(cause)
260         } catch (ex: Throwable) {
261             // Handler should never fail, if it does -- it is an unhandled exception
262             handleCoroutineException(
263                 context,
264                 CompletionHandlerException("Exception in resume onCancellation handler for $this", ex)
265             )
266         }
267     }
268 
269     /**
270      * It is used when parent is cancelled to get the cancellation cause for this continuation.
271      */
272     open fun getContinuationCancellationCause(parent: Job): Throwable =
273         parent.getCancellationException()
274 
275     private fun trySuspend(): Boolean {
276         _decisionAndIndex.loop { cur ->
277             when (cur.decision) {
278                 UNDECIDED -> if (this._decisionAndIndex.compareAndSet(cur, decisionAndIndex(SUSPENDED, cur.index))) return true
279                 RESUMED -> return false
280                 else -> error("Already suspended")
281             }
282         }
283     }
284 
285     private fun tryResume(): Boolean {
286         _decisionAndIndex.loop { cur ->
287             when (cur.decision) {
288                 UNDECIDED -> if (this._decisionAndIndex.compareAndSet(cur, decisionAndIndex(RESUMED, cur.index))) return true
289                 SUSPENDED -> return false
290                 else -> error("Already resumed")
291             }
292         }
293     }
294 
295     @PublishedApi
296     internal fun getResult(): Any? {
297         val isReusable = isReusable()
298         // trySuspend may fail either if 'block' has resumed/cancelled a continuation,
299         // or we got async cancellation from parent.
300         if (trySuspend()) {
301             /*
302              * Invariant: parentHandle is `null` *only* for reusable continuations.
303              * We were neither resumed nor cancelled, time to suspend.
304              * But first we have to install parent cancellation handle (if we didn't yet),
305              * so CC could be properly resumed on parent cancellation.
306              *
307              * This read has benign data-race with write of 'NonDisposableHandle'
308              * in 'detachChildIfNotReusable'.
309              */
310             if (parentHandle == null) {
311                 installParentHandle()
312             }
313             /*
314              * Release the continuation after installing the handle (if needed).
315              * If we were successful, then do nothing, it's ok to reuse the instance now.
316              * Otherwise, dispose the handle by ourselves.
317             */
318             if (isReusable) {
319                 releaseClaimedReusableContinuation()
320             }
321             return COROUTINE_SUSPENDED
322         }
323         // otherwise, onCompletionInternal was already invoked & invoked tryResume, and the result is in the state
324         if (isReusable) {
325             // release claimed reusable continuation for the future reuse
326             releaseClaimedReusableContinuation()
327         }
328         val state = this.state
329         if (state is CompletedExceptionally) throw recoverStackTrace(state.cause, this)
330         // if the parent job was already cancelled, then throw the corresponding cancellation exception
331         // otherwise, there is a race if suspendCancellableCoroutine { cont -> ... } does cont.resume(...)
332         // before the block returns. This getResult would return a result as opposed to cancellation
333         // exception that should have happened if the continuation is dispatched for execution later.
334         if (resumeMode.isCancellableMode) {
335             val job = context[Job]
336             if (job != null && !job.isActive) {
337                 val cause = job.getCancellationException()
338                 cancelCompletedResult(state, cause)
339                 throw recoverStackTrace(cause, this)
340             }
341         }
342         return getSuccessfulResult(state)
343     }
344 
345     private fun installParentHandle(): DisposableHandle? {
346         val parent = context[Job] ?: return null // don't do anything without a parent
347         // Install the handle
348         val handle = parent.invokeOnCompletion(
349             onCancelling = true,
350             handler = ChildContinuation(this).asHandler
351         )
352         _parentHandle.compareAndSet(null, handle)
353         return handle
354     }
355 
356     /**
357      * Tries to release reusable continuation. It can fail is there was an asynchronous cancellation,
358      * in which case it detaches from the parent and cancels this continuation.
359      */
360     internal fun releaseClaimedReusableContinuation() {
361         // Cannot be cast if e.g. invoked from `installParentHandleReusable` for context without dispatchers, but with Job in it
362         val cancellationCause = (delegate as? DispatchedContinuation<*>)?.tryReleaseClaimedContinuation(this) ?: return
363         detachChild()
364         cancel(cancellationCause)
365     }
366 
367     override fun resumeWith(result: Result<T>) =
368         resumeImpl(result.toState(this), resumeMode)
369 
370     override fun resume(value: T, onCancellation: ((cause: Throwable) -> Unit)?) =
371         resumeImpl(value, resumeMode, onCancellation)
372 
373     /**
374      * An optimized version for the code below that does not allocate
375      * a cancellation handler object and efficiently stores the specified
376      * [segment] and [index] in this [CancellableContinuationImpl].
377      *
378      * The only difference is that `segment.onCancellation(..)` is never
379      * called if this continuation is already completed;
380      *
381      * ```
382      * invokeOnCancellation { cause ->
383      *   segment.onCancellation(index, cause)
384      * }
385      * ```
386      */
387     override fun invokeOnCancellation(segment: Segment<*>, index: Int) {
388         _decisionAndIndex.update {
389             check(it.index == NO_INDEX) {
390                 "invokeOnCancellation should be called at most once"
391             }
392             decisionAndIndex(it.decision, index)
393         }
394         invokeOnCancellationImpl(segment)
395     }
396 
397     public override fun invokeOnCancellation(handler: CompletionHandler) {
398         val cancelHandler = makeCancelHandler(handler)
399         invokeOnCancellationImpl(cancelHandler)
400     }
401 
402     private fun invokeOnCancellationImpl(handler: Any) {
403         assert { handler is CancelHandler || handler is Segment<*> }
404         _state.loop { state ->
405             when (state) {
406                 is Active -> {
407                     if (_state.compareAndSet(state, handler)) return // quit on cas success
408                 }
409                 is CancelHandler, is Segment<*> -> multipleHandlersError(handler, state)
410                 is CompletedExceptionally -> {
411                     /*
412                      * Continuation was already cancelled or completed exceptionally.
413                      * NOTE: multiple invokeOnCancellation calls with different handlers are not allowed,
414                      * so we check to make sure handler was installed just once.
415                      */
416                     if (!state.makeHandled()) multipleHandlersError(handler, state)
417                     /*
418                      * Call the handler only if it was cancelled (not called when completed exceptionally).
419                      * :KLUDGE: We have to invoke a handler in platform-specific way via `invokeIt` extension,
420                      * because we play type tricks on Kotlin/JS and handler is not necessarily a function there
421                      */
422                     if (state is CancelledContinuation) {
423                         val cause: Throwable? = (state as? CompletedExceptionally)?.cause
424                         if (handler is CancelHandler) {
425                             callCancelHandler(handler, cause)
426                         } else {
427                             val segment = handler as Segment<*>
428                             callSegmentOnCancellation(segment, cause)
429                         }
430                     }
431                     return
432                 }
433                 is CompletedContinuation -> {
434                     /*
435                      * Continuation was already completed, and might already have cancel handler.
436                      */
437                     if (state.cancelHandler != null) multipleHandlersError(handler, state)
438                     // Segment.invokeOnCancellation(..) does NOT need to be called on completed continuation.
439                     if (handler is Segment<*>) return
440                     handler as CancelHandler
441                     if (state.cancelled) {
442                         // Was already cancelled while being dispatched -- invoke the handler directly
443                         callCancelHandler(handler, state.cancelCause)
444                         return
445                     }
446                     val update = state.copy(cancelHandler = handler)
447                     if (_state.compareAndSet(state, update)) return // quit on cas success
448                 }
449                 else -> {
450                     /*
451                      * Continuation was already completed normally, but might get cancelled while being dispatched.
452                      * Change its state to CompletedContinuation, unless we have Segment which
453                      * does not need to be called in this case.
454                      */
455                     if (handler is Segment<*>) return
456                     handler as CancelHandler
457                     val update = CompletedContinuation(state, cancelHandler = handler)
458                     if (_state.compareAndSet(state, update)) return // quit on cas success
459                 }
460             }
461         }
462     }
463 
464     private fun multipleHandlersError(handler: Any, state: Any?) {
465         error("It's prohibited to register multiple handlers, tried to register $handler, already has $state")
466     }
467 
468     private fun makeCancelHandler(handler: CompletionHandler): CancelHandler =
469         if (handler is CancelHandler) handler else InvokeOnCancel(handler)
470 
471     private fun dispatchResume(mode: Int) {
472         if (tryResume()) return // completed before getResult invocation -- bail out
473         // otherwise, getResult has already commenced, i.e. completed later or in other thread
474         dispatch(mode)
475     }
476 
477     private fun resumedState(
478         state: NotCompleted,
479         proposedUpdate: Any?,
480         resumeMode: Int,
481         onCancellation: ((cause: Throwable) -> Unit)?,
482         idempotent: Any?
483     ): Any? = when {
484         proposedUpdate is CompletedExceptionally -> {
485             assert { idempotent == null } // there are no idempotent exceptional resumes
486             assert { onCancellation == null } // only successful results can be cancelled
487             proposedUpdate
488         }
489         !resumeMode.isCancellableMode && idempotent == null -> proposedUpdate // cannot be cancelled in process, all is fine
490         onCancellation != null || state is CancelHandler || idempotent != null ->
491             // mark as CompletedContinuation if special cases are present:
492             // Cancellation handlers that shall be called after resume or idempotent resume
493             CompletedContinuation(proposedUpdate, state as? CancelHandler, onCancellation, idempotent)
494         else -> proposedUpdate // simple case -- use the value directly
495     }
496 
497     private fun resumeImpl(
498         proposedUpdate: Any?,
499         resumeMode: Int,
500         onCancellation: ((cause: Throwable) -> Unit)? = null
501     ) {
502         _state.loop { state ->
503             when (state) {
504                 is NotCompleted -> {
505                     val update = resumedState(state, proposedUpdate, resumeMode, onCancellation, idempotent = null)
506                     if (!_state.compareAndSet(state, update)) return@loop // retry on cas failure
507                     detachChildIfNonResuable()
508                     dispatchResume(resumeMode) // dispatch resume, but it might get cancelled in process
509                     return // done
510                 }
511                 is CancelledContinuation -> {
512                     /*
513                      * If continuation was cancelled, then resume attempt must be ignored,
514                      * because cancellation is asynchronous and may race with resume.
515                      * Racy exceptions will be lost, too.
516                      */
517                     if (state.makeResumed()) { // check if trying to resume one (otherwise error)
518                         // call onCancellation
519                         onCancellation?.let { callOnCancellation(it, state.cause) }
520                         return // done
521                     }
522                 }
523             }
524             alreadyResumedError(proposedUpdate) // otherwise, an error (second resume attempt)
525         }
526     }
527 
528     /**
529      * Similar to [tryResume], but does not actually completes resume (needs [completeResume] call).
530      * Returns [RESUME_TOKEN] when resumed, `null` when it was already resumed or cancelled.
531      */
532     private fun tryResumeImpl(
533         proposedUpdate: Any?,
534         idempotent: Any?,
535         onCancellation: ((cause: Throwable) -> Unit)?
536     ): Symbol? {
537         _state.loop { state ->
538             when (state) {
539                 is NotCompleted -> {
540                     val update = resumedState(state, proposedUpdate, resumeMode, onCancellation, idempotent)
541                     if (!_state.compareAndSet(state, update)) return@loop // retry on cas failure
542                     detachChildIfNonResuable()
543                     return RESUME_TOKEN
544                 }
545                 is CompletedContinuation -> {
546                     return if (idempotent != null && state.idempotentResume === idempotent) {
547                         assert { state.result == proposedUpdate } // "Non-idempotent resume"
548                         RESUME_TOKEN // resumed with the same token -- ok
549                     } else {
550                         null // resumed with a different token or non-idempotent -- too late
551                     }
552                 }
553                 else -> return null // cannot resume -- not active anymore
554             }
555         }
556     }
557 
558     private fun alreadyResumedError(proposedUpdate: Any?): Nothing {
559         error("Already resumed, but proposed with update $proposedUpdate")
560     }
561 
562     // Unregister from parent job
563     private fun detachChildIfNonResuable() {
564         // If instance is reusable, do not detach on every reuse, #releaseInterceptedContinuation will do it for us in the end
565         if (!isReusable()) detachChild()
566     }
567 
568     /**
569      * Detaches from the parent.
570      */
571     internal fun detachChild() {
572         val handle = parentHandle ?: return
573         handle.dispose()
574         _parentHandle.value = NonDisposableHandle
575     }
576 
577     // Note: Always returns RESUME_TOKEN | null
578     override fun tryResume(value: T, idempotent: Any?): Any? =
579         tryResumeImpl(value, idempotent, onCancellation = null)
580 
581     override fun tryResume(value: T, idempotent: Any?, onCancellation: ((cause: Throwable) -> Unit)?): Any? =
582         tryResumeImpl(value, idempotent, onCancellation)
583 
584     override fun tryResumeWithException(exception: Throwable): Any? =
585         tryResumeImpl(CompletedExceptionally(exception), idempotent = null, onCancellation = null)
586 
587     // note: token is always RESUME_TOKEN
588     override fun completeResume(token: Any) {
589         assert { token === RESUME_TOKEN }
590         dispatchResume(resumeMode)
591     }
592 
593     override fun CoroutineDispatcher.resumeUndispatched(value: T) {
594         val dc = delegate as? DispatchedContinuation
595         resumeImpl(value, if (dc?.dispatcher === this) MODE_UNDISPATCHED else resumeMode)
596     }
597 
598     override fun CoroutineDispatcher.resumeUndispatchedWithException(exception: Throwable) {
599         val dc = delegate as? DispatchedContinuation
600         resumeImpl(CompletedExceptionally(exception), if (dc?.dispatcher === this) MODE_UNDISPATCHED else resumeMode)
601     }
602 
603     @Suppress("UNCHECKED_CAST")
604     override fun <T> getSuccessfulResult(state: Any?): T =
605         when (state) {
606             is CompletedContinuation -> state.result as T
607             else -> state as T
608         }
609 
610     // The exceptional state in CancellableContinuationImpl is stored directly and it is not recovered yet.
611     // The stacktrace recovery is invoked here.
612     override fun getExceptionalResult(state: Any?): Throwable? =
613         super.getExceptionalResult(state)?.let { recoverStackTrace(it, delegate) }
614 
615     // For nicer debugging
616     public override fun toString(): String =
617         "${nameString()}(${delegate.toDebugString()}){$stateDebugRepresentation}@$hexAddress"
618 
619     protected open fun nameString(): String =
620         "CancellableContinuation"
621 
622 }
623 
624 // Marker for active continuation
625 internal interface NotCompleted
626 
627 private object Active : NotCompleted {
toStringnull628     override fun toString(): String = "Active"
629 }
630 
631 /**
632  * Base class for all [CancellableContinuation.invokeOnCancellation] handlers to avoid an extra instance
633  * on JVM, yet support JS where you cannot extend from a functional type.
634  */
635 internal abstract class CancelHandler : CancelHandlerBase(), NotCompleted
636 
637 // Wrapper for lambdas, for the performance sake CancelHandler can be subclassed directly
638 private class InvokeOnCancel( // Clashes with InvokeOnCancellation
639     private val handler: CompletionHandler
640 ) : CancelHandler() {
641     override fun invoke(cause: Throwable?) {
642         handler.invoke(cause)
643     }
644     override fun toString() = "InvokeOnCancel[${handler.classSimpleName}@$hexAddress]"
645 }
646 
647 // Completed with additional metadata
648 private data class CompletedContinuation(
649     @JvmField val result: Any?,
650     @JvmField val cancelHandler: CancelHandler? = null, // installed via invokeOnCancellation
651     @JvmField val onCancellation: ((cause: Throwable) -> Unit)? = null, // installed via resume block
652     @JvmField val idempotentResume: Any? = null,
653     @JvmField val cancelCause: Throwable? = null
654 ) {
655     val cancelled: Boolean get() = cancelCause != null
656 
invokeHandlersnull657     fun invokeHandlers(cont: CancellableContinuationImpl<*>, cause: Throwable) {
658         cancelHandler?.let { cont.callCancelHandler(it, cause) }
659         onCancellation?.let { cont.callOnCancellation(it, cause) }
660     }
661 }
662