Description
tl;dr Compiling with ES6, PThreads and Node options produces a not-quite-right *.worker.js
file.
I have some C++ code that uses PThreads (greatly simplified below, let's call it dummy.cpp
):
#include <thread>
#include <vector>
#include <emscripten/bind.h>
double executor(int nthreads) {
std::vector<std::thread> workers;
workers.reserve(nthreads);
int first = 0;
std::vector<double> output(nthreads);
for (int w = 0; w < nthreads; ++w) {
workers.emplace_back([&](int w) -> void { output[w] = w; }, w);
}
for (int w = 0; w < nthreads; ++w) {
workers[w].join();
}
double accumulated = 0;
for (auto o : output) { accumulated += o; }
return accumulated;
}
EMSCRIPTEN_BINDINGS(xxxx) {
emscripten::function("executor", &executor);
}
I compile this for Node.js with ES6 export, using Emscripten version 3.1.31 (e883361):
emcc -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE=4 -sENVIRONMENT=node -sEXPORT_NAME=loadme --bind -sEXPORT_ES6 dummy.cpp -o dummy.js
I create the following script (test.js
) - I also add a package.json
with "type": "module"
to treat it as ES6:
import loadme from "./dummy.js";
// Initializing this.
let module = await loadme();
console.log(module.executor(5));
And then run it (using Node v16.19.0), which throws the following error:
$ node --experimental-vm-modules test.js
worker sent an error! undefined:undefined: require is not defined in ES module scope, you can use import instead
This file is being treated as an ES module because it has a '.js' file extension and '/home/luna/Code/js/node-es6-pthreads/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
file:///home/luna/Code/js/node-es6-pthreads/dummy.js:169
throw ex;
^
ReferenceError [Error]: require is not defined in ES module scope, you can use import instead
This file is being treated as an ES module because it has a '.js' file extension and '/home/luna/Code/js/node-es6-pthreads/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
at file:///home/luna/Code/js/node-es6-pthreads/dummy.worker.js:20:27
at ModuleJob.run (node:internal/modules/esm/module_job:193:25)
at async Promise.all (index 0)
at async ESMLoader.import (node:internal/modules/esm/loader:530:24)
at async loadESM (node:internal/process/esm_loader:91:5)
at async handleMainPromise (node:internal/modules/run_main:65:12)
And indeed, if I look at dummy.worker.js
, there are a few require
statements in there. There is also a mysterious __filename
variable that is not defined anywhere in this file. Seems like some extra statements are required at the start:
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
import { URL } from 'url';
const __filename = new URL('', import.meta.url).pathname;
Once I add them, the script will happily run:
$ node --experimental-vm-modules test.js
10
I couldn't find any information here about not combining PThreads + ES6 + Node, so I figured that this behavior was a bug. Bit surprised that no one's reported this before, actually; I suppose ES6 hasn't really caught on with Node users yet.