Skip to content

Commit cb718a2

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 2017548 commit cb718a2

File tree

2 files changed

+46
-33
lines changed

2 files changed

+46
-33
lines changed

src/node_file.cc

+40-33
Original file line numberDiff line numberDiff line change
@@ -345,44 +345,51 @@ MaybeLocal<Promise> FileHandle::ClosePromise() {
345345
Isolate* isolate = env()->isolate();
346346
EscapableHandleScope scope(isolate);
347347
Local<Context> context = env()->context();
348+
349+
Local<Value> close_resolver =
350+
object()->GetInternalField(FileHandle::kClosingPromiseSlot);
351+
if (!close_resolver.IsEmpty() && !close_resolver->IsUndefined()) {
352+
CHECK(close_resolver->IsPromise());
353+
return close_resolver.As<Promise>();
354+
}
355+
356+
CHECK(!closed_);
357+
CHECK(!closing_);
358+
CHECK(!reading_);
359+
348360
auto maybe_resolver = Promise::Resolver::New(context);
349361
CHECK(!maybe_resolver.IsEmpty());
350362
Local<Promise::Resolver> resolver = maybe_resolver.ToLocalChecked();
351363
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;
364+
365+
Local<Object> close_req_obj;
366+
if (!env()->fdclose_constructor_template()
367+
->NewInstance(env()->context()).ToLocal(&close_req_obj)) {
368+
return MaybeLocal<Promise>();
369+
}
370+
closing_ = true;
371+
object()->SetInternalField(FileHandle::kClosingPromiseSlot, promise);
372+
373+
CloseReq* req = new CloseReq(env(), close_req_obj, promise, object());
374+
auto AfterClose = uv_fs_callback_t{[](uv_fs_t* req) {
375+
std::unique_ptr<CloseReq> close(CloseReq::from_req(req));
376+
CHECK_NOT_NULL(close);
377+
close->file_handle()->AfterClose();
378+
Isolate* isolate = close->env()->isolate();
379+
if (req->result < 0) {
380+
HandleScope handle_scope(isolate);
381+
close->Reject(
382+
UVException(isolate, static_cast<int>(req->result), "close"));
383+
} else {
384+
close->Resolve();
380385
}
381-
} else {
382-
// Already closed. Just reject the promise immediately
383-
resolver->Reject(context, UVException(isolate, UV_EBADF, "close"))
384-
.Check();
386+
}};
387+
int ret = req->Dispatch(uv_fs_close, fd_, AfterClose);
388+
if (ret < 0) {
389+
req->Reject(UVException(isolate, ret, "close"));
390+
delete req;
385391
}
392+
386393
return scope.Escape(promise);
387394
}
388395

@@ -2538,7 +2545,7 @@ void Initialize(Local<Object> target,
25382545
env->SetProtoMethod(fd, "close", FileHandle::Close);
25392546
env->SetProtoMethod(fd, "releaseFD", FileHandle::ReleaseFD);
25402547
Local<ObjectTemplate> fdt = fd->InstanceTemplate();
2541-
fdt->SetInternalFieldCount(StreamBase::kInternalFieldCount);
2548+
fdt->SetInternalFieldCount(FileHandle::kInternalFieldCount);
25422549
StreamBase::AddMethods(env, fd);
25432550
env->SetConstructorFunction(target, "FileHandle", fd);
25442551
env->set_fd_constructor_template(fdt);

src/node_file.h

+6
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,12 @@ class FileHandleReadWrap final : public ReqWrap<uv_fs_t> {
234234
// the object is garbage collected
235235
class FileHandle final : public AsyncWrap, public StreamBase {
236236
public:
237+
enum InternalFields {
238+
kFileHandleBaseField = StreamBase::kInternalFieldCount,
239+
kClosingPromiseSlot,
240+
kInternalFieldCount
241+
};
242+
237243
static FileHandle* New(BindingData* binding_data,
238244
int fd,
239245
v8::Local<v8::Object> obj = v8::Local<v8::Object>());

0 commit comments

Comments
 (0)