• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.intrinsics
6 
7 import kotlinx.coroutines.*
8 import kotlinx.coroutines.internal.*
9 import kotlin.coroutines.*
10 import kotlin.coroutines.intrinsics.*
11 
12 /**
13  * Use this function to restart a coroutine directly from inside of [suspendCoroutine],
14  * when the code is already in the context of this coroutine.
15  * It does not use [ContinuationInterceptor] and does not update the context of the current thread.
16  */
17 internal fun <T> (suspend () -> T).startCoroutineUnintercepted(completion: Continuation<T>) {
18     startDirect(completion) { actualCompletion ->
19         startCoroutineUninterceptedOrReturn(actualCompletion)
20     }
21 }
22 
23 /**
24  * Use this function to start a new coroutine in [CoroutineStart.UNDISPATCHED] mode &mdash;
25  * immediately execute the coroutine in the current thread until the next suspension.
26  * It does not use [ContinuationInterceptor], but updates the context of the current thread for the new coroutine.
27  */
startCoroutineUndispatchednull28 internal fun <T> (suspend () -> T).startCoroutineUndispatched(completion: Continuation<T>) {
29     startDirect(completion) { actualCompletion ->
30         withCoroutineContext(completion.context, null) {
31             startCoroutineUninterceptedOrReturn(actualCompletion)
32         }
33     }
34 }
35 
36 /**
37  * Use this function to start a new coroutine in [CoroutineStart.UNDISPATCHED] mode &mdash;
38  * immediately execute the coroutine in the current thread until the next suspension.
39  * It does not use [ContinuationInterceptor], but updates the context of the current thread for the new coroutine.
40  */
startCoroutineUndispatchednull41 internal fun <R, T> (suspend (R) -> T).startCoroutineUndispatched(receiver: R, completion: Continuation<T>) {
42     startDirect(completion) { actualCompletion ->
43         withCoroutineContext(completion.context, null) {
44             startCoroutineUninterceptedOrReturn(receiver, actualCompletion)
45         }
46     }
47 }
48 
49 /**
50  * Starts the given [block] immediately in the current stack-frame until the first suspension point.
51  * This method supports debug probes and thus can intercept completion, thus completion is provided
52  * as the parameter of [block].
53  */
startDirectnull54 private inline fun <T> startDirect(completion: Continuation<T>, block: (Continuation<T>) -> Any?) {
55     val actualCompletion = probeCoroutineCreated(completion)
56     val value = try {
57         block(actualCompletion)
58     } catch (e: Throwable) {
59         actualCompletion.resumeWithException(e)
60         return
61     }
62     if (value !== COROUTINE_SUSPENDED) {
63         @Suppress("UNCHECKED_CAST")
64         actualCompletion.resume(value as T)
65     }
66 }
67 
68 /**
69  * Starts this coroutine with the given code [block] in the same context and returns the coroutine result when it
70  * completes without suspension.
71  * This function shall be invoked at most once on this coroutine.
72  * This function checks cancellation of the outer [Job] on fast-path.
73  *
74  * It starts the coroutine using [startCoroutineUninterceptedOrReturn].
75  */
startUndispatchedOrReturnnull76 internal fun <T, R> ScopeCoroutine<T>.startUndispatchedOrReturn(receiver: R, block: suspend R.() -> T): Any? {
77     return undispatchedResult({ true }) {
78         block.startCoroutineUninterceptedOrReturn(receiver, this)
79     }
80 }
81 
82 /**
83  * Same as [startUndispatchedOrReturn], but ignores [TimeoutCancellationException] on fast-path.
84  */
startUndispatchedOrReturnIgnoreTimeoutnull85 internal fun <T, R> ScopeCoroutine<T>.startUndispatchedOrReturnIgnoreTimeout(
86     receiver: R, block: suspend R.() -> T
87 ): Any? {
88     return undispatchedResult({ e -> !(e is TimeoutCancellationException && e.coroutine === this) }) {
89         block.startCoroutineUninterceptedOrReturn(receiver, this)
90     }
91 }
92 
undispatchedResultnull93 private inline fun <T> ScopeCoroutine<T>.undispatchedResult(
94     shouldThrow: (Throwable) -> Boolean,
95     startBlock: () -> Any?
96 ): Any? {
97     val result = try {
98         startBlock()
99     } catch (e: Throwable) {
100         CompletedExceptionally(e)
101     }
102     /*
103      * We're trying to complete our undispatched block here and have three code-paths:
104      * (1) Coroutine is suspended.
105      * Otherwise, coroutine had returned result, so we are completing our block (and its job).
106      * (2) If we can't complete it or started waiting for children, we suspend.
107      * (3) If we have successfully completed the coroutine state machine here,
108      *     then we take the actual final state of the coroutine from makeCompletingOnce and return it.
109      *
110      * shouldThrow parameter is a special code path for timeout coroutine:
111      * If timeout is exceeded, but withTimeout() block was not suspended, we would like to return block value,
112      * not a timeout exception.
113      */
114     if (result === COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED // (1)
115     val state = makeCompletingOnce(result)
116     if (state === COMPLETING_WAITING_CHILDREN) return COROUTINE_SUSPENDED // (2)
117     return if (state is CompletedExceptionally) { // (3)
118         when {
119             shouldThrow(state.cause) -> throw recoverStackTrace(state.cause, uCont)
120             result is CompletedExceptionally -> throw recoverStackTrace(result.cause, uCont)
121             else -> result
122         }
123     } else {
124         state.unboxState()
125     }
126 }
127