@@ -70,29 +70,70 @@ private inline fun <T> startDirect(completion: Continuation<T>, block: () -> Any
70
70
}
71
71
72
72
/* *
73
- * Starts this coroutine with the given code [block] in the same context and returns result when it
73
+ * Starts this coroutine with the given code [block] in the same context and returns coroutine result when it
74
74
* completes without suspension.
75
75
* This function shall be invoked at most once on this coroutine.
76
+ * This function checks cancellation of the outer [Job] on fast-path.
76
77
*
77
78
* First, this function initializes parent job from the `parentContext` of this coroutine that was passed to it
78
79
* during construction. Second, it starts the coroutine using [startCoroutineUninterceptedOrReturn].
79
80
*/
80
81
internal fun <T , R > AbstractCoroutine<T>.startUndispatchedOrReturn (receiver : R , block : suspend R .() -> T ): Any? {
81
82
initParentJob()
82
- return undispatchedResult { block.startCoroutineUninterceptedOrReturn(receiver, this ) }
83
+ return undispatchedResult({ true }) {
84
+ block.startCoroutineUninterceptedOrReturn(receiver, this )
85
+ }
83
86
}
84
87
85
- private inline fun <T > AbstractCoroutine<T>.undispatchedResult (startBlock : () -> Any? ): Any? {
88
+ /* *
89
+ * Same as [startUndispatchedOrReturn], but ignores [TimeoutCancellationException] on fast-path.
90
+ */
91
+ internal fun <T , R > AbstractCoroutine<T>.startUndispatchedOrReturnIgnoreTimeout (
92
+ receiver : R , block : suspend R .() -> T ): Any? {
93
+ initParentJob()
94
+ return undispatchedResult({ e -> ! (e is TimeoutCancellationException && e.coroutine == = this ) }) {
95
+ block.startCoroutineUninterceptedOrReturn(receiver, this )
96
+ }
97
+ }
98
+
99
+ private inline fun <T > AbstractCoroutine<T>.undispatchedResult (
100
+ shouldThrow : (Throwable ) -> Boolean ,
101
+ startBlock : () -> Any?
102
+ ): Any? {
86
103
val result = try {
87
104
startBlock()
88
105
} catch (e: Throwable ) {
89
106
CompletedExceptionally (e)
90
107
}
108
+
109
+ /*
110
+ * We're trying to complete our undispatched block here and have three code-paths:
111
+ * 1) Suspended.
112
+ *
113
+ * Or we are completing our block (and its job).
114
+ * 2) If we can't complete it, we suspend, probably waiting for children (2)
115
+ * 3) If we have successfully completed the whole coroutine here in an undispatched manner,
116
+ * we should decide which result to return. We have two options: either return proposed update or actual final state.
117
+ * But if fact returning proposed value is not an option, otherwise we will ignore possible cancellation or child failure.
118
+ *
119
+ * shouldThrow parameter is a special code path for timeout coroutine:
120
+ * If timeout is exceeded, but withTimeout() block was not suspended, we would like to return block value,
121
+ * not a timeout exception.
122
+ */
91
123
return when {
92
124
result == = COROUTINE_SUSPENDED -> COROUTINE_SUSPENDED
93
125
makeCompletingOnce(result, MODE_IGNORE ) -> {
94
- if (result is CompletedExceptionally ) throw result.cause else result
126
+ val state = state
127
+ if (state is CompletedExceptionally ) {
128
+ when {
129
+ shouldThrow(state.cause) -> throw state.cause
130
+ result is CompletedExceptionally -> throw result.cause
131
+ else -> result
132
+ }
133
+ } else {
134
+ state
135
+ }
95
136
}
96
137
else -> COROUTINE_SUSPENDED
97
138
}
98
- }
139
+ }
0 commit comments