26
26
#include " node_file.h"
27
27
28
28
#include " req_wrap-inl.h"
29
+ #include " stream_base-inl.h"
29
30
#include " string_bytes.h"
30
31
#include " string_search.h"
31
32
41
42
#endif
42
43
43
44
#include < memory>
44
- #include < vector>
45
45
46
46
namespace node {
47
47
@@ -115,11 +115,13 @@ using v8::Value;
115
115
// The FileHandle object wraps a file descriptor and will close it on garbage
116
116
// collection if necessary. If that happens, a process warning will be
117
117
// 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 )
119
119
: 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) {
123
125
MakeWeak<FileHandle>(this );
124
126
v8::PropertyAttribute attr =
125
127
static_cast <v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
@@ -129,6 +131,19 @@ FileHandle::FileHandle(Environment* env, int fd)
129
131
attr).FromJust ();
130
132
}
131
133
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
+
132
147
FileHandle::~FileHandle () {
133
148
CHECK (!closing_); // We should not be deleting while explicitly closing!
134
149
Close (); // Close synchronously and emit warning
@@ -142,10 +157,10 @@ FileHandle::~FileHandle() {
142
157
// will crash the process immediately.
143
158
inline void FileHandle::Close () {
144
159
if (closed_) return ;
145
- closed_ = true ;
146
160
uv_fs_t req;
147
161
int ret = uv_fs_close (env ()->event_loop (), &req, fd_, nullptr );
148
162
uv_fs_req_cleanup (&req);
163
+ AfterClose ();
149
164
150
165
struct err_detail { int ret; int fd; };
151
166
@@ -219,18 +234,18 @@ inline MaybeLocal<Promise> FileHandle::ClosePromise() {
219
234
CHECK (!maybe_resolver.IsEmpty ());
220
235
Local<Promise::Resolver> resolver = maybe_resolver.ToLocalChecked ();
221
236
Local<Promise> promise = resolver.As <Promise>();
237
+ CHECK (!reading_);
222
238
if (!closed_ && !closing_) {
223
239
closing_ = true ;
224
240
CloseReq* req = new CloseReq (env (), promise, object ());
225
241
auto AfterClose = [](uv_fs_t * req) {
226
242
CloseReq* close = static_cast <CloseReq*>(req->data );
227
243
CHECK_NE (close, nullptr );
228
- close->file_handle ()->closing_ = false ;
244
+ close->file_handle ()->AfterClose () ;
229
245
Isolate* isolate = close->env ()->isolate ();
230
246
if (req->result < 0 ) {
231
247
close->Reject (UVException (isolate, req->result , " close" ));
232
248
} else {
233
- close->file_handle ()->closed_ = true ;
234
249
close->Resolve ();
235
250
}
236
251
delete close;
@@ -256,6 +271,162 @@ void FileHandle::Close(const FunctionCallbackInfo<Value>& args) {
256
271
}
257
272
258
273
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
+
259
430
void FSReqWrap::Reject (Local<Value> reject) {
260
431
MakeCallback (env ()->oncomplete_string (), 1 , &reject);
261
432
}
@@ -1730,6 +1901,17 @@ void InitFs(Local<Object> target,
1730
1901
fst->SetClassName (wrapString);
1731
1902
target->Set (context, wrapString, fst->GetFunction ()).FromJust ();
1732
1903
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
+
1733
1915
// Create Function Template for FSReqPromise
1734
1916
Local<FunctionTemplate> fpt = FunctionTemplate::New (env->isolate ());
1735
1917
AsyncWrap::AddWrapMethods (env, fpt);
@@ -1741,14 +1923,16 @@ void InitFs(Local<Object> target,
1741
1923
env->set_fsreqpromise_constructor_template (fpo);
1742
1924
1743
1925
// Create FunctionTemplate for FileHandle
1744
- Local<FunctionTemplate> fd = FunctionTemplate::New ( env->isolate () );
1926
+ Local<FunctionTemplate> fd = env->NewFunctionTemplate (FileHandle::New );
1745
1927
AsyncWrap::AddWrapMethods (env, fd);
1746
1928
env->SetProtoMethod (fd, " close" , FileHandle::Close);
1929
+ env->SetProtoMethod (fd, " releaseFD" , FileHandle::ReleaseFD);
1747
1930
Local<ObjectTemplate> fdt = fd->InstanceTemplate ();
1748
1931
fdt->SetInternalFieldCount (1 );
1749
1932
Local<String> handleString =
1750
1933
FIXED_ONE_BYTE_STRING (env->isolate (), " FileHandle" );
1751
1934
fd->SetClassName (handleString);
1935
+ StreamBase::AddMethods<FileHandle>(env, fd, StreamBase::kFlagNone );
1752
1936
target->Set (context, handleString, fd->GetFunction ()).FromJust ();
1753
1937
env->set_fd_constructor_template (fdt);
1754
1938
0 commit comments