Description
Summary
Make it possible to return from a VM even though the script in there is still not complete.
Expected use case
We're running RustPython within a WASM module in a WebWorker, so it's essentially running in it's own dedicated thread. The Script therein has the capability to send messages to other WebWorkers via channels and other WebWorkers can send back their responses. The Responses will normally trigger an event handler (onmessage) in the Webworker so that the messages can be processed.
Due to the single-threaded nature of JavaScript that event handler will never be executed unless we give back control to the underlying runtime. In our case the would mean we need to somehow return from the WASM-call and thus interrupting the running Python VM.
Possible approaches
I could imagine three ways to support temporary interruption:
Preemptively
After calling enter the underlying VM executes a bunch of instructions (maybe a fixed number or with a timeout) and returns with a value indicating the execution is not complete and can be resumed (e.g. a continuation token). The caller can then perform other duties and resume the execution later (e.g. by calling a new resume-method)
Cooperatively
The Python code can call some built-in function (e.g. await, yield) allowing the VM to return in mid-execution. Otherwise this is the same as above
async
The enter-method becomes async and calls into a #[pyfunction] will be async as well. This is similar to the cooperative approach, but permits easier integration with Rusts async ecosystem
Alternatives
I'll continue my research whether this can be solved without altering RustPython. So far I could think of the following workarounds:
- maybe wasm_bindgen supports some feature to interrupt the execution of a WASM module in a way that makes it possible to temporarily yield to the JavaScript-Runtime.
- Instead of using JavaCript-Channels I could try to build my own data-exchange framework based on SharedMemory and Atomics. So far this has been an ergonomic nightmare but it should work in theory.
- Use multithreading in WASM. Last time I tried this I ended up with a very fragile setup that was very hard to run reliably. The ergonomics from the Rust side would be very good, though. Maybe things have improved in the meantime.
Here is an example how wasmtime solves this: https://docs.rs/wasmtime/latest/wasmtime/struct.Config.html#method.consume_fuel
BTW: thanks a lot for this awesome crate ❤️ impressive work!

