kotlinx-coroutines-core/native/src/CoroutineContext.kt [44:108]: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - internal actual inline fun withCoroutineContext(context: CoroutineContext, countOrElement: Any?, block: () -> T): T { val oldValue = updateThreadContext(context, countOrElement) try { return block() } finally { restoreThreadContext(context, oldValue) } } internal actual inline fun withContinuationContext(continuation: Continuation<*>, countOrElement: Any?, block: () -> T): T { val context = continuation.context val oldValue = updateThreadContext(context, countOrElement) val undispatchedCompletion = if (oldValue !== NO_THREAD_ELEMENTS) { // Only if some values were replaced we'll go to the slow path of figuring out where/how to restore them continuation.updateUndispatchedCompletion(context, oldValue) } else { null // fast path -- don't even try to find undispatchedCompletion as there's nothing to restore in the context } try { return block() } finally { if (undispatchedCompletion == null || undispatchedCompletion.clearThreadContext()) { restoreThreadContext(context, oldValue) } } } internal fun Continuation<*>.updateUndispatchedCompletion(context: CoroutineContext, oldValue: Any?): UndispatchedCoroutine<*>? { if (this !is CoroutineStackFrame) return null /* * Fast-path to detect whether we have undispatched coroutine at all in our stack. * * Implementation note. * If we ever find that stackwalking for thread-locals is way too slow, here is another idea: * 1) Store undispatched coroutine right in the `UndispatchedMarker` instance * 2) To avoid issues with cross-dispatch boundary, remove `UndispatchedMarker` * from the context when creating dispatched coroutine in `withContext`. * Another option is to "unmark it" instead of removing to save an allocation. * Both options should work, but it requires more careful studying of the performance * and, mostly, maintainability impact. */ val potentiallyHasUndispatchedCoroutine = context[UndispatchedMarker] !== null if (!potentiallyHasUndispatchedCoroutine) return null val completion = undispatchedCompletion() completion?.saveThreadContext(context, oldValue) return completion } internal tailrec fun CoroutineStackFrame.undispatchedCompletion(): UndispatchedCoroutine<*>? { // Find direct completion of this continuation val completion: CoroutineStackFrame = when (this) { is DispatchedCoroutine<*> -> return null else -> callerFrame ?: return null // something else -- not supported } if (completion is UndispatchedCoroutine<*>) return completion // found UndispatchedCoroutine! return completion.undispatchedCompletion() // walk up the call stack with tail call } /** * Marker indicating that [UndispatchedCoroutine] exists somewhere up in the stack. * Used as a performance optimization to avoid stack walking where it is not necessary. */ private object UndispatchedMarker: CoroutineContext.Element, CoroutineContext.Key { override val key: CoroutineContext.Key<*> get() = this - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - kotlinx-coroutines-core/wasmJs/src/CoroutineContext.kt [26:90]: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - internal actual inline fun withCoroutineContext(context: CoroutineContext, countOrElement: Any?, block: () -> T): T { val oldValue = updateThreadContext(context, countOrElement) try { return block() } finally { restoreThreadContext(context, oldValue) } } internal actual inline fun withContinuationContext(continuation: Continuation<*>, countOrElement: Any?, block: () -> T): T { val context = continuation.context val oldValue = updateThreadContext(context, countOrElement) val undispatchedCompletion = if (oldValue !== NO_THREAD_ELEMENTS) { // Only if some values were replaced we'll go to the slow path of figuring out where/how to restore them continuation.updateUndispatchedCompletion(context, oldValue) } else { null // fast path -- don't even try to find undispatchedCompletion as there's nothing to restore in the context } try { return block() } finally { if (undispatchedCompletion == null || undispatchedCompletion.clearThreadContext()) { restoreThreadContext(context, oldValue) } } } internal fun Continuation<*>.updateUndispatchedCompletion(context: CoroutineContext, oldValue: Any?): UndispatchedCoroutine<*>? { if (this !is CoroutineStackFrame) return null /* * Fast-path to detect whether we have undispatched coroutine at all in our stack. * * Implementation note. * If we ever find that stackwalking for thread-locals is way too slow, here is another idea: * 1) Store undispatched coroutine right in the `UndispatchedMarker` instance * 2) To avoid issues with cross-dispatch boundary, remove `UndispatchedMarker` * from the context when creating dispatched coroutine in `withContext`. * Another option is to "unmark it" instead of removing to save an allocation. * Both options should work, but it requires more careful studying of the performance * and, mostly, maintainability impact. */ val potentiallyHasUndispatchedCoroutine = context[UndispatchedMarker] !== null if (!potentiallyHasUndispatchedCoroutine) return null val completion = undispatchedCompletion() completion?.saveThreadContext(context, oldValue) return completion } internal tailrec fun CoroutineStackFrame.undispatchedCompletion(): UndispatchedCoroutine<*>? { // Find direct completion of this continuation val completion: CoroutineStackFrame = when (this) { is DispatchedCoroutine<*> -> return null else -> callerFrame ?: return null // something else -- not supported } if (completion is UndispatchedCoroutine<*>) return completion // found UndispatchedCoroutine! return completion.undispatchedCompletion() // walk up the call stack with tail call } /** * Marker indicating that [UndispatchedCoroutine] exists somewhere up in the stack. * Used as a performance optimization to avoid stack walking where it is not necessary. */ private object UndispatchedMarker: CoroutineContext.Element, CoroutineContext.Key { override val key: CoroutineContext.Key<*> get() = this - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -