-
-
Notifications
You must be signed in to change notification settings - Fork 667
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
Support Async/Await #376
Comments
Absolutely, async/await is something that we'd like to support eventually. One missing building block at this point seems to be closure support, because promises and timeouts usually involve functions that access variables from a parent scope, though it might be possible to work around this with globals. Might also be good to note that deferring an operation to let's say the network or the filesystem is something that only the host can do at this point, and this leads us to interoperability concerns with the host, where waiting for host-bindings, reference-types et al. might make sense. |
Is there a bug to track lambdas? It seems that this could be split into two or three useful partials:
#1 seems straight forward, #2 seems straight forward if translated like Java or C++ does it with synthetic functor classes. #3 is a little more difficult since you'd need to promote a local to heap storage, but I think #1 and #2 probably capture most of the functional style of programming I see in JS and TS, and cases like #3 could be temporarily worked around by the developer by boxing the local into a holder object, and relying on support for case #2 |
Something that might help here: Binaryen now has support for pausing and resuming wasm in the new "Bysyncify" feature. Basically you get some special functions to unwind/rewind the call stack, and then Bysyncify does everything else for you - that is, it will automatically rewrite all the wasm that needs to be rewritten so that it can save/restore locals and the call stack. This adds overhead as you'd expect, but it's surprisingly small both in size and speed (thanks to integration with the Binaryen optimizer). Attached is a wip blogpost (not public yet) with more details, including complete examples in pure wasm and in JS+wasm, and benchmarks. If you're interested to use this, let me know if I can help! |
Thanks for the hint (and for making it possible in the first place ofc)! There are multiple things that I think can make use of the bysyncify pass, like
The most important at this point seems to get something-exception-handling up, but I haven't thought about how feasible this would be with bysyncify yet. Would you say that makes sense? Also, there is the mention of "using the option bysyncify-imports to Bysyncify" which I think is not yet possible with the C-API, but that's certainly not a blocker and can be introduced with a PR, if it turns out we need it. Edit: Just a crazy idea, but if it turns out that bysyncify can help with preliminary exception handling, would it be possible to "polyfill" it on that basis? Like, make actual |
Yeah, we could add a pass to lower wasm exceptions into a polyfill basically. Bysyncify has some useful tools for that, like analyzing which calls would need to be instrumented, but the new pass wouldn't need to think about locals etc., so it should have even less overhead. I believe @aheejin will work on exceptions in binaryen soon. After that's done we can add a lowering/polyfill pass, should be straightforward (I can do it, if no one else wants to). |
Nice, that would be super useful! In the meantime we can start thinking about Promises over here I guess :) |
Interesting article about stateless and stateful (fiber) coroutines: |
For async / await which import / export to/from host we could use binaryen's Asyncify, but for internal async / awayt just generate stateless coroutines or generators via FSM approach similar to Python, C# and facebook's regenerator |
@MaxGraey are there examples somewhere showing the unwind/rewind being used in Assemblyscript? |
For host side I saw only this wrapper. In general, you might be inspired this blog post probably |
The co_await implementation in ASIO, could this be leveraged in emscripten? Which could then be leveraged by AssemblyScript? |
|
Isn't co_await part of C++20 coroutines? Not sure I understand. https://en.cppreference.com/w/cpp/language/coroutines
Maybe we're getting confused w/ the C++ fiber api? |
I updated prev comment. I ment routines from |
Hey guys. Can we at least support the async await syntax? Now that visitor-as has some great support for generating ast nodes, it might be fun to start transforming that syntax. |
@MaxGraey @dcodeIO If I'm understanding correctly, we simply need to implement the interfaces required by the the clang compiler, to get stackless coroutines using co_await in emscripten, right? And starting w/ the asio implementation might be a fast way of getting there? This implementation could then be leveraged by assemblyscript for async/await? Do we agree what's being said here is true? Or am I completely confused? |
The thinking is we could have a c++ microtask executor/scheduler, similar to javascript, which gets called/flushed periodically by a timer on the javascript side. In high performance cases it could be called by requestAnimationFrame. In doing things this way, assemblyscript could leverage clang/c++ co_await directly, right? @kripken would this work? |
I had no idea, co_await actually generates a state machine. Redpanda leverages this heavily. If this conversation comes out the way I hope, I'm wondering if assemblyscript could simply layer itself atop co_await? |
BTW, Gor is the guy who created co_await. |
My two cents, after working a lot with C++20 coros. A stackful coroutine is properly not the way to go with async/await. It is much closer to the stackless nature of the C++20 coros. But I don't think you'd want to support those either. I've just finished writing some asio python bindings (https://github.com/klemens-morgenstern/asio-py experimental stuff) that allow integration between asio awaitables (the C++20 coros) and python async functions. I was just looking to assemblyscript to see if I maybe can build something similar, i.e. an assembly-script asio-based runtime, e.g. as a light-weight node.js replacement (still looking for a fitting wasm runtime) My recommendation would be to decouple async as much as possible from the actual implementation. I.e. I would envision this, if assemblyscript can take the return value into account like this: import asio
async some_coro(sock : asio.ip.tcp.socke) : asio.awaitable<i32>
{
const bytes_written = await sock.async_write("Hello world!");
return bytes_written;
} By the same token one could provide a import {promise} from "my-runtime";
async some_coro(...) : promise<i32>; This would be modeled on the C++20 design, where the return type dictates the type of coroutine that'll be used. That way my node.js runtime could use the imports from node, while my asio solution would explicitly import another set of classes. If that is not possible, there needs to be a runtime that picks up the coro and execute it's SM. Python with asyncio does this explicitly: async def main():
print('hello')
await asyncio.sleep(1)
print('world')
asyncio.run(main()) I think this model would work too, if we add an implicit runtime-function that is not baked into the assembly script itself: async function foobar() : promise<i32>;
explicitly_scheduler_it_somewhere(foobar.bind()); // similar to the above python
foobar(); // calls special function, e.g. __implicitly_schedule_it_on_my_runtime That would allow me to plug assemblyscript into another runtime, though I reckon it might introduce some additional steps to glue that into the nodejs runtime. |
所以 ,2023了要怎么做呢,有点不太理解 |
A lot of existing typescript code relies on async/await. And while WASM doesn't have a concept of the JS event loop or generators, it seems like this could be supported in some form by first lowering to a style using switch/case like async/await leveling to ES3-style (See https://blog.mariusschulz.com/2016/12/09/typescript-2-1-async-await-for-es3-es5) in combination with an implementation of Promise in the runtime. Perhaps this code transformation could be copied from the TypeScript compiler, lowering it in the AST before compilation to WASM.
The resolve/reject functions would need to be exported to JS so that the JS could drive the resolution (e.g. Promise((resolve, reject) => {.... setTimeout(() => resolve()) }) would need to be rewritten by the developer so that resolve/reject are exported being passed through some kind of "AssemblyScriptPromise" class that can record when it's invoked and step the generator along. setTimeout is an external function which invokes the resolve)
Since interop with the web is invariably async, this would go a long way to reducing the impedance mismatch.
The text was updated successfully, but these errors were encountered: