pchickey opened PR #3393 from pch/async_limiting
to main
:
<!--
Please ensure that the following steps are all taken care of before submitting
the PR.
[ ] This has been discussed in issue #..., or if not, please tell us why
here.[ ] A short description of what this does, why it is needed; if the
description becomes long, the matter should probably be discussed in an issue
first.[ ] This PR contains test cases, if meaningful.
- [ ] A reviewer from the core maintainer team has been assigned for this PR.
If you don't know who could review this, please indicate so. The list of
suggested reviewers on the right can help you.Please ensure all communication adheres to the code of conduct.
-->
alexcrichton submitted PR review.
alexcrichton submitted PR review.
alexcrichton created PR review comment:
I've commented more about this below but I'd ideally prefer to not have this since if you're asking for the store and someone forgot to configure it that's a bug rather than something we'll gracefully want to handle.
alexcrichton created PR review comment:
I think it's probably ok to skip the
limiter_
prefix on these methods (micro-nit)
alexcrichton created PR review comment:
This'll either need a different signature (taking
&mut self
) or need to beunsafe
because otherwise I don't think it's safe in Rust to go from&self
to&mut T
alexcrichton created PR review comment:
To prevent all consumers fo this trait from using
async_trait
I think it might be good to change this to have a default implementation of calling thememory_growing
method?I think it's a bit of a bummer to have both methods because if you're async you only want one and if you're not async you only want the other, but I don't know otherwise of a great way to manage this.
This does raise the question though of what happens when
Memory::new
(orMemory::grow
) is called when async is configured? Even some async embeddings don't necessarily want async memory growth so enabling async doesn't guarantee that this method is configured. Ideally it's an embedder bug ifMemory::new
is called when async memory growth is desired though.I think this might mean that we'll want a method here (perhaps default
false
?) which indicates which growth method to use (sync-or-async) and we dispatch with that on memory growth?
alexcrichton created PR review comment:
In addition to this change in this file I think we'll also want to add
Memory::new_async
andMemory::grow_async
both asasync
methods. That may actually be pretty tricky though because currently the async implementation ofblock_on
requires stack-switching and assumes we're on a wasm stack.Otherwise adding these methods though would require open-coding a bit the limit logic where we could directly
.await
the future in these functions returned by the limiter. That may be the best route here though? (should ideally only be a small amount of duplication in the runtime implementation to have a method and anasync
method)
alexcrichton created PR review comment:
Similar to
Memory
I think we'll want this to be&mut dyn Store
instead of anOption
alexcrichton created PR review comment:
With taking a store here instead of a limiter I think it's actually a bug in Wasmtime if we ever create a table/memory without a store because that means if there were a registered limiter we'd be skipping that. I think we probably want to change this constructor to take
&mut dyn Store
instead ofOption<_>
to reflect that and we can fix it if it ever trips a panic (I don't believe it will checking Wasmtime's current usage).
alexcrichton created PR review comment:
Does there need to be an async counterpart for this as well as
memory_grow_failed
? I figured the use case was "blocking" until there's enough memory to allow, but I'm not sure what sort of blocking work you'd want to do on growth failure.
alexcrichton created PR review comment:
That being said I'm also wary of this because anything that looks at the store should require that the store is set, so I'd ideally prefer to not have this method and if anyone needs to observe the store then they're on the path that already has the store set and it's safe to assert it's there.
alexcrichton created PR review comment:
I think we'll need to handle this because in this case a trap means that the future was dropped on the outside, so we need to propagate this trap to wasm to make wasm "abort" gracefully as well. Basically once the future is dropped we just need to clean it up quickly so the thinking is to propagate the error all the way back to the start.
alexcrichton created PR review comment:
I think changing this to not-an-option will also propagate out relatively far-ish. That's ok though because almost all instance allocation requests should have a store configured, only the initial blank module as well as host functions should skip their stores during construction, and the
Option
-handling is basically just here to make sure those two cases don't panic/segfault.
alexcrichton created PR review comment:
Ah yeah to expand on some thoughts from above, I don't think we'll want this conditional on the
async
cargo feature since that's on-by-default but rather some runtime configuration in the limiter itself which indicates which version to use.
pchickey submitted PR review.
pchickey created PR review comment:
Good point, I can probably leave this out. was keeping it in for completeness but I don't have a compelling use case
pchickey submitted PR review.
pchickey created PR review comment:
So, I tried having a default implementation, but it turns out that with async_trait those arent object-safe and rustc rejects them.
The other possibility is to have two public traits,
ResourceLimiter
andResourceLimiterAsync
, and an internalenum RLimiter { Sync(&mut dyn ResourceLimiter), Async(&mut dyn ResourceLimiterAsync) }
andStoreInner::limiter
has typeOption<Box<dyn FnMut(&mut T) -> RLimiter>>
and some chrome on the config methods to wrap the closure's return value in the Sync or Async variant.
pchickey submitted PR review.
pchickey created PR review comment:
Yep my initial constructor took just a
&mut dyn Store
and the option fell out of some complications elsewhere, and I hadn't thought that all the way through to indicate a bug in Wasmtime. I'll have to chase that out.
pchickey created PR review comment:
Good point, I meant to check this assumption by you. I forgot about the whole future dropped thing.
pchickey submitted PR review.
alexcrichton submitted PR review.
alexcrichton created PR review comment:
Ah ok then yeah I like your idea if we can't have defaulted
async fn
in traits, that seems reasonable to me and also solves the issue of async-runtime-still-uses-sync-limiter.
pchickey updated PR #3393 from pch/async_limiting
to main
.
pchickey submitted PR review.
pchickey created PR review comment:
This is done now!
pchickey updated PR #3393 from pch/async_limiting
to main
.
pchickey updated PR #3393 from pch/async_limiting
to main
.
pchickey updated PR #3393 from pch/async_limiting
to main
.
alexcrichton created PR review comment:
With this new signature it may also be worth mentioning in the safety documentation that this is only safe if
store: &mut dyn Store
is not used to get access to theMemory
that lives within the store. Technically you could safely, fromstore
, reachself
via an instance and get two mutable references. So long as the trait implementation ofStore
doesn't do that though this should be safe.
alexcrichton submitted PR review.
alexcrichton created PR review comment:
Ah this is a bummer, I tried to avoid this because if the instance didn't have any start function it would avoid the need for a fiber which can be somewhat expensive. I see though how this is required for memories/tables and limiting their growth. I think let's leave this as you have it here and if it's an issue in the future we can try to optimize the further if truly necessary (and it may not be at all necessary)
alexcrichton created PR review comment:
I think this can probably be removed now?
alexcrichton created PR review comment:
Yeah I think that this will want to be
unsafe
b/c there's no guarantee that thisStorePtr
wasn't used outside the original lifetime of the borrow.
alexcrichton created PR review comment:
This is one case where this can probably come up as a user error but we're not prepared to catch this panic and re-raise it across the boundary when we get back to wasm. Arguably we probably should
catch_panic
an intrinsic like this though because we're calling arbitrary user code limiting memory growth now that I think about it though.Would you be up for adding a
catch_panic
around this block (and the table version) which reraises the panic? (similar treatment fortable_grow
as well, and to be clear this isn't something new about this PR it's just something I'm realizing here. That wouldmake this.expect
safe and we could leave it as-is.
alexcrichton created PR review comment:
Ah yeah the meaning of this is that the future representing the execution of wasm was dropped by the host so the coroutine has been resumed and should "finish ASAP", so the trap here means "please stop executing code on the coroutine as fast as you can". To do that I think it'd be best to propagate the trap to wasm here, which should skip execution of all remaining wasm. I think that can be done by returning
Result<bool, Trap>
from thisStore
funtion most likely.
alexcrichton created PR review comment:
stray newline removal?
alexcrichton submitted PR review.
alexcrichton created PR review comment:
Could this (and other grow/new methods affected) have a
# Panics
section which indicates the scenario where a non-async store is used will cause a panic? Also it might be good to link to thestore
method which configures the async limiter to indicate that it's how this is intended to be used.
alexcrichton created PR review comment:
One thing I'll say though is that now that the inner loop of
run_async
andrun
are the same we could probably pull those into a shared method so they do precisely the same thing.
pchickey updated PR #3393 from pch/async_limiting
to main
.
pchickey updated PR #3393 from pch/async_limiting
to main
.
pchickey updated PR #3393 from pch/async_limiting
to main
.
alexcrichton submitted PR review.
alexcrichton created PR review comment:
Also, could you add a comment that the
catch_unwind
is specifically necessary here because we could run arbitrary user-provided code on growth?
alexcrichton submitted PR review.
alexcrichton created PR review comment:
Could this be structured with a
match
to avoid needing the.unwrap()
?
pchickey updated PR #3393 from pch/async_limiting
to main
.
pchickey updated PR #3393 from pch/async_limiting
to main
.
pchickey updated PR #3393 from pch/async_limiting
to main
.
pchickey updated PR #3393 from pch/async_limiting
to main
.
Last updated: Dec 23 2024 at 12:05 UTC