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

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: nits and leaky singleton 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
« no previous file with comments | « base/time/time.h ('k') | base/timer/hi_res_timer_manager_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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>::Leaky g_high_res_lock =
98 LAZY_INSTANCE_INITIALIZER;
99
90 } // namespace 100 } // namespace
91 101
92 // Time ----------------------------------------------------------------------- 102 // Time -----------------------------------------------------------------------
93 103
94 // The internal representation of Time uses FILETIME, whose epoch is 1601-01-01 104 // 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 105 // 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 106 // number of leap year days between 1601 and 1970: (1970-1601)/4 excluding
97 // 1700, 1800, and 1900. 107 // 1700, 1800, and 1900.
98 // static 108 // static
99 const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(11644473600000000); 109 const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(11644473600000000);
100 110
101 bool Time::high_resolution_timer_enabled_ = false;
102 int Time::high_resolution_timer_activated_ = 0;
103
104 // static 111 // static
105 Time Time::Now() { 112 Time Time::Now() {
106 if (initial_time == 0) 113 if (initial_time == 0)
107 InitializeClock(); 114 InitializeClock();
108 115
109 // We implement time using the high-resolution timers so that we can get 116 // 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 117 // timeouts which are smaller than 10-15ms. If we just used
111 // CurrentWallclockMicroseconds(), we'd have the less-granular timer. 118 // CurrentWallclockMicroseconds(), we'd have the less-granular timer.
112 // 119 //
113 // To make this work, we initialize the clock (initial_time) and the 120 // 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(); 165 result.dwLowDateTime = std::numeric_limits<DWORD>::max();
159 return result; 166 return result;
160 } 167 }
161 FILETIME utc_ft; 168 FILETIME utc_ft;
162 MicrosecondsToFileTime(us_, &utc_ft); 169 MicrosecondsToFileTime(us_, &utc_ft);
163 return utc_ft; 170 return utc_ft;
164 } 171 }
165 172
166 // static 173 // static
167 void Time::EnableHighResolutionTimer(bool enable) { 174 void Time::EnableHighResolutionTimer(bool enable) {
168 // Test for single-threaded access. 175 base::AutoLock lock(g_high_res_lock.Get());
169 static PlatformThreadId my_thread = PlatformThread::CurrentId(); 176 if (g_high_res_timer_enabled == enable)
170 DCHECK(PlatformThread::CurrentId() == my_thread);
171
172 if (high_resolution_timer_enabled_ == enable)
173 return; 177 return;
174 178 g_high_res_timer_enabled = enable;
175 high_resolution_timer_enabled_ = enable; 179 if (!g_high_res_timer_count)
180 return;
181 // Since g_high_res_timer_count != 0, an ActivateHighResolutionTimer(true)
182 // was called which called timeBeginPeriod with g_high_res_timer_enabled
183 // with a value which is the opposite of |enable|. With that information we
184 // call timeEndPeriod with the same value used in timeBeginPeriod and
185 // therefore undo the period effect.
186 if (enable) {
187 timeEndPeriod(kMinTimerIntervalLowResMs);
188 timeBeginPeriod(kMinTimerIntervalHighResMs);
189 } else {
190 timeEndPeriod(kMinTimerIntervalHighResMs);
191 timeBeginPeriod(kMinTimerIntervalLowResMs);
192 }
176 } 193 }
177 194
178 // static 195 // static
179 bool Time::ActivateHighResolutionTimer(bool activating) { 196 bool Time::ActivateHighResolutionTimer(bool activating) {
180 if (!high_resolution_timer_enabled_ && activating) 197 // We only do work on the transition from zero to one or one to zero so we
181 return false; 198 // can easily undo the effect (if necessary) when EnableHighResolutionTimer is
199 // called.
200 base::AutoLock lock(g_high_res_lock.Get());
201 UINT period = g_high_res_timer_enabled ? kMinTimerIntervalHighResMs
202 : kMinTimerIntervalLowResMs;
203 int high_res_count =
204 activating ? ++g_high_res_timer_count : --g_high_res_timer_count;
182 205
183 // Using anything other than 1ms makes timers granular
184 // to that interval.
185 const int kMinTimerIntervalMs = 1;
186 MMRESULT result;
187 if (activating) { 206 if (activating) {
188 result = timeBeginPeriod(kMinTimerIntervalMs); 207 if (high_res_count == 1)
189 high_resolution_timer_activated_++; 208 timeBeginPeriod(period);
190 } else { 209 } else {
191 result = timeEndPeriod(kMinTimerIntervalMs); 210 if (high_res_count == 0)
192 high_resolution_timer_activated_--; 211 timeEndPeriod(period);
193 } 212 }
194 return result == TIMERR_NOERROR; 213 return (period == kMinTimerIntervalHighResMs);
195 } 214 }
196 215
197 // static 216 // static
198 bool Time::IsHighResolutionTimerInUse() { 217 bool Time::IsHighResolutionTimerInUse() {
199 // Note: we should track the high_resolution_timer_activated_ value 218 base::AutoLock lock(g_high_res_lock.Get());
200 // under a lock if we want it to be accurate in a system with multiple 219 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 } 220 }
207 221
208 // static 222 // static
209 Time Time::FromExploded(bool is_local, const Exploded& exploded) { 223 Time Time::FromExploded(bool is_local, const Exploded& exploded) {
210 // Create the system struct representing our exploded time. It will either be 224 // Create the system struct representing our exploded time. It will either be
211 // in local time or UTC. 225 // in local time or UTC.
212 SYSTEMTIME st; 226 SYSTEMTIME st;
213 st.wYear = exploded.year; 227 st.wYear = exploded.year;
214 st.wMonth = exploded.month; 228 st.wMonth = exploded.month;
215 st.wDayOfWeek = exploded.day_of_week; 229 st.wDayOfWeek = exploded.day_of_week;
(...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after
531 return TimeTicks() + TimeDelta::FromMilliseconds(timeGetTime()); 545 return TimeTicks() + TimeDelta::FromMilliseconds(timeGetTime());
532 } 546 }
533 } 547 }
534 548
535 // TimeDelta ------------------------------------------------------------------ 549 // TimeDelta ------------------------------------------------------------------
536 550
537 // static 551 // static
538 TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) { 552 TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) {
539 return TimeDelta(GetHighResNowSingleton()->QPCValueToMicroseconds(qpc_value)); 553 return TimeDelta(GetHighResNowSingleton()->QPCValueToMicroseconds(qpc_value));
540 } 554 }
OLDNEW
« no previous file with comments | « base/time/time.h ('k') | base/timer/hi_res_timer_manager_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698