1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22'use strict'; 23const common = require('../common'); 24const assert = require('assert'); 25const cluster = require('cluster'); 26 27const totalWorkers = 2; 28 29// Cluster setup 30if (cluster.isWorker) { 31 const http = require('http'); 32 http.Server(() => {}).listen(0, '127.0.0.1'); 33} else if (process.argv[2] === 'cluster') { 34 // Send PID to testcase process 35 let forkNum = 0; 36 cluster.on('fork', common.mustCall(function forkEvent(worker) { 37 // Send PID 38 process.send({ 39 cmd: 'worker', 40 workerPID: worker.process.pid 41 }); 42 43 // Stop listening when done 44 if (++forkNum === totalWorkers) { 45 cluster.removeListener('fork', forkEvent); 46 } 47 }, totalWorkers)); 48 49 // Throw accidental error when all workers are listening 50 let listeningNum = 0; 51 cluster.on('listening', common.mustCall(function listeningEvent() { 52 // When all workers are listening 53 if (++listeningNum === totalWorkers) { 54 // Stop listening 55 cluster.removeListener('listening', listeningEvent); 56 57 // Throw accidental error 58 process.nextTick(() => { 59 throw new Error('accidental error'); 60 }); 61 } 62 }, totalWorkers)); 63 64 // Startup a basic cluster 65 cluster.fork(); 66 cluster.fork(); 67} else { 68 // This is the testcase 69 70 const fork = require('child_process').fork; 71 72 // List all workers 73 const workers = []; 74 75 // Spawn a cluster process 76 const master = fork(process.argv[1], ['cluster'], { silent: true }); 77 78 // Handle messages from the cluster 79 master.on('message', common.mustCall((data) => { 80 // Add worker pid to list and progress tracker 81 if (data.cmd === 'worker') { 82 workers.push(data.workerPID); 83 } 84 }, totalWorkers)); 85 86 // When cluster is dead 87 master.on('exit', common.mustCall((code) => { 88 // Check that the cluster died accidentally (non-zero exit code) 89 assert.strictEqual(code, 1); 90 91 // XXX(addaleax): The fact that this uses raw PIDs makes the test inherently 92 // flaky – another process might end up being started right after the 93 // workers finished and receive the same PID. 94 const pollWorkers = () => { 95 // When master is dead all workers should be dead too 96 if (workers.some((pid) => common.isAlive(pid))) { 97 setTimeout(pollWorkers, 50); 98 } 99 }; 100 101 // Loop indefinitely until worker exit 102 pollWorkers(); 103 })); 104} 105