@@ -38,55 +38,58 @@ internal fun <R, T> (suspend (R) -> T).startCoroutineUndispatched(receiver: R, c
38
38
*
39
39
* It starts the coroutine using [startCoroutineUninterceptedOrReturn].
40
40
*/
41
- internal fun <T , R > ScopeCoroutine<T>.startUndispatchedOrReturn (receiver : R , block : suspend R .() -> T ): Any? {
42
- return undispatchedResult({ true }) {
43
- block.startCoroutineUninterceptedOrReturn(receiver, this )
44
- }
45
- }
41
+ internal fun <T , R > ScopeCoroutine<T>.startUndispatchedOrReturn (
42
+ receiver : R , block : suspend R .() -> T
43
+ ): Any? = startUndspatched(alwaysRethrow = true , receiver, block)
46
44
47
45
/* *
48
46
* Same as [startUndispatchedOrReturn], but ignores [TimeoutCancellationException] on fast-path.
49
47
*/
50
48
internal fun <T , R > ScopeCoroutine<T>.startUndispatchedOrReturnIgnoreTimeout (
51
49
receiver : R , block : suspend R .() -> T
52
- ): Any? {
53
- return undispatchedResult({ e -> ! (e is TimeoutCancellationException && e.coroutine == = this ) }) {
54
- block.startCoroutineUninterceptedOrReturn(receiver, this )
55
- }
56
- }
50
+ ): Any? = startUndspatched(alwaysRethrow = false , receiver, block)
57
51
58
- private inline fun <T > ScopeCoroutine<T>.undispatchedResult (
59
- shouldThrow : (Throwable ) -> Boolean ,
60
- startBlock : () -> Any?
52
+ /* *
53
+ * Starts and handles the result of an undispatched coroutine, potentially with children.
54
+ * For example, it handles `coroutineScope { ...suspend of throw, maybe start children... }`
55
+ * and `launch(start = UNDISPATCHED) { ... }`
56
+ *
57
+ * @param alwaysRethrow specifies whether an exception should be unconditioanlly rethrown.
58
+ * It is a tweak for 'withTimeout' in order to successfully return values when the block was cancelled:
59
+ * i.e. `withTimeout(1ms) { Thread.sleep(1000); 42 }` should not fail.
60
+ */
61
+ private fun <T , R > ScopeCoroutine<T>.startUndspatched (
62
+ alwaysRethrow : Boolean ,
63
+ receiver : R , block : suspend R .() -> T
61
64
): Any? {
62
65
val result = try {
63
- startBlock( )
66
+ block.startCoroutineUninterceptedOrReturn(receiver, this )
64
67
} catch (e: Throwable ) {
65
68
CompletedExceptionally (e)
66
69
}
70
+
67
71
/*
68
- * We're trying to complete our undispatched block here and have three code-paths:
69
- * (1) Coroutine is suspended.
70
- * Otherwise, coroutine had returned result, so we are completing our block (and its job).
71
- * (2) If we can't complete it or started waiting for children, we suspend.
72
- * (3) If we have successfully completed the coroutine state machine here,
73
- * then we take the actual final state of the coroutine from makeCompletingOnce and return it.
74
- *
75
- * shouldThrow parameter is a special code path for timeout coroutine:
76
- * If timeout is exceeded, but withTimeout() block was not suspended, we would like to return block value,
77
- * not a timeout exception.
72
+ * We are trying to complete our undispatched block with the following possible codepaths:
73
+ * 1) The coroutine just suspended. I.e. `coroutineScope { .. suspend here }`.
74
+ * Then just suspend
75
+ * 2) The coroutine completed with something, but has active children. Wait for them, also suspend
76
+ * 3) The coroutine succesfully completed. Return or rethrow its result.
78
77
*/
79
78
if (result == = COROUTINE_SUSPENDED ) return COROUTINE_SUSPENDED // (1)
80
79
val state = makeCompletingOnce(result)
81
80
if (state == = COMPLETING_WAITING_CHILDREN ) return COROUTINE_SUSPENDED // (2)
82
81
afterCompletionUndispatched()
83
82
return if (state is CompletedExceptionally ) { // (3)
84
83
when {
85
- shouldThrow (state.cause) -> throw recoverStackTrace(state.cause, uCont)
84
+ alwaysRethrow || notOwnTimeout (state.cause) -> throw recoverStackTrace(state.cause, uCont)
86
85
result is CompletedExceptionally -> throw recoverStackTrace(result.cause, uCont)
87
86
else -> result
88
87
}
89
88
} else {
90
89
state.unboxState()
91
90
}
92
91
}
92
+
93
+ private fun ScopeCoroutine <* >.notOwnTimeout (cause : Throwable ): Boolean {
94
+ return cause !is TimeoutCancellationException || cause.coroutine != = this
95
+ }
0 commit comments