nstdlib-nightly
Version:
Node.js standard library converted to runtime-agnostic ES modules.
256 lines (223 loc) • 7.79 kB
JavaScript
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/main/worker_thread.js
import * as __hoisted_worker_threads__ from "nstdlib/lib/worker_threads";
import * as __hoisted_internal_worker__ from "nstdlib/lib/internal/worker";
import * as __hoisted_internal_modules_esm_utils__ from "nstdlib/lib/internal/modules/esm/utils";
import * as __hoisted_internal_process_execution__ from "nstdlib/lib/internal/process/execution";
import * as __hoisted_internal_modules_cjs_loader__ from "nstdlib/lib/internal/modules/cjs/loader";
import * as __hoisted_internal_error_serdes__ from "nstdlib/lib/internal/error_serdes";
import * as __hoisted_internal_async_hooks__ from "nstdlib/lib/internal/async_hooks";
import {
prepareWorkerThreadExecution,
setupUserModules,
markBootstrapComplete,
} from "nstdlib/lib/internal/process/pre_execution";
import { threadId, getEnvMessagePort } from "nstdlib/stub/binding/worker";
import * as workerIo from "nstdlib/lib/internal/worker/io";
import { setupMainThreadPort } from "nstdlib/lib/internal/worker/messaging";
import { onGlobalUncaughtException } from "nstdlib/lib/internal/process/execution";
import * as assert from "nstdlib/lib/internal/assert";
import { exitCodes as __exitCodes__ } from "nstdlib/stub/binding/errors";
import * as __hoisted_internal_process_worker_thread_only__ from "nstdlib/lib/internal/process/worker_thread_only";
// In worker threads, execute the script sent through the
// message port.
const {
messageTypes: {
// Messages that may be received by workers
LOAD_SCRIPT,
// Messages that may be posted from workers
UP_AND_RUNNING,
ERROR_MESSAGE,
COULD_NOT_SERIALIZE_ERROR,
// Messages that may be either received or posted
STDIO_PAYLOAD,
STDIO_WANTS_MORE_DATA,
},
kStdioWantsMoreDataCallback,
} = workerIo;
const { kGenericUserError } = __exitCodes__;
prepareWorkerThreadExecution();
{
/* debug */
}
// Set up the message port and start listening
const port = getEnvMessagePort();
// If the main thread is spawned with env NODE_CHANNEL_FD, it's probably
// spawned by our child_process module. In the work threads, mark the
// related IPC properties as unavailable.
if (process.env.NODE_CHANNEL_FD) {
const workerThreadSetup = __hoisted_internal_process_worker_thread_only__;
Object.defineProperty(process, "channel", {
__proto__: null,
enumerable: false,
get: workerThreadSetup.unavailable("process.channel"),
});
Object.defineProperty(process, "connected", {
__proto__: null,
enumerable: false,
get: workerThreadSetup.unavailable("process.connected"),
});
process.send = workerThreadSetup.unavailable("process.send()");
process.disconnect = workerThreadSetup.unavailable("process.disconnect()");
}
port.on("message", (message) => {
if (message.type === LOAD_SCRIPT) {
port.unref();
const {
argv,
cwdCounter,
doEval,
environmentData,
filename,
hasStdin,
publicPort,
workerData,
mainThreadPort,
} = message;
if (doEval !== "internal") {
if (argv !== undefined) {
Array.prototype.push.apply(process.argv, argv);
}
const publicWorker = __hoisted_worker_threads__;
publicWorker.parentPort = publicPort;
publicWorker.workerData = workerData;
}
__hoisted_internal_worker__.assignEnvironmentData(environmentData);
setupMainThreadPort(mainThreadPort);
if (SharedArrayBuffer !== undefined) {
// The counter is only passed to the workers created by the main thread,
// not to workers created by other workers.
let cachedCwd = "";
let lastCounter = -1;
const originalCwd = process.cwd;
process.cwd = function () {
const currentCounter = AtomicsLoad(cwdCounter, 0);
if (currentCounter === lastCounter) return cachedCwd;
lastCounter = currentCounter;
cachedCwd = originalCwd();
return cachedCwd;
};
workerIo.sharedCwdCounter = cwdCounter;
}
const isLoaderWorker =
doEval === "internal" &&
filename === __hoisted_internal_modules_esm_utils__.loaderWorkerId;
// Disable custom loaders in loader worker.
setupUserModules(isLoaderWorker);
if (!hasStdin) process.stdin.push(null);
{
/* debug */
}
port.postMessage({ type: UP_AND_RUNNING });
switch (doEval) {
case "internal": {
// Create this WeakMap in js-land because V8 has no C++ API for WeakMap.
require("binding/module_wrap").callbackMap = new WeakMap();
require(filename)(workerData, publicPort);
break;
}
case "classic": {
const { evalScript } = __hoisted_internal_process_execution__;
const name = "[worker eval]";
// This is necessary for CJS module compilation.
// TODO: pass this with something really internal.
Object.defineProperty(process, "_eval", {
__proto__: null,
configurable: true,
enumerable: true,
value: filename,
});
Array.prototype.splice.call(process.argv, 1, 0, name);
((...args) => globalThis.evalScript(...args))(name, filename);
break;
}
case "module": {
const { evalModuleEntryPoint } = __hoisted_internal_process_execution__;
Promise.prototype.then.call(
((...args) => globalThis.evalModuleEntryPoint(...args))(filename),
undefined,
(e) => {
workerOnGlobalUncaughtException(e, true);
},
);
break;
}
default: {
// script filename
// runMain here might be monkey-patched by users in --require.
// XXX: the monkey-patchability here should probably be deprecated.
Array.prototype.splice.call(process.argv, 1, 0, filename);
const CJSLoader = __hoisted_internal_modules_cjs_loader__;
CJSLoader.Module.runMain(filename);
break;
}
}
} else if (message.type === STDIO_PAYLOAD) {
const { stream, chunks } = message;
Array.prototype.forEach.call(chunks, ({ chunk, encoding }) => {
process[stream].push(chunk, encoding);
});
} else {
assert(
message.type === STDIO_WANTS_MORE_DATA,
`Unknown worker message type ${message.type}`,
);
const { stream } = message;
process[stream][kStdioWantsMoreDataCallback]();
}
});
function workerOnGlobalUncaughtException(error, fromPromise) {
{
/* debug */
}
let handled = false;
let handlerThrew = false;
try {
handled = onGlobalUncaughtException(error, fromPromise);
} catch (e) {
error = e;
handlerThrew = true;
}
{
/* debug */
}
if (handled) {
return true;
}
if (!process._exiting) {
try {
process._exiting = true;
process.exitCode = kGenericUserError;
if (!handlerThrew) {
process.emit("exit", process.exitCode);
}
} catch {
// Continue regardless of error.
}
}
let serialized;
try {
const { serializeError } = __hoisted_internal_error_serdes__;
serialized = serializeError(error);
} catch {
// Continue regardless of error.
}
{
/* debug */
}
if (serialized)
port.postMessage({
type: ERROR_MESSAGE,
error: serialized,
});
else port.postMessage({ type: COULD_NOT_SERIALIZE_ERROR });
const { clearAsyncIdStack } = __hoisted_internal_async_hooks__;
clearAsyncIdStack();
process.exit();
}
// Patch the global uncaught exception handler so it gets picked up by
// node::errors::TriggerUncaughtException().
process._fatalException = workerOnGlobalUncaughtException;
markBootstrapComplete();
// Necessary to reset RegExp statics before user code runs.
RegExp.prototype.exec.call(/^/, "");
port.start();