@@ -207,7 +207,6 @@ PHP_RINIT_FUNCTION(pcntl)
207207{
208208 php_add_tick_function (pcntl_signal_dispatch_tick_function , NULL );
209209 zend_hash_init (& PCNTL_G (php_signal_table ), 16 , NULL , ZVAL_PTR_DTOR , 0 );
210- PCNTL_G (head ) = PCNTL_G (tail ) = PCNTL_G (spares ) = NULL ;
211210 PCNTL_G (async_signals ) = 0 ;
212211 PCNTL_G (last_error ) = 0 ;
213212 PCNTL_G (num_signals ) = NSIG ;
@@ -237,7 +236,6 @@ PHP_MINIT_FUNCTION(pcntl)
237236
238237PHP_RSHUTDOWN_FUNCTION (pcntl )
239238{
240- struct php_pcntl_pending_signal * sig ;
241239 zend_long signo ;
242240 zval * handle ;
243241
@@ -250,15 +248,10 @@ PHP_RSHUTDOWN_FUNCTION(pcntl)
250248
251249 zend_hash_destroy (& PCNTL_G (php_signal_table ));
252250
253- while (PCNTL_G (head )) {
254- sig = PCNTL_G (head );
255- PCNTL_G (head ) = sig -> next ;
256- efree (sig );
257- }
258- while (PCNTL_G (spares )) {
259- sig = PCNTL_G (spares );
260- PCNTL_G (spares ) = sig -> next ;
261- efree (sig );
251+ if (PCNTL_G (pending_signals_queue )) {
252+ efree (PCNTL_G (pending_signals_queue ));
253+ PCNTL_G (pending_signals_queue ) = NULL ;
254+ PCNTL_G (pending_signals ) = false;
262255 }
263256
264257 efree (PCNTL_G (restart_syscalls ));
@@ -820,16 +813,10 @@ PHP_FUNCTION(pcntl_signal)
820813 RETURN_THROWS ();
821814 }
822815
823- if (!PCNTL_G (spares )) {
824- /* since calling malloc() from within a signal handler is not portable,
825- * pre-allocate a few records for recording signals */
826- for (unsigned int i = 0 ; i < PCNTL_G (num_signals ); i ++ ) {
827- struct php_pcntl_pending_signal * psig ;
828-
829- psig = emalloc (sizeof (* psig ));
830- psig -> next = PCNTL_G (spares );
831- PCNTL_G (spares ) = psig ;
832- }
816+ if (!PCNTL_G (pending_signals_queue )) {
817+ zend_atomic_int_store_ex (& PCNTL_G (pending_signals_head ), 0 );
818+ zend_atomic_int_store_ex (& PCNTL_G (pending_signals_tail ), 0 );
819+ PCNTL_G (pending_signals_queue ) = safe_emalloc (PCNTL_G (num_signals ), sizeof (struct php_pcntl_pending_signal ), 0 );
833820 }
834821
835822 /* If restart_syscalls was not explicitly specified and the signal is SIGALRM, then default
@@ -1344,86 +1331,87 @@ PHP_FUNCTION(pcntl_strerror)
13441331/* Our custom signal handler that calls the appropriate php_function */
13451332static void pcntl_signal_handler (int signo , siginfo_t * siginfo , void * context )
13461333{
1347- struct php_pcntl_pending_signal * psig = PCNTL_G (spares );
1348- if (!psig ) {
1334+ /* Only pcntl_signal_handler() modifies pending_signals_tail.
1335+ * Signals are masked during execution. */
1336+
1337+ int tail = zend_atomic_int_load_ex (& PCNTL_G (pending_signals_tail ));
1338+ int next_tail = (tail + 1 ) % PCNTL_G (num_signals );
1339+
1340+ if (next_tail == zend_atomic_int_load_ex (& PCNTL_G (pending_signals_head ))) {
13491341 /* oops, too many signals for us to track, so we'll forget about this one */
13501342 return ;
13511343 }
1352- PCNTL_G (spares ) = psig -> next ;
13531344
1354- psig -> signo = signo ;
1355- psig -> next = NULL ;
1345+ struct php_pcntl_pending_signal * psig = & PCNTL_G (pending_signals_queue )[tail ];
13561346
1347+ psig -> signo = signo ;
13571348 psig -> siginfo = * siginfo ;
13581349
1359- /* the head check is important, as the tick handler cannot atomically clear both
1360- * the head and tail */
1361- if (PCNTL_G (head ) && PCNTL_G (tail )) {
1362- PCNTL_G (tail )-> next = psig ;
1363- } else {
1364- PCNTL_G (head ) = psig ;
1365- }
1366- PCNTL_G (tail ) = psig ;
1350+ zend_atomic_int_store_ex (& PCNTL_G (pending_signals_tail ), next_tail );
1351+
13671352 PCNTL_G (pending_signals ) = true;
13681353 if (PCNTL_G (async_signals )) {
13691354 zend_atomic_bool_store_ex (& EG (vm_interrupt ), true);
13701355 }
13711356}
13721357
1358+ static bool pcntl_signal_dequeue (struct php_pcntl_pending_signal * sig )
1359+ {
1360+ /* Only pcntl_signal_dequeue() modifies pending_signals_head.
1361+ * pcntl_signal_dequeue() is never called concurrently with itself. */
1362+
1363+ int head = zend_atomic_int_load_ex (& PCNTL_G (pending_signals_head ));
1364+
1365+ if (head == zend_atomic_int_load_ex (& PCNTL_G (pending_signals_tail ))) {
1366+ return false; /* Queue is empty */
1367+ }
1368+
1369+ * sig = PCNTL_G (pending_signals_queue )[head ];
1370+
1371+ zend_atomic_int_store_ex (& PCNTL_G (pending_signals_head ), (head + 1 ) % PCNTL_G (num_signals ));
1372+
1373+ return true;
1374+ }
1375+
13731376zend_signal_interrupt_result pcntl_signal_dispatch (void )
13741377{
13751378 zval params [2 ], * handle , retval ;
1376- struct php_pcntl_pending_signal * queue , * next ;
1377- sigset_t mask ;
1378- sigset_t old_mask ;
1379+ struct php_pcntl_pending_signal sig ;
13791380 bool interrupt = false;
13801381
13811382 if (!PCNTL_G (pending_signals )) {
13821383 return ZEND_SIGNAL_RESTART ;
13831384 }
13841385
1385- /* Mask all signals */
1386- sigfillset (& mask );
1387- sigprocmask (SIG_BLOCK , & mask , & old_mask );
1388-
1389- /* Bail if the queue is empty or if we are already playing the queue */
1390- // TODO: For the purpose of EINTR handling, processing next signals here would be useful
1391- if (!PCNTL_G (head ) || PCNTL_G (processing_signal_queue )) {
1392- sigprocmask (SIG_SETMASK , & old_mask , NULL );
1393- return ZEND_SIGNAL_RESTART ;
1394- }
1386+ PCNTL_G (pending_signals ) = false;
13951387
13961388 /* Prevent switching fibers when handling signals */
13971389 zend_fiber_switch_block ();
13981390
1399- /* Prevent reentrant handler calls */
1400- PCNTL_G (processing_signal_queue ) = true;
1401-
1402- queue = PCNTL_G (head );
1403- PCNTL_G (head ) = NULL ; /* simple stores are atomic */
1404- PCNTL_G (tail ) = NULL ;
1405-
1406- while (queue ) {
1407- if ((handle = zend_hash_index_find (& PCNTL_G (php_signal_table ), queue -> signo )) != NULL ) {
1391+ /* Consume signals in FIFO order until the queue is empty. The queue may be
1392+ * consumed during the invocation of PHP signal handlers if new signals are
1393+ * delivered and PCNTL_G(pending_signals) is set. */
1394+ while (pcntl_signal_dequeue (& sig )) {
1395+ if ((handle = zend_hash_index_find (& PCNTL_G (php_signal_table ), sig .signo )) != NULL ) {
14081396 if (Z_TYPE_P (handle ) != IS_LONG ) {
1409- ZVAL_LONG (& params [0 ], queue -> signo );
1397+ ZVAL_LONG (& params [0 ], sig . signo );
14101398 array_init (& params [1 ]);
1411- pcntl_siginfo_to_zval (queue -> signo , & queue -> siginfo , & params [1 ]);
1399+ pcntl_siginfo_to_zval (sig . signo , & sig . siginfo , & params [1 ]);
14121400
14131401 call_user_function (NULL , NULL , handle , & retval , 2 , params );
14141402 zval_ptr_dtor (& params [1 ]);
14151403
14161404 if (Z_TYPE (retval ) == IS_OBJECT && Z_OBJCE (retval ) == SignalReturn_ce ) {
14171405 if (zend_enum_fetch_case_id (Z_OBJ (retval )) == ZEND_ENUM_Pcntl_SignalReturn_Interrupt ) {
14181406 interrupt = true;
1419- } else if (!zend_bitset_in (PCNTL_G (restart_syscalls ), queue -> signo )) {
1407+ } else if (!zend_bitset_in (PCNTL_G (restart_syscalls ), sig . signo )) {
14201408 interrupt = true;
14211409 }
14221410 } else if (Z_TYPE (retval ) > IS_NULL ) {
14231411 zend_type_error ("Signal handler must return a Pcntl\\SignalReturn or no value, %s returned" ,
14241412 zend_zval_value_name (& retval ));
14251413 interrupt = true;
1426- } else if (!zend_bitset_in (PCNTL_G (restart_syscalls ), queue -> signo )) {
1414+ } else if (!zend_bitset_in (PCNTL_G (restart_syscalls ), sig . signo )) {
14271415 interrupt = true;
14281416 }
14291417
@@ -1435,32 +1423,18 @@ zend_signal_interrupt_result pcntl_signal_dispatch(void)
14351423 }
14361424 }
14371425 }
1438-
1439- next = queue -> next ;
1440- queue -> next = PCNTL_G (spares );
1441- PCNTL_G (spares ) = queue ;
1442- queue = next ;
14431426 }
14441427
14451428 /* drain the remaining in case of exception thrown */
1446- while (queue ) {
1447- next = queue -> next ;
1448- queue -> next = PCNTL_G (spares );
1449- PCNTL_G (spares ) = queue ;
1450- queue = next ;
1429+ if (EG (exception )) {
1430+ while (pcntl_signal_dequeue (& sig )) {
1431+ /* pass */
1432+ }
14511433 }
14521434
1453- PCNTL_G (pending_signals ) = false;
1454-
1455- /* Re-enable queue */
1456- PCNTL_G (processing_signal_queue ) = false;
1457-
14581435 /* Re-enable fiber switching */
14591436 zend_fiber_switch_unblock ();
14601437
1461- /* return signal mask to previous state */
1462- sigprocmask (SIG_SETMASK , & old_mask , NULL );
1463-
14641438 return interrupt ? ZEND_SIGNAL_INTERRUPT : ZEND_SIGNAL_RESTART ;
14651439}
14661440
0 commit comments