fix core: avoid incompatible Boost.Context exception state#1295
Open
mpwaser wants to merge 1 commit into
Open
Conversation
mpwaser
marked this pull request as ready for review
July 13, 2026 09:58
mpwaser
marked this pull request as draft
July 13, 2026 10:02
mpwaser
marked this pull request as ready for review
July 13, 2026 10:13
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.
Problem
Boost.Context 1.88 added exception-state preservation around context switches. That handling conflicts with userver's task-local exception state when a pooled coroutine is reused by another task. It can restore through the prior task's exception-state pointer and corrupt state. Destroying the coroutine avoids the stale suspended activation, but gives up coroutine pooling.
Boost fixed the incompatibility upstream after Boost 1.91. However, userver currently selects the affected system implementation by default starting with Boost 1.88, so users can encounter the issue without opting into it.
Userver's vendored backend avoids the incompatible wrapper, but
core/src/engine/coro/marked_allocator.hppdirectly includes the systemboost/coroutine2/protected_fixedsize_stack.hppheader.That direct include bypasses the existing
coroutines/coroutine.hppabstraction. As a result, code usingMarkedAllocatorcan instantiate system Boost coroutine templates even when the vendored backend is selected.Why change the default
In my case, I only discovered this after spending almost two weeks investigating the downstream behavior. The failure surfaces after a context switch, far away from the stale exception-state pointer that causes it, so users have little reason to suspect the Boost.Context implementation or discover the build option on their own.
The incompatibility is already tracked in userver issue #1247, where a userver contributor confirmed that Boost had merged the underlying fix for its next release. Until a Boost release containing that fix is available, leaving Boost 1.88 through 1.91 as the default can make new and existing userver users repeat the same costly diagnosis. Selecting the existing vendored implementation by default is a bounded compatibility bridge. It preserves coroutine reuse instead of applying the more expensive coroutine-destruction workaround.
Proposed Change
Keep the vendored backend as the default with Boost 1.88 through 1.91. Starting with Boost 1.92, retain the existing preference for modern system Boost. The option remains explicitly configurable in either direction, and the existing macOS ARM64 special case is unchanged.
Also include
coroutines/coroutine.hppfrommarked_allocator.hpp. Both the system and vendored versions of that abstraction provideprotected_fixedsize_stack, so the selected backend supplies all related coroutine types consistently.The change does not alter coroutine pooling, task lifecycle, or public API.
Validation
git diff --check.Notes
The affected path depends on Boost.Context, libstdc++, and optimized context switching, so no repository unit test is added. Validation instead uses a pinned release-build reproducer. The default is intentionally conservative for all Boost 1.88 through 1.91 configurations. Unaffected configurations can still select the system backend explicitly with
USERVER_FEATURE_UBOOST_CORO=OFF.References
Note: by creating a PR or an issue you automatically agree to the CLA. See CONTRIBUTING.md. Feel free to remove this note, the agreement holds.