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

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

Issue 23469013: Reland "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: REBASE 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 namespace v8 {
47 namespace internal {
48
49 TimeDelta TimeDelta::FromDays(int days) {
50 return TimeDelta(days * Time::kMicrosecondsPerDay);
51 }
52
53
54 TimeDelta TimeDelta::FromHours(int hours) {
55 return TimeDelta(hours * Time::kMicrosecondsPerHour);
56 }
57
58
59 TimeDelta TimeDelta::FromMinutes(int minutes) {
60 return TimeDelta(minutes * Time::kMicrosecondsPerMinute);
61 }
62
63
64 TimeDelta TimeDelta::FromSeconds(int64_t seconds) {
65 return TimeDelta(seconds * Time::kMicrosecondsPerSecond);
66 }
67
68
69 TimeDelta TimeDelta::FromMilliseconds(int64_t milliseconds) {
70 return TimeDelta(milliseconds * Time::kMicrosecondsPerMillisecond);
71 }
72
73
74 TimeDelta TimeDelta::FromNanoseconds(int64_t nanoseconds) {
75 return TimeDelta(nanoseconds / Time::kNanosecondsPerMicrosecond);
76 }
77
78
79 int TimeDelta::InDays() const {
80 return static_cast<int>(delta_ / Time::kMicrosecondsPerDay);
81 }
82
83
84 int TimeDelta::InHours() const {
85 return static_cast<int>(delta_ / Time::kMicrosecondsPerHour);
86 }
87
88
89 int TimeDelta::InMinutes() const {
90 return static_cast<int>(delta_ / Time::kMicrosecondsPerMinute);
91 }
92
93
94 double TimeDelta::InSecondsF() const {
95 return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond;
96 }
97
98
99 int64_t TimeDelta::InSeconds() const {
100 return delta_ / Time::kMicrosecondsPerSecond;
101 }
102
103
104 double TimeDelta::InMillisecondsF() const {
105 return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond;
106 }
107
108
109 int64_t TimeDelta::InMilliseconds() const {
110 return delta_ / Time::kMicrosecondsPerMillisecond;
111 }
112
113
114 int64_t TimeDelta::InNanoseconds() const {
115 return delta_ * Time::kNanosecondsPerMicrosecond;
116 }
117
118
119 #if V8_OS_WIN
120
121 // We implement time using the high-resolution timers so that we can get
122 // timeouts which are smaller than 10-15ms. To avoid any drift, we
123 // periodically resync the internal clock to the system clock.
124 class Clock V8_FINAL {
125 public:
126 Clock() : initial_time_(CurrentWallclockTime()),
127 initial_ticks_(TimeTicks::Now()),
128 mutex_(OS::CreateMutex()) {}
129
130 ~Clock() { delete mutex_; }
131
132 Time Now() {
133 // This must be executed under lock.
134 ScopedLock sl(mutex_);
135
136 // Calculate the time elapsed since we started our timer.
137 TimeDelta elapsed = TimeTicks::Now() - initial_ticks_;
138
139 // Check if we don't need to synchronize with the wallclock yet.
140 if (elapsed.InMicroseconds() <= kMaxMicrosecondsToAvoidDrift) {
141 return initial_time_ + elapsed;
142 }
143
144 // Resynchronize with the wallclock.
145 initial_ticks_ = TimeTicks::Now();
146 initial_time_ = CurrentWallclockTime();
147 return initial_time_;
148 }
149
150 Time NowFromSystemTime() {
151 ScopedLock sl(mutex_);
152 initial_ticks_ = TimeTicks::Now();
153 initial_time_ = CurrentWallclockTime();
154 return initial_time_;
155 }
156
157 private:
158 // Time between resampling the un-granular clock for this API (1 minute).
159 static const int64_t kMaxMicrosecondsToAvoidDrift =
160 Time::kMicrosecondsPerMinute;
161
162 static Time CurrentWallclockTime() {
163 FILETIME ft;
164 ::GetSystemTimeAsFileTime(&ft);
165 return Time::FromFiletime(ft);
166 }
167
168 TimeTicks initial_ticks_;
169 Time initial_time_;
170 Mutex* mutex_;
171 };
172
173
174 static LazyDynamicInstance<Clock,
175 DefaultCreateTrait<Clock>,
176 ThreadSafeInitOnceTrait>::type clock = LAZY_DYNAMIC_INSTANCE_INITIALIZER;
177
178
179 Time Time::Now() {
180 return clock.Pointer()->Now();
181 }
182
183
184 Time Time::NowFromSystemTime() {
185 return clock.Pointer()->NowFromSystemTime();
186 }
187
188
189 // Time between windows epoch and standard epoch.
190 static const int64_t kTimeToEpochInMicroseconds = V8_INT64_C(11644473600000000);
191
192
193 Time Time::FromFiletime(FILETIME ft) {
194 if (ft.dwLowDateTime == 0 && ft.dwHighDateTime == 0) {
195 return Time();
196 }
197 if (ft.dwLowDateTime == std::numeric_limits<DWORD>::max() &&
198 ft.dwHighDateTime == std::numeric_limits<DWORD>::max()) {
199 return Max();
200 }
201 int64_t us = (static_cast<uint64_t>(ft.dwLowDateTime) +
202 (static_cast<uint64_t>(ft.dwHighDateTime) << 32)) / 10;
203 return Time(us - kTimeToEpochInMicroseconds);
204 }
205
206
207 FILETIME Time::ToFiletime() const {
208 ASSERT(us_ >= 0);
209 FILETIME ft;
210 if (IsNull()) {
211 ft.dwLowDateTime = 0;
212 ft.dwHighDateTime = 0;
213 return ft;
214 }
215 if (IsMax()) {
216 ft.dwLowDateTime = std::numeric_limits<DWORD>::max();
217 ft.dwHighDateTime = std::numeric_limits<DWORD>::max();
218 return ft;
219 }
220 uint64_t us = static_cast<uint64_t>(us_ + kTimeToEpochInMicroseconds) * 10;
221 ft.dwLowDateTime = static_cast<DWORD>(us);
222 ft.dwHighDateTime = static_cast<DWORD>(us >> 32);
223 return ft;
224 }
225
226 #elif V8_OS_POSIX
227
228 Time Time::Now() {
229 struct timeval tv;
230 int result = gettimeofday(&tv, NULL);
231 ASSERT_EQ(0, result);
232 USE(result);
233 return FromTimeval(tv);
234 }
235
236
237 Time Time::NowFromSystemTime() {
238 return Now();
239 }
240
241
242 Time Time::FromTimeval(struct timeval tv) {
243 ASSERT(tv.tv_usec >= 0);
244 ASSERT(tv.tv_usec < static_cast<suseconds_t>(kMicrosecondsPerSecond));
245 if (tv.tv_usec == 0 && tv.tv_sec == 0) {
246 return Time();
247 }
248 if (tv.tv_usec == static_cast<suseconds_t>(kMicrosecondsPerSecond - 1) &&
249 tv.tv_sec == std::numeric_limits<time_t>::max()) {
250 return Max();
251 }
252 return Time(tv.tv_sec * kMicrosecondsPerSecond + tv.tv_usec);
253 }
254
255
256 struct timeval Time::ToTimeval() const {
257 struct timeval tv;
258 if (IsNull()) {
259 tv.tv_sec = 0;
260 tv.tv_usec = 0;
261 return tv;
262 }
263 if (IsMax()) {
264 tv.tv_sec = std::numeric_limits<time_t>::max();
265 tv.tv_usec = static_cast<suseconds_t>(kMicrosecondsPerSecond - 1);
266 return tv;
267 }
268 tv.tv_sec = us_ / kMicrosecondsPerSecond;
269 tv.tv_usec = us_ % kMicrosecondsPerSecond;
270 return tv;
271 }
272
273 #endif // V8_OS_WIN
274
275
276 Time Time::FromJsTime(double ms_since_epoch) {
277 // The epoch is a valid time, so this constructor doesn't interpret
278 // 0 as the null time.
279 if (ms_since_epoch == std::numeric_limits<double>::max()) {
280 return Max();
281 }
282 return Time(
283 static_cast<int64_t>(ms_since_epoch * kMicrosecondsPerMillisecond));
284 }
285
286
287 double Time::ToJsTime() const {
288 if (IsNull()) {
289 // Preserve 0 so the invalid result doesn't depend on the platform.
290 return 0;
291 }
292 if (IsMax()) {
293 // Preserve max without offset to prevent overflow.
294 return std::numeric_limits<double>::max();
295 }
296 return static_cast<double>(us_) / kMicrosecondsPerMillisecond;
297 }
298
299
300 #if V8_OS_WIN
301
302 class TickClock {
303 public:
304 virtual ~TickClock() {}
305 virtual int64_t Now() = 0;
306 };
307
308
309 // Overview of time counters:
310 // (1) CPU cycle counter. (Retrieved via RDTSC)
311 // The CPU counter provides the highest resolution time stamp and is the least
312 // expensive to retrieve. However, the CPU counter is unreliable and should not
313 // be used in production. Its biggest issue is that it is per processor and it
314 // is not synchronized between processors. Also, on some computers, the counters
315 // will change frequency due to thermal and power changes, and stop in some
316 // states.
317 //
318 // (2) QueryPerformanceCounter (QPC). The QPC counter provides a high-
319 // resolution (100 nanoseconds) time stamp but is comparatively more expensive
320 // to retrieve. What QueryPerformanceCounter actually does is up to the HAL.
321 // (with some help from ACPI).
322 // According to http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx
323 // in the worst case, it gets the counter from the rollover interrupt on the
324 // programmable interrupt timer. In best cases, the HAL may conclude that the
325 // RDTSC counter runs at a constant frequency, then it uses that instead. On
326 // multiprocessor machines, it will try to verify the values returned from
327 // RDTSC on each processor are consistent with each other, and apply a handful
328 // of workarounds for known buggy hardware. In other words, QPC is supposed to
329 // give consistent result on a multiprocessor computer, but it is unreliable in
330 // reality due to bugs in BIOS or HAL on some, especially old computers.
331 // With recent updates on HAL and newer BIOS, QPC is getting more reliable but
332 // it should be used with caution.
333 //
334 // (3) System time. The system time provides a low-resolution (typically 10ms
335 // to 55 milliseconds) time stamp but is comparatively less expensive to
336 // retrieve and more reliable.
337 class HighResolutionTickClock V8_FINAL : public TickClock {
338 public:
339 explicit HighResolutionTickClock(int64_t ticks_per_second)
340 : ticks_per_second_(ticks_per_second) {
341 ASSERT_NE(0, ticks_per_second);
342 }
343 virtual ~HighResolutionTickClock() {}
344
345 virtual int64_t Now() V8_OVERRIDE {
346 LARGE_INTEGER now;
347 BOOL result = QueryPerformanceCounter(&now);
348 ASSERT(result);
349 USE(result);
350
351 // Intentionally calculate microseconds in a round about manner to avoid
352 // overflow and precision issues. Think twice before simplifying!
353 int64_t whole_seconds = now.QuadPart / ticks_per_second_;
354 int64_t leftover_ticks = now.QuadPart % ticks_per_second_;
355 int64_t ticks = (whole_seconds * Time::kMicrosecondsPerSecond) +
356 ((leftover_ticks * Time::kMicrosecondsPerSecond) / ticks_per_second_);
357
358 // Make sure we never return 0 here, so that TimeTicks::HighResNow()
359 // will never return 0.
360 return ticks + 1;
361 }
362
363 private:
364 int64_t ticks_per_second_;
365 };
366
367
368 class RolloverProtectedTickClock V8_FINAL : public TickClock {
369 public:
370 RolloverProtectedTickClock()
371 : mutex_(OS::CreateMutex()), last_seen_now_(0), rollover_ms_(1) {
372 // We initialize rollover_ms_ to 1 to ensure that we will never
373 // return 0 from TimeTicks::HighResNow() and TimeTicks::Now() below.
374 }
375 virtual ~RolloverProtectedTickClock() { delete mutex_; }
376
377 virtual int64_t Now() V8_OVERRIDE {
378 ScopedLock sl(mutex_);
379 // We use timeGetTime() to implement TimeTicks::Now(), which rolls over
380 // every ~49.7 days. We try to track rollover ourselves, which works if
381 // TimeTicks::Now() is called at least every 49 days.
382 // Note that we do not use GetTickCount() here, since timeGetTime() gives
383 // more predictable delta values, as described here:
384 // http://blogs.msdn.com/b/larryosterman/archive/2009/09/02/what-s-the-diffe rence-between-gettickcount-and-timegettime.aspx
385 DWORD now = timeGetTime();
386 if (now < last_seen_now_) {
387 rollover_ms_ += V8_INT64_C(0x100000000); // ~49.7 days.
388 }
389 last_seen_now_ = now;
390 return now + rollover_ms_;
391 }
392
393 private:
394 Mutex* mutex_;
395 DWORD last_seen_now_;
396 int64_t rollover_ms_;
397 };
398
399
400 static LazyDynamicInstance<RolloverProtectedTickClock,
401 DefaultCreateTrait<RolloverProtectedTickClock>,
402 ThreadSafeInitOnceTrait>::type tick_clock =
403 LAZY_DYNAMIC_INSTANCE_INITIALIZER;
404
405
406 struct CreateHighResTickClockTrait {
407 static TickClock* Create() {
408 // Check if the installed hardware supports a high-resolution performance
409 // counter, and if not fallback to the low-resolution tick clock.
410 LARGE_INTEGER ticks_per_second;
411 if (!QueryPerformanceFrequency(&ticks_per_second)) {
412 return tick_clock.Pointer();
413 }
414
415 // On Athlon X2 CPUs (e.g. model 15) the QueryPerformanceCounter
416 // is unreliable, fallback to the low-resolution tick clock.
417 CPU cpu;
418 if (strcmp(cpu.vendor(), "AuthenticAMD") == 0 && cpu.family() == 15) {
419 return tick_clock.Pointer();
420 }
421
422 return new HighResolutionTickClock(ticks_per_second.QuadPart);
423 }
424 };
425
426
427 static LazyDynamicInstance<TickClock,
428 CreateHighResTickClockTrait,
429 ThreadSafeInitOnceTrait>::type high_res_tick_clock =
430 LAZY_DYNAMIC_INSTANCE_INITIALIZER;
431
432
433 TimeTicks TimeTicks::Now() {
434 // Make sure we never return 0 here.
435 TimeTicks ticks(tick_clock.Pointer()->Now());
436 ASSERT(!ticks.IsNull());
437 return ticks;
438 }
439
440
441 TimeTicks TimeTicks::HighResNow() {
442 // Make sure we never return 0 here.
443 TimeTicks ticks(high_res_tick_clock.Pointer()->Now());
444 ASSERT(!ticks.IsNull());
445 return ticks;
446 }
447
448 #else // V8_OS_WIN
449
450 TimeTicks TimeTicks::Now() {
451 return HighResNow();
452 }
453
454
455 TimeTicks TimeTicks::HighResNow() {
456 int64_t ticks;
457 #if V8_OS_MACOSX
458 static struct mach_timebase_info info;
459 if (info.denom == 0) {
460 kern_return_t result = mach_timebase_info(&info);
461 ASSERT_EQ(KERN_SUCCESS, result);
462 USE(result);
463 }
464 ticks = (mach_absolute_time() / Time::kNanosecondsPerMicrosecond *
465 info.numer / info.denom);
466 #elif V8_OS_SOLARIS
467 ticks = (gethrtime() / Time::kNanosecondsPerMicrosecond);
468 #elif V8_OS_POSIX
469 struct timespec ts;
470 int result = clock_gettime(CLOCK_MONOTONIC, &ts);
471 ASSERT_EQ(0, result);
472 USE(result);
473 ticks = (ts.tv_sec * Time::kMicrosecondsPerSecond +
474 ts.tv_nsec / Time::kNanosecondsPerMicrosecond);
475 #endif // V8_OS_MACOSX
476 // Make sure we never return 0 here.
477 return TimeTicks(ticks + 1);
478 }
479
480 #endif // V8_OS_WIN
481
482 } } // 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