11cb0ef41Sopenharmony_ci'use strict'; 21cb0ef41Sopenharmony_ciconst common = require('../common'); 31cb0ef41Sopenharmony_ci 41cb0ef41Sopenharmony_ci// On some OS X versions, when passing fd's between processes: 51cb0ef41Sopenharmony_ci// When the handle associated to a specific file descriptor is closed by the 61cb0ef41Sopenharmony_ci// sender process before it's received in the destination, the handle is indeed 71cb0ef41Sopenharmony_ci// closed while it should remain opened. In order to fix this behavior, don't 81cb0ef41Sopenharmony_ci// close the handle until the `NODE_HANDLE_ACK` is received by the sender. 91cb0ef41Sopenharmony_ci// This test is basically `test-cluster-net-send` but creating lots of workers 101cb0ef41Sopenharmony_ci// so the issue reproduces on OS X consistently. 111cb0ef41Sopenharmony_ci 121cb0ef41Sopenharmony_ciif (common.isPi) { 131cb0ef41Sopenharmony_ci common.skip('Too slow for Raspberry Pi devices'); 141cb0ef41Sopenharmony_ci} 151cb0ef41Sopenharmony_ci 161cb0ef41Sopenharmony_ciconst assert = require('assert'); 171cb0ef41Sopenharmony_ciconst { fork } = require('child_process'); 181cb0ef41Sopenharmony_ciconst net = require('net'); 191cb0ef41Sopenharmony_ci 201cb0ef41Sopenharmony_ciconst N = 80; 211cb0ef41Sopenharmony_cilet messageCallbackCount = 0; 221cb0ef41Sopenharmony_ci 231cb0ef41Sopenharmony_cifunction forkWorker() { 241cb0ef41Sopenharmony_ci const messageCallback = (msg, handle) => { 251cb0ef41Sopenharmony_ci messageCallbackCount++; 261cb0ef41Sopenharmony_ci assert.strictEqual(msg, 'handle'); 271cb0ef41Sopenharmony_ci assert.ok(handle); 281cb0ef41Sopenharmony_ci worker.send('got'); 291cb0ef41Sopenharmony_ci 301cb0ef41Sopenharmony_ci let recvData = ''; 311cb0ef41Sopenharmony_ci handle.on('data', common.mustCall((data) => { 321cb0ef41Sopenharmony_ci recvData += data; 331cb0ef41Sopenharmony_ci })); 341cb0ef41Sopenharmony_ci 351cb0ef41Sopenharmony_ci handle.on('end', () => { 361cb0ef41Sopenharmony_ci assert.strictEqual(recvData, 'hello'); 371cb0ef41Sopenharmony_ci worker.kill(); 381cb0ef41Sopenharmony_ci }); 391cb0ef41Sopenharmony_ci }; 401cb0ef41Sopenharmony_ci 411cb0ef41Sopenharmony_ci const worker = fork(__filename, ['child']); 421cb0ef41Sopenharmony_ci worker.on('error', (err) => { 431cb0ef41Sopenharmony_ci if (/\bEAGAIN\b/.test(err.message)) { 441cb0ef41Sopenharmony_ci forkWorker(); 451cb0ef41Sopenharmony_ci return; 461cb0ef41Sopenharmony_ci } 471cb0ef41Sopenharmony_ci throw err; 481cb0ef41Sopenharmony_ci }); 491cb0ef41Sopenharmony_ci worker.once('message', messageCallback); 501cb0ef41Sopenharmony_ci} 511cb0ef41Sopenharmony_ci 521cb0ef41Sopenharmony_ciif (process.argv[2] !== 'child') { 531cb0ef41Sopenharmony_ci for (let i = 0; i < N; ++i) { 541cb0ef41Sopenharmony_ci forkWorker(); 551cb0ef41Sopenharmony_ci } 561cb0ef41Sopenharmony_ci process.on('exit', () => { assert.strictEqual(messageCallbackCount, N); }); 571cb0ef41Sopenharmony_ci} else { 581cb0ef41Sopenharmony_ci let socket; 591cb0ef41Sopenharmony_ci let cbcalls = 0; 601cb0ef41Sopenharmony_ci function socketConnected() { 611cb0ef41Sopenharmony_ci if (++cbcalls === 2) 621cb0ef41Sopenharmony_ci process.send('handle', socket); 631cb0ef41Sopenharmony_ci } 641cb0ef41Sopenharmony_ci 651cb0ef41Sopenharmony_ci // As a side-effect, listening for the message event will ref the IPC channel, 661cb0ef41Sopenharmony_ci // so the child process will stay alive as long as it has a parent process/IPC 671cb0ef41Sopenharmony_ci // channel. Once this is done, we can unref our client and server sockets, and 681cb0ef41Sopenharmony_ci // the only thing keeping this worker alive will be IPC. This is important, 691cb0ef41Sopenharmony_ci // because it means a worker with no parent will have no referenced handles, 701cb0ef41Sopenharmony_ci // thus no work to do, and will exit immediately, preventing process leaks. 711cb0ef41Sopenharmony_ci process.on('message', common.mustCall()); 721cb0ef41Sopenharmony_ci 731cb0ef41Sopenharmony_ci const server = net.createServer((c) => { 741cb0ef41Sopenharmony_ci process.once('message', (msg) => { 751cb0ef41Sopenharmony_ci assert.strictEqual(msg, 'got'); 761cb0ef41Sopenharmony_ci c.end('hello'); 771cb0ef41Sopenharmony_ci }); 781cb0ef41Sopenharmony_ci socketConnected(); 791cb0ef41Sopenharmony_ci }).unref(); 801cb0ef41Sopenharmony_ci server.listen(0, common.localhostIPv4, () => { 811cb0ef41Sopenharmony_ci const { port } = server.address(); 821cb0ef41Sopenharmony_ci socket = net.connect(port, common.localhostIPv4, socketConnected).unref(); 831cb0ef41Sopenharmony_ci }); 841cb0ef41Sopenharmony_ci} 85