-
Notifications
You must be signed in to change notification settings - Fork 3.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
Support for node.js workers #6567
Comments
I had forgotten that someone had already filed a bug about this previously: #6332 In any case, we should make this work for node.js as well as browsers. |
Experimenting with this, it is blocked on nodejs/node#25630 - looks like in node a blocking main does not send postMessages to workers (or perhaps blocks all logging from them). |
Just to confirm: that's what happens. You can postMessage from main to worker, no problem, but The main thread won't process that string until control returns to the event loop and fixing that is not trivial. |
I see, thanks @bnoordhuis! Maybe we can work around it by using stdout/stderr directly? Or would that not work either? |
No, unfortunately. All stdio is mediated in Node's workers. |
I see, thanks. Ok, sadly I think that means we can't use node workers in a simple way here. |
I believe bleeding edge Node should have all the support necessary. |
Latest node may be enough, yeah. Last I tried it seemed possible, but getting logging working from a pthread (while the main is busy) was hard, so it was difficult to make progress. I have a wip branch here: https://github.com/emscripten-core/emscripten/tree/node-pthreads I may not have time for this myself in the near future. If someone has time to look at that that would be great, it might be a matter of finding some hack for debugging (like maybe printing to a file?), then fixing some minor issues. |
I looked at this a little more now, there is some progress in the branch. Overall I keep hitting the many small differences between a Web Worker and a Node Worker. For example the current blocker is that on the web we use |
@kripken Maybe I’m misunderstanding, but what’s a “global eval” in that context? |
It's something like function setup() {
eval('function foo() {}'); // normal eval
console.log(typeof foo); // ok
eval.call(null, 'function baz() {}'); // global eval
console.log(typeof baz); // ok
}
setup();
console.log(typeof foo); // bad!
console.log(typeof baz); // ok Turns out in node a normal eval does see Maybe there's a better way I'm missing? |
Okay, I see – thanks for the explanation. In that case, maybe doing something like we do for the REPL in Node.js is a good approach? Basically, we copy/provide Node’s special per-module variables, I think that’s something that makes sense when your goal is to have everything in the global scope, as it would be in a Web Worker? |
Interesting, thanks - so there's a way I can copy |
So, just to avoid any confusion, I’m basically talking about doing this inside the Worker: global.require = require;
eval.call(null, 'require("console").log("hello, world!")'); The catch is that this picks one |
Thanks @addaleax! That works great. However, we do have more things than
That prints |
@kripken I think that’s because |
@addaleax Yeah, replacing all those could work. We do have a bunch of them, though, so it's not obvious what to do here, and I worry about making the code less maintainable while still supporting both the web and node. I'll think some more on this and try to get back to it when I have time, but if someone else has a good idea of how to proceed that would be great too. |
@kripken If you do want everything to work like in the browser scope-wise and hav, you could probably just wrap it all in a global eval (or Node’s (Maybe I’m having a simplistic view of things here, but ultimately I’m invested in making this work, too) |
I think in nodejs module scope is different to global scope (I am not a nodejs programmer to be clear).
And then just changing all of the variable definitions in worker.js to global variables, instead of using var. Not a particularly elegant solution, but it does work (tested on a fairly complicated threaded application). |
A problem is that we want our builds to run on both the Web and Node.js. But it seems like in worker.js we need If there's no other option we can force such builds to be either Web or Node but not both, but this is the first time we've run into such a situation, so it would be something we need to consider carefully. |
@kripken A |
Oh thanks @addaleax, I missed the part about running Meanwhile I am refactoring this code in #9569 which will remove some of those globals, and may remove almost all the ones we need to share between the files. After that lands I'll get back to this and see how things look. |
Turns out much of the pthreads runtime was not closure-safe. This PR fixes that, using quoted strings as closure expects. While doing that I noticed that some of the things in place for pthreads + MODULARIZE would also help closure. In both cases the key thing is that worker.js, that loads the main JS file, is separate from it, so it needs proper interfaces and can't just rely on global variables (which in closure are minified in the main JS, and in MODULARIZE are in another scope). So I removed things like makeAsmExportAccessInPthread, makeAsmGlobalAccessInPthread, makeAsmExportAndGlobalAssignTargetInPthread which makes the code simpler. Because of that I was also able to remove a bunch of globals from worker.js which further simplifies things, and should also help node.js workers (where the global scope is very different, #6567). Most of this PR is straightforward according to the above notes. One slightly tricky thing is the stack setup, which I refactored to use a new Module.applyStackValues method that worker.js calls at the right time (as the stack is set up after the JS loads). Code size effects: Before this PR: 69,567 bytes. After this PR: 69,644. After this PR, plus closure: 34,909. So this adds only 77 bytes, while also making the code simpler + making it possible to run closure and decrease size by 50%. In addition, I have followup work to further reduce the non-closure code size too, so this regression will be fixed (and more).
I just had a quick look, and one other issue I ran into was that node.js workers don't support |
@VirtualTim Node.js currently doesn’t support |
Ok, after merging in that PR there is good news and bad news. The good news is that it looks like a simple program can work! It creates 3 pthreads and they each do some work and print out their progress :) The bad news is that it looks like a pthread exiting shuts down the whole node.js instance. Shutdown is done by throwing an exception, which on the web stays in the worker (it's logged to the console, but that's it), while in node it seems to halt the entire application,
and the node.js process exits with code 1. This is a problem since threads should call |
@kripken Would trying to call Otherwise, listening to the |
Excellent, thanks @addaleax! That works :) |
PR is finally up, with a first passing test! #9745 Thanks again for all the help here :) |
This ended up tricky because of various node/web differences, like the global scope, the event loop, APIs, etc. With help on #6567 I think this PR finally manages to make it work, at least for an initial "hello world" type test. Thanks to everyone on that issue and in particular @addaleax! This moves some code from shell.js into side .js files, so that we can use it in multiple places. Fixes #6567 but as we add more tests I'm sure we'll find more issues, this just shows basic functionality so far. We may not be able to easily reuse the existing browser tests as they have browser-specific things in them, but we can try.
Turns out much of the pthreads runtime was not closure-safe. This PR fixes that, using quoted strings as closure expects. While doing that I noticed that some of the things in place for pthreads + MODULARIZE would also help closure. In both cases the key thing is that worker.js, that loads the main JS file, is separate from it, so it needs proper interfaces and can't just rely on global variables (which in closure are minified in the main JS, and in MODULARIZE are in another scope). So I removed things like makeAsmExportAccessInPthread, makeAsmGlobalAccessInPthread, makeAsmExportAndGlobalAssignTargetInPthread which makes the code simpler. Because of that I was also able to remove a bunch of globals from worker.js which further simplifies things, and should also help node.js workers (where the global scope is very different, emscripten-core#6567). Most of this PR is straightforward according to the above notes. One slightly tricky thing is the stack setup, which I refactored to use a new Module.applyStackValues method that worker.js calls at the right time (as the stack is set up after the JS loads). Code size effects: Before this PR: 69,567 bytes. After this PR: 69,644. After this PR, plus closure: 34,909. So this adds only 77 bytes, while also making the code simpler + making it possible to run closure and decrease size by 50%. In addition, I have followup work to further reduce the non-closure code size too, so this regression will be fixed (and more).
This ended up tricky because of various node/web differences, like the global scope, the event loop, APIs, etc. With help on emscripten-core#6567 I think this PR finally manages to make it work, at least for an initial "hello world" type test. Thanks to everyone on that issue and in particular @addaleax! This moves some code from shell.js into side .js files, so that we can use it in multiple places. Fixes emscripten-core#6567 but as we add more tests I'm sure we'll find more issues, this just shows basic functionality so far. We may not be able to easily reuse the existing browser tests as they have browser-specific things in them, but we can try.
Work has been started on workers in node.js (nodejs/node#20876). The idea looks great, and we should make sure our pthreads code works there too. The API (which presumably isn't final) looks more or less like web workers, so it shouldn't be too hard to make it work.
The text was updated successfully, but these errors were encountered: