| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 332e384 commit 7ef5591
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -4528,7 +4528,8 @@ added: v10.0.0 | |||
| 4528 | 4528 | file descriptor is closed, or will be rejected if an error occurs while | |
| 4529 | 4529 | closing. | |
| 4530 | 4530 | ||
| 4531 | - Closes the file descriptor. | ||
| 4531 | + Closes the file handle. Will wait for any pending operation on the handle | ||
| 4532 | + to complete before completing. | ||
| 4532 | 4533 | ||
| 4533 | 4534 | ```js | |
| 4534 | 4535 | const fsPromises = require('fs').promises; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -14,6 +14,8 @@ const { | |||
| 14 | 14 | MathMin, | |
| 15 | 15 | NumberIsSafeInteger, | |
| 16 | 16 | Symbol, | |
| 17 | + Error, | ||
| 18 | + Promise, | ||
| 17 | 19 | } = primordials; | |
| 18 | 20 | ||
| 19 | 21 | const { | |
@@ -64,6 +66,11 @@ const { promisify } = require('internal/util'); | |||
| 64 | 66 | ||
| 65 | 67 | const kHandle = Symbol('kHandle'); | |
| 66 | 68 | const kFd = Symbol('kFd'); | |
| 69 | + const kRefs = Symbol('kRefs'); | ||
| 70 | + const kClosePromise = Symbol('kClosePromise'); | ||
| 71 | + const kCloseResolve = Symbol('kCloseResolve'); | ||
| 72 | + const kCloseReject = Symbol('kCloseReject'); | ||
| 73 | + | ||
| 67 | 74 | const { kUsePromises } = binding; | |
| 68 | 75 | const { | |
| 69 | 76 | JSTransferable, kDeserialize, kTransfer, kTransferList | |
@@ -76,6 +83,9 @@ class FileHandle extends JSTransferable { | |||
| 76 | 83 | super(); | |
| 77 | 84 | this[kHandle] = filehandle; | |
| 78 | 85 | this[kFd] = filehandle ? filehandle.fd : -1; | |
| 86 | + | ||
| 87 | + this[kRefs] = 1; | ||
| 88 | + this[kClosePromise] = null; | ||
| 79 | 89 | } | |
| 80 | 90 | ||
| 81 | 91 | getAsyncId() { | |
@@ -87,70 +97,101 @@ class FileHandle extends JSTransferable { | |||
| 87 | 97 | } | |
| 88 | 98 | ||
| 89 | 99 | appendFile(data, options) { | |
| 90 | - return writeFile(this, data, options); | ||
| 100 | + return fsCall(writeFile, this, data, options); | ||
| 91 | 101 | } | |
| 92 | 102 | ||
| 93 | 103 | chmod(mode) { | |
| 94 | - return fchmod(this, mode); | ||
| 104 | + return fsCall(fchmod, this, mode); | ||
| 95 | 105 | } | |
| 96 | 106 | ||
| 97 | 107 | chown(uid, gid) { | |
| 98 | - return fchown(this, uid, gid); | ||
| 108 | + return fsCall(fchown, this, uid, gid); | ||
| 99 | 109 | } | |
| 100 | 110 | ||
| 101 | 111 | datasync() { | |
| 102 | - return fdatasync(this); | ||
| 112 | + return fsCall(fdatasync, this); | ||
| 103 | 113 | } | |
| 104 | 114 | ||
| 105 | 115 | sync() { | |
| 106 | - return fsync(this); | ||
| 116 | + return fsCall(fsync, this); | ||
| 107 | 117 | } | |
| 108 | 118 | ||
| 109 | 119 | read(buffer, offset, length, position) { | |
| 110 | - return read(this, buffer, offset, length, position); | ||
| 120 | + return fsCall(read, this, buffer, offset, length, position); | ||
| 111 | 121 | } | |
| 112 | 122 | ||
| 113 | 123 | readv(buffers, position) { | |
| 114 | - return readv(this, buffers, position); | ||
| 124 | + return fsCall(readv, this, buffers, position); | ||
| 115 | 125 | } | |
| 116 | 126 | ||
| 117 | 127 | readFile(options) { | |
| 118 | - return readFile(this, options); | ||
| 128 | + return fsCall(readFile, this, options); | ||
| 119 | 129 | } | |
| 120 | 130 | ||
| 121 | 131 | stat(options) { | |
| 122 | - return fstat(this, options); | ||
| 132 | + return fsCall(fstat, this, options); | ||
| 123 | 133 | } | |
| 124 | 134 | ||
| 125 | 135 | truncate(len = 0) { | |
| 126 | - return ftruncate(this, len); | ||
| 136 | + return fsCall(ftruncate, this, len); | ||
| 127 | 137 | } | |
| 128 | 138 | ||
| 129 | 139 | utimes(atime, mtime) { | |
| 130 | - return futimes(this, atime, mtime); | ||
| 140 | + return fsCall(futimes, this, atime, mtime); | ||
| 131 | 141 | } | |
| 132 | 142 | ||
| 133 | 143 | write(buffer, offset, length, position) { | |
| 134 | - return write(this, buffer, offset, length, position); | ||
| 144 | + return fsCall(write, this, buffer, offset, length, position); | ||
| 135 | 145 | } | |
| 136 | 146 | ||
| 137 | 147 | writev(buffers, position) { | |
| 138 | - return writev(this, buffers, position); | ||
| 148 | + return fsCall(writev, this, buffers, position); | ||
| 139 | 149 | } | |
| 140 | 150 | ||
| 141 | 151 | writeFile(data, options) { | |
| 142 | - return writeFile(this, data, options); | ||
| 152 | + return fsCall(writeFile, this, data, options); | ||
| 143 | 153 | } | |
| 144 | 154 | ||
| 145 | 155 | close = () => { | |
| 146 | - this[kFd] = -1; | ||
| 147 | - return this[kHandle].close(); | ||
| 156 | + if (this[kFd] === -1) { | ||
| 157 | + return Promise.resolve(); | ||
| 158 | + } | ||
| 159 | + | ||
| 160 | + if (this[kClosePromise]) { | ||
| 161 | + return this[kClosePromise]; | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + this[kRefs]--; | ||
| 165 | + if (this[kRefs] === 0) { | ||
| 166 | + this[kFd] = -1; | ||
| 167 | + this[kClosePromise] = this[kHandle].close().finally(() => { | ||
| 168 | + this[kClosePromise] = undefined; | ||
| 169 | + }); | ||
| 170 | + } else { | ||
| 171 | + this[kClosePromise] = new Promise((resolve, reject) => { | ||
| 172 | + this[kCloseResolve] = resolve; | ||
| 173 | + this[kCloseReject] = reject; | ||
| 174 | + }).finally(() => { | ||
| 175 | + this[kClosePromise] = undefined; | ||
| 176 | + this[kCloseReject] = undefined; | ||
| 177 | + this[kCloseResolve] = undefined; | ||
| 178 | + }); | ||
| 179 | + } | ||
| 180 | + | ||
| 181 | + return this[kClosePromise]; | ||
| 148 | 182 | } | |
| 149 | 183 | ||
| 150 | 184 | [kTransfer]() { | |
| 185 | + if (this[kClosePromise] || this[kRefs] > 1) { | ||
| 186 | + const DOMException = internalBinding('messaging').DOMException; | ||
| 187 | + throw new DOMException('Cannot transfer FileHandle while in use', | ||
| 188 | + 'DataCloneError'); | ||
| 189 | + } | ||
| 190 | + | ||
| 151 | 191 | const handle = this[kHandle]; | |
| 152 | 192 | this[kFd] = -1; | |
| 153 | 193 | this[kHandle] = null; | |
| 194 | + this[kRefs] = 0; | ||
| 154 | 195 | ||
| 155 | 196 | return { | |
| 156 | 197 | data: { handle }, | |
@@ -168,9 +209,31 @@ class FileHandle extends JSTransferable { | |||
| 168 | 209 | } | |
| 169 | 210 | } | |
| 170 | 211 | ||
| 171 | - function validateFileHandle(handle) { | ||
| 172 | - if (!(handle instanceof FileHandle)) | ||
| 212 | + async function fsCall(fn, handle, ...args) { | ||
| 213 | + if (handle[kRefs] === undefined) { | ||
| 173 | 214 | throw new ERR_INVALID_ARG_TYPE('filehandle', 'FileHandle', handle); | |
| 215 | + } | ||
| 216 | + | ||
| 217 | + if (handle.fd === -1) { | ||
| 218 | + // eslint-disable-next-line no-restricted-syntax | ||
| 219 | + const err = new Error('file closed'); | ||
| 220 | + err.code = 'EBADF'; | ||
| 221 | + err.syscall = fn.name; | ||
| 222 | + throw err; | ||
| 223 | + } | ||
| 224 | + | ||
| 225 | + try { | ||
| 226 | + handle[kRefs]++; | ||
| 227 | + return await fn(handle, ...args); | ||
| 228 | + } finally { | ||
| 229 | + handle[kRefs]--; | ||
| 230 | + if (handle[kRefs] === 0) { | ||
| 231 | + handle[kFd] = -1; | ||
| 232 | + handle[kHandle] | ||
| 233 | + .close() | ||
| 234 | + .then(handle[kCloseResolve], handle[kCloseReject]); | ||
| 235 | + } | ||
| 236 | + } | ||
| 174 | 237 | } | |
| 175 | 238 | ||
| 176 | 239 | async function writeFileHandle(filehandle, data) { | |
@@ -249,7 +312,6 @@ async function open(path, flags, mode) { | |||
| 249 | 312 | } | |
| 250 | 313 | ||
| 251 | 314 | async function read(handle, buffer, offset, length, position) { | |
| 252 | - validateFileHandle(handle); | ||
| 253 | 315 | validateBuffer(buffer); | |
| 254 | 316 | ||
| 255 | 317 | if (offset == null) { | |
@@ -280,7 +342,6 @@ async function read(handle, buffer, offset, length, position) { | |||
| 280 | 342 | } | |
| 281 | 343 | ||
| 282 | 344 | async function readv(handle, buffers, position) { | |
| 283 | - validateFileHandle(handle); | ||
| 284 | 345 | validateBufferArray(buffers); | |
| 285 | 346 | ||
| 286 | 347 | if (typeof position !== 'number') | |
@@ -292,8 +353,6 @@ async function readv(handle, buffers, position) { | |||
| 292 | 353 | } | |
| 293 | 354 | ||
| 294 | 355 | async function write(handle, buffer, offset, length, position) { | |
| 295 | - validateFileHandle(handle); | ||
| 296 | - | ||
| 297 | 356 | if (buffer.length === 0) | |
| 298 | 357 | return { bytesWritten: 0, buffer }; | |
| 299 | 358 | ||
@@ -321,7 +380,6 @@ async function write(handle, buffer, offset, length, position) { | |||
| 321 | 380 | } | |
| 322 | 381 | ||
| 323 | 382 | async function writev(handle, buffers, position) { | |
| 324 | - validateFileHandle(handle); | ||
| 325 | 383 | validateBufferArray(buffers); | |
| 326 | 384 | ||
| 327 | 385 | if (typeof position !== 'number') | |
@@ -346,7 +404,6 @@ async function truncate(path, len = 0) { | |||
| 346 | 404 | } | |
| 347 | 405 | ||
| 348 | 406 | async function ftruncate(handle, len = 0) { | |
| 349 | - validateFileHandle(handle); | ||
| 350 | 407 | validateInteger(len, 'len'); | |
| 351 | 408 | len = MathMax(0, len); | |
| 352 | 409 | return binding.ftruncate(handle.fd, len, kUsePromises); | |
@@ -364,12 +421,10 @@ async function rmdir(path, options) { | |||
| 364 | 421 | } | |
| 365 | 422 | ||
| 366 | 423 | async function fdatasync(handle) { | |
| 367 | - validateFileHandle(handle); | ||
| 368 | 424 | return binding.fdatasync(handle.fd, kUsePromises); | |
| 369 | 425 | } | |
| 370 | 426 | ||
| 371 | 427 | async function fsync(handle) { | |
| 372 | - validateFileHandle(handle); | ||
| 373 | 428 | return binding.fsync(handle.fd, kUsePromises); | |
| 374 | 429 | } | |
| 375 | 430 | ||
@@ -420,7 +475,6 @@ async function symlink(target, path, type_) { | |||
| 420 | 475 | } | |
| 421 | 476 | ||
| 422 | 477 | async function fstat(handle, options = { bigint: false }) { | |
| 423 | - validateFileHandle(handle); | ||
| 424 | 478 | const result = await binding.fstat(handle.fd, options.bigint, kUsePromises); | |
| 425 | 479 | return getStatsFromBinding(result); | |
| 426 | 480 | } | |
@@ -453,7 +507,6 @@ async function unlink(path) { | |||
| 453 | 507 | } | |
| 454 | 508 | ||
| 455 | 509 | async function fchmod(handle, mode) { | |
| 456 | - validateFileHandle(handle); | ||
| 457 | 510 | mode = parseFileMode(mode, 'mode'); | |
| 458 | 511 | return binding.fchmod(handle.fd, mode, kUsePromises); | |
| 459 | 512 | } | |
@@ -481,7 +534,6 @@ async function lchown(path, uid, gid) { | |||
| 481 | 534 | } | |
| 482 | 535 | ||
| 483 | 536 | async function fchown(handle, uid, gid) { | |
| 484 | - validateFileHandle(handle); | ||
| 485 | 537 | validateUint32(uid, 'uid'); | |
| 486 | 538 | validateUint32(gid, 'gid'); | |
| 487 | 539 | return binding.fchown(handle.fd, uid, gid, kUsePromises); | |
@@ -504,7 +556,6 @@ async function utimes(path, atime, mtime) { | |||
| 504 | 556 | } | |
| 505 | 557 | ||
| 506 | 558 | async function futimes(handle, atime, mtime) { | |
| 507 | - validateFileHandle(handle); | ||
| 508 | 559 | atime = toUnixTimestamp(atime, 'atime'); | |
| 509 | 560 | mtime = toUnixTimestamp(mtime, 'mtime'); | |
| 510 | 561 | return binding.futimes(handle.fd, atime, mtime, kUsePromises); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -69,3 +69,36 @@ const { once } = require('events'); | |||
| 69 | 69 | ||
| 70 | 70 | port1.postMessage('second message'); | |
| 71 | 71 | })().then(common.mustCall()); | |
| 72 | + | ||
| 73 | + (async function() { | ||
| 74 | + // Check that a FileHandle with a read in progress cannot be transferred. | ||
| 75 | + const fh = await fs.open(__filename); | ||
| 76 | + | ||
| 77 | + const { port1 } = new MessageChannel(); | ||
| 78 | + | ||
| 79 | + const readPromise = fh.readFile(); | ||
| 80 | + assert.throws(() => { | ||
| 81 | + port1.postMessage(fh, [fh]); | ||
| 82 | + }, { | ||
| 83 | + message: 'Cannot transfer FileHandle while in use', | ||
| 84 | + name: 'DataCloneError' | ||
| 85 | + }); | ||
| 86 | + | ||
| 87 | + assert.deepStrictEqual(await readPromise, await fs.readFile(__filename)); | ||
| 88 | + })().then(common.mustCall()); | ||
| 89 | + | ||
| 90 | + (async function() { | ||
| 91 | + // Check that filehandles with a close in progress cannot be transferred. | ||
| 92 | + const fh = await fs.open(__filename); | ||
| 93 | + | ||
| 94 | + const { port1 } = new MessageChannel(); | ||
| 95 | + | ||
| 96 | + const closePromise = fh.close(); | ||
| 97 | + assert.throws(() => { | ||
| 98 | + port1.postMessage(fh, [fh]); | ||
| 99 | + }, { | ||
| 100 | + message: 'Cannot transfer FileHandle while in use', | ||
| 101 | + name: 'DataCloneError' | ||
| 102 | + }); | ||
| 103 | + await closePromise; | ||
| 104 | + })().then(common.mustCall()); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments