dicej opened PR #11114 from dicej:generalized-fiber-abstraction to bytecodealliance:main:
As part of the work implementing the new Component Model async ABI in the
wasip3-prototypingrepo, I've generalized theFiberFutureabstraction inwasmtime::runtime::store::async_to support fibers which can either retain exclusive access to the store across suspend points or release it. The latter allows the store to be used by thecomponent-model-asyncevent loop and/or other fibers to run before the original fiber resumes, which is the key to allowing multiple fibers to run concurrently, passing control of the store back and forth.In the case of Pulley, the above generalization means we also need to give each fiber its own
Interpreterso that multiple concurrent fibers don't clobber each other's state.Concretely, this moves a lot of the code out of
async_.rsand into a newfiber.rssubmodule which will be shared with thecomponent-model-asyncimplementation.This also pulls in a new
StoreToken<T>utility which has been useful inwasip3-prototypingto safely convert from a&mut dyn VMStoreto aStoreContextMut<'a, T>when we previously witnessed a conversion in the other direction.Note that I've added a
'staticbound to theVMStoretrait, which simplifies use of&mut dyn VMStore, avoiding thorny lifetime issues.<!--
Please make sure you include the following information:
If this work has been discussed elsewhere, please include a link to that
conversation. If it was discussed in an issue, just mention "issue #...".Explain why this change is needed. If the details are in an issue already,
this can be brief.Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.htmlPlease ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->
dicej requested alexcrichton for a review on PR #11114.
dicej requested wasmtime-core-reviewers for a review on PR #11114.
dicej requested wasmtime-default-reviewers for a review on PR #11114.
dicej updated PR #11114.
alexcrichton submitted PR review:
I'll keep taking a look at
fiber.rs, but this is at least an initial round of feedback.
alexcrichton created PR review comment:
Mind prefixing this with
SAFETY:?
alexcrichton created PR review comment:
I was surprised how much
'staticbounds cropped up all over the place from this refactoring and this is what I ended up tracing it back to. This'staticcan be replaced with'aand with that I believe many transitive callers of this function can also remove'static<details>
<summary>diff</summary>
diff --git a/crates/wasmtime/src/runtime/component/func/typed.rs b/crates/wasmtime/src/runtime/component/func/typed.rs index f87447af7..c378742da 100644 --- a/crates/wasmtime/src/runtime/component/func/typed.rs +++ b/crates/wasmtime/src/runtime/component/func/typed.rs @@ -179,7 +179,7 @@ where ) -> Result<Return> where Params: Send + Sync, - Return: Send + Sync + 'static, + Return: Send + Sync, { let mut store = store.as_context_mut(); assert!( diff --git a/crates/wasmtime/src/runtime/fiber.rs b/crates/wasmtime/src/runtime/fiber.rs index ba34e9e6b..76a89b3e7 100644 --- a/crates/wasmtime/src/runtime/fiber.rs +++ b/crates/wasmtime/src/runtime/fiber.rs @@ -585,7 +585,7 @@ unsafe fn suspend_fiber( /// Run the specified function on a newly-created fiber and `.await` its /// completion. -pub(crate) async fn on_fiber<R: Send + 'static>( +pub(crate) async fn on_fiber<R: Send>( store: &mut StoreOpaque, func: impl FnOnce(&mut StoreOpaque) -> R + Send, ) -> Result<R> { @@ -596,7 +596,7 @@ pub(crate) async fn on_fiber<R: Send + 'static>( } /// Wrap the specified function in a fiber and return it. -fn prepare_fiber<'a, R: Send + 'static>( +fn prepare_fiber<'a, R: Send + 'a>( store: &mut dyn VMStore, func: impl FnOnce(&mut dyn VMStore) -> R + Send + 'a, ) -> Result<(StoreFiber<'a>, oneshot::Receiver<R>)> { @@ -612,7 +612,7 @@ fn prepare_fiber<'a, R: Send + 'static>( /// Run the specified function on a newly-created fiber and `.await` its /// completion. -async fn on_fiber_raw<R: Send + 'static>( +async fn on_fiber_raw<R: Send>( store: &mut StoreOpaque, func: impl FnOnce(&mut dyn VMStore) -> R + Send, ) -> Result<R> { diff --git a/crates/wasmtime/src/runtime/func/typed.rs b/crates/wasmtime/src/runtime/func/typed.rs index e9304b755..0df0dcc40 100644 --- a/crates/wasmtime/src/runtime/func/typed.rs +++ b/crates/wasmtime/src/runtime/func/typed.rs @@ -132,10 +132,7 @@ where &self, mut store: impl AsContextMut<Data: Send>, params: Params, - ) -> Result<Results> - where - Results: 'static, - { + ) -> Result<Results> { let mut store = store.as_context_mut(); assert!( store.0.async_support(), diff --git a/crates/wasmtime/src/runtime/store/async_.rs b/crates/wasmtime/src/runtime/store/async_.rs index c34d1dac4..f1c8a7454 100644 --- a/crates/wasmtime/src/runtime/store/async_.rs +++ b/crates/wasmtime/src/runtime/store/async_.rs @@ -171,7 +171,7 @@ impl StoreOpaque { /// This function will convert the synchronous `func` into an asynchronous /// future. This is done by running `func` in a fiber on a separate native /// stack which can be suspended and resumed from. - pub(crate) async fn on_fiber<R: Send + 'static>( + pub(crate) async fn on_fiber<R: Send>( &mut self, func: impl FnOnce(&mut Self) -> R + Send, ) -> Result<R> { @@ -286,7 +286,7 @@ impl StoreOpaque { impl<T> StoreContextMut<'_, T> { /// Executes a synchronous computation `func` asynchronously on a new fiber. - pub(crate) async fn on_fiber<R: Send + 'static>( + pub(crate) async fn on_fiber<R: Send>( &mut self, func: impl FnOnce(&mut StoreContextMut<'_, T>) -> R + Send, ) -> Result<R></summary>
would that be possible to avoid the extra
'staticcropping up? I can sort of see how component-model-async bits may still require'static, but for preexisting bits in Wasmtime I'd be surprised if it required'static.
alexcrichton created PR review comment:
Mind adding some brief comments as to the purpose of this type?
alexcrichton created PR review comment:
Could this be directly imported at use-sites below to avoid the #[cfg]'d import?
alexcrichton created PR review comment:
This runs a risk of causing problems because the
asyncfeature previously I believe didn't require thestdfeature, and now it does. Would it be possible to refactor to remove the need for the oneshot bits?
alexcrichton created PR review comment:
Would it be possible for this to return
&mut fiber::AsyncStateinstead of a raw pointer?
alexcrichton created PR review comment:
FWIW this style of imports is currently largely nonexistent within Wasmtime. Would you be ok reformatting this in a style more consistent with the rest of Wasmtime?
alexcrichton created PR review comment:
If possible I think it'd be good to do the same to the
Sendbounds as well, but I haven't gamed that out yet.
alexcrichton created PR review comment:
I'm not sure how exactly yet, but I'd prefer to avoid this #[cfg] necessity just to access
self.executoras it's very easy to forget an non-obvious that it's required.
alexcrichton created PR review comment:
In retrospect let's add
'statichere. There's weird things with variance about extending or shortening lifetimes in types (e.g. being able to safely change aStoreToken<T>to aStoreToken<U>where just the lifetimes differ). Having'staticmeans we wouldn't have to worry about it though.
alexcrichton created PR review comment:
Where possible I'd like to aggressively remove
pub(crate)from this file. This function for example in wasip3-prototyping is only used elsewhere inInstance::resume_fiberand the implementation looks like a good candidate for moving to this file as a method onStoreFiber<'static>, so could this function be made purely internal?
alexcrichton created PR review comment:
This to me feels like it's a safety contract that's a bit too onerous to uphold since none of the callers of this function actually have a
&mut StoreOpaqueor similar in context. You'd have to travel a layer or two up the stack to actually see that and it feels a bit too "far" to me to find the proof of "ah, yes, ok this is safe". Would it be possible to actually take&mut StoreOpaquehere as an argument? (that would also simplify the executor handling pieces I believe)
alexcrichton created PR review comment:
To expand on this some more, this is a pretty unsafe function and the boundaries of abstraction aren't really that clear to me. Understanding this function in isolation is pretty difficult and I feel it fits best within the context of
on_fiber_raworInstance::resume_fiber, which is where I'd prefer to keep all these unsafe set/reset/restore/etc bits all centralized in one function where possible so it's clear what's responsible for what.
alexcrichton created PR review comment:
One of the sticklers for taking an argument is probably
Drop for StoreFiber, and that's something where we can soup up theDrop for Storeto call a function over here to avoid relying onDrop for StoreFiber(or something like that)
alexcrichton created PR review comment:
Well, on second thought, no maybe that's not a good idea. Fibers can't be dropped unless they've finished so that would introduce footguns of forgetting to call the destructor on panics for example (or something like that)...
If the dtor is a "special case" that seems somewhat reasonable
alexcrichton created PR review comment:
Actually, on raeding this more, it looks like
Instance::resume_fiberandon_fiber_rawabove are more-or-less the same function where the only difference isInstance::resume_fibercan have a different type of return value whichon_fiber_rawwould assert never happens. Could the methods all get refactored/merged into this file?
dicej submitted PR review.
dicej created PR review comment:
I _think_ you can use
oneshotwithout enabling thefutures/stdfeature, so we might be able to just remove this line. I'll play around with it.
dicej created PR review comment:
I believe we did this to satisfy miri, but I can retest with
&mutand see what happens.
dicej submitted PR review.
dicej updated PR #11114.
dicej submitted PR review.
dicej created PR review comment:
I've applied your patch to remove the
'staticbounds; let me know if you want me to do the same forSend.
alexcrichton created PR review comment:
Yeah if possible I think we should remove
Sendhere too as I think'staticandSendare the root of https://github.com/bytecodealliance/wasip3-prototyping/issues/145 perhaps?
alexcrichton submitted PR review.
dicej created PR review comment:
Yeah, miri won't tolerate that:
test scenario::round_trip::async_round_trip_stackful ... error: Undefined Behavior: attempting a read access using <3440249> at alloc1440939[0x18], but that tag does not exist in the borrow stack for this location --> /home/dicej/p/wasmtime/crates/wasmtime/src/runtime/fiber.rs:444:57 | 444 | let _reset_executor = Reset(fiber.executor_ptr, *fiber.executor_ptr); | ^^^^^^^^^^^^^^^^^^^ | | | attempting a read access using <3440249> at alloc1440939[0x18], but that tag does not exist in the borrow stack for this location | this error occurs as part of an access at alloc1440939[0x18..0x20] | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information help: <3440249> was created by a SharedReadWrite retag at offsets [0x18..0x20] --> /home/dicej/p/wasmtime/crates/wasmtime/src/runtime/fiber.rs:476:18 | 476 | unsafe { &raw mut (*store.store_opaque_mut().async_state()).current_executor }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: <3440249> was later invalidated at offsets [0x0..0x38] by a Unique retag --> /home/dicej/p/wasmtime/crates/wasmtime/src/runtime/store.rs:1947:9 | 1947 | &mut self.async_state | ^^^^^^^^^^^^^^^^^^^^^We might be able to address it a different way (e.g. a larger refactoring), though. Not sure how deep that rabbit hole will go.
dicej submitted PR review.
dicej submitted PR review.
dicej created PR review comment:
Oof, and it looks like miri is upset for other reasons on
wasip3-prototypingmain; I seem to have caused a regression with the fiber unification PR. Debugging now.
dicej updated PR #11114.
dicej submitted PR review.
dicej created PR review comment:
Okay, just pushed a fix for that regression: https://github.com/bytecodealliance/wasmtime/pull/11114/commits/ae227b658643761ebb3603a428879a826e4f0762
dicej updated PR #11114.
alexcrichton submitted PR review.
alexcrichton created PR review comment:
Oh I forgot to mention this earlier but FWIW this looks like it's copy/pasted from elsewhere and would be good to deduplicate.
alexcrichton created PR review comment:
Toying around a bit with wasip3-prototyping, for this to be more sound we're going to want to add
Send + Syncto the bounds offun. That makes theunsafe impl Send/Syncabove a bit more sound because it at least accounts for all captured values withinfun. The fallout wasn't too large from what I saw and seemed mostly as-expected
dicej updated PR #11114.
dicej created PR review comment:
We chatted about this over Zoom and concluded that changing the
*mutto a&mutwould require a larger-scale refactoring to make sound and miri-approved, so we're leaving it as-is for now.
dicej submitted PR review.
dicej submitted PR review.
dicej created PR review comment:
I've pushed an update which addresses this.
dicej updated PR #11114.
dicej updated PR #11114.
alexcrichton updated PR #11114.
alexcrichton updated PR #11114.
dicej updated PR #11114.
dicej updated PR #11114.
dicej updated PR #11114.
dicej updated PR #11114.
alexcrichton updated PR #11114.
alexcrichton updated PR #11114.
alexcrichton updated PR #11114.
alexcrichton updated PR #11114.
alexcrichton updated PR #11114.
alexcrichton updated PR #11114.
alexcrichton updated PR #11114.
alexcrichton submitted PR review:
@dicej ok these are all the changes I'd like to make, mind reviewing the changes I made as well before merging? Afterwards feel free to go ahead and queue this for merge
dicej submitted PR review.
dicej created PR review comment:
This comment is confusing since it refers to nonexistent docs for
resume_fiber, and in any case we don't need to handwave about the "contract" anymore sinceresume_fibernow takes an explicit&mut StoreOpaqueparameter. So maybe just say something like "This fiber will only be resumed usingresume_fiber, which takes a&mut StoreOpaqueparameter and has given us exclusive access...".
dicej updated PR #11114.
dicej has enabled auto merge for PR #11114.
dicej updated PR #11114.
dicej has enabled auto merge for PR #11114.
dicej merged PR #11114.
Last updated: Dec 06 2025 at 07:03 UTC