Revision: 17080
Author: [email protected]
Date: Wed Oct 2 13:30:31 2013 UTC
Log: 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
[email protected]
Review URL: https://codereview.chromium.org/25468003
http://code.google.com/p/v8/source/detail?r=17080
Deleted:
/branches/bleeding_edge/test/mjsunit/timer.js
Modified:
/branches/bleeding_edge/src/platform/elapsed-timer.h
/branches/bleeding_edge/src/platform/time.cc
/branches/bleeding_edge/src/platform/time.h
/branches/bleeding_edge/src/platform-win32.cc
/branches/bleeding_edge/src/utils/random-number-generator.cc
/branches/bleeding_edge/test/cctest/test-time.cc
/branches/bleeding_edge/test/mjsunit/mjsunit.status
=======================================
--- /branches/bleeding_edge/test/mjsunit/timer.js Fri Sep 27 11:04:16 2013
UTC
+++ /dev/null
@@ -1,53 +0,0 @@
-// 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);
-})()
=======================================
--- /branches/bleeding_edge/src/platform/elapsed-timer.h Thu Sep 12
08:57:10 2013 UTC
+++ /branches/bleeding_edge/src/platform/elapsed-timer.h Wed Oct 2
13:30:31 2013 UTC
@@ -104,7 +104,7 @@
private:
static V8_INLINE TimeTicks Now() {
- TimeTicks now = TimeTicks::HighResNow();
+ TimeTicks now = TimeTicks::HighResolutionNow();
ASSERT(!now.IsNull());
return now;
}
=======================================
--- /branches/bleeding_edge/src/platform/time.cc Tue Sep 3 07:30:01 2013
UTC
+++ /branches/bleeding_edge/src/platform/time.cc Wed Oct 2 13:30:31 2013
UTC
@@ -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 @@
// 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.
+ // Time between resampling the un-granular clock for this API (1
minute).
+ const TimeDelta kMaxElapsedTime = TimeDelta::FromMinutes(1);
+
LockGuard<Mutex> lock_guard(&mutex_);
- // Calculate the time elapsed since we started our timer.
- TimeDelta elapsed = TimeTicks::Now() - initial_ticks_;
+ // Determine current time and ticks.
+ TimeTicks ticks = GetSystemTicks();
+ Time time = GetSystemTime();
- // Check if we don't need to synchronize with the wallclock yet.
- if (elapsed.InMicroseconds() <= kMaxMicrosecondsToAvoidDrift) {
- return initial_time_ + elapsed;
+ // 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 @@
};
-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 @@
public:
virtual ~TickClock() {}
virtual int64_t Now() = 0;
+ virtual bool IsHighResolution() = 0;
};
@@ -440,42 +434,24 @@
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 bool IsHighResolution() V8_OVERRIDE {
+ return true;
}
- 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;
- }
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 @@
// 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.
@@ -494,6 +473,10 @@
last_seen_now_ = now;
return (now + rollover_ms_) * Time::kMicrosecondsPerMillisecond;
}
+
+ virtual bool IsHighResolution() V8_OVERRIDE {
+ return false;
+ }
private:
Mutex mutex_;
@@ -502,27 +485,10 @@
};
-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::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;
@@ -607,6 +579,12 @@
// Make sure we never return 0 here.
return TimeTicks(ticks + 1);
}
+
+
+// static
+bool TimeTicks::IsHighResolutionClockWorking() {
+ return true;
+}
#endif // V8_OS_WIN
=======================================
--- /branches/bleeding_edge/src/platform/time.h Tue Sep 10 11:13:55 2013 UTC
+++ /branches/bleeding_edge/src/platform/time.h Wed Oct 2 13:30:31 2013 UTC
@@ -333,7 +333,10 @@
// 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; }
=======================================
--- /branches/bleeding_edge/src/platform-win32.cc Fri Sep 27 10:53:07 2013
UTC
+++ /branches/bleeding_edge/src/platform-win32.cc Wed Oct 2 13:30:31 2013
UTC
@@ -595,9 +595,7 @@
// 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();
}
=======================================
--- /branches/bleeding_edge/src/utils/random-number-generator.cc Tue Sep 24
13:27:58 2013 UTC
+++ /branches/bleeding_edge/src/utils/random-number-generator.cc Wed Oct 2
13:30:31 2013 UTC
@@ -99,7 +99,7 @@
// 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
=======================================
--- /branches/bleeding_edge/test/cctest/test-time.cc Mon Sep 2 12:26:06
2013 UTC
+++ /branches/bleeding_edge/test/cctest/test-time.cc Wed Oct 2 13:30:31
2013 UTC
@@ -133,7 +133,7 @@
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 @@
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);
+}
=======================================
--- /branches/bleeding_edge/test/mjsunit/mjsunit.status Wed Sep 25 12:46:45
2013 UTC
+++ /branches/bleeding_edge/test/mjsunit/mjsunit.status Wed Oct 2 13:30:31
2013 UTC
@@ -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]],
--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.