From c482180f987d92b8d112a9bebdb68b9a5ca328bc Mon Sep 17 00:00:00 2001 From: hanshenrik Date: Thu, 15 Feb 2024 18:15:53 +0100 Subject: [PATCH 01/12] int|float for sleep --- ext/standard/basic_functions.c | 33 ++++++++++++++----- ext/standard/basic_functions_arginfo.h | 2 +- .../tests/general_functions/sleep_basic.phpt | 24 ++++++++++++++ 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index d62951b63ff2..2c7ff47c37ad 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1131,18 +1131,33 @@ PHP_FUNCTION(flush) /* {{{ Delay for a given number of seconds */ PHP_FUNCTION(sleep) { - zend_long num; + zval *num; ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_LONG(num) + Z_PARAM_NUMBER(num) ZEND_PARSE_PARAMETERS_END(); - - if (num < 0) { - zend_argument_value_error(1, "must be greater than or equal to 0"); - RETURN_THROWS(); - } - - RETURN_LONG(php_sleep((unsigned int)num)); + if (Z_TYPE_P(num) == IS_DOUBLE) { + const double seconds = Z_DVAL_P(num); + if (seconds < 0) { + zend_argument_value_error(1, "must be greater than or equal to 0"); + RETURN_THROWS(); + } +#ifdef HAVE_USLEEP + const unsigned int fraction_microseconds = (unsigned int)((seconds - (unsigned int)seconds) * 1000000); + if(fraction_microseconds > 0) { + usleep(fraction_microseconds); + } +#endif + RETURN_LONG(php_sleep((unsigned int)seconds)); + } else { + ZEND_ASSERT(Z_TYPE_P(num) == IS_LONG); + zend_long seconds = Z_LVAL_P(num); + if (seconds < 0) { + zend_argument_value_error(1, "must be greater than or equal to 0"); + RETURN_THROWS(); + } + RETURN_LONG(php_sleep((unsigned int)seconds)); + } } /* }}} */ diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 4efdcb54546b..cef8ec698808 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -406,7 +406,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_flush, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sleep, 0, 1, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, seconds, MAY_BE_LONG|MAY_BE_DOUBLE, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_usleep, 0, 1, IS_VOID, 0) diff --git a/ext/standard/tests/general_functions/sleep_basic.phpt b/ext/standard/tests/general_functions/sleep_basic.phpt index 98402ad8b21f..fdf2e9a22357 100644 --- a/ext/standard/tests/general_functions/sleep_basic.phpt +++ b/ext/standard/tests/general_functions/sleep_basic.phpt @@ -8,6 +8,16 @@ if (getenv("SKIP_SLOW_TESTS")) die("skip slow test"); = 0.009; // 0.009 is 9ms, we asked usleep to sleep for 10ms +} + + $sleeptime = 1; // sleep for 1 seconds set_time_limit(20); @@ -31,9 +41,23 @@ if ($time >= $sleeplow) { } else { echo "TEST FAILED - time is {$time} secs and sleep was {$sleeptime} secs\n"; } +if(!have_usleep()) { + // ¯\_(ツ)_/¯ + echo "FRACTIONAL SLEEP TEST PASSED\n"; +} else { + $time = microtime(true); + sleep(0.1); + $time = microtime(true) - $time; + if($time >= 0.09) { + echo "FRACTIONAL SLEEP TEST PASSED\n"; + } else { + echo "FRACTIONAL SLEEP TEST FAILED\n"; + } +} ?> --EXPECTF-- *** Testing sleep() : basic functionality *** Thread slept for %f seconds Return value: 0 TEST PASSED +FRACTIONAL SLEEP TEST PASSED From 91dec0fe5b710bf4f41eaffa94e00dc2c2669231 Mon Sep 17 00:00:00 2001 From: hanshenrik Date: Thu, 15 Feb 2024 18:26:06 +0100 Subject: [PATCH 02/12] forgot stub --- ext/standard/basic_functions.stub.php | 2 +- ext/standard/basic_functions_arginfo.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index ed73d16d6d0b..f0efb42e1a81 100755 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -1966,7 +1966,7 @@ function getopt(string $short_options, array $long_options = [], &$rest_index = function flush(): void {} -function sleep(int $seconds): int {} +function sleep(int|float $seconds): int {} function usleep(int $microseconds): void {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index cef8ec698808..107c1555bc61 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 0bd0ac5d23881670cac81cda3e274cbee1e9a8dc */ + * Stub hash: 6ac9cb16cab2587c70d71cc41a2e52fd28f64bd2 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -406,7 +406,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_flush, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sleep, 0, 1, IS_LONG, 0) - ZEND_ARG_TYPE_INFO(0, seconds, MAY_BE_LONG|MAY_BE_DOUBLE, 0) + ZEND_ARG_TYPE_MASK(0, seconds, MAY_BE_LONG|MAY_BE_DOUBLE, NULL) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_usleep, 0, 1, IS_VOID, 0) From 25bd8dcef67345046523deeb89d0dac84caa7f13 Mon Sep 17 00:00:00 2001 From: hanshenrik Date: Thu, 15 Feb 2024 18:44:03 +0100 Subject: [PATCH 03/12] nit --- ext/standard/basic_functions.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 2c7ff47c37ad..0a5c1962954a 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1158,6 +1158,7 @@ PHP_FUNCTION(sleep) } RETURN_LONG(php_sleep((unsigned int)seconds)); } + ZEND_UNREACHABLE(); } /* }}} */ From e2f096ec588f630917d54bc4ba87df55a857a0e8 Mon Sep 17 00:00:00 2001 From: hanshenrik Date: Thu, 15 Feb 2024 19:04:33 +0100 Subject: [PATCH 04/12] another nit --- ext/standard/basic_functions.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 0a5c1962954a..e244553cba60 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1138,7 +1138,7 @@ PHP_FUNCTION(sleep) ZEND_PARSE_PARAMETERS_END(); if (Z_TYPE_P(num) == IS_DOUBLE) { const double seconds = Z_DVAL_P(num); - if (seconds < 0) { + if (UNEXPECTED(seconds < 0)) { zend_argument_value_error(1, "must be greater than or equal to 0"); RETURN_THROWS(); } @@ -1152,7 +1152,7 @@ PHP_FUNCTION(sleep) } else { ZEND_ASSERT(Z_TYPE_P(num) == IS_LONG); zend_long seconds = Z_LVAL_P(num); - if (seconds < 0) { + if (UNEXPECTED(seconds < 0)) { zend_argument_value_error(1, "must be greater than or equal to 0"); RETURN_THROWS(); } From 3193a8882e873107753fad501a0f273256f19c34 Mon Sep 17 00:00:00 2001 From: hanshenrik Date: Thu, 15 Feb 2024 22:43:05 +0100 Subject: [PATCH 05/12] PR feedback fixes / formatting --- ext/standard/basic_functions.c | 54 +++++++++---------- .../tests/general_functions/sleep_basic.phpt | 2 +- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index e244553cba60..f3645f9f4503 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1129,36 +1129,34 @@ PHP_FUNCTION(flush) /* }}} */ /* {{{ Delay for a given number of seconds */ -PHP_FUNCTION(sleep) -{ - zval *num; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_NUMBER(num) - ZEND_PARSE_PARAMETERS_END(); - if (Z_TYPE_P(num) == IS_DOUBLE) { - const double seconds = Z_DVAL_P(num); - if (UNEXPECTED(seconds < 0)) { - zend_argument_value_error(1, "must be greater than or equal to 0"); - RETURN_THROWS(); - } +PHP_FUNCTION(sleep) { + zval *num; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_NUMBER(num) + ZEND_PARSE_PARAMETERS_END(); + if (Z_TYPE_P(num) == IS_DOUBLE) { + const double seconds = Z_DVAL_P(num); + if (UNEXPECTED(seconds < 0)) { + zend_argument_value_error(1, "must be greater than or equal to 0"); + RETURN_THROWS(); + } #ifdef HAVE_USLEEP - const unsigned int fraction_microseconds = (unsigned int)((seconds - (unsigned int)seconds) * 1000000); - if(fraction_microseconds > 0) { - usleep(fraction_microseconds); - } -#endif - RETURN_LONG(php_sleep((unsigned int)seconds)); - } else { - ZEND_ASSERT(Z_TYPE_P(num) == IS_LONG); - zend_long seconds = Z_LVAL_P(num); - if (UNEXPECTED(seconds < 0)) { - zend_argument_value_error(1, "must be greater than or equal to 0"); - RETURN_THROWS(); - } - RETURN_LONG(php_sleep((unsigned int)seconds)); + const unsigned int fraction_microseconds = + (unsigned int)((seconds - (unsigned int)seconds) * 1000000); + if (fraction_microseconds > 0) { + usleep(fraction_microseconds); } - ZEND_UNREACHABLE(); +#endif + RETURN_LONG(php_sleep((unsigned int)seconds)); + } + ZEND_ASSERT(Z_TYPE_P(num) == IS_LONG); // Z_PARAM_NUMBER(num) above guarantee that it's double or float or throw :) + zend_long seconds = Z_LVAL_P(num); + if (UNEXPECTED(seconds < 0)) { + zend_argument_value_error(1, "must be greater than or equal to 0"); + RETURN_THROWS(); + } + RETURN_LONG(php_sleep((unsigned int)seconds)); } /* }}} */ diff --git a/ext/standard/tests/general_functions/sleep_basic.phpt b/ext/standard/tests/general_functions/sleep_basic.phpt index fdf2e9a22357..164f9ac70c6c 100644 --- a/ext/standard/tests/general_functions/sleep_basic.phpt +++ b/ext/standard/tests/general_functions/sleep_basic.phpt @@ -41,7 +41,7 @@ if ($time >= $sleeplow) { } else { echo "TEST FAILED - time is {$time} secs and sleep was {$sleeptime} secs\n"; } -if(!have_usleep()) { +if (!have_usleep()) { // ¯\_(ツ)_/¯ echo "FRACTIONAL SLEEP TEST PASSED\n"; } else { From c50d9bb3494d900821761fa20e53b22e6844cf30 Mon Sep 17 00:00:00 2001 From: hanshenrik Date: Thu, 15 Feb 2024 22:55:53 +0100 Subject: [PATCH 06/12] nit --- ext/standard/tests/general_functions/sleep_basic.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/tests/general_functions/sleep_basic.phpt b/ext/standard/tests/general_functions/sleep_basic.phpt index 164f9ac70c6c..6357a1860e1e 100644 --- a/ext/standard/tests/general_functions/sleep_basic.phpt +++ b/ext/standard/tests/general_functions/sleep_basic.phpt @@ -48,7 +48,7 @@ if (!have_usleep()) { $time = microtime(true); sleep(0.1); $time = microtime(true) - $time; - if($time >= 0.09) { + if ($time >= 0.09) { echo "FRACTIONAL SLEEP TEST PASSED\n"; } else { echo "FRACTIONAL SLEEP TEST FAILED\n"; From 2ebbc5f4ec6ca54a032767387eac10a4cf40c4c6 Mon Sep 17 00:00:00 2001 From: hanshenrik Date: Thu, 15 Feb 2024 23:15:21 +0100 Subject: [PATCH 07/12] check fractional sleep return value usleep() errors are ignored anyway, related discussion https://github.com/php/php-src/pull/13401#discussion_r1491637034 --- ext/standard/tests/general_functions/sleep_basic.phpt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/standard/tests/general_functions/sleep_basic.phpt b/ext/standard/tests/general_functions/sleep_basic.phpt index 6357a1860e1e..6d225ae3723f 100644 --- a/ext/standard/tests/general_functions/sleep_basic.phpt +++ b/ext/standard/tests/general_functions/sleep_basic.phpt @@ -46,8 +46,9 @@ if (!have_usleep()) { echo "FRACTIONAL SLEEP TEST PASSED\n"; } else { $time = microtime(true); - sleep(0.1); + $result = sleep(0.1); $time = microtime(true) - $time; + echo "Fractional sleep return value: " . $result . "\n"; if ($time >= 0.09) { echo "FRACTIONAL SLEEP TEST PASSED\n"; } else { @@ -60,4 +61,5 @@ if (!have_usleep()) { Thread slept for %f seconds Return value: 0 TEST PASSED +Fractional sleep return value: 0 FRACTIONAL SLEEP TEST PASSED From a928da9d45be0a4832f8ed724ad626eba435b1fc Mon Sep 17 00:00:00 2001 From: hanshenrik Date: Fri, 16 Feb 2024 02:55:03 +0100 Subject: [PATCH 08/12] sleep() use nanosleep() for high precision sleeping --- ext/standard/basic_functions.c | 28 +++++++++++++++++-- .../tests/general_functions/sleep_basic.phpt | 6 ++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index f3645f9f4503..bfc4cf327968 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1141,14 +1141,38 @@ PHP_FUNCTION(sleep) { zend_argument_value_error(1, "must be greater than or equal to 0"); RETURN_THROWS(); } -#ifdef HAVE_USLEEP +#ifdef HAVE_NANOSLEEP + time_t seconds_long = (time_t)seconds; + zend_long fraction_nanoseconds = (zend_long)((seconds - seconds_long) * 1000000000); + if(fraction_nanoseconds > 999999999) { + // for this to happen, you have to request to sleep for longer than + // 0.999999999 seconds and yet less than 1 second.. + // nanosleep() has a documented limit of 999999999 nanoseconds, so let's just round it up to 1 second. + // that means we'll be off by <=0.9 nanoseconds in this edge-case, probably close enough. + fraction_nanoseconds = 0; + seconds_long += 1; + } + struct timespec php_req, php_rem; + php_req.tv_sec = (time_t)seconds_long; + php_req.tv_nsec = fraction_nanoseconds; + const int result = nanosleep(&php_req, &php_rem); + if(UNEXPECTED(result == -1)) { + ZEND_ASSERT(errno != EINVAL); // this should be impossible, we carefully checked the input above + // it's probably EINTR + RETURN_DOUBLE(php_rem.tv_sec + (double)php_rem.tv_nsec / 1000000000.0); + } + RETURN_LONG(0); +#elif defined(HAVE_USLEEP) const unsigned int fraction_microseconds = (unsigned int)((seconds - (unsigned int)seconds) * 1000000); if (fraction_microseconds > 0) { usleep(fraction_microseconds); } -#endif RETURN_LONG(php_sleep((unsigned int)seconds)); +#else + // avoid -Werror=unreachable-code + RETURN_LONG(php_sleep((unsigned int)seconds)); +#endif } ZEND_ASSERT(Z_TYPE_P(num) == IS_LONG); // Z_PARAM_NUMBER(num) above guarantee that it's double or float or throw :) zend_long seconds = Z_LVAL_P(num); diff --git a/ext/standard/tests/general_functions/sleep_basic.phpt b/ext/standard/tests/general_functions/sleep_basic.phpt index 6d225ae3723f..ad7d39da6a51 100644 --- a/ext/standard/tests/general_functions/sleep_basic.phpt +++ b/ext/standard/tests/general_functions/sleep_basic.phpt @@ -44,6 +44,7 @@ if ($time >= $sleeplow) { if (!have_usleep()) { // ¯\_(ツ)_/¯ echo "FRACTIONAL SLEEP TEST PASSED\n"; + echo "Fractional sleep return value: 0\n"; } else { $time = microtime(true); $result = sleep(0.1); @@ -52,6 +53,11 @@ if (!have_usleep()) { if ($time >= 0.09) { echo "FRACTIONAL SLEEP TEST PASSED\n"; } else { + var_dump([ + 'time' => $time, + 'result' => $result, + 'expected_time' => 0.1 + ]); echo "FRACTIONAL SLEEP TEST FAILED\n"; } } From 0ee2341a840d3818c7d7f77b897408a5b211ca7f Mon Sep 17 00:00:00 2001 From: hanshenrik Date: Fri, 16 Feb 2024 09:35:49 +0100 Subject: [PATCH 09/12] update sleep return stub --- ext/standard/basic_functions.stub.php | 2 +- ext/standard/basic_functions_arginfo.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index f0efb42e1a81..dad5dd6332d0 100755 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -1966,7 +1966,7 @@ function getopt(string $short_options, array $long_options = [], &$rest_index = function flush(): void {} -function sleep(int|float $seconds): int {} +function sleep(int|float $seconds): int|float {} function usleep(int $microseconds): void {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 107c1555bc61..50bbe62f75ae 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6ac9cb16cab2587c70d71cc41a2e52fd28f64bd2 */ + * Stub hash: 69866c737bb7c648881254350a83bc2cc646f14e */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -405,7 +405,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_flush, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_sleep, 0, 1, IS_LONG, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_sleep, 0, 1, MAY_BE_LONG|MAY_BE_DOUBLE) ZEND_ARG_TYPE_MASK(0, seconds, MAY_BE_LONG|MAY_BE_DOUBLE, NULL) ZEND_END_ARG_INFO() From d64a8ccdc1d21576827059ee86c0fa073c95ffcc Mon Sep 17 00:00:00 2001 From: hanshenrik Date: Fri, 16 Feb 2024 09:42:54 +0100 Subject: [PATCH 10/12] readability --- ext/standard/basic_functions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index bfc4cf327968..d9bf78e8f708 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1159,7 +1159,7 @@ PHP_FUNCTION(sleep) { if(UNEXPECTED(result == -1)) { ZEND_ASSERT(errno != EINVAL); // this should be impossible, we carefully checked the input above // it's probably EINTR - RETURN_DOUBLE(php_rem.tv_sec + (double)php_rem.tv_nsec / 1000000000.0); + RETURN_DOUBLE(php_rem.tv_sec + (((double)php_rem.tv_nsec) / 1000000000.0)); } RETURN_LONG(0); #elif defined(HAVE_USLEEP) From b617b44cd9956340eadf245093c9e984b60cbc5d Mon Sep 17 00:00:00 2001 From: divinity76 Date: Fri, 16 Feb 2024 21:33:37 +0100 Subject: [PATCH 11/12] Update ext/standard/tests/general_functions/sleep_basic.phpt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michael Voříšek --- ext/standard/tests/general_functions/sleep_basic.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/tests/general_functions/sleep_basic.phpt b/ext/standard/tests/general_functions/sleep_basic.phpt index ad7d39da6a51..276ecde59659 100644 --- a/ext/standard/tests/general_functions/sleep_basic.phpt +++ b/ext/standard/tests/general_functions/sleep_basic.phpt @@ -43,8 +43,8 @@ if ($time >= $sleeplow) { } if (!have_usleep()) { // ¯\_(ツ)_/¯ - echo "FRACTIONAL SLEEP TEST PASSED\n"; echo "Fractional sleep return value: 0\n"; + echo "FRACTIONAL SLEEP TEST PASSED\n"; } else { $time = microtime(true); $result = sleep(0.1); From 8b42db62e2088bb6ad6823bb52bc88875da7eb09 Mon Sep 17 00:00:00 2001 From: hanshenrik Date: Sat, 17 Feb 2024 03:51:54 +0100 Subject: [PATCH 12/12] formatting formatted with the script from https://github.com/php/php-src/pull/13414 --- ext/standard/basic_functions.c | 96 +++++++++++++++++----------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 320cbcc7433e..db68b727e51b 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -1128,58 +1128,58 @@ PHP_FUNCTION(flush) /* }}} */ /* {{{ Delay for a given number of seconds */ -PHP_FUNCTION(sleep) { - zval *num; - - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_NUMBER(num) - ZEND_PARSE_PARAMETERS_END(); - if (Z_TYPE_P(num) == IS_DOUBLE) { - const double seconds = Z_DVAL_P(num); - if (UNEXPECTED(seconds < 0)) { - zend_argument_value_error(1, "must be greater than or equal to 0"); - RETURN_THROWS(); - } +PHP_FUNCTION(sleep) +{ + zval* num; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_NUMBER(num) + ZEND_PARSE_PARAMETERS_END(); + if (Z_TYPE_P(num) == IS_DOUBLE) { + const double seconds = Z_DVAL_P(num); + if (UNEXPECTED(seconds < 0)) { + zend_argument_value_error(1, "must be greater than or equal to 0"); + RETURN_THROWS(); + } #ifdef HAVE_NANOSLEEP - time_t seconds_long = (time_t)seconds; - zend_long fraction_nanoseconds = (zend_long)((seconds - seconds_long) * 1000000000); - if(fraction_nanoseconds > 999999999) { - // for this to happen, you have to request to sleep for longer than - // 0.999999999 seconds and yet less than 1 second.. - // nanosleep() has a documented limit of 999999999 nanoseconds, so let's just round it up to 1 second. - // that means we'll be off by <=0.9 nanoseconds in this edge-case, probably close enough. - fraction_nanoseconds = 0; - seconds_long += 1; - } - struct timespec php_req, php_rem; - php_req.tv_sec = (time_t)seconds_long; - php_req.tv_nsec = fraction_nanoseconds; - const int result = nanosleep(&php_req, &php_rem); - if(UNEXPECTED(result == -1)) { - ZEND_ASSERT(errno != EINVAL); // this should be impossible, we carefully checked the input above - // it's probably EINTR - RETURN_DOUBLE(php_rem.tv_sec + (((double)php_rem.tv_nsec) / 1000000000.0)); - } - RETURN_LONG(0); + time_t seconds_long = (time_t)seconds; + zend_long fraction_nanoseconds = (zend_long)((seconds - seconds_long) * 1000000000); + if (fraction_nanoseconds > 999999999) { + // for this to happen, you have to request to sleep for longer than + // 0.999999999 seconds and yet less than 1 second.. + // nanosleep() has a documented limit of 999999999 nanoseconds, so let's just round it up to 1 second. + // that means we'll be off by <=0.9 nanoseconds in this edge-case, probably close enough. + fraction_nanoseconds = 0; + seconds_long += 1; + } + struct timespec php_req, php_rem; + php_req.tv_sec = (time_t)seconds_long; + php_req.tv_nsec = fraction_nanoseconds; + const int result = nanosleep(&php_req, &php_rem); + if (UNEXPECTED(result == -1)) { + ZEND_ASSERT(errno != EINVAL); // this should be impossible, we carefully checked the input above + // it's probably EINTR + RETURN_DOUBLE(php_rem.tv_sec + (((double)php_rem.tv_nsec) / 1000000000.0)); + } + RETURN_LONG(0); #elif defined(HAVE_USLEEP) - const unsigned int fraction_microseconds = - (unsigned int)((seconds - (unsigned int)seconds) * 1000000); - if (fraction_microseconds > 0) { - usleep(fraction_microseconds); - } - RETURN_LONG(php_sleep((unsigned int)seconds)); + const unsigned int fraction_microseconds = (unsigned int)((seconds - (unsigned int)seconds) * 1000000); + if (fraction_microseconds > 0) { + usleep(fraction_microseconds); + } + RETURN_LONG(php_sleep((unsigned int)seconds)); #else - // avoid -Werror=unreachable-code - RETURN_LONG(php_sleep((unsigned int)seconds)); + // avoid -Werror=unreachable-code + RETURN_LONG(php_sleep((unsigned int)seconds)); #endif - } - ZEND_ASSERT(Z_TYPE_P(num) == IS_LONG); // Z_PARAM_NUMBER(num) above guarantee that it's double or float or throw :) - zend_long seconds = Z_LVAL_P(num); - if (UNEXPECTED(seconds < 0)) { - zend_argument_value_error(1, "must be greater than or equal to 0"); - RETURN_THROWS(); - } - RETURN_LONG(php_sleep((unsigned int)seconds)); + } + ZEND_ASSERT(Z_TYPE_P(num) == IS_LONG); // Z_PARAM_NUMBER(num) above guarantee that it's double or float or throw :) + zend_long seconds = Z_LVAL_P(num); + if (UNEXPECTED(seconds < 0)) { + zend_argument_value_error(1, "must be greater than or equal to 0"); + RETURN_THROWS(); + } + RETURN_LONG(php_sleep((unsigned int)seconds)); } /* }}} */