Commit d242f3d4055 for nodejs
commit d242f3d405517ad46bd9c4b59c2ae41b8b3730c9
Author: Christian Aurich Zanettini Martins <christian.aurichzm@gmail.com>
Date: Sat Oct 31 10:33:07 2026 -0300
test: deflake test-worker-message-port-transfer-filehandle-fd0
The child closed stdin and expected the next open() to return fd 0,
but another thread can open a descriptor in between and take it.
Pass the file to the child as stdin and wrap fd 0 in a FileHandle, so
it is open from the start. After the transfer is discarded, accept an
fd 0 that was reused for another file.
Signed-off-by: Christian Aurich Zanettini Martins <christian.aurichzm@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66980
Refs: https://github.com/nodejs/reliability/issues/1701
Refs: https://github.com/nodejs/node/pull/66750
Reviewed-By: Filip Skokan <panva.ip@gmail.com>
diff --git a/test/parallel/test-worker-message-port-transfer-filehandle-fd0.js b/test/parallel/test-worker-message-port-transfer-filehandle-fd0.js
index f13dccb7c6a..661c417b299 100644
--- a/test/parallel/test-worker-message-port-transfer-filehandle-fd0.js
+++ b/test/parallel/test-worker-message-port-transfer-filehandle-fd0.js
@@ -1,13 +1,14 @@
'use strict';
-// A FileHandle whose transfer is discarded has to close its file descriptor.
-// Descriptor 0 is an ordinary descriptor once stdin has been closed, so the
-// scenario runs in a child process that can afford to lose its stdin.
+// A FileHandle whose transfer is discarded has to close its file descriptor,
+// descriptor 0 included. The child gets this file as stdin, so descriptor 0
+// refers to it from the start and no other thread can take it before the
+// child wraps it in a FileHandle. The child can afford to lose its stdin.
const common = require('../common');
if (common.isWindows)
- common.skip('descriptor numbering after closing stdin is POSIX-specific');
+ common.skip('libuv does not close descriptors 0 to 2 on Windows');
const assert = require('assert');
const fs = require('fs');
@@ -16,11 +17,13 @@ if (process.argv[2] === 'child') {
const vm = require('vm');
const { MessageChannel, moveMessagePortToContext } =
require('worker_threads');
+ const { internalBinding } = require('internal/test/binding');
+ const { FileHandle } = require('internal/fs/promises');
+ const { FileHandle: NativeFileHandle } = internalBinding('fs');
(async function() {
- fs.closeSync(0);
- const fh = await fs.promises.open(__filename);
- assert.strictEqual(fh.fd, 0);
+ const { dev, ino } = fs.fstatSync(0, { bigint: true });
+ const fh = new FileHandle(new NativeFileHandle(0));
const { port1, port2 } = new MessageChannel();
// A port living in another context cannot receive the handle, so the
@@ -34,13 +37,24 @@ if (process.argv[2] === 'child') {
port1.postMessage(fh, [ fh ]);
await discarded;
- assert.throws(() => fs.fstatSync(0), { code: 'EBADF' });
+ // Another thread may already have reused descriptor 0, but not for this
+ // file.
+ let stats;
+ try {
+ stats = fs.fstatSync(0, { bigint: true });
+ } catch (err) {
+ assert.strictEqual(err.code, 'EBADF');
+ }
+ if (stats)
+ assert.notDeepStrictEqual([stats.dev, stats.ino], [dev, ino]);
port1.close();
})().then(common.mustCall());
return;
}
-const { spawnSync } = require('child_process');
-const result = spawnSync(process.execPath, [ __filename, 'child' ],
- { stdio: [ 'ignore', 'inherit', 'inherit' ] });
-assert.strictEqual(result.status, 0);
+const { spawnSyncAndExitWithoutError } = require('../common/child_process');
+const fd = fs.openSync(__filename);
+spawnSyncAndExitWithoutError(process.execPath,
+ [ '--expose-internals', __filename, 'child' ],
+ { stdio: [ fd, 'inherit', 'inherit' ] });
+fs.closeSync(fd);