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