| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 100c6da commit 2bb7667
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2865,16 +2865,18 @@ To maintain existing behavior `response.finished` should be replaced with | |||
| 2865 | 2865 | ||
| 2866 | 2866 | <!-- YAML | |
| 2867 | 2867 | changes: | |
| 2868 | + - version: REPLACEME | ||
| 2869 | + pr-url: https://github.com/nodejs/node/pull/58536 | ||
| 2870 | + description: End-of-Life. | ||
| 2868 | 2871 | - version: v14.0.0 | |
| 2869 | 2872 | pr-url: https://github.com/nodejs/node/pull/28396 | |
| 2870 | 2873 | description: Runtime deprecation. | |
| 2871 | 2874 | --> | |
| 2872 | 2875 | ||
| 2873 | - Type: Runtime | ||
| 2876 | + Type: End-of-Life | ||
| 2874 | 2877 | ||
| 2875 | - Allowing a [`fs.FileHandle`][] object to be closed on garbage collection is | ||
| 2876 | - deprecated. In the future, doing so might result in a thrown error that will | ||
| 2877 | - terminate the process. | ||
| 2878 | + Allowing a [`fs.FileHandle`][] object to be closed on garbage collection used | ||
| 2879 | + to be allowed, but now throws an error. | ||
| 2878 | 2880 | ||
| 2879 | 2881 | Please ensure that all `fs.FileHandle` objects are explicitly closed using | |
| 2880 | 2882 | `FileHandle.prototype.close()` when the `fs.FileHandle` is no longer needed: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -540,7 +540,7 @@ const { | |||
| 540 | 540 | ||
| 541 | 541 | While the `ReadableStream` will read the file to completion, it will not | |
| 542 | 542 | close the `FileHandle` automatically. User code must still call the | |
| 543 | - `fileHandle.close()` method. | ||
| 543 | + `fileHandle.close()` method unless the `autoClose` option is set to `true`. | ||
| 544 | 544 | ||
| 545 | 545 | #### `filehandle.readFile(options)` | |
| 546 | 546 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -683,14 +683,6 @@ inline bool Environment::no_browser_globals() const { | |||
| 683 | 683 | #endif | |
| 684 | 684 | } | |
| 685 | 685 | ||
| 686 | - bool Environment::filehandle_close_warning() const { | ||
| 687 | - return emit_filehandle_warning_; | ||
| 688 | - } | ||
| 689 | - | ||
| 690 | - void Environment::set_filehandle_close_warning(bool on) { | ||
| 691 | - emit_filehandle_warning_ = on; | ||
| 692 | - } | ||
| 693 | - | ||
| 694 | 686 | void Environment::set_source_maps_enabled(bool on) { | |
| 695 | 687 | source_maps_enabled_ = on; | |
| 696 | 688 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -822,9 +822,6 @@ class Environment final : public MemoryRetainer { | |||
| 822 | 822 | inline node_module* extra_linked_bindings_tail(); | |
| 823 | 823 | inline const Mutex& extra_linked_bindings_mutex() const; | |
| 824 | 824 | ||
| 825 | - inline bool filehandle_close_warning() const; | ||
| 826 | - inline void set_filehandle_close_warning(bool on); | ||
| 827 | - | ||
| 828 | 825 | inline void set_source_maps_enabled(bool on); | |
| 829 | 826 | inline bool source_maps_enabled() const; | |
| 830 | 827 | ||
@@ -1106,7 +1103,6 @@ class Environment final : public MemoryRetainer { | |||
| 1106 | 1103 | bool trace_sync_io_ = false; | |
| 1107 | 1104 | bool emit_env_nonstring_warning_ = true; | |
| 1108 | 1105 | bool emit_err_name_warning_ = true; | |
| 1109 | - bool emit_filehandle_warning_ = true; | ||
| 1110 | 1106 | bool source_maps_enabled_ = false; | |
| 1111 | 1107 | ||
| 1112 | 1108 | size_t async_callback_scope_depth_ = 0; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -333,12 +333,10 @@ BaseObjectPtr<BaseObject> FileHandle::TransferData::Deserialize( | |||
| 333 | 333 | return BaseObjectPtr<BaseObject> { FileHandle::New(bd, fd) }; | |
| 334 | 334 | } | |
| 335 | 335 | ||
| 336 | - // Close the file descriptor if it hasn't already been closed. A process | ||
| 337 | - // warning will be emitted using a SetImmediate to avoid calling back to | ||
| 338 | - // JS during GC. If closing the fd fails at this point, a fatal exception | ||
| 339 | - // will crash the process immediately. | ||
| 336 | + // Throw an exception if the file handle has not yet been closed. | ||
| 340 | 337 | inline void FileHandle::Close() { | |
| 341 | 338 | if (closed_ || closing_) return; | |
| 339 | + | ||
| 342 | 340 | uv_fs_t req; | |
| 343 | 341 | CHECK_NE(fd_, -1); | |
| 344 | 342 | FS_SYNC_TRACE_BEGIN(close); | |
@@ -352,42 +350,38 @@ inline void FileHandle::Close() { | |||
| 352 | 350 | ||
| 353 | 351 | AfterClose(); | |
| 354 | 352 | ||
| 355 | - if (ret < 0) { | ||
| 356 | - // Do not unref this | ||
| 357 | - env()->SetImmediate([detail](Environment* env) { | ||
| 353 | + // Even though we closed the file descriptor, we still throw an error | ||
| 354 | + // if the FileHandle object was not closed before garbage collection. | ||
| 355 | + // Because this method is called during garbage collection, we will defer | ||
| 356 | + // throwing the error until the next immediate queue tick so as not | ||
| 357 | + // to interfere with the gc process. | ||
| 358 | + // | ||
| 359 | + // This exception will end up being fatal for the process because | ||
| 360 | + // it is being thrown from within the SetImmediate handler and | ||
| 361 | + // there is no JS stack to bubble it to. In other words, tearing | ||
| 362 | + // down the process is the only reasonable thing we can do here. | ||
| 363 | + env()->SetImmediate([detail](Environment* env) { | ||
| 364 | + HandleScope handle_scope(env->isolate()); | ||
| 365 | + | ||
| 366 | + // If there was an error while trying to close the file descriptor, | ||
| 367 | + // we will throw that instead. | ||
| 368 | + if (detail.ret < 0) { | ||
| 358 | 369 | char msg[70]; | |
| 359 | - snprintf(msg, arraysize(msg), | ||
| 360 | - "Closing file descriptor %d on garbage collection failed", | ||
| 361 | - detail.fd); | ||
| 362 | - // This exception will end up being fatal for the process because | ||
| 363 | - // it is being thrown from within the SetImmediate handler and | ||
| 364 | - // there is no JS stack to bubble it to. In other words, tearing | ||
| 365 | - // down the process is the only reasonable thing we can do here. | ||
| 370 | + snprintf(msg, | ||
| 371 | + arraysize(msg), | ||
| 372 | + "Closing file descriptor %d on garbage collection failed", | ||
| 373 | + detail.fd); | ||
| 366 | 374 | HandleScope handle_scope(env->isolate()); | |
| 367 | 375 | env->ThrowUVException(detail.ret, "close", msg); | |
| 368 | - }); | ||
| 369 | - return; | ||
| 370 | - } | ||
| 371 | - | ||
| 372 | - // If the close was successful, we still want to emit a process warning | ||
| 373 | - // to notify that the file descriptor was gc'd. We want to be noisy about | ||
| 374 | - // this because not explicitly closing the FileHandle is a bug. | ||
| 376 | + return; | ||
| 377 | + } | ||
| 375 | 378 | ||
| 376 | - env()->SetImmediate([detail](Environment* env) { | ||
| 377 | - ProcessEmitWarning(env, | ||
| 378 | - "Closing file descriptor %d on garbage collection", | ||
| 379 | - detail.fd); | ||
| 380 | - if (env->filehandle_close_warning()) { | ||
| 381 | - env->set_filehandle_close_warning(false); | ||
| 382 | - USE(ProcessEmitDeprecationWarning( | ||
| 383 | - env, | ||
| 384 | - "Closing a FileHandle object on garbage collection is deprecated. " | ||
| 385 | - "Please close FileHandle objects explicitly using " | ||
| 386 | - "FileHandle.prototype.close(). In the future, an error will be " | ||
| 387 | - "thrown if a file descriptor is closed during garbage collection.", | ||
| 388 | - "DEP0137")); | ||
| 389 | - } | ||
| 390 | - }, CallbackFlags::kUnrefed); | ||
| 379 | + THROW_ERR_INVALID_STATE( | ||
| 380 | + env, | ||
| 381 | + "A FileHandle object was closed during garbage collection. " | ||
| 382 | + "This used to be allowed with a deprecation warning but is now " | ||
| 383 | + "considered an error. Please close FileHandle objects explicitly."); | ||
| 384 | + }); | ||
| 391 | 385 | } | |
| 392 | 386 | ||
| 393 | 387 | void FileHandle::CloseReq::Resolve() { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,33 +8,21 @@ const { internalBinding } = require('internal/test/binding'); | |||
| 8 | 8 | const fs = internalBinding('fs'); | |
| 9 | 9 | const { stringToFlags } = require('internal/fs/utils'); | |
| 10 | 10 | ||
| 11 | - // Verifies that the FileHandle object is garbage collected and that a | ||
| 12 | - // warning is emitted if it is not closed. | ||
| 11 | + // Verifies that the FileHandle object is garbage collected and that an | ||
| 12 | + // error is thrown if it is not closed. | ||
| 13 | + process.on('uncaughtException', common.mustCall((err) => { | ||
| 14 | + assert.strictEqual(err.code, 'ERR_INVALID_STATE'); | ||
| 15 | + assert.match(err.message, /^A FileHandle object was closed during/); | ||
| 16 | + })); | ||
| 17 | + | ||
| 13 | 18 | ||
| 14 | - let fdnum; | ||
| 15 | 19 | { | |
| 16 | 20 | const ctx = {}; | |
| 17 | - fdnum = fs.openFileHandle(path.toNamespacedPath(__filename), | ||
| 18 | - stringToFlags('r'), 0o666, undefined, ctx).fd; | ||
| 21 | + fs.openFileHandle(path.toNamespacedPath(__filename), | ||
| 22 | + stringToFlags('r'), 0o666, undefined, ctx); | ||
| 19 | 23 | assert.strictEqual(ctx.errno, undefined); | |
| 20 | 24 | } | |
| 21 | 25 | ||
| 22 | - const deprecationWarning = | ||
| 23 | - 'Closing a FileHandle object on garbage collection is deprecated. ' + | ||
| 24 | - 'Please close FileHandle objects explicitly using ' + | ||
| 25 | - 'FileHandle.prototype.close(). In the future, an error will be ' + | ||
| 26 | - 'thrown if a file descriptor is closed during garbage collection.'; | ||
| 27 | - | ||
| 28 | - common.expectWarning({ | ||
| 29 | - 'internal/test/binding': [ | ||
| 30 | - 'These APIs are for internal testing only. Do not use them.', | ||
| 31 | - ], | ||
| 32 | - 'Warning': [ | ||
| 33 | - `Closing file descriptor ${fdnum} on garbage collection`, | ||
| 34 | - ], | ||
| 35 | - 'DeprecationWarning': [[deprecationWarning, 'DEP0137']] | ||
| 36 | - }); | ||
| 37 | - | ||
| 38 | 26 | globalThis.gc(); | |
| 39 | 27 | ||
| 40 | 28 | setTimeout(() => {}, 10); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,7 +22,7 @@ tmpdir.refresh(); | |||
| 22 | 22 | ||
| 23 | 23 | async function validateReadFile() { | |
| 24 | 24 | const filePath = path.resolve(tmpDir, 'tmp-read-file.txt'); | |
| 25 | - const fileHandle = await open(filePath, 'w+'); | ||
| 25 | + await using fileHandle = await open(filePath, 'w+'); | ||
| 26 | 26 | const buffer = Buffer.from('Hello world'.repeat(100), 'utf8'); | |
| 27 | 27 | ||
| 28 | 28 | const fd = fs.openSync(filePath, 'w+'); | |
@@ -31,8 +31,6 @@ async function validateReadFile() { | |||
| 31 | 31 | ||
| 32 | 32 | const readFileData = await fileHandle.readFile(); | |
| 33 | 33 | assert.deepStrictEqual(buffer, readFileData); | |
| 34 | - | ||
| 35 | - await fileHandle.close(); | ||
| 36 | 34 | } | |
| 37 | 35 | ||
| 38 | 36 | async function validateReadFileProc() { | |
@@ -46,48 +44,36 @@ async function validateReadFileProc() { | |||
| 46 | 44 | if (!common.isLinux) | |
| 47 | 45 | return; | |
| 48 | 46 | ||
| 49 | - const fileHandle = await open('/proc/sys/kernel/hostname', 'r'); | ||
| 50 | - try { | ||
| 51 | - const hostname = await fileHandle.readFile(); | ||
| 52 | - assert.ok(hostname.length > 0); | ||
| 53 | - } finally { | ||
| 54 | - await fileHandle.close(); | ||
| 55 | - } | ||
| 47 | + await using fileHandle = await open('/proc/sys/kernel/hostname', 'r'); | ||
| 48 | + const hostname = await fileHandle.readFile(); | ||
| 49 | + assert.ok(hostname.length > 0); | ||
| 56 | 50 | } | |
| 57 | 51 | ||
| 58 | 52 | async function doReadAndCancel() { | |
| 59 | 53 | // Signal aborted from the start | |
| 60 | 54 | { | |
| 61 | 55 | const filePathForHandle = path.resolve(tmpDir, 'dogs-running.txt'); | |
| 62 | - const fileHandle = await open(filePathForHandle, 'w+'); | ||
| 63 | - try { | ||
| 64 | - const buffer = Buffer.from('Dogs running'.repeat(10000), 'utf8'); | ||
| 65 | - fs.writeFileSync(filePathForHandle, buffer); | ||
| 66 | - const signal = AbortSignal.abort(); | ||
| 67 | - await assert.rejects(readFile(fileHandle, common.mustNotMutateObjectDeep({ signal })), { | ||
| 68 | - name: 'AbortError' | ||
| 69 | - }); | ||
| 70 | - } finally { | ||
| 71 | - await fileHandle.close(); | ||
| 72 | - } | ||
| 56 | + await using fileHandle = await open(filePathForHandle, 'w+'); | ||
| 57 | + const buffer = Buffer.from('Dogs running'.repeat(10000), 'utf8'); | ||
| 58 | + fs.writeFileSync(filePathForHandle, buffer); | ||
| 59 | + const signal = AbortSignal.abort(); | ||
| 60 | + await assert.rejects(readFile(fileHandle, common.mustNotMutateObjectDeep({ signal })), { | ||
| 61 | + name: 'AbortError' | ||
| 62 | + }); | ||
| 73 | 63 | } | |
| 74 | 64 | ||
| 75 | 65 | // Signal aborted on first tick | |
| 76 | 66 | { | |
| 77 | 67 | const filePathForHandle = path.resolve(tmpDir, 'dogs-running1.txt'); | |
| 78 | - const fileHandle = await open(filePathForHandle, 'w+'); | ||
| 79 | - try { | ||
| 80 | - const buffer = Buffer.from('Dogs running'.repeat(10000), 'utf8'); | ||
| 81 | - fs.writeFileSync(filePathForHandle, buffer); | ||
| 82 | - const controller = new AbortController(); | ||
| 83 | - const { signal } = controller; | ||
| 84 | - process.nextTick(() => controller.abort()); | ||
| 85 | - await assert.rejects(readFile(fileHandle, common.mustNotMutateObjectDeep({ signal })), { | ||
| 86 | - name: 'AbortError' | ||
| 87 | - }, 'tick-0'); | ||
| 88 | - } finally { | ||
| 89 | - await fileHandle.close(); | ||
| 90 | - } | ||
| 68 | + await using fileHandle = await open(filePathForHandle, 'w+'); | ||
| 69 | + const buffer = Buffer.from('Dogs running'.repeat(10000), 'utf8'); | ||
| 70 | + fs.writeFileSync(filePathForHandle, buffer); | ||
| 71 | + const controller = new AbortController(); | ||
| 72 | + const { signal } = controller; | ||
| 73 | + process.nextTick(() => controller.abort()); | ||
| 74 | + await assert.rejects(readFile(fileHandle, common.mustNotMutateObjectDeep({ signal })), { | ||
| 75 | + name: 'AbortError' | ||
| 76 | + }, 'tick-0'); | ||
| 91 | 77 | } | |
| 92 | 78 | ||
| 93 | 79 | // Signal aborted right before buffer read | |
@@ -96,18 +82,14 @@ async function doReadAndCancel() { | |||
| 96 | 82 | const buffer = Buffer.from('Dogs running'.repeat(1000), 'utf8'); | |
| 97 | 83 | fs.writeFileSync(newFile, buffer); | |
| 98 | 84 | ||
| 99 | - const fileHandle = await open(newFile, 'r'); | ||
| 100 | - try { | ||
| 101 | - const controller = new AbortController(); | ||
| 102 | - const { signal } = controller; | ||
| 103 | - tick(1, () => controller.abort()); | ||
| 104 | - await assert.rejects(fileHandle.readFile( | ||
| 105 | - common.mustNotMutateObjectDeep({ signal, encoding: 'utf8' })), { | ||
| 106 | - name: 'AbortError' | ||
| 107 | - }, 'tick-1'); | ||
| 108 | - } finally { | ||
| 109 | - await fileHandle.close(); | ||
| 110 | - } | ||
| 85 | + await using fileHandle = await open(newFile, 'r'); | ||
| 86 | + const controller = new AbortController(); | ||
| 87 | + const { signal } = controller; | ||
| 88 | + tick(1, () => controller.abort()); | ||
| 89 | + await assert.rejects(fileHandle.readFile( | ||
| 90 | + common.mustNotMutateObjectDeep({ signal, encoding: 'utf8' })), { | ||
| 91 | + name: 'AbortError' | ||
| 92 | + }, 'tick-1'); | ||
| 111 | 93 | } | |
| 112 | 94 | ||
| 113 | 95 | // Validate file size is within range for reading | |
@@ -123,13 +105,12 @@ async function doReadAndCancel() { | |||
| 123 | 105 | await writeFile(newFile, Buffer.from('0')); | |
| 124 | 106 | await truncate(newFile, kIoMaxLength + 1); | |
| 125 | 107 | ||
| 126 | - const fileHandle = await open(newFile, 'r'); | ||
| 108 | + await using fileHandle = await open(newFile, 'r'); | ||
| 127 | 109 | ||
| 128 | 110 | await assert.rejects(fileHandle.readFile(), { | |
| 129 | 111 | name: 'RangeError', | |
| 130 | 112 | code: 'ERR_FS_FILE_TOO_LARGE' | |
| 131 | 113 | }); | |
| 132 | - await fileHandle.close(); | ||
| 133 | 114 | } | |
| 134 | 115 | } | |
| 135 | 116 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments