-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow async yield from epoch interruption callback #6464
Allow async yield from epoch interruption callback #6464
Conversation
My guess is if anybody has objections to this PR it'll probably be @alexcrichton or @fitzgen so maybe I should have picked one of you as reviewers instead of letting it auto-assign. Oh well. |
Subscribe to Label Actioncc @peterhuene
This issue or pull request has been labeled: "wasmtime:api"
Thus the following users have been cc'd because of the following labels:
To subscribe or unsubscribe from this label, edit the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mind adding some tests as well?
crates/wasmtime/src/store.rs
Outdated
@@ -943,7 +941,7 @@ impl<T> Store<T> { | |||
/// for an introduction to epoch-based interruption. | |||
pub fn epoch_deadline_callback( | |||
&mut self, | |||
callback: impl FnMut(StoreContextMut<T>) -> Result<u64> + Send + Sync + 'static, | |||
callback: impl FnMut(StoreContextMut<T>) -> DeadlineResult + Send + Sync + 'static, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From an ergonomic perspective could this remain Result<DeadlineResult>
? (maybe with a bit of renaming)
That'll allow ?
to be used for fallible operations and matches how traps are handled elsewhere in the embedding API where it's the Err
of a Result
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be clear, this would remove the DeadlineResult::Trap
variant right? Since that would become the Result::Err
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's what I'm thinking
crates/wasmtime/src/store.rs
Outdated
None => Err(Trap::Interrupt.into()), | ||
Some(callback) => { | ||
let delta = match callback((&mut *self).as_context_mut()) { | ||
// Note: If the callback returns an error, we don't call it again. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any particular reason as to why? If something traps I could see it still being possible resuming other execution in the Store
which may be unrelated which could require the same callback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I could tell this was the existing behavior, but it seemed non-obvious so I added a comment. I don't think there's a strong argument for behaving this way and I agree that it could be useful to preserve the callback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once something in a store traps, you really aren't supposed to use the store again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok if this is preserving the existing behavior that's ok, but otherwise while you're here I think it might be good to "fix" this and put the callback back in place regardless of the result.
I agree that most of the time stores/instances/etc should be considered "poisoned" after trapping but that feels like a higher level concern which wouldn't affect a lower-level detail like this. Mostly in that we don't have anything official per-se about nullifying a store when it traps.
@@ -943,7 +941,7 @@ impl<T> Store<T> { | |||
/// for an introduction to epoch-based interruption. | |||
pub fn epoch_deadline_callback( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can the documentation for this method be updated to talk about how returning Yield
will panic if async_support
is disabled?
crates/wasmtime/src/store.rs
Outdated
DeadlineResult::Yield(delta) => { | ||
// Do the async yield. May return a trap if future was | ||
// canceled while we're yielded. | ||
self.async_yield_impl()?; | ||
delta | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In this case I believe that async_yield_impl
will panic internally if it's not configured right but technically that method's panic should be allowable to be a debug_assert!
or an unwrap_unchecked
or something like that. I think it'd be good to have a dedicated assert here for the use case that this covers where Yield
is returned but async isn't enabled or configured.
crates/wasmtime/src/store.rs
Outdated
@@ -230,6 +231,18 @@ enum CallHookInner<T> { | |||
Async(Box<dyn CallHookHandler<T> + Send + Sync>), | |||
} | |||
|
|||
/// What to do after returning from a deadline callback. | |||
pub enum DeadlineResult { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe
pub enum DeadlineResult { | |
pub enum DeadlineBehavior { |
to make Result<DeadlineBehavior>
?
Another option is to have this return a std::ops::ControlFlow
where the break is an anyhow::Error
and the continue is either a sync or async enum with a u64
payload. This would also allow for ?
usage. But my experience with ControlFlow
has not been super great.
e3c4a2f
to
fc75f91
Compare
I think I've addressed all your review comments. I've also added a brief note to the release notes, though I'm not sure if it needs to be louder since it's an API break? I'm hoping to get this into the next release, and I just realized today that's forking off on Monday. Sorry for not leaving a lot of time for review. |
When an epoch interruption deadline arrives, previously it was possible to yield to the async executor, or to invoke a callback on the wasm stack, but not both. This changes the API to allow callbacks to run and then request yielding to the async executor.
fc75f91
to
00b1c74
Compare
Subscribe to Label Actioncc @peterhuene
This issue or pull request has been labeled: "wasmtime:c-api", "wasmtime:docs"
Thus the following users have been cc'd because of the following labels:
To subscribe or unsubscribe from this label, edit the |
…6464) When an epoch interruption deadline arrives, previously it was possible to yield to the async executor, or to invoke a callback on the wasm stack, but not both. This changes the API to allow callbacks to run and then request yielding to the async executor.
When an epoch interruption deadline arrives, previously it was possible to yield to the async executor, or to invoke a callback on the wasm stack, but not both. This changes the API to allow callbacks to run and then request yielding to the async executor.
We discussed this in today's Wasmtime bi-weekly call and I believe this PR reflects the consensus from that discussion. But I'm open to bikeshedding the API.
I was tempted to revise the out-of-gas/fuel API to match the epoch interruption API as well, but decided not to do that in this PR.