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

Side by Side Diff: base/time/time_win.cc

Issue 489793003: Fix logic on high Windows resolution timer and have (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: lock fixes and utest Created 6 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
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 5
6 // Windows Timer Primer 6 // Windows Timer Primer
7 // 7 //
8 // A good article: http://www.ddj.com/windows/184416651 8 // A good article: http://www.ddj.com/windows/184416651
9 // A good mozilla bug: http://bugzilla.mozilla.org/show_bug.cgi?id=363258 9 // A good mozilla bug: http://bugzilla.mozilla.org/show_bug.cgi?id=363258
10 // 10 //
(...skipping 11 matching lines...) Expand all
22 // 1ms, but only if you call APIs (timeBeginPeriod()) which affect all other 22 // 1ms, but only if you call APIs (timeBeginPeriod()) which affect all other
23 // applications on the system. By default, precision is only 15.5ms. 23 // applications on the system. By default, precision is only 15.5ms.
24 // Unfortunately, we don't want to call timeBeginPeriod because we don't 24 // Unfortunately, we don't want to call timeBeginPeriod because we don't
25 // want to affect other applications. Further, on mobile platforms, use of 25 // want to affect other applications. Further, on mobile platforms, use of
26 // faster multimedia timers can hurt battery life. See the intel 26 // faster multimedia timers can hurt battery life. See the intel
27 // article about this here: 27 // article about this here:
28 // http://softwarecommunity.intel.com/articles/eng/1086.htm 28 // http://softwarecommunity.intel.com/articles/eng/1086.htm
29 // 29 //
30 // To work around all this, we're going to generally use timeGetTime(). We 30 // To work around all this, we're going to generally use timeGetTime(). We
31 // will only increase the system-wide timer if we're not running on battery 31 // will only increase the system-wide timer if we're not running on battery
32 // power. Using timeBeginPeriod(1) is a requirement in order to make our 32 // power.
33 // message loop waits have the same resolution that our time measurements
34 // do. Otherwise, WaitForSingleObject(..., 1) will no less than 15ms when
35 // there is nothing else to waken the Wait.
36 33
37 #include "base/time/time.h" 34 #include "base/time/time.h"
38 35
39 #pragma comment(lib, "winmm.lib") 36 #pragma comment(lib, "winmm.lib")
40 #include <windows.h> 37 #include <windows.h>
41 #include <mmsystem.h> 38 #include <mmsystem.h>
42 39
43 #include "base/basictypes.h" 40 #include "base/basictypes.h"
44 #include "base/cpu.h" 41 #include "base/cpu.h"
45 #include "base/lazy_instance.h" 42 #include "base/lazy_instance.h"
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
80 const int kMaxMillisecondsToAvoidDrift = 60 * Time::kMillisecondsPerSecond; 77 const int kMaxMillisecondsToAvoidDrift = 60 * Time::kMillisecondsPerSecond;
81 78
82 int64 initial_time = 0; 79 int64 initial_time = 0;
83 TimeTicks initial_ticks; 80 TimeTicks initial_ticks;
84 81
85 void InitializeClock() { 82 void InitializeClock() {
86 initial_ticks = TimeTicks::Now(); 83 initial_ticks = TimeTicks::Now();
87 initial_time = CurrentWallclockMicroseconds(); 84 initial_time = CurrentWallclockMicroseconds();
88 } 85 }
89 86
87 // The two values that ActivateHighResolutionTimer uses to set the systemwide
88 // timer interrupt frequency on Windows. It controls how precise timers are
89 // but also has a big impact on battery life.
90 const int kMinTimerIntervalHighResMs = 1;
91 const int kMinTimerIntervalLowResMs = 4;
92 // Track if kMinTimerIntervalHighResMs or kMinTimerIntervalLowResMs is active.
93 bool g_high_res_timer_enabled = false;
94 // How many times the high resolution timer has been called.
95 int g_high_res_timer_count = 0;
96 // The lock to control access to the above two variables.
97 base::LazyInstance<base::Lock> g_high_res_lock = LAZY_INSTANCE_INITIALIZER;
Nico 2014/08/26 20:15:58 Can you make this ::Leaky?
98
90 } // namespace 99 } // namespace
91 100
92 // Time ----------------------------------------------------------------------- 101 // Time -----------------------------------------------------------------------
93 102
94 // The internal representation of Time uses FILETIME, whose epoch is 1601-01-01 103 // The internal representation of Time uses FILETIME, whose epoch is 1601-01-01
95 // 00:00:00 UTC. ((1970-1601)*365+89)*24*60*60*1000*1000, where 89 is the 104 // 00:00:00 UTC. ((1970-1601)*365+89)*24*60*60*1000*1000, where 89 is the
96 // number of leap year days between 1601 and 1970: (1970-1601)/4 excluding 105 // number of leap year days between 1601 and 1970: (1970-1601)/4 excluding
97 // 1700, 1800, and 1900. 106 // 1700, 1800, and 1900.
98 // static 107 // static
99 const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(11644473600000000); 108 const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(11644473600000000);
100 109
101 bool Time::high_resolution_timer_enabled_ = false;
102 int Time::high_resolution_timer_activated_ = 0;
103
104 // static 110 // static
105 Time Time::Now() { 111 Time Time::Now() {
106 if (initial_time == 0) 112 if (initial_time == 0)
107 InitializeClock(); 113 InitializeClock();
108 114
109 // We implement time using the high-resolution timers so that we can get 115 // We implement time using the high-resolution timers so that we can get
110 // timeouts which are smaller than 10-15ms. If we just used 116 // timeouts which are smaller than 10-15ms. If we just used
111 // CurrentWallclockMicroseconds(), we'd have the less-granular timer. 117 // CurrentWallclockMicroseconds(), we'd have the less-granular timer.
112 // 118 //
113 // To make this work, we initialize the clock (initial_time) and the 119 // To make this work, we initialize the clock (initial_time) and the
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
158 result.dwLowDateTime = std::numeric_limits<DWORD>::max(); 164 result.dwLowDateTime = std::numeric_limits<DWORD>::max();
159 return result; 165 return result;
160 } 166 }
161 FILETIME utc_ft; 167 FILETIME utc_ft;
162 MicrosecondsToFileTime(us_, &utc_ft); 168 MicrosecondsToFileTime(us_, &utc_ft);
163 return utc_ft; 169 return utc_ft;
164 } 170 }
165 171
166 // static 172 // static
167 void Time::EnableHighResolutionTimer(bool enable) { 173 void Time::EnableHighResolutionTimer(bool enable) {
168 // Test for single-threaded access. 174 base::AutoLock lock(g_high_res_lock.Get());
169 static PlatformThreadId my_thread = PlatformThread::CurrentId(); 175 if (g_high_res_timer_enabled == enable)
170 DCHECK(PlatformThread::CurrentId() == my_thread);
171
172 if (high_resolution_timer_enabled_ == enable)
173 return; 176 return;
174 177 g_high_res_timer_enabled = enable;
175 high_resolution_timer_enabled_ = enable; 178 if (!g_high_res_timer_count)
179 return;
180 if (enable) {
jamesr 2014/08/26 19:47:40 this bit might be worth a comment explaining the s
181 timeEndPeriod(kMinTimerIntervalLowResMs);
182 timeBeginPeriod(kMinTimerIntervalHighResMs);
183 } else {
184 timeEndPeriod(kMinTimerIntervalHighResMs);
185 timeBeginPeriod(kMinTimerIntervalLowResMs);
186 }
176 } 187 }
177 188
178 // static 189 // static
179 bool Time::ActivateHighResolutionTimer(bool activating) { 190 bool Time::ActivateHighResolutionTimer(bool activating) {
180 if (!high_resolution_timer_enabled_ && activating) 191 // We only do work on the transition from zero to one or one to zero so we
181 return false; 192 // can easily undo the effect (if necessary) when EnableHighResolutionTimer is
193 // called.
194 base::AutoLock lock(g_high_res_lock.Get());
195 UINT period = g_high_res_timer_enabled ? kMinTimerIntervalHighResMs
196 : kMinTimerIntervalLowResMs;
197 int high_res_count =
198 activating ? ++g_high_res_timer_count : --g_high_res_timer_count;
182 199
183 // Using anything other than 1ms makes timers granular
184 // to that interval.
185 const int kMinTimerIntervalMs = 1;
186 MMRESULT result;
187 if (activating) { 200 if (activating) {
188 result = timeBeginPeriod(kMinTimerIntervalMs); 201 if (high_res_count == 1)
189 high_resolution_timer_activated_++; 202 timeBeginPeriod(period);
190 } else { 203 } else {
191 result = timeEndPeriod(kMinTimerIntervalMs); 204 if (high_res_count == 0)
192 high_resolution_timer_activated_--; 205 timeEndPeriod(period);
193 } 206 }
194 return result == TIMERR_NOERROR; 207 return (period == kMinTimerIntervalHighResMs);
195 } 208 }
196 209
197 // static 210 // static
198 bool Time::IsHighResolutionTimerInUse() { 211 bool Time::IsHighResolutionTimerInUse() {
199 // Note: we should track the high_resolution_timer_activated_ value 212 base::AutoLock lock(g_high_res_lock.Get());
200 // under a lock if we want it to be accurate in a system with multiple 213 return g_high_res_timer_enabled && g_high_res_timer_count > 0;
201 // message loops. We don't do that - because we don't want to take the
202 // expense of a lock for this. We *only* track this value so that unit
203 // tests can see if the high resolution timer is on or off.
204 return high_resolution_timer_enabled_ &&
205 high_resolution_timer_activated_ > 0;
206 } 214 }
207 215
208 // static 216 // static
209 Time Time::FromExploded(bool is_local, const Exploded& exploded) { 217 Time Time::FromExploded(bool is_local, const Exploded& exploded) {
210 // Create the system struct representing our exploded time. It will either be 218 // Create the system struct representing our exploded time. It will either be
211 // in local time or UTC. 219 // in local time or UTC.
212 SYSTEMTIME st; 220 SYSTEMTIME st;
213 st.wYear = exploded.year; 221 st.wYear = exploded.year;
214 st.wMonth = exploded.month; 222 st.wMonth = exploded.month;
215 st.wDayOfWeek = exploded.day_of_week; 223 st.wDayOfWeek = exploded.day_of_week;
(...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after
531 return TimeTicks() + TimeDelta::FromMilliseconds(timeGetTime()); 539 return TimeTicks() + TimeDelta::FromMilliseconds(timeGetTime());
532 } 540 }
533 } 541 }
534 542
535 // TimeDelta ------------------------------------------------------------------ 543 // TimeDelta ------------------------------------------------------------------
536 544
537 // static 545 // static
538 TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) { 546 TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) {
539 return TimeDelta(GetHighResNowSingleton()->QPCValueToMicroseconds(qpc_value)); 547 return TimeDelta(GetHighResNowSingleton()->QPCValueToMicroseconds(qpc_value));
540 } 548 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698