1'use strict'; 2 3// In worker threads, execute the script sent through the 4// message port. 5 6const { 7 ObjectDefineProperty, 8} = primordials; 9 10const { 11 patchProcessObject, 12 setupCoverageHooks, 13 setupInspectorHooks, 14 setupWarningHandler, 15 setupDebugEnv, 16 initializeDeprecations, 17 initializeWASI, 18 initializeCJSLoader, 19 initializeESMLoader, 20 initializeFrozenIntrinsics, 21 initializeReport, 22 loadPreloadModules, 23 setupTraceCategoryState 24} = require('internal/bootstrap/pre_execution'); 25 26const { 27 threadId, 28 getEnvMessagePort 29} = internalBinding('worker'); 30 31const workerIo = require('internal/worker/io'); 32const { 33 messageTypes: { 34 // Messages that may be received by workers 35 LOAD_SCRIPT, 36 // Messages that may be posted from workers 37 UP_AND_RUNNING, 38 ERROR_MESSAGE, 39 COULD_NOT_SERIALIZE_ERROR, 40 // Messages that may be either received or posted 41 STDIO_PAYLOAD, 42 STDIO_WANTS_MORE_DATA, 43 }, 44 kStdioWantsMoreDataCallback 45} = workerIo; 46 47const { 48 onGlobalUncaughtException 49} = require('internal/process/execution'); 50 51const publicWorker = require('worker_threads'); 52let debug = require('internal/util/debuglog').debuglog('worker', (fn) => { 53 debug = fn; 54}); 55 56const assert = require('internal/assert'); 57 58patchProcessObject(); 59setupInspectorHooks(); 60setupDebugEnv(); 61 62setupWarningHandler(); 63 64// Since worker threads cannot switch cwd, we do not need to 65// overwrite the process.env.NODE_V8_COVERAGE variable. 66if (process.env.NODE_V8_COVERAGE) { 67 setupCoverageHooks(process.env.NODE_V8_COVERAGE); 68} 69 70debug(`[${threadId}] is setting up worker child environment`); 71 72// Set up the message port and start listening 73const port = getEnvMessagePort(); 74 75// If the main thread is spawned with env NODE_CHANNEL_FD, it's probably 76// spawned by our child_process module. In the work threads, mark the 77// related IPC properties as unavailable. 78if (process.env.NODE_CHANNEL_FD) { 79 const workerThreadSetup = require('internal/process/worker_thread_only'); 80 ObjectDefineProperty(process, 'channel', { 81 enumerable: false, 82 get: workerThreadSetup.unavailable('process.channel') 83 }); 84 85 ObjectDefineProperty(process, 'connected', { 86 enumerable: false, 87 get: workerThreadSetup.unavailable('process.connected') 88 }); 89 90 process.send = workerThreadSetup.unavailable('process.send()'); 91 process.disconnect = 92 workerThreadSetup.unavailable('process.disconnect()'); 93} 94 95port.on('message', (message) => { 96 if (message.type === LOAD_SCRIPT) { 97 port.unref(); 98 const { 99 argv, 100 cwdCounter, 101 filename, 102 doEval, 103 workerData, 104 publicPort, 105 manifestSrc, 106 manifestURL, 107 hasStdin 108 } = message; 109 110 setupTraceCategoryState(); 111 initializeReport(); 112 if (manifestSrc) { 113 require('internal/process/policy').setup(manifestSrc, manifestURL); 114 } 115 initializeDeprecations(); 116 initializeWASI(); 117 initializeCJSLoader(); 118 initializeESMLoader(); 119 120 const CJSLoader = require('internal/modules/cjs/loader'); 121 assert(!CJSLoader.hasLoadedAnyUserCJSModule); 122 loadPreloadModules(); 123 initializeFrozenIntrinsics(); 124 if (argv !== undefined) { 125 process.argv = process.argv.concat(argv); 126 } 127 publicWorker.parentPort = publicPort; 128 publicWorker.workerData = workerData; 129 130 // The counter is only passed to the workers created by the main thread, not 131 // to workers created by other workers. 132 let cachedCwd = ''; 133 const originalCwd = process.cwd; 134 135 process.cwd = function() { 136 cachedCwd = originalCwd(); 137 return cachedCwd; 138 }; 139 workerIo.sharedCwdCounter = cwdCounter; 140 141 if (!hasStdin) 142 process.stdin.push(null); 143 144 debug(`[${threadId}] starts worker script ${filename} ` + 145 `(eval = ${eval}) at cwd = ${process.cwd()}`); 146 port.postMessage({ type: UP_AND_RUNNING }); 147 if (doEval) { 148 const { evalScript } = require('internal/process/execution'); 149 const name = '[worker eval]'; 150 // This is necessary for CJS module compilation. 151 // TODO: pass this with something really internal. 152 ObjectDefineProperty(process, '_eval', { 153 configurable: true, 154 enumerable: true, 155 value: filename, 156 }); 157 process.argv.splice(1, 0, name); 158 evalScript(name, filename); 159 } else { 160 // script filename 161 // runMain here might be monkey-patched by users in --require. 162 // XXX: the monkey-patchability here should probably be deprecated. 163 process.argv.splice(1, 0, filename); 164 CJSLoader.Module.runMain(filename); 165 } 166 } else if (message.type === STDIO_PAYLOAD) { 167 const { stream, chunks } = message; 168 for (const { chunk, encoding } of chunks) 169 process[stream].push(chunk, encoding); 170 } else { 171 assert( 172 message.type === STDIO_WANTS_MORE_DATA, 173 `Unknown worker message type ${message.type}` 174 ); 175 const { stream } = message; 176 process[stream][kStdioWantsMoreDataCallback](); 177 } 178}); 179 180function workerOnGlobalUncaughtException(error, fromPromise) { 181 debug(`[${threadId}] gets uncaught exception`); 182 let handled = false; 183 try { 184 handled = onGlobalUncaughtException(error, fromPromise); 185 } catch (e) { 186 error = e; 187 } 188 debug(`[${threadId}] uncaught exception handled = ${handled}`); 189 190 if (handled) { 191 return true; 192 } 193 194 let serialized; 195 try { 196 const { serializeError } = require('internal/error_serdes'); 197 serialized = serializeError(error); 198 } catch {} 199 debug(`[${threadId}] uncaught exception serialized = ${!!serialized}`); 200 if (serialized) 201 port.postMessage({ 202 type: ERROR_MESSAGE, 203 error: serialized 204 }); 205 else 206 port.postMessage({ type: COULD_NOT_SERIALIZE_ERROR }); 207 208 const { clearAsyncIdStack } = require('internal/async_hooks'); 209 clearAsyncIdStack(); 210 211 process.exit(); 212} 213 214// Patch the global uncaught exception handler so it gets picked up by 215// node::errors::TriggerUncaughtException(). 216process._fatalException = workerOnGlobalUncaughtException; 217 218markBootstrapComplete(); 219 220port.start(); 221