Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions benchmark/child_process/child-process-spawn-path.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict';
const common = require('../common.js');
const { spawnSync } = require('child_process');
const { basename, delimiter, dirname, join } = require('path');
const { tmpdir } = require('os');

const bench = common.createBenchmark(main, {
n: [100],
pathEntries: [0, 8, 32],
file: ['path', 'absolute'],
});

function main({ n, pathEntries, file }) {
const originalPath = process.env.PATH;
const executable = process.execPath;
const executableDir = dirname(executable);
const executableName = basename(executable);
const missingPaths = [];

for (let i = 0; i < pathEntries; ++i) {
missingPaths.push(join(tmpdir(), `node-spawn-path-missing-${process.pid}-${i}`));
}

process.env.PATH = [...missingPaths, executableDir].join(delimiter);

const command = file === 'path' ? executableName : executable;
const args = ['-e', ''];
const options = { stdio: 'ignore' };

try {
const warmup = spawnSync(command, args, options);
if (warmup.error)
throw warmup.error;

bench.start();
for (let i = 0; i < n; ++i) {
const result = spawnSync(command, args, options);
if (result.error)
throw result.error;
}
bench.end(n);
} finally {
if (originalPath === undefined)
delete process.env.PATH;
else
process.env.PATH = originalPath;
}
}
21 changes: 17 additions & 4 deletions deps/uv/src/unix/process.c
Original file line number Diff line number Diff line change
Expand Up @@ -682,6 +682,7 @@ static int uv__spawn_resolve_and_spawn(const uv_process_options_t* options,
const char *p;
const char *z;
const char *path;
const char *parent_path;
size_t l;
size_t k;
int err;
Expand All @@ -701,10 +702,7 @@ static int uv__spawn_resolve_and_spawn(const uv_process_options_t* options,
if (options->env != NULL)
env = options->env;

/* If options->file contains a slash, posix_spawn/posix_spawnp should behave
* the same, and do not involve PATH resolution at all. The libc
* `posix_spawnp` provided by Apple is buggy (since 10.15), so we now emulate it
* here, per https://github.com/libuv/libuv/pull/3583. */
/* If options->file contains a slash, PATH resolution is not needed. */
if (strchr(options->file, '/') != NULL) {
do
err = posix_spawn(pid, options->file, actions, attrs, options->args, env);
Expand All @@ -715,6 +713,21 @@ static int uv__spawn_resolve_and_spawn(const uv_process_options_t* options,
/* Look for the definition of PATH in the provided env */
path = uv__spawn_find_path_in_env(env);

/* Apple's posix_spawnp() resolves PATH from the parent's environment rather
* than env. It is safe to use when no cwd change is requested and both PATH
* values are identical. This avoids one posix_spawn() call per PATH entry in
* the common case. */
parent_path = getenv("PATH");
if (options->cwd == NULL &&
path != NULL &&
parent_path != NULL &&
strcmp(path, parent_path) == 0) {
do
err = posix_spawnp(pid, options->file, actions, attrs, options->args, env);
while (err == EINTR);
return err;
}

/* The following resolution logic (execvpe emulation) is copied from
* https://git.musl-libc.org/cgit/musl/tree/src/process/execvp.c
* and adapted to work for our specific usage */
Expand Down