diff --git a/NEWS b/NEWS index a0bce4ed6f43..85da9f475ec9 100644 --- a/NEWS +++ b/NEWS @@ -172,6 +172,8 @@ PHP NEWS . Fixed quoted_printable_decode() truncating input at NUL bytes. (Weilin Du) . Fixed version_compare() silently truncating version strings at NUL bytes by rejecting such input. (Weilin Du) + . Dispatch asynchronous signal handlers while time_sleep_until() waits. + (Nicolas Grekas) . Fixed bug #60110 (fclose(), file_put_contents(), copy() do not return false properly). (Jakub Zelenka, Ilija Tovilo) . Fixed three Windows-only proc_open() defects: an uninitialized diff --git a/UPGRADING b/UPGRADING index d9242ecb3c17..c96b800ee7e3 100644 --- a/UPGRADING +++ b/UPGRADING @@ -63,6 +63,11 @@ PHP 8.7 UPGRADE NOTES . bindec(), octdec(), hexdec() and base_convert() now throw an Exception when the passed string contains characters that are invalid for the specified number base. + . With pcntl_async_signals(true), time_sleep_until() now runs signal + handlers before the sleep ends. Previously, handlers waited until the + deadline. If a handler returns normally, the sleep resumes until the + original deadline. A handler can now interrupt the sleep by throwing an + exception or calling exit. . The number of filters that can be chained in a php://filter URL is limited to 16 by default. Set the stream context option max_filter_count to change this. diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index db302ab724ab..38ed73752cbb 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1257,6 +1257,12 @@ PHP_FUNCTION(time_sleep_until) if (errno != EINTR) { RETURN_FALSE; } + if (UNEXPECTED(atomic_load(&EG(vm_interrupt)))) { + zend_fcall_interrupt(execute_data); + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } + } zend_hrtime_t elapsed = zend_hrtime() - start; if (elapsed >= diff_ns) { break; diff --git a/ext/standard/tests/misc/time_sleep_until_async_signals.phpt b/ext/standard/tests/misc/time_sleep_until_async_signals.phpt new file mode 100644 index 000000000000..51e6a1b4d31a --- /dev/null +++ b/ext/standard/tests/misc/time_sleep_until_async_signals.phpt @@ -0,0 +1,30 @@ +--TEST-- +time_sleep_until() dispatches async signals and keeps the original deadline +--EXTENSIONS-- +pcntl +--SKIPIF-- + +--FILE-- += $deadline); +var_dump(microtime(true) < $deadline + 0.8); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) diff --git a/ext/standard/tests/misc/time_sleep_until_async_signals_exception.phpt b/ext/standard/tests/misc/time_sleep_until_async_signals_exception.phpt new file mode 100644 index 000000000000..9ad0df39c4b6 --- /dev/null +++ b/ext/standard/tests/misc/time_sleep_until_async_signals_exception.phpt @@ -0,0 +1,29 @@ +--TEST-- +An async signal handler can throw during time_sleep_until() +--EXTENSIONS-- +pcntl +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; +} +?> +--EXPECT-- +bool(true) +Interrupted diff --git a/ext/standard/tests/misc/time_sleep_until_async_signals_exit.phpt b/ext/standard/tests/misc/time_sleep_until_async_signals_exit.phpt new file mode 100644 index 000000000000..a0d879779db2 --- /dev/null +++ b/ext/standard/tests/misc/time_sleep_until_async_signals_exit.phpt @@ -0,0 +1,24 @@ +--TEST-- +An async signal handler can exit during time_sleep_until() +--EXTENSIONS-- +pcntl +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) diff --git a/ext/standard/tests/misc/time_sleep_until_async_signals_resume.phpt b/ext/standard/tests/misc/time_sleep_until_async_signals_resume.phpt new file mode 100644 index 000000000000..0dad0106d5ca --- /dev/null +++ b/ext/standard/tests/misc/time_sleep_until_async_signals_resume.phpt @@ -0,0 +1,30 @@ +--TEST-- +time_sleep_until() resumes until its deadline after an async signal handler returns +--EXTENSIONS-- +pcntl +--SKIPIF-- + +--FILE-- += 1_900_000_000); +?> +--EXPECT-- +bool(true) +bool(true) +bool(true) diff --git a/ext/standard/tests/misc/time_sleep_until_manual_signals.phpt b/ext/standard/tests/misc/time_sleep_until_manual_signals.phpt new file mode 100644 index 000000000000..aada70df57b4 --- /dev/null +++ b/ext/standard/tests/misc/time_sleep_until_manual_signals.phpt @@ -0,0 +1,27 @@ +--TEST-- +time_sleep_until() leaves manually dispatched signals pending +--EXTENSIONS-- +pcntl +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) +bool(false) +bool(true)