terminate.js 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. 'use strict';
  2. const sleep = require('mz-modules/sleep');
  3. const awaitEvent = require('await-event');
  4. const pstree = require('ps-tree');
  5. module.exports = function* (subProcess, timeout) {
  6. const pid = subProcess.process ? subProcess.process.pid : subProcess.pid;
  7. const childPids = yield getChildPids(pid);
  8. yield [
  9. killProcess(subProcess, timeout),
  10. killChildren(childPids, timeout),
  11. ];
  12. };
  13. // kill process, if SIGTERM not work, try SIGKILL
  14. function* killProcess(subProcess, timeout) {
  15. subProcess.kill('SIGTERM');
  16. yield Promise.race([
  17. awaitEvent(subProcess, 'exit'),
  18. sleep(timeout),
  19. ]);
  20. if (subProcess.killed) return;
  21. // SIGKILL: http://man7.org/linux/man-pages/man7/signal.7.html
  22. // worker: https://github.com/nodejs/node/blob/master/lib/internal/cluster/worker.js#L22
  23. // subProcess.kill is wrapped to subProcess.destroy, it will wait to disconnected.
  24. (subProcess.process || subProcess).kill('SIGKILL');
  25. }
  26. // kill all children processes, if SIGTERM not work, try SIGKILL
  27. function* killChildren(children, timeout) {
  28. if (!children.length) return;
  29. kill(children, 'SIGTERM');
  30. const start = Date.now();
  31. // if timeout is 1000, it will check twice.
  32. const checkInterval = 400;
  33. let unterminated = [];
  34. while (Date.now() - start < timeout - checkInterval) {
  35. yield sleep(checkInterval);
  36. unterminated = getUnterminatedProcesses(children);
  37. if (!unterminated.length) return;
  38. }
  39. kill(unterminated, 'SIGKILL');
  40. }
  41. function getChildPids(pid) {
  42. return new Promise(resolve => {
  43. pstree(pid, (err, children) => {
  44. // if get children error, just ignore it
  45. if (err) children = [];
  46. resolve(children.map(children => parseInt(children.PID)));
  47. });
  48. });
  49. }
  50. function kill(pids, signal) {
  51. for (const pid of pids) {
  52. try {
  53. process.kill(pid, signal);
  54. } catch (_) {
  55. // ignore
  56. }
  57. }
  58. }
  59. function getUnterminatedProcesses(pids) {
  60. return pids.filter(pid => {
  61. try {
  62. // success means it's still alive
  63. process.kill(pid, 0);
  64. return true;
  65. } catch (err) {
  66. // error means it's dead
  67. return false;
  68. }
  69. });
  70. }