Skip to content

Commit 749a2ab

Browse files
CanadaHonkanonrig
authored andcommitted
fs: improve error perf of sync lstat+fstat
1 parent dc1c50b commit 749a2ab

File tree

5 files changed

+63
-70
lines changed

5 files changed

+63
-70
lines changed

benchmark/fs/bench-statSync-failure.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,29 @@ const fs = require('fs');
55
const path = require('path');
66

77
const bench = common.createBenchmark(main, {
8-
n: [1e6],
9-
statSyncType: ['throw', 'noThrow'],
8+
n: [1e4],
9+
statSyncType: ['fstatSync', 'lstatSync', 'statSync'],
10+
throwType: [ 'throw', 'noThrow' ],
11+
}, {
12+
// Do not allow noThrow fstatSync
13+
combinationFilter: ({ statSyncType, throwType }) => !(statSyncType === 'fstatSync' && throwType === 'noThrow'),
1014
});
1115

1216

13-
function main({ n, statSyncType }) {
14-
const arg = path.join(__dirname, 'non.existent');
17+
function main({ n, statSyncType, throwType }) {
18+
const arg = (statSyncType === 'fstatSync' ?
19+
(1 << 30) :
20+
path.join(__dirname, 'non.existent'));
21+
22+
const fn = fs[statSyncType];
1523

1624
bench.start();
1725
for (let i = 0; i < n; i++) {
18-
if (statSyncType === 'noThrow') {
19-
fs.statSync(arg, { throwIfNoEntry: false });
26+
if (throwType === 'noThrow') {
27+
fn(arg, { throwIfNoEntry: false });
2028
} else {
2129
try {
22-
fs.statSync(arg);
30+
fn(arg);
2331
} catch {
2432
// Continue regardless of error.
2533
}

benchmark/fs/bench-statSync.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const common = require('../common');
44
const fs = require('fs');
55

66
const bench = common.createBenchmark(main, {
7-
n: [1e6],
7+
n: [1e4],
88
statSyncType: ['fstatSync', 'lstatSync', 'statSync'],
99
});
1010

lib/fs.js

Lines changed: 27 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,6 @@ const {
7272
ERR_INVALID_ARG_VALUE,
7373
},
7474
AbortError,
75-
uvErrmapGet,
76-
uvException,
7775
} = require('internal/errors');
7876

7977
const {
@@ -398,11 +396,9 @@ function readFile(path, options, callback) {
398396
}
399397

400398
function tryStatSync(fd, isUserFd) {
401-
const ctx = {};
402-
const stats = binding.fstat(fd, false, undefined, ctx);
403-
if (ctx.errno !== undefined && !isUserFd) {
399+
const stats = binding.fstat(fd, false);
400+
if (stats === undefined && !isUserFd) {
404401
fs.closeSync(fd);
405-
throw uvException(ctx);
406402
}
407403
return stats;
408404
}
@@ -1621,33 +1617,21 @@ function statfs(path, options = { bigint: false }, callback) {
16211617
binding.statfs(pathModule.toNamespacedPath(path), options.bigint, req);
16221618
}
16231619

1624-
function hasNoEntryError(ctx) {
1625-
if (ctx.errno) {
1626-
const uvErr = uvErrmapGet(ctx.errno);
1627-
return uvErr?.[0] === 'ENOENT';
1628-
}
1629-
1630-
if (ctx.error) {
1631-
return ctx.error.code === 'ENOENT';
1632-
}
1633-
1634-
return false;
1635-
}
1636-
16371620
/**
16381621
* Synchronously retrieves the `fs.Stats` for
16391622
* the file descriptor.
16401623
* @param {number} fd
16411624
* @param {{
16421625
* bigint?: boolean;
16431626
* }} [options]
1644-
* @returns {Stats}
1627+
* @returns {Stats | undefined}
16451628
*/
16461629
function fstatSync(fd, options = { bigint: false }) {
16471630
fd = getValidatedFd(fd);
1648-
const ctx = { fd };
1649-
const stats = binding.fstat(fd, options.bigint, undefined, ctx);
1650-
handleErrorFromBinding(ctx);
1631+
const stats = binding.fstat(fd, options.bigint);
1632+
if (stats === undefined) {
1633+
return;
1634+
}
16511635
return getStatsFromBinding(stats);
16521636
}
16531637

@@ -1659,17 +1643,18 @@ function fstatSync(fd, options = { bigint: false }) {
16591643
* bigint?: boolean;
16601644
* throwIfNoEntry?: boolean;
16611645
* }} [options]
1662-
* @returns {Stats}
1646+
* @returns {Stats | undefined}
16631647
*/
16641648
function lstatSync(path, options = { bigint: false, throwIfNoEntry: true }) {
16651649
path = getValidatedPath(path);
1666-
const ctx = { path };
1667-
const stats = binding.lstat(pathModule.toNamespacedPath(path),
1668-
options.bigint, undefined, ctx);
1669-
if (options.throwIfNoEntry === false && hasNoEntryError(ctx)) {
1670-
return undefined;
1650+
const stats = binding.lstat(
1651+
pathModule.toNamespacedPath(path),
1652+
options.bigint,
1653+
);
1654+
1655+
if (stats === undefined) {
1656+
return;
16711657
}
1672-
handleErrorFromBinding(ctx);
16731658
return getStatsFromBinding(stats);
16741659
}
16751660

@@ -2656,9 +2641,10 @@ function realpathSync(p, options) {
26562641

26572642
// On windows, check that the root exists. On unix there is no need.
26582643
if (isWindows) {
2659-
const ctx = { path: base };
2660-
binding.lstat(pathModule.toNamespacedPath(base), false, undefined, ctx);
2661-
handleErrorFromBinding(ctx);
2644+
const out = binding.lstat(pathModule.toNamespacedPath(base), false);
2645+
if (out === undefined) {
2646+
return;
2647+
}
26622648
knownHard.add(base);
26632649
}
26642650

@@ -2698,9 +2684,10 @@ function realpathSync(p, options) {
26982684
// for our internal use.
26992685

27002686
const baseLong = pathModule.toNamespacedPath(base);
2701-
const ctx = { path: base };
2702-
const stats = binding.lstat(baseLong, true, undefined, ctx);
2703-
handleErrorFromBinding(ctx);
2687+
const stats = binding.lstat(baseLong, true);
2688+
if (stats === undefined) {
2689+
return;
2690+
}
27042691

27052692
if (!isFileType(stats, S_IFLNK)) {
27062693
knownHard.add(base);
@@ -2741,9 +2728,10 @@ function realpathSync(p, options) {
27412728

27422729
// On windows, check that the root exists. On unix there is no need.
27432730
if (isWindows && !knownHard.has(base)) {
2744-
const ctx = { path: base };
2745-
binding.lstat(pathModule.toNamespacedPath(base), false, undefined, ctx);
2746-
handleErrorFromBinding(ctx);
2731+
const out = binding.lstat(pathModule.toNamespacedPath(base), false);
2732+
if (out === undefined) {
2733+
return;
2734+
}
27472735
knownHard.add(base);
27482736
}
27492737
}

src/node_file.cc

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,27 +1186,25 @@ static void LStat(const FunctionCallbackInfo<Value>& args) {
11861186
Environment* env = realm->env();
11871187

11881188
const int argc = args.Length();
1189-
CHECK_GE(argc, 3);
1189+
CHECK_GE(argc, 2);
11901190

11911191
BufferValue path(realm->isolate(), args[0]);
11921192
CHECK_NOT_NULL(*path);
11931193

11941194
bool use_bigint = args[1]->IsTrue();
1195-
FSReqBase* req_wrap_async = GetReqWrap(args, 2, use_bigint);
1196-
if (req_wrap_async != nullptr) { // lstat(path, use_bigint, req)
1195+
if (argc > 2) { // lstat(path, use_bigint, req)
1196+
FSReqBase* req_wrap_async = GetReqWrap(args, 2, use_bigint);
11971197
FS_ASYNC_TRACE_BEGIN1(
11981198
UV_FS_LSTAT, req_wrap_async, "path", TRACE_STR_COPY(*path))
11991199
AsyncCall(env, req_wrap_async, args, "lstat", UTF8, AfterStat,
12001200
uv_fs_lstat, *path);
12011201
} else { // lstat(path, use_bigint, undefined, ctx)
1202-
CHECK_EQ(argc, 4);
1203-
FSReqWrapSync req_wrap_sync;
1202+
FSReqWrapSync req_wrap_sync("lstat", *path);
12041203
FS_SYNC_TRACE_BEGIN(lstat);
1205-
int err = SyncCall(env, args[3], &req_wrap_sync, "lstat", uv_fs_lstat,
1206-
*path);
1204+
int err = SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_lstat, *path);
12071205
FS_SYNC_TRACE_END(lstat);
1208-
if (err != 0) {
1209-
return; // error info is in ctx
1206+
if (is_uv_error(err)) {
1207+
return;
12101208
}
12111209

12121210
Local<Value> arr = FillGlobalStatsArray(binding_data, use_bigint,
@@ -1227,19 +1225,18 @@ static void FStat(const FunctionCallbackInfo<Value>& args) {
12271225
int fd = args[0].As<Int32>()->Value();
12281226

12291227
bool use_bigint = args[1]->IsTrue();
1230-
FSReqBase* req_wrap_async = GetReqWrap(args, 2, use_bigint);
1231-
if (req_wrap_async != nullptr) { // fstat(fd, use_bigint, req)
1228+
if (argc > 2) { // fstat(fd, use_bigint, req)
1229+
FSReqBase* req_wrap_async = GetReqWrap(args, 2, use_bigint);
12321230
FS_ASYNC_TRACE_BEGIN0(UV_FS_FSTAT, req_wrap_async)
12331231
AsyncCall(env, req_wrap_async, args, "fstat", UTF8, AfterStat,
12341232
uv_fs_fstat, fd);
1235-
} else { // fstat(fd, use_bigint, undefined, ctx)
1236-
CHECK_EQ(argc, 4);
1237-
FSReqWrapSync req_wrap_sync;
1233+
} else { // fstat(fd, use_bigint)
1234+
FSReqWrapSync req_wrap_sync("fstat");
12381235
FS_SYNC_TRACE_BEGIN(fstat);
1239-
int err = SyncCall(env, args[3], &req_wrap_sync, "fstat", uv_fs_fstat, fd);
1236+
int err = SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_fstat, fd);
12401237
FS_SYNC_TRACE_END(fstat);
1241-
if (err != 0) {
1242-
return; // error info is in ctx
1238+
if (is_uv_error(err)) {
1239+
return;
12431240
}
12441241

12451242
Local<Value> arr = FillGlobalStatsArray(binding_data, use_bigint,

typings/internalBinding/fs.d.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,9 @@ declare namespace InternalFSBinding {
9191
function fstat(fd: number, useBigint: boolean, req: FSReqCallback<Float64Array | BigUint64Array>): void;
9292
function fstat(fd: number, useBigint: true, req: FSReqCallback<BigUint64Array>): void;
9393
function fstat(fd: number, useBigint: false, req: FSReqCallback<Float64Array>): void;
94-
function fstat(fd: number, useBigint: boolean, req: undefined, ctx: FSSyncContext): Float64Array | BigUint64Array;
95-
function fstat(fd: number, useBigint: true, req: undefined, ctx: FSSyncContext): BigUint64Array;
96-
function fstat(fd: number, useBigint: false, req: undefined, ctx: FSSyncContext): Float64Array;
94+
function fstat(fd: number, useBigint: boolean): Float64Array | BigUint64Array;
95+
function fstat(fd: number, useBigint: true): BigUint64Array;
96+
function fstat(fd: number, useBigint: false): Float64Array;
9797
function fstat(fd: number, useBigint: boolean, usePromises: typeof kUsePromises): Promise<Float64Array | BigUint64Array>;
9898
function fstat(fd: number, useBigint: true, usePromises: typeof kUsePromises): Promise<BigUint64Array>;
9999
function fstat(fd: number, useBigint: false, usePromises: typeof kUsePromises): Promise<Float64Array>;
@@ -124,9 +124,9 @@ declare namespace InternalFSBinding {
124124
function lstat(path: StringOrBuffer, useBigint: boolean, req: FSReqCallback<Float64Array | BigUint64Array>): void;
125125
function lstat(path: StringOrBuffer, useBigint: true, req: FSReqCallback<BigUint64Array>): void;
126126
function lstat(path: StringOrBuffer, useBigint: false, req: FSReqCallback<Float64Array>): void;
127-
function lstat(path: StringOrBuffer, useBigint: boolean, req: undefined, ctx: FSSyncContext): Float64Array | BigUint64Array;
128-
function lstat(path: StringOrBuffer, useBigint: true, req: undefined, ctx: FSSyncContext): BigUint64Array;
129-
function lstat(path: StringOrBuffer, useBigint: false, req: undefined, ctx: FSSyncContext): Float64Array;
127+
function lstat(path: StringOrBuffer, useBigint: boolean): Float64Array | BigUint64Array;
128+
function lstat(path: StringOrBuffer, useBigint: true): BigUint64Array;
129+
function lstat(path: StringOrBuffer, useBigint: false): Float64Array;
130130
function lstat(path: StringOrBuffer, useBigint: boolean, usePromises: typeof kUsePromises): Promise<Float64Array | BigUint64Array>;
131131
function lstat(path: StringOrBuffer, useBigint: true, usePromises: typeof kUsePromises): Promise<BigUint64Array>;
132132
function lstat(path: StringOrBuffer, useBigint: false, usePromises: typeof kUsePromises): Promise<Float64Array>;

0 commit comments

Comments
 (0)