Skip to content

Commit c412150

Browse files
committed
src: make FileHandle a (readonly) StreamBase
This enables accessing files using a more standard pattern. Once some more refactoring has been performed on the other existing `StreamBase` streams, this could also be used to implement `fs` streams in a more standard manner. PR-URL: #18936 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]>
1 parent 8695273 commit c412150

File tree

8 files changed

+279
-14
lines changed

8 files changed

+279
-14
lines changed

src/env-inl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,11 @@ Environment::fs_stats_field_array() {
484484
return &fs_stats_field_array_;
485485
}
486486

487+
inline std::vector<std::unique_ptr<fs::FileHandleReadWrap>>&
488+
Environment::file_handle_read_wrap_freelist() {
489+
return file_handle_read_wrap_freelist_;
490+
}
491+
487492
void Environment::CreateImmediate(native_immediate_callback cb,
488493
void* data,
489494
v8::Local<v8::Object> obj,

src/env.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "async_wrap.h"
33
#include "node_buffer.h"
44
#include "node_platform.h"
5+
#include "node_file.h"
56

67
#include <stdio.h>
78
#include <algorithm>

src/env.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ struct nghttp2_rcbuf;
4848

4949
namespace node {
5050

51+
namespace fs {
52+
class FileHandleReadWrap;
53+
}
54+
5155
namespace performance {
5256
class performance_state;
5357
}
@@ -297,6 +301,7 @@ struct PackageConfig {
297301
V(context, v8::Context) \
298302
V(domain_callback, v8::Function) \
299303
V(fd_constructor_template, v8::ObjectTemplate) \
304+
V(filehandlereadwrap_template, v8::ObjectTemplate) \
300305
V(fsreqpromise_constructor_template, v8::ObjectTemplate) \
301306
V(fdclose_constructor_template, v8::ObjectTemplate) \
302307
V(host_import_module_dynamically_callback, v8::Function) \
@@ -642,6 +647,9 @@ class Environment {
642647

643648
inline AliasedBuffer<double, v8::Float64Array>* fs_stats_field_array();
644649

650+
inline std::vector<std::unique_ptr<fs::FileHandleReadWrap>>&
651+
file_handle_read_wrap_freelist();
652+
645653
inline performance::performance_state* performance_state();
646654
inline std::map<std::string, uint64_t>* performance_marks();
647655

@@ -822,6 +830,9 @@ class Environment {
822830
static const int kFsStatsFieldsLength = 2 * 14;
823831
AliasedBuffer<double, v8::Float64Array> fs_stats_field_array_;
824832

833+
std::vector<std::unique_ptr<fs::FileHandleReadWrap>>
834+
file_handle_read_wrap_freelist_;
835+
825836
struct ExitCallback {
826837
void (*cb_)(void* arg);
827838
void* arg_;

src/node_file.cc

Lines changed: 193 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "node_file.h"
2727

2828
#include "req_wrap-inl.h"
29+
#include "stream_base-inl.h"
2930
#include "string_bytes.h"
3031
#include "string_search.h"
3132

@@ -41,7 +42,6 @@
4142
#endif
4243

4344
#include <memory>
44-
#include <vector>
4545

4646
namespace node {
4747

@@ -115,11 +115,13 @@ using v8::Value;
115115
// The FileHandle object wraps a file descriptor and will close it on garbage
116116
// collection if necessary. If that happens, a process warning will be
117117
// emitted (or a fatal exception will occur if the fd cannot be closed.)
118-
FileHandle::FileHandle(Environment* env, int fd)
118+
FileHandle::FileHandle(Environment* env, int fd, Local<Object> obj)
119119
: AsyncWrap(env,
120-
env->fd_constructor_template()
121-
->NewInstance(env->context()).ToLocalChecked(),
122-
AsyncWrap::PROVIDER_FILEHANDLE), fd_(fd) {
120+
obj.IsEmpty() ? env->fd_constructor_template()
121+
->NewInstance(env->context()).ToLocalChecked() : obj,
122+
AsyncWrap::PROVIDER_FILEHANDLE),
123+
StreamBase(env),
124+
fd_(fd) {
123125
MakeWeak<FileHandle>(this);
124126
v8::PropertyAttribute attr =
125127
static_cast<v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
@@ -129,6 +131,19 @@ FileHandle::FileHandle(Environment* env, int fd)
129131
attr).FromJust();
130132
}
131133

134+
void FileHandle::New(const v8::FunctionCallbackInfo<v8::Value>& args) {
135+
Environment* env = Environment::GetCurrent(args);
136+
CHECK(args.IsConstructCall());
137+
CHECK(args[0]->IsInt32());
138+
139+
FileHandle* handle =
140+
new FileHandle(env, args[0].As<v8::Int32>()->Value(), args.This());
141+
if (args[1]->IsNumber())
142+
handle->read_offset_ = args[1]->IntegerValue(env->context()).FromJust();
143+
if (args[2]->IsNumber())
144+
handle->read_length_ = args[2]->IntegerValue(env->context()).FromJust();
145+
}
146+
132147
FileHandle::~FileHandle() {
133148
CHECK(!closing_); // We should not be deleting while explicitly closing!
134149
Close(); // Close synchronously and emit warning
@@ -142,10 +157,10 @@ FileHandle::~FileHandle() {
142157
// will crash the process immediately.
143158
inline void FileHandle::Close() {
144159
if (closed_) return;
145-
closed_ = true;
146160
uv_fs_t req;
147161
int ret = uv_fs_close(env()->event_loop(), &req, fd_, nullptr);
148162
uv_fs_req_cleanup(&req);
163+
AfterClose();
149164

150165
struct err_detail { int ret; int fd; };
151166

@@ -219,18 +234,18 @@ inline MaybeLocal<Promise> FileHandle::ClosePromise() {
219234
CHECK(!maybe_resolver.IsEmpty());
220235
Local<Promise::Resolver> resolver = maybe_resolver.ToLocalChecked();
221236
Local<Promise> promise = resolver.As<Promise>();
237+
CHECK(!reading_);
222238
if (!closed_ && !closing_) {
223239
closing_ = true;
224240
CloseReq* req = new CloseReq(env(), promise, object());
225241
auto AfterClose = [](uv_fs_t* req) {
226242
CloseReq* close = static_cast<CloseReq*>(req->data);
227243
CHECK_NE(close, nullptr);
228-
close->file_handle()->closing_ = false;
244+
close->file_handle()->AfterClose();
229245
Isolate* isolate = close->env()->isolate();
230246
if (req->result < 0) {
231247
close->Reject(UVException(isolate, req->result, "close"));
232248
} else {
233-
close->file_handle()->closed_ = true;
234249
close->Resolve();
235250
}
236251
delete close;
@@ -256,6 +271,162 @@ void FileHandle::Close(const FunctionCallbackInfo<Value>& args) {
256271
}
257272

258273

274+
void FileHandle::ReleaseFD(const FunctionCallbackInfo<Value>& args) {
275+
FileHandle* fd;
276+
ASSIGN_OR_RETURN_UNWRAP(&fd, args.Holder());
277+
// Just act as if this FileHandle has been closed.
278+
fd->AfterClose();
279+
}
280+
281+
282+
void FileHandle::AfterClose() {
283+
closing_ = false;
284+
closed_ = true;
285+
if (reading_ && !persistent().IsEmpty())
286+
EmitRead(UV_EOF);
287+
}
288+
289+
290+
FileHandleReadWrap::FileHandleReadWrap(FileHandle* handle, Local<Object> obj)
291+
: ReqWrap(handle->env(), obj, AsyncWrap::PROVIDER_FSREQWRAP),
292+
file_handle_(handle) {}
293+
294+
int FileHandle::ReadStart() {
295+
if (!IsAlive() || IsClosing())
296+
return UV_EOF;
297+
298+
reading_ = true;
299+
300+
if (current_read_)
301+
return 0;
302+
303+
std::unique_ptr<FileHandleReadWrap> read_wrap;
304+
305+
if (read_length_ == 0) {
306+
EmitRead(UV_EOF);
307+
return 0;
308+
}
309+
310+
{
311+
// Create a new FileHandleReadWrap or re-use one.
312+
// Either way, we need these two scopes for AsyncReset() or otherwise
313+
// for creating the new instance.
314+
HandleScope handle_scope(env()->isolate());
315+
AsyncHooks::DefaultTriggerAsyncIdScope trigger_scope(this);
316+
317+
auto& freelist = env()->file_handle_read_wrap_freelist();
318+
if (freelist.size() > 0) {
319+
read_wrap = std::move(freelist.back());
320+
freelist.pop_back();
321+
read_wrap->AsyncReset();
322+
read_wrap->file_handle_ = this;
323+
} else {
324+
Local<Object> wrap_obj = env()->filehandlereadwrap_template()
325+
->NewInstance(env()->context()).ToLocalChecked();
326+
read_wrap.reset(new FileHandleReadWrap(this, wrap_obj));
327+
}
328+
}
329+
int64_t recommended_read = 65536;
330+
if (read_length_ >= 0 && read_length_ <= recommended_read)
331+
recommended_read = read_length_;
332+
333+
read_wrap->buffer_ = EmitAlloc(recommended_read);
334+
read_wrap->Dispatched();
335+
336+
current_read_ = std::move(read_wrap);
337+
338+
uv_fs_read(env()->event_loop(),
339+
current_read_->req(),
340+
fd_,
341+
&current_read_->buffer_,
342+
1,
343+
read_offset_,
344+
[](uv_fs_t* req) {
345+
FileHandle* handle;
346+
{
347+
FileHandleReadWrap* req_wrap = FileHandleReadWrap::from_req(req);
348+
handle = req_wrap->file_handle_;
349+
CHECK_EQ(handle->current_read_.get(), req_wrap);
350+
}
351+
352+
// ReadStart() checks whether current_read_ is set to determine whether
353+
// a read is in progress. Moving it into a local variable makes sure that
354+
// the ReadStart() call below doesn’t think we’re still actively reading.
355+
std::unique_ptr<FileHandleReadWrap> read_wrap =
356+
std::move(handle->current_read_);
357+
358+
int result = req->result;
359+
uv_buf_t buffer = read_wrap->buffer_;
360+
361+
uv_fs_req_cleanup(req);
362+
363+
// Push the read wrap back to the freelist, or let it be destroyed
364+
// once we’re exiting the current scope.
365+
constexpr size_t wanted_freelist_fill = 100;
366+
auto& freelist = handle->env()->file_handle_read_wrap_freelist();
367+
if (freelist.size() < wanted_freelist_fill)
368+
freelist.emplace_back(std::move(read_wrap));
369+
370+
if (result >= 0) {
371+
// Read at most as many bytes as we originally planned to.
372+
if (handle->read_length_ >= 0 && handle->read_length_ < result)
373+
result = handle->read_length_;
374+
375+
// If we read data and we have an expected length, decrease it by
376+
// how much we have read.
377+
if (handle->read_length_ >= 0)
378+
handle->read_length_ -= result;
379+
380+
// If we have an offset, increase it by how much we have read.
381+
if (handle->read_offset_ >= 0)
382+
handle->read_offset_ += result;
383+
}
384+
385+
// Reading 0 bytes from a file always means EOF, or that we reached
386+
// the end of the requested range.
387+
if (result == 0)
388+
result = UV_EOF;
389+
390+
handle->EmitRead(result, buffer);
391+
392+
// Start over, if EmitRead() didn’t tell us to stop.
393+
if (handle->reading_)
394+
handle->ReadStart();
395+
});
396+
397+
return 0;
398+
}
399+
400+
int FileHandle::ReadStop() {
401+
reading_ = false;
402+
return 0;
403+
}
404+
405+
typedef SimpleShutdownWrap<ReqWrap<uv_fs_t>> FileHandleCloseWrap;
406+
407+
ShutdownWrap* FileHandle::CreateShutdownWrap(Local<Object> object) {
408+
return new FileHandleCloseWrap(this, object);
409+
}
410+
411+
int FileHandle::DoShutdown(ShutdownWrap* req_wrap) {
412+
FileHandleCloseWrap* wrap = static_cast<FileHandleCloseWrap*>(req_wrap);
413+
closing_ = true;
414+
wrap->Dispatched();
415+
uv_fs_close(env()->event_loop(), wrap->req(), fd_, [](uv_fs_t* req) {
416+
FileHandleCloseWrap* wrap = static_cast<FileHandleCloseWrap*>(
417+
FileHandleCloseWrap::from_req(req));
418+
FileHandle* handle = static_cast<FileHandle*>(wrap->stream());
419+
handle->AfterClose();
420+
421+
int result = req->result;
422+
uv_fs_req_cleanup(req);
423+
wrap->Done(result);
424+
});
425+
426+
return 0;
427+
}
428+
429+
259430
void FSReqWrap::Reject(Local<Value> reject) {
260431
MakeCallback(env()->oncomplete_string(), 1, &reject);
261432
}
@@ -1730,6 +1901,17 @@ void InitFs(Local<Object> target,
17301901
fst->SetClassName(wrapString);
17311902
target->Set(context, wrapString, fst->GetFunction()).FromJust();
17321903

1904+
// Create FunctionTemplate for FileHandleReadWrap. There’s no need
1905+
// to do anything in the constructor, so we only store the instance template.
1906+
Local<FunctionTemplate> fh_rw = FunctionTemplate::New(env->isolate());
1907+
fh_rw->InstanceTemplate()->SetInternalFieldCount(1);
1908+
AsyncWrap::AddWrapMethods(env, fh_rw);
1909+
Local<String> fhWrapString =
1910+
FIXED_ONE_BYTE_STRING(env->isolate(), "FileHandleReqWrap");
1911+
fh_rw->SetClassName(fhWrapString);
1912+
env->set_filehandlereadwrap_template(
1913+
fst->InstanceTemplate());
1914+
17331915
// Create Function Template for FSReqPromise
17341916
Local<FunctionTemplate> fpt = FunctionTemplate::New(env->isolate());
17351917
AsyncWrap::AddWrapMethods(env, fpt);
@@ -1741,14 +1923,16 @@ void InitFs(Local<Object> target,
17411923
env->set_fsreqpromise_constructor_template(fpo);
17421924

17431925
// Create FunctionTemplate for FileHandle
1744-
Local<FunctionTemplate> fd = FunctionTemplate::New(env->isolate());
1926+
Local<FunctionTemplate> fd = env->NewFunctionTemplate(FileHandle::New);
17451927
AsyncWrap::AddWrapMethods(env, fd);
17461928
env->SetProtoMethod(fd, "close", FileHandle::Close);
1929+
env->SetProtoMethod(fd, "releaseFD", FileHandle::ReleaseFD);
17471930
Local<ObjectTemplate> fdt = fd->InstanceTemplate();
17481931
fdt->SetInternalFieldCount(1);
17491932
Local<String> handleString =
17501933
FIXED_ONE_BYTE_STRING(env->isolate(), "FileHandle");
17511934
fd->SetClassName(handleString);
1935+
StreamBase::AddMethods<FileHandle>(env, fd, StreamBase::kFlagNone);
17521936
target->Set(context, handleString, fd->GetFunction()).FromJust();
17531937
env->set_fd_constructor_template(fdt);
17541938

0 commit comments

Comments
 (0)