Bug #69044 [Ana]: time_sleep_until() starts to early

From: Date: Sat, 14 Feb 2015 13:32:56 +0000
Subject: Bug #69044 [Ana]: time_sleep_until() starts to early
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-190682@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=69044&edit=1

 ID:                 69044
 User updated by:    marc at gutt dot it
 Reported by:        marc at gutt dot it
 Summary:            time_sleep_until() starts to early
 Status:             Analyzed
 Type:               Bug
 Package:            Date/time related
 Operating System:   Debian
 PHP Version:        5.4.37
 Block user comment: N
 Private report:     N

 New Comment:

Yes, date() seems to have a bug:
<?php
function udate($format, $timestamp=null) {
	if (!isset($timestamp)) $timestamp = microtime();
	// microtime(true)
	if (count($t = explode(" ", $timestamp)) == 1) {
		list($timestamp, $usec) = explode(".", $timestamp);
		$usec = "." . $usec;
	}
	// microtime() is more precise
	else {
		$usec = $t[0];
		$timestamp = $t[1];
	}
	// 7 decimal places for "u" is maximum
	$usec = substr(sprintf('%.7f', $usec), 1);
	$date = new DateTime(date('Y-m-d H:i:s' . $usec, $timestamp));
	return $date->format($format);
}
echo "### first test ###\n";
$sleep_until = round(microtime(true)) + 3.002;
echo "sleep: " . udate("Y-m-d\TH:i:s.u", $sleep_until) . "\n";
time_sleep_until($sleep_until);
echo "udate: " . udate("Y-m-d\TH:i:s.u", microtime(true)) . "\n";
echo "date : " . date("Y-m-d\TH:i:s") . "\n";
echo "### second test ###\n";
$sleep_until = round(microtime(true)) + 3.003;
echo "sleep: " . udate("Y-m-d\TH:i:s.u", $sleep_until) . "\n";
time_sleep_until($sleep_until);
echo "udate: " . udate("Y-m-d\TH:i:s.u", microtime(true)) . "\n";
echo "date : " . date("Y-m-d\TH:i:s") . "\n";
/* results:
### first test ###
sleep: 2015-02-14T14:29:51.002000
udate: 2015-02-14T14:29:51.002100
date : 2015-02-14T14:29:50
### second test ###
sleep: 2015-02-14T14:29:54.003000
udate: 2015-02-14T14:29:54.003100
date : 2015-02-14T14:29:54
*/
?>


Previous Comments:
------------------------------------------------------------------------
[2015-02-14 02:00:02] requinix@php.net

By the way, the code works fine on Windows: time() - $sleep_until = 0 consistently. Tried the same
code on Ubuntu 14.04 and would get -1 consistently.

------------------------------------------------------------------------
[2015-02-14 01:44:44] yohgaki@php.net

You can see it's sleeping as it supposed by replacing date() to microtime()
<?php
$sleep_until = microtime(true) + 1;
echo "sleep until " . $sleep_until ."\n";
time_sleep_until($sleep_until);
echo "woke up at " . microtime(true);
?>

------------------------------------------------------------------------
[2015-02-14 01:39:34] yohgaki@php.net

My previous analysis is wrong.
time_sleep_until() sleeps as it supposed. It seems date() is doing something wrong.

------------------------------------------------------------------------
[2015-02-14 00:47:54] yohgaki@php.net

Oops. Round margin should be something like 0.99. Or use ceil() to be precise.

------------------------------------------------------------------------
[2015-02-14 00:45:32] yohgaki@php.net

The cause of this is floating point calculation. It's rounded.
Possible patch is

diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c
index 2b70414..44a41e4 100644
--- a/ext/standard/basic_functions.c
+++ b/ext/standard/basic_functions.c
@@ -4486,8 +4486,8 @@ PHP_FUNCTION(time_sleep_until)
        if (php_req.tv_sec > c_ts) { /* rounding up occurred */
                php_req.tv_sec--;
        }
-       /* 1sec = 1000000000 nanoseconds */
-       php_req.tv_nsec = (long) ((c_ts - php_req.tv_sec) * 1000000000.00);
+       /* 1sec = 1000000000 nanoseconds. Add 0.5 sec round margin */
+       php_req.tv_nsec = (long) ((c_ts - php_req.tv_sec) * 1000000000.00 + 0.5) ;
 
        while (nanosleep(&php_req, &php_rem)) {
                if (errno == EINTR) {

------------------------------------------------------------------------


The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at

    https://bugs.php.net/bug.php?id=69044


--
Edit this bug report at https://bugs.php.net/bug.php?id=69044&edit=1


Thread (11 messages)

« previous php.bugs (#190682) next »