xref: /aosp_15_r20/external/kotlinx.coroutines/kotlinx-coroutines-core/common/src/Yield.kt (revision 7a7160fed73afa6648ef8aa100d4a336fe921d9a)

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