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.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 restart a coroutine directly from inside of [suspendCoroutine],
25 * when the code is already in the context of this coroutine.
26 * It does not use [ContinuationInterceptor] and does not update the context of the current thread.
27 */
startCoroutineUninterceptednull28 internal fun <R, T> (suspend (R) -> T).startCoroutineUnintercepted(receiver: R, completion: Continuation<T>) {
29 startDirect(completion) { actualCompletion ->
30 startCoroutineUninterceptedOrReturn(receiver, actualCompletion)
31 }
32 }
33
34 /**
35 * Use this function to start a new coroutine in [CoroutineStart.UNDISPATCHED] mode —
36 * immediately execute the coroutine in the current thread until the next suspension.
37 * It does not use [ContinuationInterceptor], but updates the context of the current thread for the new coroutine.
38 */
startCoroutineUndispatchednull39 internal fun <T> (suspend () -> T).startCoroutineUndispatched(completion: Continuation<T>) {
40 startDirect(completion) { actualCompletion ->
41 withCoroutineContext(completion.context, null) {
42 startCoroutineUninterceptedOrReturn(actualCompletion)
43 }
44 }
45 }
46
47 /**
48 * Use this function to start a new coroutine in [CoroutineStart.UNDISPATCHED] mode —
49 * immediately execute the coroutine in the current thread until the next suspension.
50 * It does not use [ContinuationInterceptor], but updates the context of the current thread for the new coroutine.
51 */
startCoroutineUndispatchednull52 internal fun <R, T> (suspend (R) -> T).startCoroutineUndispatched(receiver: R, completion: Continuation<T>) {
53 startDirect(completion) { actualCompletion ->
54 withCoroutineContext(completion.context, null) {
55 startCoroutineUninterceptedOrReturn(receiver, actualCompletion)
56 }
57 }
58 }
59
60 /**
61 * Starts the given [block] immediately in the current stack-frame until the first suspension point.
62 * This method supports debug probes and thus can intercept completion, thus completion is provided
63 * as the parameter of [block].
64 */
startDirectnull65 private inline fun <T> startDirect(completion: Continuation<T>, block: (Continuation<T>) -> Any?) {
66 val actualCompletion = probeCoroutineCreated(completion)
67 val value = try {
68 block(actualCompletion)
69 } catch (e: Throwable) {
70 actualCompletion.resumeWithException(e)
71 return
72 }
73 if (value !== COROUTINE_SUSPENDED) {
74 @Suppress("UNCHECKED_CAST")
75 actualCompletion.resume(value as T)
76 }
77 }
78
79 /**
80 * Starts this coroutine with the given code [block] in the same context and returns the coroutine result when it
81 * completes without suspension.
82 * This function shall be invoked at most once on this coroutine.
83 * This function checks cancellation of the outer [Job] on fast-path.
84 *
85 * First, this function initializes the parent job from the `parentContext` of this coroutine that was passed to it
86 * during construction. Second, it starts the coroutine using [startCoroutineUninterceptedOrReturn].
87 */
startUndispatchedOrReturnnull88 internal fun <T, R> ScopeCoroutine<T>.startUndispatchedOrReturn(receiver: R, block: suspend R.() -> T): Any? {
89 initParentJob()
90 return undispatchedResult({ true }) {
91 block.startCoroutineUninterceptedOrReturn(receiver, this)
92 }
93 }
94
95 /**
96 * Same as [startUndispatchedOrReturn], but ignores [TimeoutCancellationException] on fast-path.
97 */
startUndispatchedOrReturnIgnoreTimeoutnull98 internal fun <T, R> ScopeCoroutine<T>.startUndispatchedOrReturnIgnoreTimeout(
99 receiver: R, block: suspend R.() -> T): Any? {
100 initParentJob()
101 return undispatchedResult({ e -> !(e is TimeoutCancellationException && e.coroutine === this) }) {
102 block.startCoroutineUninterceptedOrReturn(receiver, this)
103 }
104 }
105
undispatchedResultnull106 private inline fun <T> ScopeCoroutine<T>.undispatchedResult(
107 shouldThrow: (Throwable) -> Boolean,
108 startBlock: () -> Any?
109 ): Any? {
110 val result = try {
111 startBlock()
112 } catch (e: Throwable) {
113 CompletedExceptionally(e)
114 }
115 /*
116 * We're trying to complete our undispatched block here and have three code-paths:
117 * (1) Coroutine is suspended.
118 * Otherwise, coroutine had returned result, so we are completing our block (and its job).
119 * (2) If we can't complete it or started waiting for children, we suspend.
120 * (3) If we have successfully completed the coroutine state machine here,
121 * then we take the actual final state of the coroutine from makeCompletingOnce and return it.
122 *
123 * shouldThrow parameter is a special code path for timeout coroutine:
124 * If timeout is exceeded, but withTimeout() block was not suspended, we would like to return block value,
125 * not a timeout exception.
126 */
127 if (result === COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED // (1)
128 val state = makeCompletingOnce(result)
129 if (state === COMPLETING_WAITING_CHILDREN) return COROUTINE_SUSPENDED // (2)
130 return if (state is CompletedExceptionally) { // (3)
131 when {
132 shouldThrow(state.cause) -> throw recoverStackTrace(state.cause, uCont)
133 result is CompletedExceptionally -> throw recoverStackTrace(result.cause, uCont)
134 else -> result
135 }
136 } else {
137 state.unboxState()
138 }
139 }
140