• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright 2016-2018 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 &mdash;
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 &mdash;
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> AbstractCoroutine<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> AbstractCoroutine<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> AbstractCoroutine<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     /*
117      * We're trying to complete our undispatched block here and have three code-paths:
118      * 1) Suspended.
119      *
120      * Or we are completing our block (and its job).
121      * 2) If we can't complete it, we suspend, probably waiting for children (2)
122      * 3) If we have successfully completed the whole coroutine here in an undispatched manner,
123      *    we should decide which result to return. We have two options: either return proposed update or actual final state.
124      *    But if fact returning proposed value is not an option, otherwise we will ignore possible cancellation or child failure.
125      *
126      * shouldThrow parameter is a special code path for timeout coroutine:
127      * If timeout is exceeded, but withTimeout() block was not suspended, we would like to return block value,
128      * not a timeout exception.
129      */
130     return when {
131         result === COROUTINE_SUSPENDED -> COROUTINE_SUSPENDED
132         makeCompletingOnce(result, MODE_IGNORE) -> {
133             val state = state
134             if (state is CompletedExceptionally) {
135                 when {
136                     shouldThrow(state.cause) -> throw tryRecover(state.cause)
137                     result is CompletedExceptionally -> throw tryRecover(result.cause)
138                     else -> result
139                 }
140             } else {
141                 state.unboxState()
142             }
143         }
144         else -> COROUTINE_SUSPENDED
145     }
146 }
147