-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
Currently as of today the Instance type in the wasmtime crate is not Send. 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 is Send and Sync along with most other types in wasmtime. We, in this world, get to decide whether we want Instance to be Send or not. As a quick note we fundamentally cannot make Instance 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, where Send 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
Rcsomewhere it's fundamentally neitherSendnorSync. Types likeGlobal, however, internally contain anRc. We may be able to fix this by only allowing acquisition of&GlobalfromInstance, 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
Callabletrait, and those values are stored within anInstance(transitively throughFuncand such). TheCallable, trait, however, does not requireSend, which means that, again,Instanceis 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 the Callable trait, but this also unfortunately is not a great API decision. That requires everyone, even those who don't need it, to implement the Send trait. That's surprisingly restrictive because there are legitimate cases where you don't want to implement Send 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> conditionally Send based on T, allowing users to enable an instance to be Send if they configure it accordingly, but also accomodating users who don't want to deal with Send and only want to work on one thread.
This "real solution", though, doesn't really make sense to me. What is T in Instance<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:
- I assert that it is a local maximum today (and likely beyond) for
Instanceto not beSend. - For
Instanceto beSend, I think it will require a type parameter, likeInstance<T>whereTis 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 being Send, and then getting as much mileage out of that as we can.