Fix uncaught_exceptions() not accounting for forced_unwind#337
Open
xokdvium wants to merge 1 commit into
Open
Conversation
3 tasks
Unwound fibers would see std::uncaught_exceptions() == 0, while a forced_unwind exception is in "flight". This goes against the contract of std::uncaught_exceptions() that scope guards rely upon. Failing to report the correct number of uncaught exceptions (especially misreporting zero) will lead to scope guards to misbehave badly and skip running cleanup code which branches on whether the destructor is called during stack unwinding or not. This is because the "throw" would happen before the destructor is run on the fiber stack being switched to, but the increment would be clobbered by the destructor of manage_exception_state. I'm not sure what the contract of run ontop_fcontext is wrt to whether the the caller provided function can throw or not, but in my best understanding the forced_unwind mechanism is mostly internal and so is throwing from ontop_fcontext in the switched-to fiber. Thus, I've kept the catch block scoped to detail::forced_unwind.
xokdvium
force-pushed
the
uncaught-exceptions-forced-unwind
branch
from
July 21, 2026 21:21
fd76812 to
5883212
Compare
Author
|
Ok the new test surfaces the bug under the fcontext backend. |
xokdvium
marked this pull request as ready for review
July 21, 2026 21:24
xokdvium
commented
Jul 21, 2026
Comment on lines
+407
to
+416
| return { detail::ontop_fcontext( | ||
| #if defined(BOOST_NO_CXX14_STD_EXCHANGE) | ||
| detail::exchange( fctx_, nullptr), | ||
| detail::exchange( fctx_, nullptr), | ||
| #else | ||
| std::exchange( fctx_, nullptr), | ||
| std::exchange( fctx_, nullptr), | ||
| #endif | ||
| & p, | ||
| detail::fiber_ontop< fiber, decltype(p) >).fctx }; | ||
| & p, | ||
| detail::fiber_ontop< fiber, decltype(p) >).fctx }; | ||
| } catch ( detail::forced_unwind const& ) { | ||
| exstate.from_forced_unwind(); |
Author
There was a problem hiding this comment.
I guess if an exception is thrown on the switched-to context it would also need to be handled here - and that's probably in-scope. Happy to tackle this (maybe in a follow-up), but I've started by putting out the immediate fire under our feet in nix.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Unwound fibers would see std::uncaught_exceptions() == 0, while a forced_unwind exception is in "flight". This goes against the contract of std::uncaught_exceptions() that scope guards rely upon. Failing to report the correct number of uncaught exceptions (especially misreporting zero) will lead to scope guards to misbehave badly and skip running cleanup code which branches on whether the destructor is called during stack unwinding or not.
This is because the "throw" would happen before the destructor is run on the fiber stack being switched to, but the increment would be clobbered by the destructor of manage_exception_state.
I'm not sure what the contract of run ontop_fcontext is wrt to whether the the caller provided function can throw or not, but in my best understanding the forced_unwind mechanism is mostly internal and so is throwing from ontop_fcontext in the switched-to fiber. Thus, I've kept the catch block scoped to detail::forced_unwind.
TODO: