Skip to content

Commit a85841e

Browse files
committed
fs: add fast api for fs.closeSync only
1 parent 592c79d commit a85841e

File tree

6 files changed

+87
-20
lines changed

6 files changed

+87
-20
lines changed

lib/fs.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ function close(fd, callback = defaultCloseCallback) {
515515
* @returns {void}
516516
*/
517517
function closeSync(fd) {
518-
binding.close(fd);
518+
binding.closeSync(fd);
519519
}
520520

521521
/**

src/env.cc

+20-3
Original file line numberDiff line numberDiff line change
@@ -1841,12 +1841,29 @@ void Environment::AddUnmanagedFd(int fd) {
18411841
}
18421842
}
18431843

1844-
void Environment::RemoveUnmanagedFd(int fd) {
1844+
void Environment::RemoveUnmanagedFd(int fd,
1845+
ProcessEmitScheduleType schedule_type) {
18451846
if (!tracks_unmanaged_fds()) return;
18461847
size_t removed_count = unmanaged_fds_.erase(fd);
18471848
if (removed_count == 0) {
1848-
ProcessEmitWarning(
1849-
this, "File descriptor %d closed but not opened in unmanaged mode", fd);
1849+
switch (schedule_type) {
1850+
case kSync:
1851+
ProcessEmitWarning(
1852+
this,
1853+
"File descriptor %d closed but not opened in unmanaged mode",
1854+
fd);
1855+
break;
1856+
case kSetImmediate:
1857+
SetImmediateThreadsafe([&](Environment* env) {
1858+
ProcessEmitWarning(
1859+
env,
1860+
"File descriptor %d closed but not opened in unmanaged mode",
1861+
fd);
1862+
});
1863+
break;
1864+
default:
1865+
UNREACHABLE();
1866+
}
18501867
}
18511868
}
18521869

src/env.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -1026,8 +1026,13 @@ class Environment : public MemoryRetainer {
10261026
uv_buf_t allocate_managed_buffer(const size_t suggested_size);
10271027
std::unique_ptr<v8::BackingStore> release_managed_buffer(const uv_buf_t& buf);
10281028

1029+
enum ProcessEmitScheduleType {
1030+
kSync,
1031+
kSetImmediate,
1032+
};
1033+
10291034
void AddUnmanagedFd(int fd);
1030-
void RemoveUnmanagedFd(int fd);
1035+
void RemoveUnmanagedFd(int fd, ProcessEmitScheduleType schedule_type = kSync);
10311036

10321037
template <typename T>
10331038
void ForEachRealm(T&& iterator) const;

src/node_external_reference.h

+4
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ using CFunctionWithInt64Fallback = void (*)(v8::Local<v8::Value>,
5555
const int64_t,
5656
v8::FastApiCallbackOptions&);
5757
using CFunctionWithBool = void (*)(v8::Local<v8::Value>, bool);
58+
using CFunctionWithUint32AndOptions = void (*)(v8::Local<v8::Object>,
59+
const uint32_t,
60+
v8::FastApiCallbackOptions&);
5861

5962
// This class manages the external references from the V8 heap
6063
// to the C++ addresses in Node.js.
@@ -79,6 +82,7 @@ class ExternalReferenceRegistry {
7982
V(CFunctionWithDoubleReturnDouble) \
8083
V(CFunctionWithInt64Fallback) \
8184
V(CFunctionWithBool) \
85+
V(CFunctionWithUint32AndOptions) \
8286
V(const v8::CFunctionInfo*) \
8387
V(v8::FunctionCallback) \
8488
V(v8::AccessorNameGetterCallback) \

src/node_file.cc

+54-14
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ namespace fs {
6060

6161
using v8::Array;
6262
using v8::BigInt;
63+
using v8::CFunction;
6364
using v8::Context;
6465
using v8::EscapableHandleScope;
6566
using v8::FastApiCallbackOptions;
@@ -966,32 +967,67 @@ void Access(const FunctionCallbackInfo<Value>& args) {
966967
}
967968
}
968969

969-
void Close(const FunctionCallbackInfo<Value>& args) {
970+
static void Close(const FunctionCallbackInfo<Value>& args) {
970971
Environment* env = Environment::GetCurrent(args);
971972

972-
const int argc = args.Length();
973-
CHECK_GE(argc, 1);
973+
CHECK_EQ(args.Length(), 2); // fd, req
974974

975975
int fd;
976976
if (!GetValidatedFd(env, args[0]).To(&fd)) {
977977
return;
978978
}
979979
env->RemoveUnmanagedFd(fd);
980980

981-
if (argc > 1) { // close(fd, req)
982-
FSReqBase* req_wrap_async = GetReqWrap(args, 1);
983-
CHECK_NOT_NULL(req_wrap_async);
984-
FS_ASYNC_TRACE_BEGIN0(UV_FS_CLOSE, req_wrap_async)
985-
AsyncCall(env, req_wrap_async, args, "close", UTF8, AfterNoArgs,
986-
uv_fs_close, fd);
987-
} else { // close(fd)
988-
FSReqWrapSync req_wrap_sync("close");
989-
FS_SYNC_TRACE_BEGIN(close);
990-
SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_close, fd);
991-
FS_SYNC_TRACE_END(close);
981+
FSReqBase* req_wrap_async = GetReqWrap(args, 1);
982+
CHECK_NOT_NULL(req_wrap_async);
983+
FS_ASYNC_TRACE_BEGIN0(UV_FS_CLOSE, req_wrap_async)
984+
AsyncCall(
985+
env, req_wrap_async, args, "close", UTF8, AfterNoArgs, uv_fs_close, fd);
986+
}
987+
988+
// Separate implementations are required to provide fast API for closeSync.
989+
// If both close and closeSync are implemented using the same function, and
990+
// if a fast API implementation is added for closeSync, close(fd, req) will
991+
// also trigger the fast API implementation and cause an incident.
992+
// Ref: https://github.com/nodejs/node/issues/53902
993+
static void CloseSync(const FunctionCallbackInfo<Value>& args) {
994+
Environment* env = Environment::GetCurrent(args);
995+
CHECK_EQ(args.Length(), 1);
996+
997+
int fd;
998+
if (!GetValidatedFd(env, args[0]).To(&fd)) {
999+
return;
1000+
}
1001+
env->RemoveUnmanagedFd(fd);
1002+
FSReqWrapSync req_wrap_sync("close");
1003+
FS_SYNC_TRACE_BEGIN(close);
1004+
SyncCallAndThrowOnError(env, &req_wrap_sync, uv_fs_close, fd);
1005+
FS_SYNC_TRACE_END(close);
1006+
}
1007+
1008+
static void FastCloseSync(Local<Object> recv,
1009+
const uint32_t fd,
1010+
// NOLINTNEXTLINE(runtime/references) This is V8 api.
1011+
v8::FastApiCallbackOptions& options) {
1012+
Environment* env = Environment::GetCurrent(recv->GetCreationContextChecked());
1013+
1014+
uv_fs_t req;
1015+
FS_SYNC_TRACE_BEGIN(close);
1016+
int err = uv_fs_close(nullptr, &req, fd, nullptr);
1017+
FS_SYNC_TRACE_END(close);
1018+
1019+
if (is_uv_error(err)) {
1020+
options.fallback = true;
1021+
} else {
1022+
// Note: Only remove unmanaged fds if the close was successful.
1023+
// RemoveUnmanagedFd() can call ProcessEmitWarning() which calls back into
1024+
// JS process.emitWarning() and violates the fast API protocol.
1025+
env->RemoveUnmanagedFd(fd, Environment::kSetImmediate);
9921026
}
9931027
}
9941028

1029+
CFunction fast_close_sync_(CFunction::Make(FastCloseSync));
1030+
9951031
static void ExistsSync(const FunctionCallbackInfo<Value>& args) {
9961032
Environment* env = Environment::GetCurrent(args);
9971033
Isolate* isolate = env->isolate();
@@ -3408,6 +3444,7 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data,
34083444
GetFormatOfExtensionlessFile);
34093445
SetMethod(isolate, target, "access", Access);
34103446
SetMethod(isolate, target, "close", Close);
3447+
SetFastMethod(isolate, target, "closeSync", CloseSync, &fast_close_sync_);
34113448
SetMethod(isolate, target, "existsSync", ExistsSync);
34123449
SetMethod(isolate, target, "open", Open);
34133450
SetMethod(isolate, target, "openFileHandle", OpenFileHandle);
@@ -3533,6 +3570,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
35333570

35343571
registry->Register(GetFormatOfExtensionlessFile);
35353572
registry->Register(Close);
3573+
registry->Register(CloseSync);
3574+
registry->Register(FastCloseSync);
3575+
registry->Register(fast_close_sync_.GetTypeInfo());
35363576
registry->Register(ExistsSync);
35373577
registry->Register(Open);
35383578
registry->Register(OpenFileHandle);

typings/internalBinding/fs.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ declare namespace InternalFSBinding {
7070
function chown(path: string, uid: number, gid: number): void;
7171

7272
function close(fd: number, req: FSReqCallback): void;
73-
function close(fd: number): void;
73+
function closeSync(fd: number): void;
7474

7575
function copyFile(src: StringOrBuffer, dest: StringOrBuffer, mode: number, req: FSReqCallback): void;
7676
function copyFile(src: StringOrBuffer, dest: StringOrBuffer, mode: number, req: undefined, ctx: FSSyncContext): void;
@@ -256,6 +256,7 @@ export interface FsBinding {
256256
chmod: typeof InternalFSBinding.chmod;
257257
chown: typeof InternalFSBinding.chown;
258258
close: typeof InternalFSBinding.close;
259+
closeSync: typeof InternalFSBinding.closeSync;
259260
copyFile: typeof InternalFSBinding.copyFile;
260261
fchmod: typeof InternalFSBinding.fchmod;
261262
fchown: typeof InternalFSBinding.fchown;

0 commit comments

Comments
 (0)