-
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
Reconciling wasmtime::Instance
and Send
#793
Comments
I would very much like Instance to be Send. I don't necessarily see myself shipping an Instance explicitly between threads, but rather working with interfaces that expect things to be Send (e.g. a multi-threaded async runtime). On the Rc front, it seems like that can be pretty trivially solved by just switching to Arc, right? It seems like it'd be possible through some kind of marker trait design to parameterize over Send-ness. Particularly if it's an opaque, sealed trait it should allow the implementation to remain flexible - the associated types and methods on the trait can vary as the implementation wants. |
Yes it should be simple enough to switch I'm not sure I understand though what you mean by a marker trait, which presumably is targeted at this problem, can you elaborate what you're thinkign? |
Sure - roughly something like this: pub trait Style: Sealed {
// some public API associated types
type Func;
// but mostly hidden implementation-defined glue code
}
pub enum LocalStyle {}
impl Style for LocalStyle {
type Func = NonSendFunc;
// ...
}
pub enum SendStyle {}
impl Style for SendStyle {
type Func = SendFunc;
// ...
}
pub struct Instance<S>
where
S: Style,
{
// ....
} So the idea is that the Style parameter contains all of the types/functionality that would change depending on if the instance's environment is Send or not. Since it's a sealed trait you can maintain flexibility on the implementation side by adding and removing "private" APIs within the trait without worrying about breaking downstream impls since those can't exist. |
Ah thanks, makes sense. I feel though that it would be pretty difficult to compose crates if that were used. If only one crate at a time used That being said it's definitely a more concrete suggestion than my own, so it's likely a good starting point! |
I think you should be able to define some kind of inheritance (or at least conversion) to go from a SendFunc to a NonSendFunc, but yeah, the details of the API would need to be worked out for sure. |
Another option is to require funcs be send but provide some kind of escape hatches like proxy-access-to-the-original-thread and/or make-call-fallible-and-error-out-dynamically-if-we're-on-the-wrong-thread. |
I'm running into problems right now in a few downstream crates that I'm writing on top of wasmtime where I am having to jump through all kinds of hoops and create some pretty gnarly looking code because the wasmtime Instance isn't send. I can't even hide the instance under an |
How does #1363 affect this? |
It seems the most up-to-date explanation of the problem here may be at #777 (comment) |
At this point |
So basically there's no way to enforce in the type system that you move everything connected to the |
Correct, yeah, we haven't implemented a way of doing that yet. (I'm not sure if it exists for us) |
Thanks. Turns out Wasmer just has an And a comment saying |
It isn't safe. There is a Edit: missed that |
Can we run the wasi example on the async pattern now ? It looks like we just have async Func and Store, but we do not have async Linker. If we can, is there any tutorial to run wasi example on async mode? |
This commit writes a page of documentation for the Wasmtime book to serve as guidance for embedders looking to add multithreading with Wasmtime support. As always with any safe Rust API this reading is optional because you can't mis-use Wasmtime without `unsafe`, but I'm hoping that this documentation can serve as a point of reference for folks who want to add multithreading but are confused/annoyed that Wasmtime's types do not implement the `Send` and `Sync` traits. Closes bytecodealliance#793
* Document guidance around multithreading and Wasmtime This commit writes a page of documentation for the Wasmtime book to serve as guidance for embedders looking to add multithreading with Wasmtime support. As always with any safe Rust API this reading is optional because you can't mis-use Wasmtime without `unsafe`, but I'm hoping that this documentation can serve as a point of reference for folks who want to add multithreading but are confused/annoyed that Wasmtime's types do not implement the `Send` and `Sync` traits. Closes #793 * I can type
@Knight-X currently |
Currently as of today the
Instance
type in thewasmtime
crate is notSend
. This means that you cannot send it to other threads once it's created. There's a whole bunch of technical issues as to why this is, but for the sake of this issue I'd like to assume a world where we can instantly design everything the way we want.In our eventual world, for example,
Module
isSend
andSync
along with most other types inwasmtime
. We, in this world, get to decide whether we wantInstance
to beSend
or not. As a quick note we fundamentally cannot makeInstance
Sync
because that would allow concurently invoking the same function and (at least) concurrently modifying globals in a non-atomic fashion. This is what we've historically discussed, whereSend
is desired to send instances to other threads.I want to write down some reasoning, though, for why I think this may actually be extremely difficult if not impossible. There's two issues I see with trying to make
Instance
a send-able type:One is that all the externals today rely on reference counting. Once you put
Rc
somewhere it's fundamentally neitherSend
norSync
. Types likeGlobal
, however, internally contain anRc
. We may be able to fix this by only allowing acquisition of&Global
fromInstance
, however. This is the easier of the constraints to solve.Perhaps the more fundamental constraint though is how we want to deal with function imports. Today this is done with the
Callable
trait, and those values are stored within anInstance
(transitively throughFunc
and such). TheCallable
, trait, however, does not requireSend
, which means that, again,Instance
is not send. For the rest of this issue I'll discuss this problem.One possible solution to the latter point would be to add
Send
as a requirement to theCallable
trait, but this also unfortunately is not a great API decision. That requires everyone, even those who don't need it, to implement theSend
trait. That's surprisingly restrictive because there are legitimate cases where you don't want to implementSend
or deal with the overhead.The only real solution I know of is to somehow get generics into the picture. For example we can make
Instance<T>
conditionallySend
based onT
, allowing users to enable an instance to beSend
if they configure it accordingly, but also accomodating users who don't want to deal withSend
and only want to work on one thread.This "real solution", though, doesn't really make sense to me. What is
T
inInstance<T>
? Naively it's basically "the import object" but how can we express this? Is there a way we can create a trait to represent this?The tl;dr; of this issue is basically:
Instance
to not beSend
.Instance
to beSend
, I think it will require a type parameter, likeInstance<T>
whereT
is the "import object" with some trait to make it work. I haven't the foggiest though how this trait would be designed that satisfies all our constraints for possible optimizations and such.As a result, I'd like to propose that we commit to, for now, the
Instance
type not beingSend
, and then getting as much mileage out of that as we can.The text was updated successfully, but these errors were encountered: