From:             jdolecek at NetBSD dot org
Operating system: Windows XP
PHP version:      5.2.4
PHP Bug Type:     Date/time related
Bug description:  microtime() loses time in long-running scripts

Description:
------------
When PHP script runs for long time, the value returned by microtime()
gradually drifts away from time(). The exact value depends on system, but
we've seen 5 hour difference to current time for script running about a
week, with drift about 1 minute per 5 minutes run (system running under
VMware). The script is daemon-like process, i.e. it's blocked in select()
most of time, and only occassionally wakes up to process request.

Looking at win32/time.c, I spot one problematic spot in that the code
might not notice counter wrap-off if timer happens to be a higher value
after wrap-around then the last time the function was called. However, that
shouldn't be normally problem, since the counter wraps-around very
infrequently.

Reproduce code:
---------------
<?php

ini_set('date.timezone', 'CET');
putenv("TZ=CET");

while(true) {
        $sec = microtime(true);

        echo "msec: ". sprintf("[%s.%04d]",
                                date("Y-m-d H:i:s", floor($sec)),
                                (float) ($sec - floor($sec)) *
10000)."\n";
        echo "sec : ".date('[Y-m-d H:i:s]')."\n";

        sleep(300);
}

C program which demostrates how the two times diverge (cut&paste from
win32/time.c, so please forgive whitespace/style):

#include <windows.h>
#include <winbase.h>
#include <time.h>
#include <winsock2.h>

int getfilesystemtime(struct timeval *time_Info)
{
        FILETIME ft;
        __int64 ff;
        LARGE_INTEGER li;

    GetSystemTimeAsFileTime(&ft);   /* 100 ns blocks since 01-Jan-1641 */
                                    /* resolution seems to be 0.01 sec */
    li.u.LowPart = ft.dwLowDateTime;
    li.u.HighPart = ft.dwHighDateTime;
    ff = li.QuadPart;

    time_Info->tv_sec =
(int)(ff/(__int64)10000000-(__int64)11644473600ULL);
    time_Info->tv_usec = (int)(ff % 10000000)/10;
    return 0;
}

#define PW32G(x)        (x)

int
main()
{
        LARGE_INTEGER li;
        __int64 freq, timer;
        double dt;
        struct timeval tv, *time_Info = &tv, starttime, tv2;

        QueryPerformanceFrequency(&li);
        freq = li.QuadPart;

        getfilesystemtime(&starttime);
                        QueryPerformanceCounter(&li);
                    timer = li.QuadPart;
                    dt = (double)timer/PW32G(freq);
                    PW32G(starttime).tv_usec -=
(int)((dt-(int)dt)*1000000);
                    if (PW32G(starttime).tv_usec < 0) {
                        PW32G(starttime).tv_usec += 1000000;
                        --PW32G(starttime).tv_sec;
                    }
                    PW32G(starttime).tv_sec -= (int)dt;


        while(1) {
                sleep(10);

        QueryPerformanceCounter(&li);
        getfilesystemtime(&tv2);

        timer = li.QuadPart;
                    dt = (double)timer/PW32G(freq);
                    time_Info->tv_sec = PW32G(starttime).tv_sec +
(int)dt;
                    time_Info->tv_usec = PW32G(starttime).tv_usec +
(int)((dt-(int)dt)*1000000);
                    if (time_Info->tv_usec > 1000000) {
                        time_Info->tv_usec -= 1000000;
                        ++time_Info->tv_sec;
                    }

                printf("%ld.%06ld\n",
                        tv2.tv_sec - time_Info->tv_sec,
                        tv2.tv_usec - time_Info->tv_usec);

        }
}


Expected result:
----------------
the time on 'msec' line should be same as for 'sec' line. Since the second
date() call fetches time after the microtime() call, the 'sec' line may
print time 1 second after the 'msec' line occassionally, but the times
should match most of the time.

The C program should print ~constant time difference.

Actual result:
--------------
The longer the script runs, the bigger difference between the 'msec' and
'sec' time.

Example output (after about 12 hours):

msec: [2007-09-13 12:22:25.7074]
sec : [2007-09-13 12:22:26]
msec: [2007-09-13 12:27:25.6848]
sec : [2007-09-13 12:27:26]
msec: [2007-09-13 12:32:25.6624]
sec : [2007-09-13 12:32:26]

It _appears_ microtime() loses about 0.02 seconds on each 5 minute
measurement on this machine, but this may be just local value and the exact
value could differ on different hardware.

C program output (from start):
0.002466
0.002998
0.003723

It appears performance counter based time loses about 0.0006 sec on each
10 second measurement compared to GetSystemTimeAsFileTime(). This matches
the difference observed in PHP script.


-- 
Edit bug report at http://bugs.php.net/?id=42659&edit=1
-- 
Try a CVS snapshot (PHP 4.4): 
http://bugs.php.net/fix.php?id=42659&r=trysnapshot44
Try a CVS snapshot (PHP 5.2): 
http://bugs.php.net/fix.php?id=42659&r=trysnapshot52
Try a CVS snapshot (PHP 6.0): 
http://bugs.php.net/fix.php?id=42659&r=trysnapshot60
Fixed in CVS:                 http://bugs.php.net/fix.php?id=42659&r=fixedcvs
Fixed in release:             
http://bugs.php.net/fix.php?id=42659&r=alreadyfixed
Need backtrace:               http://bugs.php.net/fix.php?id=42659&r=needtrace
Need Reproduce Script:        http://bugs.php.net/fix.php?id=42659&r=needscript
Try newer version:            http://bugs.php.net/fix.php?id=42659&r=oldversion
Not developer issue:          http://bugs.php.net/fix.php?id=42659&r=support
Expected behavior:            http://bugs.php.net/fix.php?id=42659&r=notwrong
Not enough info:              
http://bugs.php.net/fix.php?id=42659&r=notenoughinfo
Submitted twice:              
http://bugs.php.net/fix.php?id=42659&r=submittedtwice
register_globals:             http://bugs.php.net/fix.php?id=42659&r=globals
PHP 3 support discontinued:   http://bugs.php.net/fix.php?id=42659&r=php3
Daylight Savings:             http://bugs.php.net/fix.php?id=42659&r=dst
IIS Stability:                http://bugs.php.net/fix.php?id=42659&r=isapi
Install GNU Sed:              http://bugs.php.net/fix.php?id=42659&r=gnused
Floating point limitations:   http://bugs.php.net/fix.php?id=42659&r=float
No Zend Extensions:           http://bugs.php.net/fix.php?id=42659&r=nozend
MySQL Configuration Error:    http://bugs.php.net/fix.php?id=42659&r=mysqlcfg

Reply via email to