Skip to content

Improve handling of DispatchException in an undispatched case #4272

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 10, 2025

Conversation

qwwdfsad
Copy link
Collaborator

No description provided.

@qwwdfsad qwwdfsad changed the title Dispatch exception path Improve handling of DispatchException in an undispatched case Nov 11, 2024
@qwwdfsad
Copy link
Collaborator Author

qwwdfsad commented Nov 11, 2024

Looking at #4209 (comment), I have yet to see an elegant, generalized solution to our problem that is neither a breaking change nor an obvious contract breach


private fun ScopeCoroutine<*>.dispatchException(e: DispatchException) {
makeCompleting(CompletedExceptionally(e.cause))
throw throw recoverStackTrace(e.cause, uCont)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One throw should be enough.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to be extra-sure the exception is thrown

return cause !is TimeoutCancellationException || cause.coroutine !== this
}

private fun ScopeCoroutine<*>.dispatchException(e: DispatchException) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this function should be inlined. It's not obvious that it calls makeCompleting.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am, in general, for the opposite approach -- get rid of all "very uncommon" cases from a code path that is supposed to be followed. I renamed the function and changed return type to be Nothing for the readability sake

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have examples of where this approach improves code clarity? My intuition is that this only works in backend-like applications where you let all errors bubble up as exceptions and then just restart the request.

My approach 95% of the time is that a function must be a non-leaky abstraction: it hides the details that are not important to you, but doesn't hide the details that are. dispatchExceptionAndMakeCompleting is not an abstraction, the name is a (non-compiler-verified) enumeration of what it does. If the idea is to use as little space as possible for this codepath, we can join the two statements with ;.

Comment on lines 13 to 14
import org.junit.runners.model.TestTimedOutException
import java.util.concurrent.TimeoutException
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these imports used?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. Fixed

}

private fun ScopeCoroutine<*>.dispatchException(e: DispatchException) {
makeCompleting(CompletedExceptionally(e.cause))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not makeCompletingOnce, to highlight the aspects of this codepath that are different from the happy path?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like this path to be as robust as possible. There might be obscure reasons for DispatchException, for example a dispatcher sent coroutine to execute and then failed in the post-send code-path.
makeCompletingOnce can throw, and I would like to avoid extra try-catches here to preserve the contextual information (i.e. the suppressed dispatch exception)

fun testFlowOn() {
// See #4142, this test ensures that `coroutineScope { produce(failingDispatcher, ATOMIC) }`
// rethrows an exception. It does not help with the completion of such a coroutine though.
// Hack to avoid waiting for test completion
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where's the hack?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made the comment more straightforward

…t immediately

Otherwise, in the face of malfunctioning dispatcher, there might be no trace of any error, making it extremely hard to debug.

The root cause should be addressed by #4209

Fixes #4142
@qwwdfsad qwwdfsad force-pushed the dispatch-exception-path branch from f8503e4 to 8389768 Compare February 18, 2025 11:08
@dkhalanskyjb dkhalanskyjb merged commit 3f3f4f7 into develop Mar 10, 2025
1 check passed
@dkhalanskyjb dkhalanskyjb deleted the dispatch-exception-path branch March 10, 2025 11:39
svc-squareup-copybara pushed a commit to cashapp/misk that referenced this pull request Apr 8, 2025
| Package | Type | Package file | Manager | Update | Change |
|---|---|---|---|---|---|
|
[org.jetbrains.kotlinx:kotlinx-coroutines-test](https://github.com/Kotlin/kotlinx.coroutines)
| dependencies | misk/gradle/libs.versions.toml | gradle | patch |
`1.10.1` -> `1.10.2` |
|
[org.jetbrains.kotlinx:kotlinx-coroutines-slf4j](https://github.com/Kotlin/kotlinx.coroutines)
| dependencies | misk/gradle/libs.versions.toml | gradle | patch |
`1.10.1` -> `1.10.2` |
|
[org.jetbrains.kotlinx:kotlinx-coroutines-core](https://github.com/Kotlin/kotlinx.coroutines)
| dependencies | misk/gradle/libs.versions.toml | gradle | patch |
`1.10.1` -> `1.10.2` |
| [software.amazon.awssdk:sdk-core](https://aws.amazon.com/sdkforjava) |
dependencies | misk/gradle/libs.versions.toml | gradle | patch |
`2.31.16` -> `2.31.17` |
| [software.amazon.awssdk:sqs](https://aws.amazon.com/sdkforjava) |
dependencies | misk/gradle/libs.versions.toml | gradle | patch |
`2.31.16` -> `2.31.17` |
|
[software.amazon.awssdk:dynamodb-enhanced](https://aws.amazon.com/sdkforjava)
| dependencies | misk/gradle/libs.versions.toml | gradle | patch |
`2.31.16` -> `2.31.17` |
| [software.amazon.awssdk:dynamodb](https://aws.amazon.com/sdkforjava) |
dependencies | misk/gradle/libs.versions.toml | gradle | patch |
`2.31.16` -> `2.31.17` |
| [software.amazon.awssdk:aws-core](https://aws.amazon.com/sdkforjava) |
dependencies | misk/gradle/libs.versions.toml | gradle | patch |
`2.31.16` -> `2.31.17` |
| [software.amazon.awssdk:bom](https://aws.amazon.com/sdkforjava) |
dependencies | misk/gradle/libs.versions.toml | gradle | patch |
`2.31.16` -> `2.31.17` |
| [software.amazon.awssdk:auth](https://aws.amazon.com/sdkforjava) |
dependencies | misk/gradle/libs.versions.toml | gradle | patch |
`2.31.16` -> `2.31.17` |

---

### Release Notes

<details>
<summary>Kotlin/kotlinx.coroutines
(org.jetbrains.kotlinx:kotlinx-coroutines-test)</summary>

###
[`v1.10.2`](https://github.com/Kotlin/kotlinx.coroutines/blob/HEAD/CHANGES.md#Version-1102)

[Compare
Source](Kotlin/kotlinx.coroutines@1.10.1...1.10.2)

- Fixed the `kotlinx-coroutines-debug` JAR file including the
`module-info.class` file twice, resulting in failures in various tooling
([#&#8203;4314](Kotlin/kotlinx.coroutines#4314)).
Thanks, [@&#8203;RyuNen344](https://github.com/RyuNen344)!
- Fixed `Flow.stateIn` hanging when the scope is cancelled in advance or
the flow is empty
([#&#8203;4322](Kotlin/kotlinx.coroutines#4322)).
Thanks, [@&#8203;francescotescari](https://github.com/francescotescari)!
- Improved handling of dispatcher failures in `.limitedParallelism`
([#&#8203;4330](Kotlin/kotlinx.coroutines#4330))
and during flow collection
([#&#8203;4272](Kotlin/kotlinx.coroutines#4272)).
- Fixed `runBlocking` failing to run its coroutine to completion in some
cases if its JVM thread got interrupted
([#&#8203;4399](Kotlin/kotlinx.coroutines#4399)).
-   Small tweaks, fixes, and documentation improvements.

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "after 6pm every weekday,before 2am
every weekday" in timezone Australia/Melbourne, Automerge - At any time
(no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Never, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config help](https://github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR has been generated by [Renovate
Bot](https://github.com/renovatebot/renovate).

GitOrigin-RevId: 3449fc325ac14573dff5b670e407e99eab23757b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants