Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Forked process cannot capture any process events "exit", "SIGINT", "SIGTERM" #3467

Closed
stanleyxu2005 opened this issue Feb 15, 2018 · 2 comments

Comments

@stanleyxu2005
Copy link

stanleyxu2005 commented Feb 15, 2018

Environment: Windows 7 (x64) + Node 9.5.0

The Problem

CODE: app.js

setInterval(() => {
  console.log(new Date(), 'alive')
}, 1000)

process.on('SIGINT', () => {
  console.log('SIGINT, byebye')
  process.exit(0)
})

process.on('SIGTERM', () => {
  console.log('SIGTERM, byebye')
  process.exit(1)
})

process.on('exit', (code, signal) => {
  console.log('Process exited')
})

If I run node app.js and then press CTRL+C, I can see SIGINT, byebye.

But if I run the code with PM2

npx pm2 start app.js
npx pm2 stop app
npx pm2 log app

Related messages are not appear in log. It seems that none of these events (exit, SIGINT and SIGTERM) are called. It seems the nodejs process is not killed gracefully. (Correct me if I'm wrong)

My Investigation and Questions

I know Windows does not handle posix signals. But we still need to find a way to properly kill forked process, right? If a nodejs process cannot be killed gracefully, how can we make sure other non nodejs process can be killed well?

After some investigation, I found this question on SO. The trick is

  1. Spawn process with {detached:true, stdio: 'ignore'}
  2. Create a readline interface in app.js and manually send a process.emit('SIGINT') in close` event.

Here is how PM2 forks a process.

      try {
        var cspr = spawn(command, args, {
          env      : pm2_env,
          detached : true,
          windowsHide: true,
          cwd      : pm2_env.pm_cwd || process.cwd(),
          stdio    : ['pipe', 'pipe', 'pipe', 'ipc'] //Same as fork() in node core
        });
      } catch(e) {
        God.logAndGenerateError(e);
        return cb(e);
      }
      ...
      cspr.unref();

As you can see, stdio is pipe, not ignore. I need to understand can PM2 developer change pipe to ignore so that forked process can be killed gracefully?

Workaround

As I only use PM2 as a pure process manager, no monitoring tasks, no deploy tasks. So for the time being, I made some small changes without having a serious problem. Here is what I have done:

  1. Changed stdio to 'ignore' in ForkMode.js
  2. Added the following code to the end of ProcessContainerFork.js
// Looks like when subprocess is forked, realine.on('close') will be called immediately. 
// So delay to monitor the close event. TODO: Need to understand how stdin get closed.
setTimeout(function() {
    var readline = require('readline');
    var rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
    });
    rl.once('close', function() {
        process.emit('SIGINT')
    });
}, 1000);

I dont think my patch is a good patch. Help is needed.

@stanleyxu2005 stanleyxu2005 changed the title Cannot capture process exit event Forked process cannot capture any process events "exit", "SIGINT", "SIGTERM" event Feb 17, 2018
@stanleyxu2005 stanleyxu2005 changed the title Forked process cannot capture any process events "exit", "SIGINT", "SIGTERM" event Forked process cannot capture any process events "exit", "SIGINT", "SIGTERM" Feb 17, 2018
@stanleyxu2005
Copy link
Author

It could be invalid. Close for now.

@khelkun
Copy link

khelkun commented Jul 8, 2020

I definitely need this too e.g an option to prevent pm2 to treekill all the detached processes spawned by my nodejs application on Windows. My nodejs application spawns processes this way:

        const childProc = spawn(processPath, appConfig.binArgs,
        {
            stdio: "ignore",
            detached: true,
            cwd: appInstallDir
        });

This is explained on options.detached of the nodejs child process documentation

When using the detached option to start a long-running process, the process will not stay running in the background after the parent exits unless it is provided with a stdio configuration that is not connected to the parent. If the parent's stdio is inherited, the child will remain attached to the controlling terminal.

An option for the pm2 fork_mode could allow to change the options.stdio here) to "ignore":

      try {
        var options = {
          env      : pm2_env,
          detached : true,
          cwd      : pm2_env.pm_cwd || process.cwd(),
          stdio    : 'ignore'
        }

        if (typeof(pm2_env.windowsHide) === "boolean") {
          options.windowsHide = pm2_env.windowsHide;
        } else {
          options.windowsHide = true;
        }

        if (pm2_env.uid) {
          options.uid = pm2_env.uid
        }

        if (pm2_env.gid) {
          options.gid = pm2_env.gid
        }

        var cspr = spawn(command, args, options);
      } catch(e) {
        God.logAndGenerateError(e);
        return cb(e);
      }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants