Skip to content

Emscripten fails on Node.js with PThreads and ES6 exports #18626

@LTLA

Description

@LTLA

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions