phpdaemonpcntl

What's a "good" setting for PHP ticks with pcntl_signal?


I'm running a PHP daemon with a signal handler to do an orderly cleanup and to reconfigure without stopping:

declare(ticks = 5);
function sig_handler($signo)
{
    ...
}
pcntl_signal(SIGHUP, 'sig_handler');
pcntl_signal(SIGINT, 'sig_handler');
...

A tick is an event that occurs for every N low-level tickable statements executed by the parser within the declare block... Not all statements are tickable. Typically, condition expressions and argument expressions are not tickable.

If we declare ticks = 1 then the signal will be checked between most lines of executing PHP code, which seems inefficient. So,

(Side note: The formal specs are even less specific, as this is probably interpreter-dependent.)


Solution

  • The following are my findings, I cannot provide you any reference to documentation to prove my statements. However, I learned the way the PHP interpreter handles the ticks and the signals by reading the PHP's source code (the pcntl extension).

    • If we set ticks = 5, does that mean we have a 4 in 5 chance the signal handler won't be called at all?

    PHP is an interpreted language. Your PHP signal handlers are not called by the OS to handle the signals. The interpreter registers a signal handler with the OS and puts all the signals it receives into a queue.

    The OS signals come asynchronous. The interpreter dispatches the signals to the handlers defined by you when it's the most appropriate time for it. This happens between the low-level tickable statements.

    declare(ticks = 5); makes the signals dispatcher run once every 5 tickable statements. The signals that arrive during two calls to the dispatcher are not lost; they are added to the queue and processed on the next call.

    The code of the signals dispatcher is pretty light; it is not a big overhead to use declare(ticks = 1).

    • How can I determine a consistent but efficient setting?

    It depends on what your program does. There is no recipe for that. Try different settings, pick the one that suits you best.

    • What happens during blocking calls, like database queries? Is the signal thrown away or processed when it returns?

    As I said above, the signals come and are processed asynchronously by the signal handler installed by the interpreter. The handling only puts the signals in the queue. They are picked from the queue and dispatched to your code when it's the most appropriate moment for the interpreter (and your PHP code too). Nothing is lost.

    Regarding the database, I only checked (some years ago) with mysql. My code was using pcntl_fork() to create worker processes and was processing SIGCHLD to keep track of workers activity. For mysterious reasons it was failing on the MySQL queries frequently with the message "connection lost" without any apparent reason. After extensive investigation and reading of a lot of documentation, I found out that the signals make the functions from the sleep() family return early and that makes the code of the mysql client library think the connection was lost.

    The solution was very simple. I used pcntl_sigprocmask() to block the SIGCHLD signal before running a MySQL query and unblock it immediately after the query completes. I was not interested in other signals. Letting them come would lead to process termination; why bothering about a failed MySQL query when the program was about to exit anyway?

    I don't know if the signals affect the operation of mysqli or PDO but I think they do. The operation that was affected in my case was part of the low-level communication between the MySQL client and server, deep down in the libmysql and not in the PHP extension.

    In your case, you probably need to block SIGHUP (this is the signal that is usually sent to the daemons to make them re-load their configuration).