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.coroutines.internal.* 8 import kotlin.coroutines.* 9 import kotlin.coroutines.intrinsics.* 10 11 /** 12 * Yields the thread (or thread pool) of the current coroutine dispatcher 13 * to other coroutines on the same dispatcher to run if possible. 14 * 15 * This suspending function is cancellable. 16 * If the [Job] of the current coroutine is cancelled or completed when this suspending function is invoked or while 17 * this function is waiting for dispatch, it resumes with a [CancellationException]. 18 * There is a **prompt cancellation guarantee**. If the job was cancelled while this function was 19 * suspended, it will not resume successfully. See [suspendCancellableCoroutine] documentation for low-level details. 20 * 21 * **Note**: This function always [checks for cancellation][ensureActive] even when it does not suspend. 22 * 23 * ### Implementation details 24 * 25 * If the coroutine dispatcher is [Unconfined][Dispatchers.Unconfined], this 26 * functions suspends only when there are other unconfined coroutines working and forming an event-loop. 27 * For other dispatchers, this function calls [CoroutineDispatcher.dispatch] and 28 * always suspends to be resumed later regardless of the result of [CoroutineDispatcher.isDispatchNeeded]. 29 * If there is no [CoroutineDispatcher] in the context, it does not suspend. 30 */ 31 public suspend fun yield(): Unit = suspendCoroutineUninterceptedOrReturn sc@ { uCont -> 32 val context = uCont.context 33 context.ensureActive() 34 val cont = uCont.intercepted() as? DispatchedContinuation<Unit> ?: return@sc Unit 35 if (cont.dispatcher.isDispatchNeeded(context)) { 36 // this is a regular dispatcher -- do simple dispatchYield 37 cont.dispatchYield(context, Unit) 38 } else { 39 // This is either an "immediate" dispatcher or the Unconfined dispatcher 40 // This code detects the Unconfined dispatcher even if it was wrapped into another dispatcher 41 val yieldContext = YieldContext() 42 cont.dispatchYield(context + yieldContext, Unit) 43 // Special case for the unconfined dispatcher that can yield only in existing unconfined loop 44 if (yieldContext.dispatcherWasUnconfined) { 45 // Means that the Unconfined dispatcher got the call, but did not do anything. 46 // See also code of "Unconfined.dispatch" function. 47 return@sc if (cont.yieldUndispatched()) COROUTINE_SUSPENDED else Unit 48 } 49 // Otherwise, it was some other dispatcher that successfully dispatched the coroutine 50 } 51 COROUTINE_SUSPENDED 52 } 53