Skip to content

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

Closed
@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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions