php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #69044 time_sleep_until() starts to early
Submitted: 2015-02-12 21:08 UTC Modified: 2015-02-14 13:32 UTC
Votes:2
Avg. Score:3.0 ± 2.0
Reproduced:1 of 2 (50.0%)
Same Version:0 (0.0%)
Same OS:0 (0.0%)
From: marc at gutt dot it Assigned:
Status: Analyzed Package: Date/time related
PHP Version: 5.4.37 OS: Debian
Private report: No CVE-ID: None
Have you experienced this issue?
Rate the importance of this bug to you:

 [2015-02-12 21:08 UTC] marc at gutt dot it
Description:
------------
---
From manual page: http://www.php.net/function.time-sleep-until
---

time_sleep_until() starts one second to early.

Test script:
---------------
<?php
$sleep_until = mktime(date('H'), date('i'), date('s') + 15);
echo "sleep until " . date('c', $sleep_until) . "\n";
time_sleep_until($sleep_until);
echo "woke up at " . date('c');

/* returns:
sleep until 2015-02-12T22:01:19+01:00
woke up at 2015-02-12T22:01:18+01:00
*/
?>


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2015-02-14 00:45 UTC] yohgaki@php.net
-Status: Open +Status: Analyzed
 [2015-02-14 00:45 UTC] 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) {
 [2015-02-14 00:47 UTC] yohgaki@php.net
Oops. Round margin should be something like 0.99. Or use ceil() to be precise.
 [2015-02-14 01:39 UTC] 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 01:44 UTC] 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 02:00 UTC] 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 13:32 UTC] marc at gutt dot it
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
*/
?>
 
PHP Copyright © 2001-2017 The PHP Group
All rights reserved.
Last updated: Sun Nov 19 01:31:42 2017 UTC