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
4142#endif
4243
4344#include < memory>
44- #include < vector>
4545
4646namespace 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+
132147FileHandle::~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.
143158inline 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+ ¤t_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+
259430void 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