Commit 81e45507 authored by bmeurer@chromium.org's avatar bmeurer@chromium.org

Always use timeGetTime() for TimeTicks::Now() on Windows.

This way, we also ensure that timeGetTime() is used for Time::Now(),
and thereby Date.now() even if GetTickCount64() is available.

Also add test coverage for Time::Now(), TimeTicks::Now() and
TimeTicks::HighResNow().

BUG=chromium:288924
TEST=cctest/test-timer
R=hpayer@chromium.org

Review URL: https://codereview.chromium.org/25468003

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@17080 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 9492c1b5
......@@ -595,9 +595,7 @@ int OS::GetUserTime(uint32_t* secs, uint32_t* usecs) {
// Returns current time as the number of milliseconds since
// 00:00:00 UTC, January 1, 1970.
double OS::TimeCurrentMillis() {
Win32Time t;
t.SetToCurrentTime();
return t.ToJSTime();
return Time::Now().ToJsTime();
}
......
......@@ -104,7 +104,7 @@ class ElapsedTimer V8_FINAL BASE_EMBEDDED {
private:
static V8_INLINE TimeTicks Now() {
TimeTicks now = TimeTicks::HighResNow();
TimeTicks now = TimeTicks::HighResolutionNow();
ASSERT(!now.IsNull());
return now;
}
......
......@@ -43,13 +43,6 @@
#include "win32-headers.h"
#endif
#if V8_OS_WIN
// Prototype for GetTickCount64() procedure.
extern "C" {
typedef ULONGLONG (WINAPI *GETTICKCOUNT64PROC)(void);
}
#endif
namespace v8 {
namespace internal {
......@@ -175,43 +168,43 @@ struct timespec TimeDelta::ToTimespec() const {
// periodically resync the internal clock to the system clock.
class Clock V8_FINAL {
public:
Clock() : initial_time_(CurrentWallclockTime()),
initial_ticks_(TimeTicks::Now()) {}
Clock() : initial_ticks_(GetSystemTicks()), initial_time_(GetSystemTime()) {}
Time Now() {
// This must be executed under lock.
LockGuard<Mutex> lock_guard(&mutex_);
// Time between resampling the un-granular clock for this API (1 minute).
const TimeDelta kMaxElapsedTime = TimeDelta::FromMinutes(1);
// Calculate the time elapsed since we started our timer.
TimeDelta elapsed = TimeTicks::Now() - initial_ticks_;
LockGuard<Mutex> lock_guard(&mutex_);
// Check if we don't need to synchronize with the wallclock yet.
if (elapsed.InMicroseconds() <= kMaxMicrosecondsToAvoidDrift) {
return initial_time_ + elapsed;
// Determine current time and ticks.
TimeTicks ticks = GetSystemTicks();
Time time = GetSystemTime();
// Check if we need to synchronize with the system clock due to a backwards
// time change or the amount of time elapsed.
TimeDelta elapsed = ticks - initial_ticks_;
if (time < initial_time_ || elapsed > kMaxElapsedTime) {
initial_ticks_ = ticks;
initial_time_ = time;
return time;
}
// Resynchronize with the wallclock.
initial_ticks_ = TimeTicks::Now();
initial_time_ = CurrentWallclockTime();
return initial_time_;
return initial_time_ + elapsed;
}
Time NowFromSystemTime() {
// This must be executed under lock.
LockGuard<Mutex> lock_guard(&mutex_);
// Resynchronize with the wallclock.
initial_ticks_ = TimeTicks::Now();
initial_time_ = CurrentWallclockTime();
initial_ticks_ = GetSystemTicks();
initial_time_ = GetSystemTime();
return initial_time_;
}
private:
// Time between resampling the un-granular clock for this API (1 minute).
static const int64_t kMaxMicrosecondsToAvoidDrift =
Time::kMicrosecondsPerMinute;
static TimeTicks GetSystemTicks() {
return TimeTicks::Now();
}
static Time CurrentWallclockTime() {
static Time GetSystemTime() {
FILETIME ft;
::GetSystemTimeAsFileTime(&ft);
return Time::FromFiletime(ft);
......@@ -223,9 +216,9 @@ class Clock V8_FINAL {
};
static LazyDynamicInstance<Clock,
DefaultCreateTrait<Clock>,
ThreadSafeInitOnceTrait>::type clock = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
static LazyStaticInstance<Clock,
DefaultConstructTrait<Clock>,
ThreadSafeInitOnceTrait>::type clock = LAZY_STATIC_INSTANCE_INITIALIZER;
Time Time::Now() {
......@@ -388,6 +381,7 @@ class TickClock {
public:
virtual ~TickClock() {}
virtual int64_t Now() = 0;
virtual bool IsHighResolution() = 0;
};
......@@ -440,42 +434,24 @@ class HighResolutionTickClock V8_FINAL : public TickClock {
int64_t ticks = (whole_seconds * Time::kMicrosecondsPerSecond) +
((leftover_ticks * Time::kMicrosecondsPerSecond) / ticks_per_second_);
// Make sure we never return 0 here, so that TimeTicks::HighResNow()
// Make sure we never return 0 here, so that TimeTicks::HighResolutionNow()
// will never return 0.
return ticks + 1;
}
private:
int64_t ticks_per_second_;
};
// The GetTickCount64() API is what we actually want for the regular tick
// clock, but this is only available starting with Windows Vista.
class WindowsVistaTickClock V8_FINAL : public TickClock {
public:
explicit WindowsVistaTickClock(GETTICKCOUNT64PROC func) : func_(func) {
ASSERT(func_ != NULL);
}
virtual ~WindowsVistaTickClock() {}
virtual int64_t Now() V8_OVERRIDE {
// Query the current ticks (in ms).
ULONGLONG tick_count_ms = (*func_)();
// Convert to microseconds (make sure to never return 0 here).
return (tick_count_ms * Time::kMicrosecondsPerMillisecond) + 1;
virtual bool IsHighResolution() V8_OVERRIDE {
return true;
}
private:
GETTICKCOUNT64PROC func_;
int64_t ticks_per_second_;
};
class RolloverProtectedTickClock V8_FINAL : public TickClock {
public:
// We initialize rollover_ms_ to 1 to ensure that we will never
// return 0 from TimeTicks::HighResNow() and TimeTicks::Now() below.
// return 0 from TimeTicks::HighResolutionNow() and TimeTicks::Now() below.
RolloverProtectedTickClock() : last_seen_now_(0), rollover_ms_(1) {}
virtual ~RolloverProtectedTickClock() {}
......@@ -487,6 +463,9 @@ class RolloverProtectedTickClock V8_FINAL : public TickClock {
// Note that we do not use GetTickCount() here, since timeGetTime() gives
// more predictable delta values, as described here:
// http://blogs.msdn.com/b/larryosterman/archive/2009/09/02/what-s-the-difference-between-gettickcount-and-timegettime.aspx
// timeGetTime() provides 1ms granularity when combined with
// timeBeginPeriod(). If the host application for V8 wants fast timers, it
// can use timeBeginPeriod() to increase the resolution.
DWORD now = timeGetTime();
if (now < last_seen_now_) {
rollover_ms_ += V8_INT64_C(0x100000000); // ~49.7 days.
......@@ -495,6 +474,10 @@ class RolloverProtectedTickClock V8_FINAL : public TickClock {
return (now + rollover_ms_) * Time::kMicrosecondsPerMillisecond;
}
virtual bool IsHighResolution() V8_OVERRIDE {
return false;
}
private:
Mutex mutex_;
DWORD last_seen_now_;
......@@ -502,27 +485,10 @@ class RolloverProtectedTickClock V8_FINAL : public TickClock {
};
struct CreateTickClockTrait {
static TickClock* Create() {
// Try to load GetTickCount64() from kernel32.dll (available since Vista).
HMODULE kernel32 = ::GetModuleHandleA("kernel32.dll");
ASSERT(kernel32 != NULL);
FARPROC proc = ::GetProcAddress(kernel32, "GetTickCount64");
if (proc != NULL) {
return new WindowsVistaTickClock(
reinterpret_cast<GETTICKCOUNT64PROC>(proc));
}
// Fallback to the rollover protected tick clock.
return new RolloverProtectedTickClock;
}
};
static LazyDynamicInstance<TickClock,
CreateTickClockTrait,
static LazyStaticInstance<RolloverProtectedTickClock,
DefaultConstructTrait<RolloverProtectedTickClock>,
ThreadSafeInitOnceTrait>::type tick_clock =
LAZY_DYNAMIC_INSTANCE_INITIALIZER;
LAZY_STATIC_INSTANCE_INITIALIZER;
struct CreateHighResTickClockTrait {
......@@ -560,21 +526,27 @@ TimeTicks TimeTicks::Now() {
}
TimeTicks TimeTicks::HighResNow() {
TimeTicks TimeTicks::HighResolutionNow() {
// Make sure we never return 0 here.
TimeTicks ticks(high_res_tick_clock.Pointer()->Now());
ASSERT(!ticks.IsNull());
return ticks;
}
// static
bool TimeTicks::IsHighResolutionClockWorking() {
return high_res_tick_clock.Pointer()->IsHighResolution();
}
#else // V8_OS_WIN
TimeTicks TimeTicks::Now() {
return HighResNow();
return HighResolutionNow();
}
TimeTicks TimeTicks::HighResNow() {
TimeTicks TimeTicks::HighResolutionNow() {
int64_t ticks;
#if V8_OS_MACOSX
static struct mach_timebase_info info;
......@@ -608,6 +580,12 @@ TimeTicks TimeTicks::HighResNow() {
return TimeTicks(ticks + 1);
}
// static
bool TimeTicks::IsHighResolutionClockWorking() {
return true;
}
#endif // V8_OS_WIN
} } // namespace v8::internal
......@@ -333,7 +333,10 @@ class TimeTicks V8_FINAL BASE_EMBEDDED {
// resolution. THIS CALL IS GENERALLY MUCH MORE EXPENSIVE THAN Now() AND
// SHOULD ONLY BE USED WHEN IT IS REALLY NEEDED.
// This method never returns a null TimeTicks.
static TimeTicks HighResNow();
static TimeTicks HighResolutionNow();
// Returns true if the high-resolution clock is working on this system.
static bool IsHighResolutionClockWorking();
// Returns true if this object has not been initialized.
bool IsNull() const { return ticks_ == 0; }
......
......@@ -99,7 +99,7 @@ RandomNumberGenerator::RandomNumberGenerator() {
// which provides reasonable entropy, see:
// https://code.google.com/p/v8/issues/detail?id=2905
int64_t seed = Time::NowFromSystemTime().ToInternalValue() << 24;
seed ^= TimeTicks::HighResNow().ToInternalValue() << 16;
seed ^= TimeTicks::HighResolutionNow().ToInternalValue() << 16;
seed ^= TimeTicks::Now().ToInternalValue() << 8;
SetSeed(seed);
#endif // V8_OS_CYGWIN || V8_OS_WIN
......
......@@ -133,7 +133,7 @@ TEST(TimeTicksIsMonotonic) {
timer.Start();
while (!timer.HasExpired(TimeDelta::FromMilliseconds(100))) {
TimeTicks normal_ticks = TimeTicks::Now();
TimeTicks highres_ticks = TimeTicks::HighResNow();
TimeTicks highres_ticks = TimeTicks::HighResolutionNow();
CHECK_GE(normal_ticks, previous_normal_ticks);
CHECK_GE((normal_ticks - previous_normal_ticks).InMicroseconds(), 0);
CHECK_GE(highres_ticks, previous_highres_ticks);
......@@ -142,3 +142,54 @@ TEST(TimeTicksIsMonotonic) {
previous_highres_ticks = highres_ticks;
}
}
template <typename T>
static void ResolutionTest(T (*Now)(), TimeDelta target_granularity) {
// We're trying to measure that intervals increment in a VERY small amount
// of time -- according to the specified target granularity. Unfortunately,
// if we happen to have a context switch in the middle of our test, the
// context switch could easily exceed our limit. So, we iterate on this
// several times. As long as we're able to detect the fine-granularity
// timers at least once, then the test has succeeded.
static const TimeDelta kExpirationTimeout = TimeDelta::FromSeconds(1);
ElapsedTimer timer;
timer.Start();
TimeDelta delta;
do {
T start = Now();
T now = start;
// Loop until we can detect that the clock has changed. Non-HighRes timers
// will increment in chunks, i.e. 15ms. By spinning until we see a clock
// change, we detect the minimum time between measurements.
do {
now = Now();
delta = now - start;
} while (now <= start);
CHECK_NE(static_cast<int64_t>(0), delta.InMicroseconds());
} while (delta > target_granularity && !timer.HasExpired(kExpirationTimeout));
CHECK_LE(delta, target_granularity);
}
TEST(TimeNowResolution) {
// We assume that Time::Now() has at least 16ms resolution.
static const TimeDelta kTargetGranularity = TimeDelta::FromMilliseconds(16);
ResolutionTest<Time>(&Time::Now, kTargetGranularity);
}
TEST(TimeTicksNowResolution) {
// We assume that TimeTicks::Now() has at least 16ms resolution.
static const TimeDelta kTargetGranularity = TimeDelta::FromMilliseconds(16);
ResolutionTest<TimeTicks>(&TimeTicks::Now, kTargetGranularity);
}
TEST(TimeTicksHighResolutionNowResolution) {
if (!TimeTicks::IsHighResolutionClockWorking()) return;
// We assume that TimeTicks::HighResolutionNow() has sub-ms resolution.
static const TimeDelta kTargetGranularity = TimeDelta::FromMilliseconds(1);
ResolutionTest<TimeTicks>(&TimeTicks::HighResolutionNow, kTargetGranularity);
}
......@@ -43,10 +43,6 @@
# This test non-deterministically runs out of memory on Windows ia32.
'regress/regress-crbug-160010': [SKIP],
# This test fails on Windows XP and Windows Vista.
# Issue 288924
'timer' : [['system == windows', FAIL]],
##############################################################################
# Too slow in debug mode with --stress-opt mode.
'compiler/regress-stacktrace-methods': [PASS, ['mode == debug', SKIP]],
......
// Copyright 2013 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --allow-natives-syntax
// Tests timer milliseconds granularity.
// Don't run this test in gc stress mode. Time differences may be long
// due to garbage collections.
%SetFlags("--gc-interval=-1");
%SetFlags("--nostress-compaction");
(function run() {
var start_test = Date.now();
// Let the retry run for maximum 100ms to reduce flakiness.
for (var start = Date.now(); start - start_test < 100; start = Date.now()) {
var end = Date.now();
while (end - start == 0) {
end = Date.now();
}
if (end - start == 1) {
// Found milliseconds granularity.
return;
} else {
print("Timer difference too big: " + (end - start) + "ms");
}
}
assertTrue(false);
})()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment