Skip to content

fix core: avoid incompatible Boost.Context exception state#1295

Open
mpwaser wants to merge 1 commit into
userver-framework:developfrom
mpwaser:fix/coroutine-eh-state-reuse
Open

fix core: avoid incompatible Boost.Context exception state#1295
mpwaser wants to merge 1 commit into
userver-framework:developfrom
mpwaser:fix/coroutine-eh-state-reuse

Conversation

@mpwaser

@mpwaser mpwaser commented Jul 13, 2026

Copy link
Copy Markdown

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.hpp directly includes the system boost/coroutine2/protected_fixedsize_stack.hpp header.

That direct include bypasses the existing coroutines/coroutine.hpp abstraction. As a result, code using MarkedAllocator can 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.hpp from marked_allocator.hpp. Both the system and vendored versions of that abstraction provide protected_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

  • Tested with Boost 1.91 on Ubuntu 24.04 using GCC 13.3 on x86_64 and ARM64.
  • Confirmed on both architectures that the default configuration selects the vendored coroutine backend and that core builds and installs successfully.
  • Confirmed with a focused reproducer that coroutine reuse works reliably with the default backend on both architectures.
  • Confirmed on both architectures that core also builds and installs when the system backend is explicitly selected.
  • Ran 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.

@mpwaser
mpwaser marked this pull request as ready for review July 13, 2026 09:58
@mpwaser mpwaser changed the title fix core: avoid incompatible Boost.Context exception state Draft: fix core: avoid incompatible Boost.Context exception state Jul 13, 2026
@mpwaser mpwaser changed the title Draft: fix core: avoid incompatible Boost.Context exception state fix core: avoid incompatible Boost.Context exception state Jul 13, 2026
@mpwaser
mpwaser marked this pull request as draft July 13, 2026 10:02
@mpwaser
mpwaser marked this pull request as ready for review July 13, 2026 10:13
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.

1 participant