Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(766)

Side by Side Diff: src/platform/time.cc

Issue 23490015: Reland^2 "Add Chromium-style TimeDelta, Time and TimeTicks classes, and a new ElapsedTimer class." (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Some windows cleanup Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/platform/time.h ('k') | src/profile-generator.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 #include "platform/time.h"
29
30 #if V8_OS_POSIX
31 #include <sys/time.h>
32 #endif
33 #if V8_OS_MACOSX
34 #include <mach/mach_time.h>
35 #endif
36
37 #include <cstring>
38
39 #include "checks.h"
40 #include "cpu.h"
41 #include "platform.h"
42 #if V8_OS_WIN
43 #include "win32-headers.h"
44 #endif
45
46 #if V8_OS_WIN
47 // Prototype for GetTickCount64() procedure.
48 extern "C" {
49 typedef ULONGLONG (WINAPI *GETTICKCOUNT64PROC)(void);
50 }
51 #endif
52
53 namespace v8 {
54 namespace internal {
55
56 TimeDelta TimeDelta::FromDays(int days) {
57 return TimeDelta(days * Time::kMicrosecondsPerDay);
58 }
59
60
61 TimeDelta TimeDelta::FromHours(int hours) {
62 return TimeDelta(hours * Time::kMicrosecondsPerHour);
63 }
64
65
66 TimeDelta TimeDelta::FromMinutes(int minutes) {
67 return TimeDelta(minutes * Time::kMicrosecondsPerMinute);
68 }
69
70
71 TimeDelta TimeDelta::FromSeconds(int64_t seconds) {
72 return TimeDelta(seconds * Time::kMicrosecondsPerSecond);
73 }
74
75
76 TimeDelta TimeDelta::FromMilliseconds(int64_t milliseconds) {
77 return TimeDelta(milliseconds * Time::kMicrosecondsPerMillisecond);
78 }
79
80
81 TimeDelta TimeDelta::FromNanoseconds(int64_t nanoseconds) {
82 return TimeDelta(nanoseconds / Time::kNanosecondsPerMicrosecond);
83 }
84
85
86 int TimeDelta::InDays() const {
87 return static_cast<int>(delta_ / Time::kMicrosecondsPerDay);
88 }
89
90
91 int TimeDelta::InHours() const {
92 return static_cast<int>(delta_ / Time::kMicrosecondsPerHour);
93 }
94
95
96 int TimeDelta::InMinutes() const {
97 return static_cast<int>(delta_ / Time::kMicrosecondsPerMinute);
98 }
99
100
101 double TimeDelta::InSecondsF() const {
102 return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond;
103 }
104
105
106 int64_t TimeDelta::InSeconds() const {
107 return delta_ / Time::kMicrosecondsPerSecond;
108 }
109
110
111 double TimeDelta::InMillisecondsF() const {
112 return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond;
113 }
114
115
116 int64_t TimeDelta::InMilliseconds() const {
117 return delta_ / Time::kMicrosecondsPerMillisecond;
118 }
119
120
121 int64_t TimeDelta::InNanoseconds() const {
122 return delta_ * Time::kNanosecondsPerMicrosecond;
123 }
124
125
126 #if V8_OS_WIN
127
128 // We implement time using the high-resolution timers so that we can get
129 // timeouts which are smaller than 10-15ms. To avoid any drift, we
130 // periodically resync the internal clock to the system clock.
131 class Clock V8_FINAL {
132 public:
133 Clock() : initial_time_(CurrentWallclockTime()),
134 initial_ticks_(TimeTicks::Now()),
135 mutex_(OS::CreateMutex()) {}
136
137 ~Clock() { delete mutex_; }
138
139 Time Now() {
140 // This must be executed under lock.
141 ScopedLock sl(mutex_);
142
143 // Calculate the time elapsed since we started our timer.
144 TimeDelta elapsed = TimeTicks::Now() - initial_ticks_;
145
146 // Check if we don't need to synchronize with the wallclock yet.
147 if (elapsed.InMicroseconds() <= kMaxMicrosecondsToAvoidDrift) {
148 return initial_time_ + elapsed;
149 }
150
151 // Resynchronize with the wallclock.
152 initial_ticks_ = TimeTicks::Now();
153 initial_time_ = CurrentWallclockTime();
154 return initial_time_;
155 }
156
157 Time NowFromSystemTime() {
158 ScopedLock sl(mutex_);
159 initial_ticks_ = TimeTicks::Now();
160 initial_time_ = CurrentWallclockTime();
161 return initial_time_;
162 }
163
164 private:
165 // Time between resampling the un-granular clock for this API (1 minute).
166 static const int64_t kMaxMicrosecondsToAvoidDrift =
167 Time::kMicrosecondsPerMinute;
168
169 static Time CurrentWallclockTime() {
170 FILETIME ft;
171 ::GetSystemTimeAsFileTime(&ft);
172 return Time::FromFiletime(ft);
173 }
174
175 TimeTicks initial_ticks_;
176 Time initial_time_;
177 Mutex* mutex_;
178 };
179
180
181 static LazyDynamicInstance<Clock,
182 DefaultCreateTrait<Clock>,
183 ThreadSafeInitOnceTrait>::type clock = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
184
185
186 Time Time::Now() {
187 return clock.Pointer()->Now();
188 }
189
190
191 Time Time::NowFromSystemTime() {
192 return clock.Pointer()->NowFromSystemTime();
193 }
194
195
196 // Time between windows epoch and standard epoch.
197 static const int64_t kTimeToEpochInMicroseconds = V8_INT64_C(11644473600000000);
198
199
200 Time Time::FromFiletime(FILETIME ft) {
201 if (ft.dwLowDateTime == 0 && ft.dwHighDateTime == 0) {
202 return Time();
203 }
204 if (ft.dwLowDateTime == std::numeric_limits<DWORD>::max() &&
205 ft.dwHighDateTime == std::numeric_limits<DWORD>::max()) {
206 return Max();
207 }
208 int64_t us = (static_cast<uint64_t>(ft.dwLowDateTime) +
209 (static_cast<uint64_t>(ft.dwHighDateTime) << 32)) / 10;
210 return Time(us - kTimeToEpochInMicroseconds);
211 }
212
213
214 FILETIME Time::ToFiletime() const {
215 ASSERT(us_ >= 0);
216 FILETIME ft;
217 if (IsNull()) {
218 ft.dwLowDateTime = 0;
219 ft.dwHighDateTime = 0;
220 return ft;
221 }
222 if (IsMax()) {
223 ft.dwLowDateTime = std::numeric_limits<DWORD>::max();
224 ft.dwHighDateTime = std::numeric_limits<DWORD>::max();
225 return ft;
226 }
227 uint64_t us = static_cast<uint64_t>(us_ + kTimeToEpochInMicroseconds) * 10;
228 ft.dwLowDateTime = static_cast<DWORD>(us);
229 ft.dwHighDateTime = static_cast<DWORD>(us >> 32);
230 return ft;
231 }
232
233 #elif V8_OS_POSIX
234
235 Time Time::Now() {
236 struct timeval tv;
237 int result = gettimeofday(&tv, NULL);
238 ASSERT_EQ(0, result);
239 USE(result);
240 return FromTimeval(tv);
241 }
242
243
244 Time Time::NowFromSystemTime() {
245 return Now();
246 }
247
248
249 Time Time::FromTimeval(struct timeval tv) {
250 ASSERT(tv.tv_usec >= 0);
251 ASSERT(tv.tv_usec < static_cast<suseconds_t>(kMicrosecondsPerSecond));
252 if (tv.tv_usec == 0 && tv.tv_sec == 0) {
253 return Time();
254 }
255 if (tv.tv_usec == static_cast<suseconds_t>(kMicrosecondsPerSecond - 1) &&
256 tv.tv_sec == std::numeric_limits<time_t>::max()) {
257 return Max();
258 }
259 return Time(tv.tv_sec * kMicrosecondsPerSecond + tv.tv_usec);
260 }
261
262
263 struct timeval Time::ToTimeval() const {
264 struct timeval tv;
265 if (IsNull()) {
266 tv.tv_sec = 0;
267 tv.tv_usec = 0;
268 return tv;
269 }
270 if (IsMax()) {
271 tv.tv_sec = std::numeric_limits<time_t>::max();
272 tv.tv_usec = static_cast<suseconds_t>(kMicrosecondsPerSecond - 1);
273 return tv;
274 }
275 tv.tv_sec = us_ / kMicrosecondsPerSecond;
276 tv.tv_usec = us_ % kMicrosecondsPerSecond;
277 return tv;
278 }
279
280 #endif // V8_OS_WIN
281
282
283 Time Time::FromJsTime(double ms_since_epoch) {
284 // The epoch is a valid time, so this constructor doesn't interpret
285 // 0 as the null time.
286 if (ms_since_epoch == std::numeric_limits<double>::max()) {
287 return Max();
288 }
289 return Time(
290 static_cast<int64_t>(ms_since_epoch * kMicrosecondsPerMillisecond));
291 }
292
293
294 double Time::ToJsTime() const {
295 if (IsNull()) {
296 // Preserve 0 so the invalid result doesn't depend on the platform.
297 return 0;
298 }
299 if (IsMax()) {
300 // Preserve max without offset to prevent overflow.
301 return std::numeric_limits<double>::max();
302 }
303 return static_cast<double>(us_) / kMicrosecondsPerMillisecond;
304 }
305
306
307 #if V8_OS_WIN
308
309 class TickClock {
310 public:
311 virtual ~TickClock() {}
312 virtual int64_t Now() = 0;
313 };
314
315
316 // Overview of time counters:
317 // (1) CPU cycle counter. (Retrieved via RDTSC)
318 // The CPU counter provides the highest resolution time stamp and is the least
319 // expensive to retrieve. However, the CPU counter is unreliable and should not
320 // be used in production. Its biggest issue is that it is per processor and it
321 // is not synchronized between processors. Also, on some computers, the counters
322 // will change frequency due to thermal and power changes, and stop in some
323 // states.
324 //
325 // (2) QueryPerformanceCounter (QPC). The QPC counter provides a high-
326 // resolution (100 nanoseconds) time stamp but is comparatively more expensive
327 // to retrieve. What QueryPerformanceCounter actually does is up to the HAL.
328 // (with some help from ACPI).
329 // According to http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx
330 // in the worst case, it gets the counter from the rollover interrupt on the
331 // programmable interrupt timer. In best cases, the HAL may conclude that the
332 // RDTSC counter runs at a constant frequency, then it uses that instead. On
333 // multiprocessor machines, it will try to verify the values returned from
334 // RDTSC on each processor are consistent with each other, and apply a handful
335 // of workarounds for known buggy hardware. In other words, QPC is supposed to
336 // give consistent result on a multiprocessor computer, but it is unreliable in
337 // reality due to bugs in BIOS or HAL on some, especially old computers.
338 // With recent updates on HAL and newer BIOS, QPC is getting more reliable but
339 // it should be used with caution.
340 //
341 // (3) System time. The system time provides a low-resolution (typically 10ms
342 // to 55 milliseconds) time stamp but is comparatively less expensive to
343 // retrieve and more reliable.
344 class HighResolutionTickClock V8_FINAL : public TickClock {
345 public:
346 explicit HighResolutionTickClock(int64_t ticks_per_second)
347 : ticks_per_second_(ticks_per_second) {
348 ASSERT_LT(0, ticks_per_second);
349 }
350 virtual ~HighResolutionTickClock() {}
351
352 virtual int64_t Now() V8_OVERRIDE {
353 LARGE_INTEGER now;
354 BOOL result = QueryPerformanceCounter(&now);
355 ASSERT(result);
356 USE(result);
357
358 // Intentionally calculate microseconds in a round about manner to avoid
359 // overflow and precision issues. Think twice before simplifying!
360 int64_t whole_seconds = now.QuadPart / ticks_per_second_;
361 int64_t leftover_ticks = now.QuadPart % ticks_per_second_;
362 int64_t ticks = (whole_seconds * Time::kMicrosecondsPerSecond) +
363 ((leftover_ticks * Time::kMicrosecondsPerSecond) / ticks_per_second_);
364
365 // Make sure we never return 0 here, so that TimeTicks::HighResNow()
366 // will never return 0.
367 return ticks + 1;
368 }
369
370 private:
371 int64_t ticks_per_second_;
372 };
373
374
375 // The GetTickCount64() API is what we actually want for the regular tick
376 // clock, but this is only available starting with Windows Vista.
377 class WindowsVistaTickClock V8_FINAL : public TickClock {
378 public:
379 explicit WindowsVistaTickClock(GETTICKCOUNT64PROC func) : func_(func) {
380 ASSERT(func_ != NULL);
381 }
382 virtual ~WindowsVistaTickClock() {}
383
384 virtual int64_t Now() V8_OVERRIDE {
385 // Query the current ticks (in ms).
386 ULONGLONG tick_count_ms = (*func_)();
387
388 // Convert to microseconds (make sure to never return 0 here).
389 return (tick_count_ms * Time::kMicrosecondsPerMillisecond) + 1;
390 }
391
392 private:
393 GETTICKCOUNT64PROC func_;
394 };
395
396
397 class RolloverProtectedTickClock V8_FINAL : public TickClock {
398 public:
399 RolloverProtectedTickClock()
400 : mutex_(OS::CreateMutex()), last_seen_now_(0), rollover_ms_(1) {
401 // We initialize rollover_ms_ to 1 to ensure that we will never
402 // return 0 from TimeTicks::HighResNow() and TimeTicks::Now() below.
403 }
404 virtual ~RolloverProtectedTickClock() { delete mutex_; }
405
406 virtual int64_t Now() V8_OVERRIDE {
407 ScopedLock sl(mutex_);
408 // We use timeGetTime() to implement TimeTicks::Now(), which rolls over
409 // every ~49.7 days. We try to track rollover ourselves, which works if
410 // TimeTicks::Now() is called at least every 49 days.
411 // Note that we do not use GetTickCount() here, since timeGetTime() gives
412 // more predictable delta values, as described here:
413 // http://blogs.msdn.com/b/larryosterman/archive/2009/09/02/what-s-the-diffe rence-between-gettickcount-and-timegettime.aspx
414 DWORD now = timeGetTime();
415 if (now < last_seen_now_) {
416 rollover_ms_ += V8_INT64_C(0x100000000); // ~49.7 days.
417 }
418 last_seen_now_ = now;
419 return (now + rollover_ms_) * Time::kMicrosecondsPerMillisecond;
420 }
421
422 private:
423 Mutex* mutex_;
424 DWORD last_seen_now_;
425 int64_t rollover_ms_;
426 };
427
428
429 struct CreateTickClockTrait {
430 static TickClock* Create() {
431 // Try to load GetTickCount64() from kernel32.dll (available since Vista).
432 HMODULE kernel32 = ::GetModuleHandleA("kernel32.dll");
433 ASSERT(kernel32 != NULL);
434 FARPROC proc = ::GetProcAddress(kernel32, "GetTickCount64");
435 if (proc != NULL) {
436 return new WindowsVistaTickClock(
437 reinterpret_cast<GETTICKCOUNT64PROC>(proc));
438 }
439
440 // Fallback to the rollover protected tick clock.
441 return new RolloverProtectedTickClock;
442 }
443 };
444
445
446 static LazyDynamicInstance<TickClock,
447 CreateTickClockTrait,
448 ThreadSafeInitOnceTrait>::type tick_clock =
449 LAZY_DYNAMIC_INSTANCE_INITIALIZER;
450
451
452 struct CreateHighResTickClockTrait {
453 static TickClock* Create() {
454 // Check if the installed hardware supports a high-resolution performance
455 // counter, and if not fallback to the low-resolution tick clock.
456 LARGE_INTEGER ticks_per_second;
457 if (!QueryPerformanceFrequency(&ticks_per_second)) {
458 return tick_clock.Pointer();
459 }
460
461 // On Athlon X2 CPUs (e.g. model 15) the QueryPerformanceCounter
462 // is unreliable, fallback to the low-resolution tick clock.
463 CPU cpu;
464 if (strcmp(cpu.vendor(), "AuthenticAMD") == 0 && cpu.family() == 15) {
465 return tick_clock.Pointer();
466 }
467
468 return new HighResolutionTickClock(ticks_per_second.QuadPart);
469 }
470 };
471
472
473 static LazyDynamicInstance<TickClock,
474 CreateHighResTickClockTrait,
475 ThreadSafeInitOnceTrait>::type high_res_tick_clock =
476 LAZY_DYNAMIC_INSTANCE_INITIALIZER;
477
478
479 TimeTicks TimeTicks::Now() {
480 // Make sure we never return 0 here.
481 TimeTicks ticks(tick_clock.Pointer()->Now());
482 ASSERT(!ticks.IsNull());
483 return ticks;
484 }
485
486
487 TimeTicks TimeTicks::HighResNow() {
488 // Make sure we never return 0 here.
489 TimeTicks ticks(high_res_tick_clock.Pointer()->Now());
490 ASSERT(!ticks.IsNull());
491 return ticks;
492 }
493
494 #else // V8_OS_WIN
495
496 TimeTicks TimeTicks::Now() {
497 return HighResNow();
498 }
499
500
501 TimeTicks TimeTicks::HighResNow() {
502 int64_t ticks;
503 #if V8_OS_MACOSX
504 static struct mach_timebase_info info;
505 if (info.denom == 0) {
506 kern_return_t result = mach_timebase_info(&info);
507 ASSERT_EQ(KERN_SUCCESS, result);
508 USE(result);
509 }
510 ticks = (mach_absolute_time() / Time::kNanosecondsPerMicrosecond *
511 info.numer / info.denom);
512 #elif V8_OS_SOLARIS
513 ticks = (gethrtime() / Time::kNanosecondsPerMicrosecond);
514 #elif V8_OS_POSIX
515 struct timespec ts;
516 int result = clock_gettime(CLOCK_MONOTONIC, &ts);
517 ASSERT_EQ(0, result);
518 USE(result);
519 ticks = (ts.tv_sec * Time::kMicrosecondsPerSecond +
520 ts.tv_nsec / Time::kNanosecondsPerMicrosecond);
521 #endif // V8_OS_MACOSX
522 // Make sure we never return 0 here.
523 return TimeTicks(ticks + 1);
524 }
525
526 #endif // V8_OS_WIN
527
528 } } // namespace v8::internal
OLDNEW
« no previous file with comments | « src/platform/time.h ('k') | src/profile-generator.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698