Skip to content

Commit b96c9a2

Browse files
committed
Simplify, document and reduce the binary size of Undispatched.kt
1 parent 589e75e commit b96c9a2

File tree

1 file changed

+28
-25
lines changed

1 file changed

+28
-25
lines changed

kotlinx-coroutines-core/common/src/intrinsics/Undispatched.kt

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -38,55 +38,58 @@ internal fun <R, T> (suspend (R) -> T).startCoroutineUndispatched(receiver: R, c
3838
*
3939
* It starts the coroutine using [startCoroutineUninterceptedOrReturn].
4040
*/
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)
4644

4745
/**
4846
* Same as [startUndispatchedOrReturn], but ignores [TimeoutCancellationException] on fast-path.
4947
*/
5048
internal fun <T, R> ScopeCoroutine<T>.startUndispatchedOrReturnIgnoreTimeout(
5149
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)
5751

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
6164
): Any? {
6265
val result = try {
63-
startBlock()
66+
block.startCoroutineUninterceptedOrReturn(receiver, this)
6467
} catch (e: Throwable) {
6568
CompletedExceptionally(e)
6669
}
70+
6771
/*
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.
7877
*/
7978
if (result === COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED // (1)
8079
val state = makeCompletingOnce(result)
8180
if (state === COMPLETING_WAITING_CHILDREN) return COROUTINE_SUSPENDED // (2)
8281
afterCompletionUndispatched()
8382
return if (state is CompletedExceptionally) { // (3)
8483
when {
85-
shouldThrow(state.cause) -> throw recoverStackTrace(state.cause, uCont)
84+
alwaysRethrow || notOwnTimeout(state.cause) -> throw recoverStackTrace(state.cause, uCont)
8685
result is CompletedExceptionally -> throw recoverStackTrace(result.cause, uCont)
8786
else -> result
8887
}
8988
} else {
9089
state.unboxState()
9190
}
9291
}
92+
93+
private fun ScopeCoroutine<*>.notOwnTimeout(cause: Throwable): Boolean {
94+
return cause !is TimeoutCancellationException || cause.coroutine !== this
95+
}

0 commit comments

Comments
 (0)