Skip to content

Commit 660afca

Browse files
committed
MFB: Fix for #32974
1 parent 13ba056 commit 660afca

File tree

3 files changed

+77
-41
lines changed

3 files changed

+77
-41
lines changed

ext/pcntl/pcntl.c

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -162,17 +162,13 @@ void php_register_signal_constants(INIT_FUNC_ARGS)
162162

163163
static void php_pcntl_init_globals(zend_pcntl_globals *pcntl_globals)
164164
{
165-
/* Just in case ... */
166-
memset(&pcntl_globals->php_signal_queue,0,sizeof(pcntl_globals->php_signal_queue));
167-
168-
zend_llist_init(&pcntl_globals->php_signal_queue, sizeof (long), NULL, 1);
169-
pcntl_globals->signal_queue_ready = 0;
170-
pcntl_globals->processing_signal_queue = 0;
165+
memset(pcntl_globals, 0, sizeof(*pcntl_globals));
171166
}
172167

173168
PHP_RINIT_FUNCTION(pcntl)
174169
{
175-
zend_hash_init(&PCNTL_G(php_signal_table), 16, NULL, ZVAL_PTR_DTOR, 1);
170+
zend_hash_init(&PCNTL_G(php_signal_table), 16, NULL, ZVAL_PTR_DTOR, 0);
171+
PCNTL_G(head) = PCNTL_G(tail) = PCNTL_G(spares) = NULL;
176172
return SUCCESS;
177173
}
178174

@@ -187,13 +183,26 @@ PHP_MINIT_FUNCTION(pcntl)
187183

188184
PHP_MSHUTDOWN_FUNCTION(pcntl)
189185
{
190-
zend_llist_destroy(&PCNTL_G(php_signal_queue));
191186
return SUCCESS;
192187
}
193188

194189
PHP_RSHUTDOWN_FUNCTION(pcntl)
195190
{
191+
struct php_pcntl_pending_signal *sig;
192+
193+
/* FIXME: if a signal is delivered after this point, things will go pear shaped;
194+
* need to remove signal handlers */
196195
zend_hash_destroy(&PCNTL_G(php_signal_table));
196+
while (PCNTL_G(head)) {
197+
sig = PCNTL_G(head);
198+
PCNTL_G(head) = sig->next;
199+
efree(sig);
200+
}
201+
while (PCNTL_G(spares)) {
202+
sig = PCNTL_G(spares);
203+
PCNTL_G(spares) = sig->next;
204+
efree(sig);
205+
}
197206
return SUCCESS;
198207
}
199208

@@ -520,6 +529,19 @@ PHP_FUNCTION(pcntl_signal)
520529
return;
521530
}
522531

532+
if (!PCNTL_G(spares)) {
533+
/* since calling malloc() from within a signal handler is not portable,
534+
* pre-allocate a few records for recording signals */
535+
int i;
536+
for (i = 0; i < 32; i++) {
537+
struct php_pcntl_pending_signal *psig;
538+
539+
psig = emalloc(sizeof(*psig));
540+
psig->next = PCNTL_G(spares);
541+
PCNTL_G(spares) = psig;
542+
}
543+
}
544+
523545
/* Special long value case for SIG_DFL and SIG_IGN */
524546
if (Z_TYPE_P(handle)==IS_LONG) {
525547
if (Z_LVAL_P(handle)!= (long) SIG_DFL && Z_LVAL_P(handle) != (long) SIG_IGN) {
@@ -631,57 +653,63 @@ PHP_FUNCTION(pcntl_setpriority)
631653
/* Our custom signal handler that calls the appropriate php_function */
632654
static void pcntl_signal_handler(int signo)
633655
{
634-
long signal_num = signo;
656+
struct php_pcntl_pending_signal *psig;
635657
TSRMLS_FETCH();
636658

637-
IF_DEBUG(DEBUG_OUT("Caught signo %d\n", signo));
638-
if (! PCNTL_G(processing_signal_queue)) {
639-
zend_llist_add_element(&PCNTL_G(php_signal_queue), &signal_num);
640-
PCNTL_G(signal_queue_ready) = 1;
641-
IF_DEBUG(DEBUG_OUT("Added queue entry\n"));
659+
psig = PCNTL_G(spares);
660+
if (!psig) {
661+
/* oops, too many signals for us to track, so we'll forget about this one */
662+
return;
663+
}
664+
PCNTL_G(spares) = psig->next;
665+
666+
psig->signo = signo;
667+
psig->next = NULL;
668+
669+
/* the head check is important, as the tick handler cannot atomically clear both
670+
* the head and tail */
671+
if (PCNTL_G(head) && PCNTL_G(tail)) {
672+
PCNTL_G(tail)->next = psig;
673+
} else {
674+
PCNTL_G(head) = psig;
642675
}
643-
return;
676+
PCNTL_G(tail) = psig;
644677
}
645678

646679
void pcntl_tick_handler()
647680
{
648-
zend_llist_element *element;
649681
zval *param, **handle, *retval;
682+
struct php_pcntl_pending_signal *queue, *next;
650683
TSRMLS_FETCH();
651684

652685
/* Bail if the queue is empty or if we are already playing the queue*/
653-
if (! PCNTL_G(signal_queue_ready) || PCNTL_G(processing_signal_queue))
686+
if (! PCNTL_G(head) || PCNTL_G(processing_signal_queue))
654687
return;
655688

656-
/* Mark our queue empty */
657-
PCNTL_G(signal_queue_ready) = 0;
658-
659-
/* If for some reason our signal queue is empty then return */
660-
if (zend_llist_count(&PCNTL_G(php_signal_queue)) <= 0) {
661-
return;
662-
}
663-
664689
/* Prevent reentrant handler calls */
665690
PCNTL_G(processing_signal_queue) = 1;
666691

692+
queue = PCNTL_G(head);
693+
PCNTL_G(head) = NULL; /* simple stores are atomic */
694+
667695
/* Allocate */
668696
MAKE_STD_ZVAL(param);
669697
MAKE_STD_ZVAL(retval);
670698

671-
/* Traverse through our signal queue and call the appropriate php functions */
672-
for (element = (&PCNTL_G(php_signal_queue))->head; element; element = element->next) {
673-
long *signal_num = (long *)&element->data;
674-
if (zend_hash_index_find(&PCNTL_G(php_signal_table), *signal_num, (void **) &handle)==FAILURE) {
675-
continue;
699+
while (queue) {
700+
if (zend_hash_index_find(&PCNTL_G(php_signal_table), queue->signo, (void **) &handle)==SUCCESS) {
701+
ZVAL_LONG(param, queue->signo);
702+
703+
/* Call php signal handler - Note that we do not report errors, and we ignore the return value */
704+
/* FIXME: this is probably broken when multiple signals are handled in this while loop (retval) */
705+
call_user_function(EG(function_table), NULL, *handle, retval, 1, &param TSRMLS_CC);
676706
}
677707

678-
ZVAL_LONG(param, *signal_num);
679-
680-
/* Call php signal handler - Note that we do not report errors, and we ignore the return value */
681-
call_user_function(EG(function_table), NULL, *handle, retval, 1, &param TSRMLS_CC);
708+
next = queue->next;
709+
queue->next = PCNTL_G(spares);
710+
PCNTL_G(spares) = queue;
711+
queue = next;
682712
}
683-
/* Clear */
684-
zend_llist_clean(&PCNTL_G(php_signal_queue));
685713

686714
/* Re-enable queue */
687715
PCNTL_G(processing_signal_queue) = 0;
@@ -691,6 +719,8 @@ void pcntl_tick_handler()
691719
efree(retval);
692720
}
693721

722+
723+
694724
/*
695725
* Local variables:
696726
* tab-width: 4

ext/pcntl/php_pcntl.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,17 @@ PHP_FUNCTION(pcntl_getpriority);
5858
PHP_FUNCTION(pcntl_setpriority);
5959
#endif
6060

61+
struct php_pcntl_pending_signal {
62+
struct php_pcntl_pending_signal *next;
63+
long signo;
64+
};
65+
6166
ZEND_BEGIN_MODULE_GLOBALS(pcntl)
6267
HashTable php_signal_table;
63-
zend_llist php_signal_queue;
64-
int signal_queue_ready;
6568
int processing_signal_queue;
69+
struct php_pcntl_pending_signal *head, *tail, *spares;
6670
ZEND_END_MODULE_GLOBALS(pcntl)
71+
6772
#ifdef ZTS
6873
#define PCNTL_G(v) TSRMG(pcntl_globals_id, zend_pcntl_globals *, v)
6974
#else

ext/pcntl/test-pcntl.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
declare(ticks=1);
55

66
function alarm_handle($signal){
7-
if ($signal==SIGALRM) print "Caught SIGALRM!!!\n";
7+
if ($signal==SIGALRM) print "Child: Caught SIGALRM!!!\n";
88
}
99

1010
function usr1_handle($signal){
11-
if ($signal==SIGUSR1) print "Caught SIGUSR1!!!\n";
11+
if ($signal==SIGUSR1) print "Child: Caught SIGUSR1!!!\n";
1212
}
1313

1414
print "This test will demonstrate a fork followed by ipc via signals.\n";
@@ -23,6 +23,7 @@ function usr1_handle($signal){
2323
sleep(100);
2424
print "Child: Resetting Alarm handler to Ignore....\n";
2525
pcntl_signal(SIGALRM, SIG_IGN);
26+
print "Child: sleeping for 10 seconds....\n";
2627
sleep(10);
2728
print "Done\n";
2829
} else {
@@ -33,7 +34,7 @@ function usr1_handle($signal){
3334
sleep(1);
3435
print "Parent: Senging SIGUSR1 to Child\n";
3536
posix_kill($pid,SIGUSR1);
36-
sleep(1);
37+
sleep(2);
3738
print "Parent: Sending SIGALRM to Child\n";
3839
pcntl_waitpid($pid, &$status, $options);
3940
}

0 commit comments

Comments
 (0)