phpsignalsexitpcntl

Can I rely on register_shutdown_function() being called on SIGTERM, if pcntl_signal() is set up?


I am working on an application that has periodically-called background processes. One of these was being called by cron, but I am looking for something more robust, so am converting it to run under Supervisor. (It will probably run for 10 minutes, during which time it can detect work to do, or idle. Once it exits, Supervisor will automatically respawn a clean instance.)

Since Supervisor is better at ensuring that only a specified number of instances of something are running in parallel, I can get away with running them longer. This does mean however that my processes are more likely to receive termination signals, either from kill directly, or because they have been stopped via Supervisor. I am therefore experimenting with how to handle this in PHP.

It looks like the basic solution is to use pcntl_signal() like so:

declare(ticks = 1);
pcntl_signal(SIGTERM, 'signalHandler');
pcntl_signal(SIGINT, 'signalHandler');

function signalHandler($signal) {
    switch($signal) {
        case SIGTERM:
        case SIGINT:
            echo "Exiting now...\n";
            exit();
    }
}

However, I have several points in my code that could do with careful shutdown handling. One approach is to make one handler call those various things, but it would require a bit of refactoring, which I would like to avoid. The alternative is to add pcntl_signal() everywhere I need it, but unfortunately it seems only one handler can be installed at once.

However, it looks like I might be able to use register_shutdown_function(). This does not trap ^C or other termination sigs on its own, and the manual is quite clear on this point:

Shutdown functions will not be executed if the process is killed with a SIGTERM or SIGKILL signal

What is surprising is that I have found that if I employ pcntl_signal() to just do an exit, then the shutdown handlers are indeed called. Furthermore, since one can have many shutdown handlers, this solves my problem nicely - each class in my code that wishes to handle termination gracefully can capture and manage its own shutdown.

My initial question is, why does this work? I have tried registering a shutdown function without a signal handler, and this does not seem to be called, as the manual says. I guess the process is kept alive by PHP in order to handle the signal, which causes shutdown handlers to be called?

Also, can I rely on this behaviour, when the manual casts doubt on it? I am using PHP 5.5, and am not looking to upgrade to PHP7 just yet. So I'd be interested in whether this works on 5.5 and 5.6, on a variety of distributions.

Of course, whether it would (or would not) work on 7.0 and/or 7.1, would be interesting too - I am aware though that ticks will be handled differently in 7.1, so there is a greater chance of this having a different behaviour.


Solution

  • PHP user shutdown functions are called during ordinary process termination, somewhat analogous to functions registered with atexit in other programming languages. An explicit exit or implicit exit at end-of-script is an ordinary process termination.

    Signal death, however, is abnormal process termination. The default behavior for SIGTERM (and the mandatory behavior for SIGKILL) is to terminate the running process immediately, without running any cleanup handlers.

    When you intercept a SIGTERM with pcntl_signal, you are installing different behavior and giving yourself the opportunity to experience ordinary process termination.

    Yes, you can rely on this behavior. While the main manual entry is not explicit, the rest of the "Note" you quote reads:

    [Y]ou can use pcntl_signal() to install a handler for a SIGTERM which uses exit() to end cleanly.