Ignore:
Timestamp:
Sep 17, 2008, 4:54:31 PM (17 years ago)
Author:
[email protected]
Message:

Improve timer accuracy for JavaScript Date object on Windows.


Use a combination of ftime and QueryPerformanceCounter.
ftime returns the information we want, but doesn't have sufficient resolution.
QueryPerformanceCounter has high resolution, but is only usable to measure time intervals.
To combine them, we call ftime and QueryPerformanceCounter initially. Later calls will use
QueryPerformanceCounter by itself, adding the delta to the saved ftime. We re-sync to
correct for drift if the low-res and high-res elapsed time between calls differs by more
than twice the low-resolution timer resolution.


QueryPerformanceCounter may be inaccurate due to a problems with:


Reviewed by Darin Adler.

  • kjs/DateMath.cpp: (JSC::highResUpTime): (JSC::lowResUTCTime): (JSC::qpcAvailable): (JSC::getCurrentUTCTimeWithMicroseconds):
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/kjs/DateMath.cpp

    r36263 r36578  
    288288}
    289289
    290 double getCurrentUTCTimeWithMicroseconds()
    291 {
    292290#if PLATFORM(WIN_OS)
    293     // FIXME: the implementation for Windows is only millisecond resolution.
    294 #if COMPILER(BORLAND)
    295     struct timeb timebuffer;
    296     ftime(&timebuffer);
    297 #else
     291
     292static LARGE_INTEGER qpcFrequency;
     293static bool syncedTime;
     294
     295static double highResUpTime()
     296{
     297    // We use QPC, but only after sanity checking its result, due to bugs:
     298    // http://support.microsoft.com/kb/274323
     299    // http://support.microsoft.com/kb/895980
     300    // http://msdn.microsoft.com/en-us/library/ms644904.aspx ("...you can get different results on different processors due to bugs in the basic input/output system (BIOS) or the hardware abstraction layer (HAL)."
     301
     302    static LARGE_INTEGER qpcLast;
     303    static DWORD tickCountLast;
     304    static bool inited;
     305
     306    LARGE_INTEGER qpc;
     307    QueryPerformanceCounter(&qpc);
     308    DWORD tickCount = GetTickCount();
     309
     310    if (inited) {
     311        __int64 qpcElapsed = ((qpc.QuadPart - qpcLast.QuadPart) * 1000) / qpcFrequency.QuadPart;
     312        __int64 tickCountElapsed;
     313        if (tickCount >= tickCountLast)
     314            tickCountElapsed = (tickCount - tickCountLast);
     315        else {
     316            __int64 tickCountLarge = tickCount + 0x100000000I64;
     317            tickCountElapsed = tickCountLarge - tickCountLast;
     318        }
     319
     320        // force a re-sync if QueryPerformanceCounter differs from GetTickCount by more than 500ms.
     321        // (500ms value is from http://support.microsoft.com/kb/274323)
     322        __int64 diff = tickCountElapsed - qpcElapsed;
     323        if (diff > 500 || diff < -500)
     324            syncedTime = false;
     325    } else
     326        inited = true;
     327
     328    qpcLast = qpc;
     329    tickCountLast = tickCount;
     330
     331    return (1000.0 * qpc.QuadPart) / static_cast<double>(qpcFrequency.QuadPart);;
     332}
     333
     334static double lowResUTCTime()
     335{
    298336    struct _timeb timebuffer;
    299337    _ftime(&timebuffer);
    300 #endif
    301     double utc = timebuffer.time * msPerSecond + timebuffer.millitm;
     338    return timebuffer.time * msPerSecond + timebuffer.millitm;
     339}
     340
     341static bool qpcAvailable()
     342{
     343    static bool available;
     344    static bool checked;
     345
     346    if (checked)
     347        return available;
     348
     349    available = QueryPerformanceFrequency(&qpcFrequency);
     350    checked = true;
     351    return available;
     352}
     353
     354#endif
     355
     356double getCurrentUTCTimeWithMicroseconds()
     357{
     358#if PLATFORM(WIN_OS)
     359    // Use a combination of ftime and QueryPerformanceCounter.
     360    // ftime returns the information we want, but doesn't have sufficient resolution.
     361    // QueryPerformanceCounter has high resolution, but is only usable to measure time intervals.
     362    // To combine them, we call ftime and QueryPerformanceCounter initially. Later calls will use QueryPerformanceCounter
     363    // by itself, adding the delta to the saved ftime.  We periodically re-sync to correct for drift.
     364    static bool started;
     365    static double syncLowResUTCTime;
     366    static double syncHighResUpTime;
     367    static double lastUTCTime;
     368
     369    double lowResTime = lowResUTCTime();
     370
     371    if (!qpcAvailable())
     372        return lowResTime;
     373
     374    double highResTime = highResUpTime();
     375
     376    if (!syncedTime) {
     377        timeBeginPeriod(1); // increase time resolution around low-res time getter
     378        syncLowResUTCTime = lowResTime = lowResUTCTime();
     379        timeEndPeriod(1); // restore time resolution
     380        syncHighResUpTime = highResTime;
     381        syncedTime = true;
     382    }
     383
     384    double highResElapsed = highResTime - syncHighResUpTime;
     385    double utc = syncLowResUTCTime + highResElapsed;
     386
     387    // force a clock re-sync if we've drifted
     388    double lowResElapsed = lowResTime - syncLowResUTCTime;
     389    const double maximumAllowedDriftMsec = 15.625 * 2.0; // 2x the typical low-res accuracy
     390    if (fabs(highResElapsed - lowResElapsed) > maximumAllowedDriftMsec)
     391        syncedTime = false;
     392
     393    // make sure time doesn't run backwards (only correct if difference is < 2 seconds, since DST or clock changes could occur)
     394    const double backwardTimeLimit = 2000.0;
     395    if (utc < lastUTCTime && (lastUTCTime - utc) < backwardTimeLimit)
     396        return lastUTCTime;
     397    lastUTCTime = utc;
    302398#else
    303399    struct timeval tv;
Note: See TracChangeset for help on using the changeset viewer.