From d7cae12696b96500c05dd2d430f6238922c20c96 Mon Sep 17 00:00:00 2001 From: "initial.commit" Date: Sat, 26 Jul 2008 21:49:38 +0000 Subject: Add base to the repository. git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8 0039d316-1c4b-4281-b951-d872f2087c98 --- base/time_win.cc | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 base/time_win.cc (limited to 'base/time_win.cc') diff --git a/base/time_win.cc b/base/time_win.cc new file mode 100644 index 0000000..e7faa86 --- /dev/null +++ b/base/time_win.cc @@ -0,0 +1,244 @@ +// Copyright 2008, Google Inc. +// 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. + +#include "base/time.h" + +#pragma comment(lib, "winmm.lib") +#include +#include +#include "base/basictypes.h" +#include "base/lock.h" +#include "base/logging.h" + +namespace { + +// From MSDN, FILETIME "Contains a 64-bit value representing the number of +// 100-nanosecond intervals since January 1, 1601 (UTC)." +int64 FileTimeToMicroseconds(const FILETIME& ft) { + // Need to bit_cast to fix alignment, then divide by 10 to convert + // 100-nanoseconds to milliseconds. This only works on little-endian + // machines. + return bit_cast(ft) / 10; +} + +void MicrosecondsToFileTime(int64 us, FILETIME* ft) { + DCHECK(us >= 0) << "Time is less than 0, negative values are not " + "representable in FILETIME"; + + // Multiply by 10 to convert milliseconds to 100-nanoseconds. Bit_cast will + // handle alignment problems. This only works on little-endian machines. + *ft = bit_cast(us * 10); +} + +} // namespace + +// Time ----------------------------------------------------------------------- + +// The internal representation of Time uses FILETIME, whose epoch is 1601-01-01 +// 00:00:00 UTC. ((1970-1601)*365+89)*24*60*60*1000*1000, where 89 is the +// number of leap year days between 1601 and 1970: (1970-1601)/4 excluding +// 1700, 1800, and 1900. +// static +const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(11644473600000000); + +// static +int64 Time::CurrentWallclockMicroseconds() { + FILETIME ft; + ::GetSystemTimeAsFileTime(&ft); + return FileTimeToMicroseconds(ft); +} + +// static +Time Time::FromFileTime(FILETIME ft) { + return Time(FileTimeToMicroseconds(ft)); +} + +FILETIME Time::ToFileTime() const { + FILETIME utc_ft; + MicrosecondsToFileTime(us_, &utc_ft); + return utc_ft; +} + +// static +Time Time::FromExploded(bool is_local, const Exploded& exploded) { + // Create the system struct representing our exploded time. It will either be + // in local time or UTC. + SYSTEMTIME st; + st.wYear = exploded.year; + st.wMonth = exploded.month; + st.wDayOfWeek = exploded.day_of_week; + st.wDay = exploded.day_of_month; + st.wHour = exploded.hour; + st.wMinute = exploded.minute; + st.wSecond = exploded.second; + st.wMilliseconds = exploded.millisecond; + + // Convert to FILETIME. + FILETIME ft; + if (!SystemTimeToFileTime(&st, &ft)) { + NOTREACHED() << "Unable to convert time"; + return Time(0); + } + + // Ensure that it's in UTC. + if (is_local) { + FILETIME utc_ft; + LocalFileTimeToFileTime(&ft, &utc_ft); + return Time(FileTimeToMicroseconds(utc_ft)); + } + return Time(FileTimeToMicroseconds(ft)); +} + +void Time::Explode(bool is_local, Exploded* exploded) const { + // FILETIME in UTC. + FILETIME utc_ft; + MicrosecondsToFileTime(us_, &utc_ft); + + // FILETIME in local time if necessary. + BOOL success = TRUE; + FILETIME ft; + if (is_local) + success = FileTimeToLocalFileTime(&utc_ft, &ft); + else + ft = utc_ft; + + // FILETIME in SYSTEMTIME (exploded). + SYSTEMTIME st; + if (!success || !FileTimeToSystemTime(&ft, &st)) { + NOTREACHED() << "Unable to convert time, don't know why"; + ZeroMemory(exploded, sizeof(exploded)); + return; + } + + exploded->year = st.wYear; + exploded->month = st.wMonth; + exploded->day_of_week = st.wDayOfWeek; + exploded->day_of_month = st.wDay; + exploded->hour = st.wHour; + exploded->minute = st.wMinute; + exploded->second = st.wSecond; + exploded->millisecond = st.wMilliseconds; +} + +// TimeTicks ------------------------------------------------------------------ + +TimeTicks::TickFunction TimeTicks::tick_function_= + reinterpret_cast(&timeGetTime); + +// static +TimeTicks TimeTicks::Now() { + // Uses the multimedia timers on Windows to get a higher resolution clock. + // timeGetTime() provides a resolution which is variable depending on + // hardware and system configuration. It can also be changed by other + // apps. This class does not attempt to change the resolution of the + // timer, because we don't want to affect other applications. + + // timeGetTime() should at least be accurate to ~5ms on all systems. + // timeGetTime() returns a 32-bit millisecond counter which has rollovers + // every ~49 days. + static DWORD last_tick_count = 0; + static int64 tick_rollover_accum = 0; + static Lock* tick_lock = NULL; // To protect during rollover periods. + + // Lazily create the lock we use. + if (!tick_lock) { + Lock* new_lock = new Lock; + if (InterlockedCompareExchangePointer( + reinterpret_cast(&tick_lock), new_lock, NULL)) { + delete new_lock; + } + } + + // Atomically protect the low and high 32bit values for time. + // In the future we may be able to optimize with + // InterlockedCompareExchange64, but that doesn't work on XP. + DWORD tick_count; + int64 rollover_count; + { + AutoLock lock(*tick_lock); + tick_count = tick_function_(); + if (tick_count < last_tick_count) + tick_rollover_accum += GG_INT64_C(0x100000000); + + last_tick_count = tick_count; + rollover_count = tick_rollover_accum; + } + + // GetTickCount returns milliseconds, we want microseconds. + return TimeTicks((tick_count + rollover_count) * + Time::kMicrosecondsPerMillisecond); +} + +// Overview of time counters: +// (1) CPU cycle counter. (Retrieved via RDTSC) +// The CPU counter provides the highest resolution time stamp and is the least +// expensive to retrieve. However, the CPU counter is unreliable and should not +// be used in production. Its biggest issue is that it is per processor and it +// is not synchronized between processors. Also, on some computers, the counters +// will change frequency due to thermal and power changes, and stop in some +// states. +// +// (2) QueryPerformanceCounter (QPC). The QPC counter provides a high- +// resolution (100 nanoseconds) time stamp but is comparatively more expensive +// to retrieve. What QueryPerformanceCounter actually does is up to the HAL. +// (with some help from ACPI). +// According to http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx +// in the worst case, it gets the counter from the rollover interrupt on the +// programmable interrupt timer. In best cases, the HAL may conclude that the +// RDTSC counter runs at a constant frequency, then it uses that instead. On +// multiprocessor machines, it will try to verify the values returned from +// RDTSC on each processor are consistent with each other, and apply a handful +// of workarounds for known buggy hardware. In other words, QPC is supposed to +// give consistent result on a multiprocessor computer, but it is unreliable in +// reality due to bugs in BIOS or HAL on some, especially old computers. +// With recent updates on HAL and newer BIOS, QPC is getting more reliable but +// it should be used with caution. +// +// (3) System time. The system time provides a low-resolution (typically 10ms +// to 55 milliseconds) time stamp but is comparatively less expensive to +// retrieve and more reliable. + +// static +TimeTicks TimeTicks::UnreliableHighResNow() { + // Cached clock frequency -> microseconds. This assumes that the clock + // frequency is faster than one microsecond (which is 1MHz, should be OK). + static int64 ticks_per_microsecond = 0; + + if (ticks_per_microsecond == 0) { + LARGE_INTEGER ticks_per_sec = { 0, 0 }; + if (!QueryPerformanceFrequency(&ticks_per_sec)) + return TimeTicks(0); // Broken, we don't guarantee this function works. + ticks_per_microsecond = + ticks_per_sec.QuadPart / Time::kMicrosecondsPerSecond; + } + + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + return TimeTicks(now.QuadPart / ticks_per_microsecond); +} -- cgit v1.1