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