UNPKG

hsd

Version:
195 lines (151 loc) 3.58 kB
/*! * child.js - child processes for hsd * Copyright (c) 2017-2018, Christopher Jeffrey (MIT License). * https://github.com/handshake-org/hsd */ 'use strict'; const EventEmitter = require('events'); const path = require('path'); const cp = require('child_process'); const children = new Set(); let exitBound = false; /** * Child * Represents a child process. * @alias module:workers.Child * @extends EventEmitter * @ignore */ class Child extends EventEmitter { /** * Represents a child process. * @constructor * @param {String} file */ constructor(file) { super(); bindExit(); children.add(this); this.init(file); } /** * Test whether child process support is available. * @returns {Boolean} */ static hasSupport() { return true; } /** * Initialize child process (node.js). * @private * @param {String} file */ init(file) { const bin = process.argv[0]; const filename = path.resolve(__dirname, file); const options = { stdio: 'pipe', env: process.env }; this.child = cp.spawn(bin, [filename], options); this.child.unref(); this.child.stdin.unref(); this.child.stdout.unref(); this.child.stderr.unref(); this.child.on('error', (err) => { this.emit('error', err); }); this.child.once('exit', (code, signal) => { children.delete(this); this.emit('exit', code == null ? -1 : code, signal); }); this.child.stdin.on('error', (err) => { this.emit('error', err); }); this.child.stdout.on('error', (err) => { this.emit('error', err); }); this.child.stderr.on('error', (err) => { this.emit('error', err); }); this.child.stdout.on('data', (data) => { this.emit('data', data); }); } /** * Send data to child process. * @param {Buffer} data * @returns {Boolean} */ write(data) { return this.child.stdin.write(data); } /** * Destroy the child process. */ destroy() { this.child.kill('SIGTERM'); } } /** * Cleanup all child processes. * @private */ function bindExit() { if (exitBound) return; exitBound = true; listenExit(() => { for (const child of children) child.destroy(); }); } /** * Listen for exit. * @param {Function} handler * @private */ function listenExit(handler) { const onSighup = () => { process.exit(1 | 0x80); }; const onSigint = () => { process.exit(2 | 0x80); }; const onSigterm = () => { process.exit(15 | 0x80); }; const onError = (err) => { if (err && err.stack) console.error(String(err.stack)); else console.error(String(err)); process.exit(1); }; process.once('exit', handler); if (process.listenerCount('SIGHUP') === 0) process.once('SIGHUP', onSighup); if (process.listenerCount('SIGINT') === 0) process.once('SIGINT', onSigint); if (process.listenerCount('SIGTERM') === 0) process.once('SIGTERM', onSigterm); if (process.listenerCount('uncaughtException') === 0) process.once('uncaughtException', onError); process.on('newListener', (name) => { switch (name) { case 'SIGHUP': process.removeListener(name, onSighup); break; case 'SIGINT': process.removeListener(name, onSigint); break; case 'SIGTERM': process.removeListener(name, onSigterm); break; case 'uncaughtException': process.removeListener(name, onError); break; } }); } /* * Expose */ module.exports = Child;