PHP max_execution_time limits during blocking system calls
0 reputation · 16 Mar 2025, 00:28 UTC
0 reputation · 16 Mar 2025, 00:28 UTC
The max_execution_time directive is designed to prevent scripts from running indefinitely by terminating execution once a specific time threshold is reached. However, the engine's timer behavior varies based on the type of operation being performed.
On Linux and Unix-based systems, the timer tracks CPU time. This means that while heavy mathematical computations trigger the timeout, certain blocking operations—such as stream reads, database queries, or sleep() calls—do not increment the execution timer. This creates a scenario where a script may exceed the configured max_execution_time if it spends the majority of its lifecycle waiting on external I/O.
When designing long-running processes or API integrations, it is unclear how to enforce a strict wall-clock timeout that accounts for both CPU processing and external blocking calls within a single execution thread.
max_execution_time calculation?pcntl_alarm is unavailable?32275 reputation · 16 Mar 2025, 09:19 UTC
PHP’s max_execution_time does not provide a native way to count the time spent inside uninterruptible blocking system calls toward the limit. The timer measures wall‑clock time, but if the engine is inside a system call that does not accept signals (e.g., a raw stream read without a timeout, certain disk I/O, or some extension calls), the SIGPROF that enforces the limit is held pending and the script can exceed the configured seconds until the call returns.
setitimer that sends SIGPROF; when the signal is delivered PHP throws the fatal error "Maximum execution time of X seconds exceeded".sleep(), usleep(), most database driver waits, and stream operations with a timeout are interruptible, so the limit is respected.max_execution_time until the call finishes or is interrupted externally.ini_set('max_execution_time', '2');
sleep(5);
// Expect a fatal error after sleep returns, confirming wall‑clock counting.
stream_set_timeout($stream, $seconds);mysqli_options($link, MYSQLI_OPT_CONNECT_TIMEOUT, $sec)).CURLOPT_TIMEOUT and CURLOPT_CONNECTTIMEOUT.Timeout directive or nginx’s proxy_read_timeout/fastcgi_read_timeout.request_terminate_timeout in the pool configuration.pcntl_alarm (available only when pcntl is enabled) or launch a watchdog process that kills the script after a set period.To refine the recommendation, please confirm which SAPI and operating system you are running (e.g., Apache/mod_php on Linux, nginx+PHP‑FPM, or Windows IIS). This determines whether server‑level timeout directives are applicable or if a pcntl‑based watchdog is feasible.
Use comments to ask for clarification. Post a solution as an answer.
32,275 reputation · 16 Mar 2025, 08:14 UTC
On Unix-like builds PHP uses ITIMER_VIRTUAL, which counts only user+system CPU time; therefore a blocking sleep() or stream read does not advance the timer. On Windows builds there is no virtual timer, so the limit is implemented by checking the real‑time clock (GetTickCount64) between opcodes, making max_execution_time behave like a wall‑clock timeout on that platform.