Skip to content

Commit

Permalink
fs: improve error perf of sync chmod+fchmod
Browse files Browse the repository at this point in the history
PR-URL: #49859
Refs: nodejs/performance#106
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Darshan Sen <raisinten@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
  • Loading branch information
CanadaHonk authored and anonrig committed Oct 12, 2023
1 parent 6bd77db commit 6bc7fa7
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 24 deletions.
41 changes: 41 additions & 0 deletions benchmark/fs/bench-chmodSync.js
@@ -0,0 +1,41 @@
'use strict';

const common = require('../common');
const fs = require('fs');
const tmpdir = require('../../test/common/tmpdir');
tmpdir.refresh();

const bench = common.createBenchmark(main, {
type: ['existing', 'non-existing'],
n: [1e3],
});

function main({ n, type }) {
switch (type) {
case 'existing': {
for (let i = 0; i < n; i++) {
fs.writeFileSync(tmpdir.resolve(`chmodsync-bench-file-${i}`), 'bench');
}

bench.start();
for (let i = 0; i < n; i++) {
fs.chmodSync(tmpdir.resolve(`chmodsync-bench-file-${i}`), 0o666);
}
bench.end(n);
break;
}
case 'non-existing':
bench.start();
for (let i = 0; i < n; i++) {
try {
fs.chmodSync(tmpdir.resolve(`chmod-non-existing-file-${i}`), 0o666);
} catch {
// do nothing
}
}
bench.end(n);
break;
default:
new Error('Invalid type');
}
}
60 changes: 60 additions & 0 deletions benchmark/fs/bench-fchmodSync.js
@@ -0,0 +1,60 @@
'use strict';

const common = require('../common');
const fs = require('fs');
const tmpdir = require('../../test/common/tmpdir');
tmpdir.refresh();

const bench = common.createBenchmark(main, {
type: ['existing', 'non-existing'],
n: [1e3],
});

function main({ n, type }) {
let files;

switch (type) {
case 'existing':
files = [];

// Populate tmpdir with mock files
for (let i = 0; i < n; i++) {
const path = tmpdir.resolve(`fchmodsync-bench-file-${i}`);
fs.writeFileSync(path, 'bench');
files.push(path);
}
break;
case 'non-existing':
files = new Array(n).fill(tmpdir.resolve(`.non-existing-file-${Date.now()}`));
break;
default:
new Error('Invalid type');
}

const fds = files.map((x) => {
// Try to open, if not return likely invalid fd (1 << 30)
try {
return fs.openSync(x, 'r');
} catch {
return 1 << 30;
}
});

bench.start();
for (let i = 0; i < n; i++) {
try {
fs.fchmodSync(fds[i], 0o666);
} catch {
// do nothing
}
}
bench.end(n);

for (const x of fds) {
try {
fs.closeSync(x);
} catch {
// do nothing
}
}
}
16 changes: 8 additions & 8 deletions lib/fs.js
Expand Up @@ -1903,11 +1903,10 @@ function fchmod(fd, mode, callback) {
* @returns {void}
*/
function fchmodSync(fd, mode) {
fd = getValidatedFd(fd);
mode = parseFileMode(mode, 'mode');
const ctx = {};
binding.fchmod(fd, mode, undefined, ctx);
handleErrorFromBinding(ctx);
binding.fchmod(
getValidatedFd(fd),
parseFileMode(mode, 'mode'),
);
}

/**
Expand Down Expand Up @@ -1982,9 +1981,10 @@ function chmodSync(path, mode) {
path = getValidatedPath(path);
mode = parseFileMode(mode, 'mode');

const ctx = { path };
binding.chmod(pathModule.toNamespacedPath(path), mode, undefined, ctx);
handleErrorFromBinding(ctx);
binding.chmod(
pathModule.toNamespacedPath(path),
mode,
);
}

/**
Expand Down
24 changes: 10 additions & 14 deletions src/node_file.cc
Expand Up @@ -2517,18 +2517,16 @@ static void Chmod(const FunctionCallbackInfo<Value>& args) {
CHECK(args[1]->IsInt32());
int mode = args[1].As<Int32>()->Value();

FSReqBase* req_wrap_async = GetReqWrap(args, 2);
if (req_wrap_async != nullptr) { // chmod(path, mode, req)
if (argc > 2) { // chmod(path, mode, req)
FSReqBase* req_wrap_async = GetReqWrap(args, 2);
FS_ASYNC_TRACE_BEGIN1(
UV_FS_CHMOD, req_wrap_async, "path", TRACE_STR_COPY(*path))
AsyncCall(env, req_wrap_async, args, "chmod", UTF8, AfterNoArgs,
uv_fs_chmod, *path, mode);
} else { // chmod(path, mode, undefined, ctx)
CHECK_EQ(argc, 4);
FSReqWrapSync req_wrap_sync;
} else { // chmod(path, mode)
FSReqWrapSync req_wrap_sync("chmod", *path);
FS_SYNC_TRACE_BEGIN(chmod);
SyncCall(env, args[3], &req_wrap_sync, "chmod",
uv_fs_chmod, *path, mode);
SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_chmod, *path, mode);
FS_SYNC_TRACE_END(chmod);
}
}
Expand All @@ -2549,17 +2547,15 @@ static void FChmod(const FunctionCallbackInfo<Value>& args) {
CHECK(args[1]->IsInt32());
const int mode = args[1].As<Int32>()->Value();

FSReqBase* req_wrap_async = GetReqWrap(args, 2);
if (req_wrap_async != nullptr) { // fchmod(fd, mode, req)
if (argc > 2) { // fchmod(fd, mode, req)
FSReqBase* req_wrap_async = GetReqWrap(args, 2);
FS_ASYNC_TRACE_BEGIN0(UV_FS_FCHMOD, req_wrap_async)
AsyncCall(env, req_wrap_async, args, "fchmod", UTF8, AfterNoArgs,
uv_fs_fchmod, fd, mode);
} else { // fchmod(fd, mode, undefined, ctx)
CHECK_EQ(argc, 4);
FSReqWrapSync req_wrap_sync;
} else { // fchmod(fd, mode)
FSReqWrapSync req_wrap_sync("fchmod");
FS_SYNC_TRACE_BEGIN(fchmod);
SyncCall(env, args[3], &req_wrap_sync, "fchmod",
uv_fs_fchmod, fd, mode);
SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_fchmod, fd, mode);
FS_SYNC_TRACE_END(fchmod);
}
}
Expand Down
4 changes: 2 additions & 2 deletions typings/internalBinding/fs.d.ts
Expand Up @@ -61,7 +61,7 @@ declare namespace InternalFSBinding {
function access(path: StringOrBuffer, mode: number, usePromises: typeof kUsePromises): Promise<void>;

function chmod(path: string, mode: number, req: FSReqCallback): void;
function chmod(path: string, mode: number, req: undefined, ctx: FSSyncContext): void;
function chmod(path: string, mode: number): void;
function chmod(path: string, mode: number, usePromises: typeof kUsePromises): Promise<void>;

function chown(path: string, uid: number, gid: number, req: FSReqCallback): void;
Expand All @@ -76,7 +76,7 @@ declare namespace InternalFSBinding {
function copyFile(src: StringOrBuffer, dest: StringOrBuffer, mode: number, usePromises: typeof kUsePromises): Promise<void>;

function fchmod(fd: number, mode: number, req: FSReqCallback): void;
function fchmod(fd: number, mode: number, req: undefined, ctx: FSSyncContext): void;
function fchmod(fd: number, mode: number): void;
function fchmod(fd: number, mode: number, usePromises: typeof kUsePromises): Promise<void>;

function fchown(fd: number, uid: number, gid: number, req: FSReqCallback): void;
Expand Down

0 comments on commit 6bc7fa7

Please sign in to comment.