summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuben Bridgewater <ruben@bridgewater.de>2019-04-14 17:34:08 +0200
committerMichaël Zasso <targos@protonmail.com>2019-05-05 12:40:37 +0200
commit91b7f5e10339644027f3050d468eca819e58830c (patch)
treed6c53bc42f9c0793358efb95cebd96ea0b16d721
parent7bbf95109567bfbf75b655e4c81679e914c3c036 (diff)
downloadnode-new-91b7f5e10339644027f3050d468eca819e58830c.tar.gz
process: improve cwd performance
This caches the current working directory and only updates the variable if `process.chdir()` is called. PR-URL: https://github.com/nodejs/node/pull/27224 Reviewed-By: John-David Dalton <john.david.dalton@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Michaël Zasso <targos@protonmail.com> Backport-PR-URL: https://github.com/nodejs/node/pull/27483
-rw-r--r--lib/internal/bootstrap/node.js3
-rw-r--r--lib/internal/main/worker_thread.js20
-rw-r--r--lib/internal/process/main_thread_only.js17
-rw-r--r--lib/internal/worker.js19
-rw-r--r--test/parallel/test-process-abort.js12
-rw-r--r--test/parallel/test-process-chdir.js5
-rw-r--r--test/parallel/test-worker-process-cwd.js44
7 files changed, 109 insertions, 11 deletions
diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js
index f00e1c2fbd..321bfabe02 100644
--- a/lib/internal/bootstrap/node.js
+++ b/lib/internal/bootstrap/node.js
@@ -73,6 +73,7 @@ if (isMainThread) {
const wrapped = mainThreadSetup.wrapProcessMethods(rawMethods);
process.umask = wrapped.umask;
process.chdir = wrapped.chdir;
+ process.cwd = wrapped.cwd;
// TODO(joyeecheung): deprecate and remove these underscore methods
process._debugProcess = rawMethods._debugProcess;
@@ -86,11 +87,11 @@ if (isMainThread) {
process.abort = workerThreadSetup.unavailable('process.abort()');
process.chdir = workerThreadSetup.unavailable('process.chdir()');
process.umask = wrapped.umask;
+ process.cwd = rawMethods.cwd;
}
// Set up methods on the process object for all threads
{
- process.cwd = rawMethods.cwd;
process.dlopen = rawMethods.dlopen;
process.uptime = rawMethods.uptime;
diff --git a/lib/internal/main/worker_thread.js b/lib/internal/main/worker_thread.js
index 46152f6742..932f282a1b 100644
--- a/lib/internal/main/worker_thread.js
+++ b/lib/internal/main/worker_thread.js
@@ -25,6 +25,7 @@ const {
getEnvMessagePort
} = internalBinding('worker');
+const workerIo = require('internal/worker/io');
const {
messageTypes: {
// Messages that may be received by workers
@@ -38,7 +39,7 @@ const {
STDIO_WANTS_MORE_DATA,
},
kStdioWantsMoreDataCallback
-} = require('internal/worker/io');
+} = workerIo;
const {
fatalException: originalFatalException
@@ -89,6 +90,7 @@ if (process.env.NODE_CHANNEL_FD) {
port.on('message', (message) => {
if (message.type === LOAD_SCRIPT) {
const {
+ cwdCounter,
filename,
doEval,
workerData,
@@ -111,6 +113,22 @@ port.on('message', (message) => {
publicWorker.parentPort = publicPort;
publicWorker.workerData = workerData;
+ // 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 = Atomics.load(cwdCounter, 0);
+ if (currentCounter === lastCounter)
+ return cachedCwd;
+ lastCounter = currentCounter;
+ cachedCwd = originalCwd();
+ return cachedCwd;
+ };
+ workerIo.sharedCwdCounter = cwdCounter;
+
if (!hasStdin)
process.stdin.push(null);
diff --git a/lib/internal/process/main_thread_only.js b/lib/internal/process/main_thread_only.js
index 358e2b37b0..0cb3edbf9a 100644
--- a/lib/internal/process/main_thread_only.js
+++ b/lib/internal/process/main_thread_only.js
@@ -20,9 +20,15 @@ const { signals } = internalBinding('constants').os;
// The execution of this function itself should not cause any side effects.
function wrapProcessMethods(binding) {
+ // Cache the working directory to prevent lots of lookups. If the working
+ // directory is changed by `chdir`, it'll be updated.
+ let cachedCwd = '';
+
function chdir(directory) {
validateString(directory, 'directory');
- return binding.chdir(directory);
+ binding.chdir(directory);
+ // Mark cache that it requires an update.
+ cachedCwd = '';
}
function umask(mask) {
@@ -32,9 +38,16 @@ function wrapProcessMethods(binding) {
return binding.umask(mask);
}
+ function cwd() {
+ if (cachedCwd === '')
+ cachedCwd = binding.cwd();
+ return cachedCwd;
+ }
+
return {
chdir,
- umask
+ umask,
+ cwd
};
}
diff --git a/lib/internal/worker.js b/lib/internal/worker.js
index e9d961f35a..fc2a5eceed 100644
--- a/lib/internal/worker.js
+++ b/lib/internal/worker.js
@@ -1,5 +1,7 @@
'use strict';
+/* global SharedArrayBuffer */
+
const { Object } = primordials;
const EventEmitter = require('events');
@@ -16,6 +18,7 @@ const {
const { validateString } = require('internal/validators');
const { getOptionValue } = require('internal/options');
+const workerIo = require('internal/worker/io');
const {
drainMessagePort,
MessageChannel,
@@ -26,8 +29,8 @@ const {
kStdioWantsMoreDataCallback,
setupPortReferencing,
ReadableWorkerStdio,
- WritableWorkerStdio,
-} = require('internal/worker/io');
+ WritableWorkerStdio
+} = workerIo;
const { deserializeError } = require('internal/error-serdes');
const { pathToFileURL } = require('url');
@@ -50,6 +53,17 @@ const kParentSideStdio = Symbol('kParentSideStdio');
const SHARE_ENV = Symbol.for('nodejs.worker_threads.SHARE_ENV');
const debug = require('internal/util/debuglog').debuglog('worker');
+let cwdCounter;
+
+if (isMainThread) {
+ cwdCounter = new Uint32Array(new SharedArrayBuffer(4));
+ const originalChdir = process.chdir;
+ process.chdir = function(path) {
+ Atomics.add(cwdCounter, 0, 1);
+ originalChdir(path);
+ };
+}
+
class Worker extends EventEmitter {
constructor(filename, options = {}) {
super();
@@ -131,6 +145,7 @@ class Worker extends EventEmitter {
type: messageTypes.LOAD_SCRIPT,
filename,
doEval: !!options.eval,
+ cwdCounter: cwdCounter || workerIo.sharedCwdCounter,
workerData: options.workerData,
publicPort: port2,
manifestSrc: getOptionValue('--experimental-policy') ?
diff --git a/test/parallel/test-process-abort.js b/test/parallel/test-process-abort.js
new file mode 100644
index 0000000000..665e1399a3
--- /dev/null
+++ b/test/parallel/test-process-abort.js
@@ -0,0 +1,12 @@
+'use strict';
+
+const common = require('../common');
+const assert = require('assert');
+
+if (!common.isMainThread)
+ common.skip('process.abort() is not available in Workers');
+
+// Check that our built-in methods do not have a prototype/constructor behaviour
+// if they don't need to. This could be tested for any of our C++ methods.
+assert.strictEqual(process.abort.prototype, undefined);
+assert.throws(() => new process.abort(), TypeError);
diff --git a/test/parallel/test-process-chdir.js b/test/parallel/test-process-chdir.js
index 005e17fac2..e66d366fb7 100644
--- a/test/parallel/test-process-chdir.js
+++ b/test/parallel/test-process-chdir.js
@@ -42,8 +42,3 @@ const err = {
};
common.expectsError(function() { process.chdir({}); }, err);
common.expectsError(function() { process.chdir(); }, err);
-
-// Check that our built-in methods do not have a prototype/constructor behaviour
-// if they don't need to. This could be tested for any of our C++ methods.
-assert.strictEqual(process.cwd.prototype, undefined);
-assert.throws(() => new process.cwd(), TypeError);
diff --git a/test/parallel/test-worker-process-cwd.js b/test/parallel/test-worker-process-cwd.js
new file mode 100644
index 0000000000..dec70ac07c
--- /dev/null
+++ b/test/parallel/test-worker-process-cwd.js
@@ -0,0 +1,44 @@
+'use strict';
+const assert = require('assert');
+const common = require('../common');
+const { Worker, isMainThread, parentPort } = require('worker_threads');
+
+// Do not use isMainThread directly, otherwise the test would time out in case
+// it's started inside of another worker thread.
+if (!process.env.HAS_STARTED_WORKER) {
+ process.env.HAS_STARTED_WORKER = '1';
+ if (!isMainThread) {
+ common.skip('This test can only run as main thread');
+ }
+ const w = new Worker(__filename);
+ process.chdir('..');
+ w.on('message', common.mustCall((message) => {
+ assert.strictEqual(message, process.cwd());
+ process.chdir('..');
+ w.postMessage(process.cwd());
+ }));
+} else if (!process.env.SECOND_WORKER) {
+ process.env.SECOND_WORKER = '1';
+ const firstCwd = process.cwd();
+ const w = new Worker(__filename);
+ w.on('message', common.mustCall((message) => {
+ assert.strictEqual(message, process.cwd());
+ parentPort.postMessage(firstCwd);
+ parentPort.onmessage = common.mustCall((obj) => {
+ const secondCwd = process.cwd();
+ assert.strictEqual(secondCwd, obj.data);
+ assert.notStrictEqual(secondCwd, firstCwd);
+ w.postMessage(secondCwd);
+ parentPort.unref();
+ });
+ }));
+} else {
+ const firstCwd = process.cwd();
+ parentPort.postMessage(firstCwd);
+ parentPort.onmessage = common.mustCall((obj) => {
+ const secondCwd = process.cwd();
+ assert.strictEqual(secondCwd, obj.data);
+ assert.notStrictEqual(secondCwd, firstCwd);
+ process.exit();
+ });
+}