SebTardif opened PR #13918 from SebTardif:fix/restore-thread-on-lower-error to bytecodealliance:main:
Summary
Temporary
set_threadswitches used during concurrent component lowering (so guestrealloc/ callbacks see the correct thread) were not restored on error paths. An early?/bail!afterset_threadleft the store's current thread pointing at the temporary thread, so later concurrent operations could run with the wrong thread context.This adds
with_store_thread/restore_thread_afterthat always restore the previous thread (preferring the original work error if restore also fails), and converts every temporary save/restore pair in this class.Why this is needed
#12736 correctly established that stream/future lowering can call guest
reallocand therefore must set the current thread first. That PR also added restore on the success path. Error paths that return before the matching restore still leave the wrong thread installed.Concrete example in
futures_and_streams::lowerwhenT::MAY_REQUIRE_REALLOCis true:
set_thread(caller_thread)installs the caller.- Alignment / bounds checks or
linear_store_list_to_memorycan fail with?.- The matching
set_thread(old_thread)never runs.The same pattern existed for guest↔guest future/stream
copy, event delivery callbacks, stackless/stackful guest calls, async return adapters, host-task result lowering, and explicit thread start.Fix
StoreOpaque::restore_thread_afteralways restores after a workResult.- Free function
with_store_threadfor the common scoped form.AsStoreOpaqueforStoreContextMutso the helper works at those call sites.- Converted all temporary
set_threadpairs found by greppingcrates/wasmtime/src/runtime/component.Permanent thread transitions (enter/exit sync call, host task create, fiber resume bookkeeping) are unchanged.
Testing
Unit tests in
with_store_thread_tests:
- Restores the previous current thread on success.
- Restores the previous current thread when work returns an error (red-green: skipping restore fails the assertion with
got Noneafter installing a host marker thread).- Prefer work errors over restore errors.
cargo test -p wasmtime --features component-model-async --lib with_store_thread # 3 passedAlso ran other concurrent unit tests in the crate (
cargo test -p wasmtime --features component-model-async --lib concurrent).Related
SebTardif requested pchickey for a review on PR #13918.
SebTardif requested wasmtime-core-reviewers for a review on PR #13918.
github-actions[bot] added the label wasmtime:api on PR #13918.
pchickey unassigned pchickey from PR #13918 Fix current-thread leak on concurrent lower error paths.
pchickey requested alexcrichton for a review on PR #13918.
:cross_mark: alexcrichton closed without merge PR #13918.
alexcrichton commented on PR #13918:
I'd advise against blindly following LLM advice when it comes to the deep internals of the runtime, especially an async runtime. It's intentional that error paths right now do not have the same degree of cleanup as successful paths because errors indicate traps after which a component is entirely locked down and unable to be used again.
Are you able to write tests that misbehave using just wasm? Tests for the internal state aren't too interesting since they're not guaranteed to be exposed by the public API.
alexcrichton reopened PR #13918 from SebTardif:fix/restore-thread-on-lower-error to bytecodealliance:main.
alexcrichton commented on PR #13918:
er, sorry, didn't mean to close
SebTardif commented on PR #13918:
@alexcrichton
It's intentional that error paths right now do not have the same degree of cleanup as successful paths because errors indicate traps after which a component is entirely locked down and unable to be used again.
Thanks for the context. That tracks: these host-side errors (alignment/bounds in
lower, callback failures) propagate via?throughpoll_until, so the event loop exits before anything reads the stale thread state.Are you able to write tests that misbehave using just wasm?
No. The event loop terminates on error before anything could observe the stale state.
Closing this; the cleanup asymmetry is consistent with the design.
:cross_mark: SebTardif closed without merge PR #13918.
Last updated: Jul 29 2026 at 05:03 UTC