Skip to content

Commit 489b6c5

Browse files
committed
fs: fix FileHandle::ClosePromise to return persisted Promise
Makes the FileHandle::ClosePromise() idempotent, always returning the same Promise after it has already been successfully called once. Avoids the possibility of accidental double close events. Signed-off-by: James M Snell <[email protected]>
1 parent 12951f2 commit 489b6c5

File tree

2 files changed

+38
-32
lines changed

2 files changed

+38
-32
lines changed

src/node_file.cc

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -345,44 +345,48 @@ MaybeLocal<Promise> FileHandle::ClosePromise() {
345345
Isolate* isolate = env()->isolate();
346346
EscapableHandleScope scope(isolate);
347347
Local<Context> context = env()->context();
348+
349+
Local<Promise::Resolver> close_resolver = close_promise_.Get(isolate);
350+
if (!close_resolver.IsEmpty())
351+
return close_resolver.As<Promise>();
352+
353+
CHECK(!closed_);
354+
CHECK(!closing_);
355+
CHECK(!reading_);
356+
348357
auto maybe_resolver = Promise::Resolver::New(context);
349358
CHECK(!maybe_resolver.IsEmpty());
350359
Local<Promise::Resolver> resolver = maybe_resolver.ToLocalChecked();
351360
Local<Promise> promise = resolver.As<Promise>();
352-
CHECK(!reading_);
353-
if (!closed_ && !closing_) {
354-
closing_ = true;
355-
Local<Object> close_req_obj;
356-
if (!env()
357-
->fdclose_constructor_template()
358-
->NewInstance(env()->context())
359-
.ToLocal(&close_req_obj)) {
360-
return MaybeLocal<Promise>();
361-
}
362-
CloseReq* req = new CloseReq(env(), close_req_obj, promise, object());
363-
auto AfterClose = uv_fs_callback_t{[](uv_fs_t* req) {
364-
std::unique_ptr<CloseReq> close(CloseReq::from_req(req));
365-
CHECK_NOT_NULL(close);
366-
close->file_handle()->AfterClose();
367-
Isolate* isolate = close->env()->isolate();
368-
if (req->result < 0) {
369-
HandleScope handle_scope(isolate);
370-
close->Reject(
371-
UVException(isolate, static_cast<int>(req->result), "close"));
372-
} else {
373-
close->Resolve();
374-
}
375-
}};
376-
int ret = req->Dispatch(uv_fs_close, fd_, AfterClose);
377-
if (ret < 0) {
378-
req->Reject(UVException(isolate, ret, "close"));
379-
delete req;
361+
362+
Local<Object> close_req_obj;
363+
if (!env()->fdclose_constructor_template()
364+
->NewInstance(env()->context()).ToLocal(&close_req_obj)) {
365+
return MaybeLocal<Promise>();
366+
}
367+
closing_ = true;
368+
close_promise_.Reset(isolate, resolver);
369+
370+
CloseReq* req = new CloseReq(env(), close_req_obj, promise, object());
371+
auto AfterClose = uv_fs_callback_t{[](uv_fs_t* req) {
372+
std::unique_ptr<CloseReq> close(CloseReq::from_req(req));
373+
CHECK_NOT_NULL(close);
374+
close->file_handle()->AfterClose();
375+
Isolate* isolate = close->env()->isolate();
376+
if (req->result < 0) {
377+
HandleScope handle_scope(isolate);
378+
close->Reject(
379+
UVException(isolate, static_cast<int>(req->result), "close"));
380+
} else {
381+
close->Resolve();
380382
}
381-
} else {
382-
// Already closed. Just reject the promise immediately
383-
resolver->Reject(context, UVException(isolate, UV_EBADF, "close"))
384-
.Check();
383+
}};
384+
int ret = req->Dispatch(uv_fs_close, fd_, AfterClose);
385+
if (ret < 0) {
386+
req->Reject(UVException(isolate, ret, "close"));
387+
delete req;
385388
}
389+
386390
return scope.Escape(promise);
387391
}
388392

src/node_file.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,8 @@ class FileHandle final : public AsyncWrap, public StreamBase {
351351
BaseObjectPtr<FileHandleReadWrap> current_read_;
352352

353353
BaseObjectPtr<BindingData> binding_data_;
354+
355+
v8::Global<v8::Promise::Resolver> close_promise_{};
354356
};
355357

356358
int MKDirpSync(uv_loop_t* loop,

0 commit comments

Comments
 (0)