-
Notifications
You must be signed in to change notification settings - Fork 560
Fix fatal error propagation in async operations #4512
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
base: series/3.6.x
Are you sure you want to change the base?
Fix fatal error propagation in async operations #4512
Conversation
|
Hi @armanbilge, @durban Would you mind taking a look at this when you have a moment? Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR!
- Since this is a bug fix, you can target the
series/3.6.xbranch - Can you add a test for a minimized version of the issue? You may need to add it to
IOAppSuitebecause this deals with fatal errors.
| * @param fut | ||
| * The `java.util.concurrent.CompletableFuture` to suspend in `F[_]` | ||
| */ | ||
| def fromCompletableFuture[A](fut: F[CompletableFuture[A]]): F[A] = cont { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are changes to this method necessary? #4505 (comment) suggests that the problem is actually in async and not directly in fromCompletableFuture.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's right that async_ is the core issue, but i think both changes are needed:
fromCompletableFuture: Uses CompletableFuture.handle() which catches all exceptions, including fatal ones which without fix, OutOfMemoryError gets wrapped instead of crashing the JVM.
async_: The IO.delay() wrapper was catching fatal errors during callback registration, causing the same problem.
Both mechanisms need the fix to ensure fatal errors consistently crash the JVM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added more insights on how the error re-surface from onError here: #4505 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @tpetillot
I added more insights on how the error re-surface from
onErrorhere: #4505 (comment)
looks like this code (Attached on the comment) is not detecting the original fatal error, but it's actually:
- Detecting errors thrown BY the error handler
(f(error))but NOT detecting the original fatal error that triggeredonError
The fatal error detection happens after the onError machinery, not during it.
I need to be guided if that is right 😊
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, definitely missed that, tried to draw the route of the error outcome for OOM with IO.cont here: #4505 (comment)
4b68700 to
ee0f110
Compare
Thanks for the review and guidance. Just learnt about all these upstream branches now 😊 |
|
Hello @armanbilge |
Description
Fixes #4505
Fixed fatal error propagation in async operations so fatal errors crash the JVM instead of being caught and wrapped.
Changes
kernel/jvm/src/main/scala/cats/effect/kernel/AsyncPlatform.scala: Added fatal error detection infromCompletableFutureto re-throw fatal errors instead of wrapping themcore/shared/src/main/scala/cats/effect/IO.scala: RemovedIO.delay()wrapper inIO.async_to allow fatal errors to propagate during callback registrationWhy both changes?
fromCompletableFutureusesCompletableFuture.handle()which catches all exceptions, including fatal onesasync_hadIO.delay()wrapper catching fatal errors during callback registrationTesting
fromCompletableFutureandasync_fatal error scenarios.onError(_ => IO.unit)workaround mentioned in the issue