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
ext/pcntl: do not drop queued signals when an exception is pending (#23624)
* ext/pcntl: run signal handlers when dispatch happens with an exception pending
ZEND_DO_FCALL runs its interrupt check right after an internal function returns,
before the pending exception is handled, and zend_call_function() does the same
for the calls it makes, so pcntl_interrupt_function() can reach the dispatcher
with EG(exception) set. call_user_function() returns without calling anything in
that state, the "if (EG(exception)) break" added by 296fad1 fires on the
first entry, and the drain loop then recycles the whole queue without a single
handler having run. The signal is destroyed rather than delayed: a later
pcntl_signal_dispatch() finds nothing left.
Set the exception aside while the handlers run and chain it back afterwards.
The frame is left as found, EG(opline_before_exception) included: depending on
the caller, the exception may not be registered on it yet, or may already be on
its way to a catch block, and the caller finishes the job once we return.
zend_test_raise_and_throw() and the user frame test are from Arnaud Le Blanc.
Any long blocking internal call that throws on timeout reaches this. pecl/amqp
throws "Consumer timeout exceed" out of AMQPQueue::consume(), which makes a
Symfony messenger worker miss every SIGTERM whatever the timeout is. PDO/SQLite
throws "database is locked" once busy_timeout expires, which kills a keepalive
SIGALRM for the rest of the process's life.
* ext/pcntl: keep the signals a throwing handler left in the queue
When a handler threw, the signals queued behind it were recycled without ever
being delivered. Put them back on the queue instead, and re-arm the interrupt so
that the engine dispatches them once the exception has been handled, rather than
leaving them to wait for another signal to come in.
Not calling further handlers while the exception propagates is unchanged.
0 commit comments