Skip to content
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

Ability to pause wasm execution and call back JavaScript (async JS support) #1294

Closed
amirouche opened this issue Aug 6, 2019 · 19 comments
Closed

Comments

@amirouche
Copy link

amirouche commented Aug 6, 2019

There is as of right now no way to interop JavaScript asynchronous behaviours (DOM events, XHR, ...) with web assembly program.

There is workarounds like emscripten life-cycle function pause and resume and its modern incarnation that will be based on binaryen asyncify but I think the latter only support top-level invocation.

As far as I understand, it would be enough to be able to pause the execution of the wasm program passing to a JavaScript callback on_pause what is on the stack. The control of execution will be given to JavaScript which will have the responsibility to call resume with an argument that will be added to the stack web assembly side.

Otherwise said, general continuations or continuable exception are not required.

The workaround I found to implement Scheme in web assembly is to rely on a trampoline that receive severals arguments via mutable globals, I think it could be better with pause.

Let me know what you think.

ref: #1171
ref: #1252

@amirouche amirouche changed the title Ability to pause wasm execution on call back JavaScript (async JS support) Ability to pause wasm execution and call back JavaScript (async JS support) Aug 6, 2019
@kripken
Copy link
Member

kripken commented Aug 6, 2019

@amirouche I'm not sure what you mean by Asyncify only supporting "top-level invocation"?

If I understood the rest correctly then Asyncify does support that: pause and resume wasm at any location, and JS can be given a callback that will resume the wasm from where it was paused. Here is an example: https://emscripten.org/docs/porting/asyncify.html#making-async-web-apis-behave-as-if-they-were-synchronous

Asyncify does have overhead, though (something like 50%), so it would be good to have native wasm support for stack switching/coroutines, etc. But that would just be an optimization.

Happy to help with this in your Scheme compiler if you want!

@amirouche
Copy link
Author

that would just be an optimization.

Yes.

I will experiment with asyncify and my trampoline and report back.

@elliott-wen
Copy link

I am working on coroutine in web assembly level.
My original goal is to allow multiple processes to run on a single web worker. To do that, I may use coroutine
That's to say, we have an independent stack, memory, file tables for each process. If we can successfully implement the coroutine mechaism, we may be able to interop asynchronous. But this may be an ambitious target as my compiler skill is pretty rusty now.

@amirouche
Copy link
Author

Coroutine support is orthogonal to the ability to pause / resume the whole wasm program.

@kripken
Copy link
Member

kripken commented Aug 11, 2019

Pause/resume is enough to implement coroutines, I think? For example Julia's wasm port implements coroutines using Asyncify's pause/resume.

@pmp-p
Copy link

pmp-p commented Aug 13, 2019

Pause/resume is enough to implement coroutines, I think? For example Julia's wasm port implements coroutines using Asyncify's pause/resume.

indeed, but (actually) :

  • losing dlopen / FFI or setjmp (no: -fPIC / SIDE_MODULE / MAIN_MODULE ) .

  • clang wasm backend still have problems with > 30kLOC when using jump tables.

@kripken
Copy link
Member

kripken commented Aug 13, 2019

@pmp-p I'm not sure I understand. How does asyncify interact with the issues you mention? Are there bugs filed on them?

@pmp-p
Copy link

pmp-p commented Aug 13, 2019

@kripken the context is porting VM over wasm VM @amirouche is on a scheme flavour and i'm on micropython/cpython :
but i can only speak for micropython actually : when trying to use a register vm instead of stack ( to gain ASYNCIFY behaviour on fastcomp ) i stumble on micropython/micropython#4993 (comment)

apparently related to bugs filed :

for setjmp need to drop dlopen and so FFI

asyncify (behaviour) is hard to get right on fastcomp because of pointers :

@kripken
Copy link
Member

kripken commented Aug 13, 2019

Thanks @pmp-p!

For that GOT.func issue, it sounds like in the linked bug they don't have a testcase yet and @sbc100 was asking for one. Perhaps you can add instructions from micropython to emscripten-core/emscripten#8995 ?

About the dynamic linking issue with indirect calls, yes, that is a known issue on fastcomp, but it should work on upstream.

About the pasted phi error, the full stack trace might show in which pass it happens, which helps triage. But it doesn't look like a known issue in fastcomp - that is in fastcomp as well, correct? In general we recommend people use upstream now, as it should avoid a bunch of issues, including this and the previous one mentioned here.

@tqchen
Copy link

tqchen commented May 15, 2020

I opened a related thread here WebAssembly/WASI#276

@amirouche
Copy link
Author

Thanks for the updates. For what it is worth, I did not implement the wasm backend in my scheme compiler mostly because it requires anyref support which is only supported in chrome / chromium (and possibly multiple table and / or mutable globals).

The approach taken in schism is to rely on tail-call but that as far as I understand does not help to deal with asynchronous calls (xhr) or resume execution on a DOM event.

I mocked the target web assembly code, the gist of the idea is to rely on continuous-passing-style (CPS) transformation and a trampoline, arguments, and multiple return values are passed as anyref in table(s) and primitives are implemented in JavaScript. The return value also encodes what is the continuation: another wasm call, "pause" to give back control to javascript or exit. It looks like the following in pseudo-code:

def trampoline(init):
    global thunk
    if init:
        thunk = scheme_program_entrypoint
        init = False
    while True:
        continuation = thunk()
        if continuation.is_pause:
            thunk = continuation.continuation
            return
        else:
            thunk = continuation.thunk

Mind the fact, that ultimately given the state of web assembly, the design I am thinking about only speeds up the trampoline which is based on my current compiler where most of the time is spent.

(On on the somewhat related note: maybe the overhead of switching between wasm and JavaScript because of anyref will be noticeable enough to make the trampoline-in-wasm optimisation useless, I do not know)

Maybe @eholk and @wingo can provide insightful feedback.

@amirouche
Copy link
Author

I read through the await proposal that is closed at the moment and part of the continuations issue. I even re-read this thread and figured I forgot to mention and stress the fact that I am/was trying to implement a scheme to wasm compiler.

await looks like what I was looking.

The trampoline trick I was mentioning in the previous comment requires at least one non-standard feature anyref.

Commit that introduce emscripten build in chibi scheme which definitly works great except slow startup time. In anycase, whether it is my toy compiler, Gambit Scheme or Chibi, my needs are already satisfied. I will continue to experiment, even try to build something useful, with Scheme in the browser.

I am closing the issue to avoid noise (and because, if there is a performance issue, it will be better handled by someone else).

Thanks!

@amirouche
Copy link
Author

Here is a fork of wasmtime that implements something similar to Scheme's call/cc: https://wasmk.github.io/

@amirouche
Copy link
Author

I continued my exploration of targeting web assembly to have Scheme in the browser (Chibi via emscripten produce a payload that is too big, Schism does not have call/cc and does not allow to interop with the JavaScript, Gambit targets only JavaScript).

I put together a web assembly module that demonstrate how to implement call/cc, while making it possible to pause wasm execution and resume it from JavaScript that works with nodejs 14 and nightly spidermonkey's jsshell.

My take away is that (call_with_pause $func) and (call_with_pause_indirect $table0 (i32.const 42)) will be helpful beyond Scheme specifics. In particular call/cc inside web assembly like wasm/k might be overkill for most cases, the development effort must weighted with the advantage it provides. In the case of Scheme, with the ability to do tail calls, it would not be necessary to have a trampoline to implement call/cc.

Similarly coroutines, and exceptions can be implemented with a global CPS transformation, but given that that transformation might be slow, adding support for call/cc inside wasm engines will cost a lot of effort and will probably have performance impact on all web assembly modules (whether or not they use call/cc).

My next goal is to produce a compiler that targets web assembly for a subset of Scheme that support call/cc, tail calls and the ability to pause and resume wasm. At which point comparing with asyncify will make sense. Spoiler: initial benchmarks are encouraging.

@fgmccabe
Copy link

There is an active group within the wasm community group that is looking at related topics. It would be good if you could join us.

@amirouche
Copy link
Author

I forwarded my question to w3c's wasm mailing list, see https://lists.w3.org/Archives/Public/public-webassembly/2021May/0000.html

@conrad-watt
Copy link
Contributor

conrad-watt commented May 17, 2021

@amirouche, I'd be interested to see whether you get any other replies on that list, but @fgmccabe was probably referring to the stack subgroup, which is meeting today and every two weeks after.

The meeting today in fact seems to be about coroutines in Scheme. I don't know if there's any special registration required to join, but maybe someone who attends the group can clarify (the zoom link seems public).

@amirouche
Copy link
Author

Thanks for the heads-up. I will be able to attend, if it is open.

@slindley
Copy link

You're welcome to join today's meeting. No special registration is required, but if you plan to attend regularly then you should (if you haven't already) sign up to the WebAssembly Community Group:

https://www.w3.org/community/webassembly/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants