From ea7902de130c9773cb6497ec2839fabe4dccfc80 Mon Sep 17 00:00:00 2001 From: Yagiz Nizipli Date: Fri, 29 Sep 2023 13:10:04 -0400 Subject: [PATCH] fs: improve error performance of `chownSync` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/49962 Refs: https://github.com/nodejs/performance/issues/106 Reviewed-By: Matteo Collina Reviewed-By: Stephen Belanger Reviewed-By: Vinícius Lourenço Claro Cardoso --- benchmark/fs/bench-chownSync.js | 54 +++++++++++++++++++++++++++++++++ lib/fs.js | 8 +++-- src/node_file.cc | 12 +++----- typings/internalBinding/fs.d.ts | 1 + 4 files changed, 65 insertions(+), 10 deletions(-) create mode 100644 benchmark/fs/bench-chownSync.js diff --git a/benchmark/fs/bench-chownSync.js b/benchmark/fs/bench-chownSync.js new file mode 100644 index 00000000000000..d07f3b65949979 --- /dev/null +++ b/benchmark/fs/bench-chownSync.js @@ -0,0 +1,54 @@ +'use strict'; + +const common = require('../common'); +const fs = require('fs'); +const assert = require('assert'); +const tmpdir = require('../../test/common/tmpdir'); + +if (process.platform === 'win32') { + console.log('Skipping: Windows does not have `getuid` or `getgid`'); + process.exit(0); +} + +const bench = common.createBenchmark(main, { + type: ['existing', 'non-existing'], + method: ['chownSync', 'lchownSync'], + n: [1e4], +}); + +function main({ n, type, method }) { + const uid = process.getuid(); + const gid = process.getgid(); + const fsMethod = fs[method]; + + switch (type) { + case 'existing': { + tmpdir.refresh(); + const tmpfile = tmpdir.resolve(`.existing-file-${process.pid}`); + fs.writeFileSync(tmpfile, 'this-is-for-a-benchmark', 'utf8'); + bench.start(); + for (let i = 0; i < n; i++) { + fsMethod(tmpfile, uid, gid); + } + bench.end(n); + break; + } + case 'non-existing': { + const path = tmpdir.resolve(`.non-existing-file-${Date.now()}`); + let hasError = false; + bench.start(); + for (let i = 0; i < n; i++) { + try { + fs[method](path, uid, gid); + } catch { + hasError = true; + } + } + bench.end(n); + assert(hasError); + break; + } + default: + new Error('Invalid type'); + } +} diff --git a/lib/fs.js b/lib/fs.js index 557ef282932b86..feb3dec25bbd4d 100644 --- a/lib/fs.js +++ b/lib/fs.js @@ -2087,9 +2087,11 @@ function chownSync(path, uid, gid) { path = getValidatedPath(path); validateInteger(uid, 'uid', -1, kMaxUserId); validateInteger(gid, 'gid', -1, kMaxUserId); - const ctx = { path }; - binding.chown(pathModule.toNamespacedPath(path), uid, gid, undefined, ctx); - handleErrorFromBinding(ctx); + binding.chown( + pathModule.toNamespacedPath(path), + uid, + gid, + ); } /** diff --git a/src/node_file.cc b/src/node_file.cc index 4e3ffa56cfece3..00d71b81a9e511 100644 --- a/src/node_file.cc +++ b/src/node_file.cc @@ -2607,18 +2607,16 @@ static void Chown(const FunctionCallbackInfo& args) { CHECK(IsSafeJsInt(args[2])); const uv_gid_t gid = static_cast(args[2].As()->Value()); - FSReqBase* req_wrap_async = GetReqWrap(args, 3); - if (req_wrap_async != nullptr) { // chown(path, uid, gid, req) + if (argc > 3) { // chown(path, uid, gid, req) + FSReqBase* req_wrap_async = GetReqWrap(args, 3); FS_ASYNC_TRACE_BEGIN1( UV_FS_CHOWN, req_wrap_async, "path", TRACE_STR_COPY(*path)) AsyncCall(env, req_wrap_async, args, "chown", UTF8, AfterNoArgs, uv_fs_chown, *path, uid, gid); - } else { // chown(path, uid, gid, undefined, ctx) - CHECK_EQ(argc, 5); - FSReqWrapSync req_wrap_sync; + } else { // chown(path, uid, gid) + FSReqWrapSync req_wrap_sync("chown", *path); FS_SYNC_TRACE_BEGIN(chown); - SyncCall(env, args[4], &req_wrap_sync, "chown", - uv_fs_chown, *path, uid, gid); + SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_chown, *path, uid, gid); FS_SYNC_TRACE_END(chown); } } diff --git a/typings/internalBinding/fs.d.ts b/typings/internalBinding/fs.d.ts index bdab2e80be7f26..0f5c29a13025b5 100644 --- a/typings/internalBinding/fs.d.ts +++ b/typings/internalBinding/fs.d.ts @@ -67,6 +67,7 @@ declare namespace InternalFSBinding { function chown(path: string, uid: number, gid: number, req: FSReqCallback): void; function chown(path: string, uid: number, gid: number, req: undefined, ctx: FSSyncContext): void; function chown(path: string, uid: number, gid: number, usePromises: typeof kUsePromises): Promise; + function chown(path: string, uid: number, gid: number): void; function close(fd: number, req: FSReqCallback): void; function close(fd: number, req: undefined, ctx: FSSyncContext): void;