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);