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 DetailsThis 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 |
alexcrichton
left a comment
There was a problem hiding this comment.
Mind adding some tests as well?
| 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.
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.
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.
Yeah that's what I'm thinking
| 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.
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.
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.
Once something in a store traps, you really aren't supposed to use the store again.
There was a problem hiding this comment.
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.
Can the documentation for this method be updated to talk about how returning Yield will panic if async_support is disabled?
| 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.
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.
| } | ||
|
|
||
| /// What to do after returning from a deadline callback. | ||
| pub enum DeadlineResult { |
There was a problem hiding this comment.
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 DetailsThis 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.