• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
checkCompletionnull53 internal fun CoroutineContext.checkCompletion() {
54     val job = get(Job)
55     if (job != null && !job.isActive) throw job.getCancellationException()
56 }
57