You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: cross-platform force-kill primitive for stuck PHP threads
Introduces a small, self-contained primitive that unblocks a PHP thread
stuck in a blocking call (sleep, synchronous I/O, etc.) so the graceful
drain used by RestartWorkers and DrainWorkers can make progress instead
of waiting for the block to return on its own. The primitive is useful
on its own and gives follow-up graceful-shutdown work a reviewed
foundation to build on.
- frankenphp.c: add frankenphp_init_force_kill / frankenphp_save_php_timer
/ frankenphp_force_kill_thread / frankenphp_destroy_force_kill. The
per-thread PHP timer handle (Linux/FreeBSD ZTS) or OS thread handle
(Windows) is captured at thread boot and stored in a pre-sized array
so the kill path can fire from any goroutine without touching
per-thread PHP state. Linux/FreeBSD arm PHP's max_execution_time timer
(delivers SIGALRM -> "Maximum execution time exceeded"); Windows uses
CancelSynchronousIo + QueueUserAPC to interrupt I/O and alertable
waits; macOS and other platforms are a safe no-op (the thread is
abandoned and exits when the blocking call returns naturally).
- phpmainthread.go: wire frankenphp_init_force_kill into initPHPThreads
(sized to maxThreads, matching the thread_metrics allocation) and
frankenphp_destroy_force_kill into drainPHPThreads.
- worker.go: add a 5-second graceful-drain grace period to
drainWorkerThreads. Once elapsed, arm the force-kill primitive on any
thread still outside Yielding and keep waiting on ready.Wait(); the
kill lets the thread return from its blocking call so the drain
completes in bounded time instead of hanging.
- worker_test.go + testdata/worker-sleep.php: TestRestartWorkersForceKillsStuckThread
drives the path end-to-end. A worker blocks inside sleep(60) below
frankenphp_handle_request (so drainChan close can't reach it); the
test asserts RestartWorkers returns within 8s (grace + slack). The
test skips on platforms without the underlying primitive.
0 commit comments