1

I have a software written in PHP which can run for a long time, it's lauched via command line (not a web application). I wanted to make sure to call a function when the software exit, including when killed via ctrl+c. When killed by a signal, the shutdown functions (destructors and functions registered via register_shutdown_fucntion() ) are not called.

After some reading, I realized I had to add a handler on every relevant signal just to call "exit;". Doing so solved my problem and works.

My issue is that in some case, the signal is not handled right away. It can take seconds, minutes and in some case, is not handled at all. Just like if the handler code was never reached. I don't really know where to start to debug that. I've tried others signals (sigkill, sighup, etc), same behaviour.

My code is withing these brackets

declare(ticks=1)
{...}

I can't find any correlation between the time it get treated right away and the ones that it doesn't.

Any help would be appreciated.

2 Answers2

2

The signal should be handled this way : First, you have to make a signal handler like this :

function signalHandler($signo = null) {
    $pid = posix_getpid();
    switch ($signo) {
        //case SIGKILL: // you can't override SIGKILL so it is useless
        case SIGTERM:
        case SIGINT:
            // unexpected shut down
            exit(3);
            break;
        case SIGCHLD:
        case SIGHUP:
            // just ignore it
            break;
        case 10:
            // user signal 1 received. Process exited normally
            exit(0);
            break;
        case 12:
            // user signal 2 received. Precess exited with a catched error
            exit(3);
            break;
        default:
            // ignore other signals
    }
}

then, in your main process, you have to tell PCNTL you are using this handler :

    pcntl_signal(SIGTERM, "signalHandler");
    pcntl_signal(SIGINT, "signalHandler");
//  pcntl_signal(SIGKILL, "signalHandler"); // you can't override SIGKILL so it is useless
    pcntl_signal(SIGCHLD, "signalHandler");
    pcntl_signal(SIGHUP, "signalHandler");
    pcntl_signal(10, "signalHandler");
    pcntl_signal(12, "signalHandler");

I'm using this and seems to work for 200 process, 8 process running at the same time, and 10 sec for each process.

Here, when a process exits, he sends a signal 10 or 12 (for success or error) using posix_kill($pid, 10)

Random
  • 3,158
  • 1
  • 15
  • 25
  • Let me point out that the "SIGKILL" part of the switch statement is totally unnecessary, since "SIGKILL" is an uncatchable signal thus the execution will never reach that section. This is a safety measure in unix. Even if your code fails to process SIGTERM/SIGHUP/etc. for some reason, the SIGKILL will always remain as a last resort to shut down the process. – Gergely Lukacsy Jan 06 '16 at 16:27
  • @GergelyLukacsy Indeed, SIGKILL cannot be catched, it is maid to be able to kill a process whatever it does. So for a malicious program, it would be a bad thing if it could catch the SIGKILL signal (and so ignore it). So SIGTERM and SIGINT mean you ask the program "can you please shut down ?", making it able to safely close connections etc..., and SIGKILL is "I removed your alimentation cable, you WILL shut down, whatever you were doing". I removed it from the switch, thanks – Random Jan 06 '16 at 17:12
1

I think the problem here is that maybe you make a call to a function that has loop in it and every iteration of the loop takes a lot of time (for example, fetching data from database). In this case, when your program receives a stop signal, it adds this signal to stack, waits, until data will be fetched from database, and then calls signal handler function.

I'm also looking for a way to immediately call signal handler function. Maybe PHP just doesn't have this option.

UPDATE:

I think I found the solution. Try to pass false as the 3rd argument to function pcntl_signal() like this:

pcntl_signal(SIGINT, function () {
    echo 'Do something here.';
}, false);