OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2008 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 #include "base/timer.h" | 5 #include "base/timer.h" |
6 | 6 |
7 #include <math.h> | |
8 #if defined(OS_WIN) | |
9 #include <mmsystem.h> | |
10 #endif | |
11 | |
12 #include "base/atomic_sequence_num.h" | |
13 #include "base/logging.h" | |
14 #include "base/message_loop.h" | 7 #include "base/message_loop.h" |
15 #include "base/task.h" | |
16 | 8 |
17 namespace base { | 9 namespace base { |
18 | 10 |
19 // A sequence number for all allocated times (used to break ties when | |
20 // comparing times in the TimerManager, and assure FIFO execution sequence). | |
21 static AtomicSequenceNumber timer_id_counter_(base::LINKER_INITIALIZED); | |
22 | |
23 //----------------------------------------------------------------------------- | |
24 // Timer | |
25 | |
26 Timer::Timer(int delay, Task* task, bool repeating) | |
27 : task_(task), | |
28 delay_(delay), | |
29 repeating_(repeating) { | |
30 timer_id_ = timer_id_counter_.GetNext(); | |
31 DCHECK(delay >= 0); | |
32 Reset(); | |
33 } | |
34 | |
35 Timer::Timer(Time fire_time, Task* task) | |
36 : fire_time_(fire_time), | |
37 task_(task), | |
38 repeating_(false) { | |
39 timer_id_ = timer_id_counter_.GetNext(); | |
40 | |
41 // TODO(darin): kill off this stuff. because we are forced to compute 'now' | |
42 // in order to determine the delay, it is possible that our fire time could | |
43 // be in the past. /sigh/ | |
44 creation_time_ = Time::Now(); | |
45 delay_ = static_cast<int>((fire_time_ - creation_time_).InMilliseconds()); | |
46 if (delay_ < 0) | |
47 delay_ = 0; | |
48 } | |
49 | |
50 void Timer::Reset() { | |
51 creation_time_ = Time::Now(); | |
52 fire_time_ = creation_time_ + TimeDelta::FromMilliseconds(delay_); | |
53 DHISTOGRAM_COUNTS(L"Timer.Durations", delay_); | |
54 } | |
55 | |
56 //----------------------------------------------------------------------------- | |
57 // TimerPQueue | |
58 | |
59 void TimerPQueue::RemoveTimer(Timer* timer) { | |
60 const std::vector<Timer*>::iterator location = | |
61 find(c.begin(), c.end(), timer); | |
62 if (location != c.end()) { | |
63 c.erase(location); | |
64 make_heap(c.begin(), c.end(), TimerComparison()); | |
65 } | |
66 } | |
67 | |
68 bool TimerPQueue::ContainsTimer(const Timer* timer) const { | |
69 return find(c.begin(), c.end(), timer) != c.end(); | |
70 } | |
71 | |
72 //----------------------------------------------------------------------------- | |
73 // TimerManager | |
74 | |
75 TimerManager::TimerManager(MessageLoop* message_loop) | |
76 : use_broken_delay_(false), | |
77 message_loop_(message_loop) { | |
78 #if defined(OS_WIN) | |
79 // We've experimented with all sorts of timers, and initially tried | |
80 // to avoid using timeBeginPeriod because it does affect the system | |
81 // globally. However, after much investigation, it turns out that all | |
82 // of the major plugins (flash, windows media 9-11, and quicktime) | |
83 // already use timeBeginPeriod to increase the speed of the clock. | |
84 // Since the browser must work with these plugins, the browser already | |
85 // needs to support a fast clock. We may as well use this ourselves, | |
86 // as it really is the best timer mechanism for our needs. | |
87 timeBeginPeriod(1); | |
88 #endif | |
89 } | |
90 | |
91 TimerManager::~TimerManager() { | |
92 #if defined(OS_WIN) | |
93 // Match timeBeginPeriod() from construction. | |
94 timeEndPeriod(1); | |
95 #endif | |
96 | |
97 // Be nice to unit tests, and discard and delete all timers along with the | |
98 // embedded task objects by handing off to MessageLoop (which would have Run() | |
99 // and optionally deleted the objects). | |
100 while (timers_.size()) { | |
101 Timer* pending = timers_.top(); | |
102 timers_.pop(); | |
103 message_loop_->DiscardTimer(pending); | |
104 } | |
105 } | |
106 | |
107 | |
108 Timer* TimerManager::StartTimer(int delay, Task* task, bool repeating) { | |
109 Timer* t = new Timer(delay, task, repeating); | |
110 StartTimer(t); | |
111 return t; | |
112 } | |
113 | |
114 void TimerManager::StopTimer(Timer* timer) { | |
115 // Make sure the timer is actually running. | |
116 if (!IsTimerRunning(timer)) | |
117 return; | |
118 // Kill the active timer, and remove the pending entry from the queue. | |
119 if (timer != timers_.top()) { | |
120 timers_.RemoveTimer(timer); | |
121 } else { | |
122 timers_.pop(); | |
123 DidChangeNextTimer(); | |
124 } | |
125 } | |
126 | |
127 void TimerManager::ResetTimer(Timer* timer) { | |
128 StopTimer(timer); | |
129 timer->Reset(); | |
130 StartTimer(timer); | |
131 } | |
132 | |
133 bool TimerManager::IsTimerRunning(const Timer* timer) const { | |
134 return timers_.ContainsTimer(timer); | |
135 } | |
136 | |
137 Timer* TimerManager::PeekTopTimer() { | |
138 if (timers_.empty()) | |
139 return NULL; | |
140 return timers_.top(); | |
141 } | |
142 | |
143 bool TimerManager::RunSomePendingTimers() { | |
144 bool did_work = false; | |
145 // Process a small group of timers. Cap the maximum number of timers we can | |
146 // process so we don't deny cycles to other parts of the process when lots of | |
147 // timers have been set. | |
148 const int kMaxTimersPerCall = 2; | |
149 for (int i = 0; i < kMaxTimersPerCall; ++i) { | |
150 if (timers_.empty() || timers_.top()->fire_time() > Time::Now()) | |
151 break; | |
152 | |
153 // Get a pending timer. Deal with updating the timers_ queue and setting | |
154 // the TopTimer. We'll execute the timer task only after the timer queue | |
155 // is back in a consistent state. | |
156 Timer* pending = timers_.top(); | |
157 | |
158 // If pending task isn't invoked_later, then it must be possible to run it | |
159 // now (i.e., current task needs to be reentrant). | |
160 // TODO(jar): We may block tasks that we can queue from being popped. | |
161 if (!message_loop_->NestableTasksAllowed() && | |
162 !pending->task()->owned_by_message_loop_) | |
163 break; | |
164 | |
165 timers_.pop(); | |
166 did_work = true; | |
167 | |
168 // If the timer is repeating, add it back to the list of timers to process. | |
169 if (pending->repeating()) { | |
170 pending->Reset(); | |
171 timers_.push(pending); | |
172 } | |
173 | |
174 message_loop_->RunTimerTask(pending); | |
175 } | |
176 | |
177 // Restart the WM_TIMER (if necessary). | |
178 if (did_work) | |
179 DidChangeNextTimer(); | |
180 | |
181 return did_work; | |
182 } | |
183 | |
184 // Note: Caller is required to call timer->Reset() before calling StartTimer(). | |
185 // TODO(jar): change API so that Reset() is called as part of StartTimer, making | |
186 // the API a little less error prone. | |
187 void TimerManager::StartTimer(Timer* timer) { | |
188 // Make sure the timer is not running. | |
189 if (IsTimerRunning(timer)) | |
190 return; | |
191 | |
192 timers_.push(timer); // Priority queue will sort the timer into place. | |
193 | |
194 if (timers_.top() == timer) // We are new head of queue. | |
195 DidChangeNextTimer(); | |
196 } | |
197 | |
198 Time TimerManager::GetNextFireTime() const { | |
199 if (timers_.empty()) | |
200 return Time(); | |
201 | |
202 return timers_.top()->fire_time(); | |
203 } | |
204 | |
205 void TimerManager::DidChangeNextTimer() { | |
206 // Determine if the next timer expiry actually changed... | |
207 if (!timers_.empty()) { | |
208 const Time& expiry = timers_.top()->fire_time(); | |
209 if (expiry == next_timer_expiry_) | |
210 return; | |
211 next_timer_expiry_ = expiry; | |
212 } else { | |
213 next_timer_expiry_ = Time(); | |
214 } | |
215 message_loop_->DidChangeNextTimerExpiry(); | |
216 } | |
217 | |
218 //----------------------------------------------------------------------------- | 11 //----------------------------------------------------------------------------- |
219 // BaseTimer_Helper | 12 // BaseTimer_Helper |
220 | 13 |
221 void BaseTimer_Helper::OrphanDelayedTask() { | 14 void BaseTimer_Helper::OrphanDelayedTask() { |
222 if (delayed_task_) { | 15 if (delayed_task_) { |
223 delayed_task_->timer_ = NULL; | 16 delayed_task_->timer_ = NULL; |
224 delayed_task_ = NULL; | 17 delayed_task_ = NULL; |
225 } | 18 } |
226 } | 19 } |
227 | 20 |
228 void BaseTimer_Helper::InitiateDelayedTask(TimerTask* timer_task) { | 21 void BaseTimer_Helper::InitiateDelayedTask(TimerTask* timer_task) { |
229 OrphanDelayedTask(); | 22 OrphanDelayedTask(); |
230 | 23 |
231 delayed_task_ = timer_task; | 24 delayed_task_ = timer_task; |
232 delayed_task_->timer_ = this; | 25 delayed_task_->timer_ = this; |
233 MessageLoop::current()->PostDelayedTask( | 26 MessageLoop::current()->PostDelayedTask( |
234 FROM_HERE, timer_task, | 27 FROM_HERE, timer_task, |
235 static_cast<int>(timer_task->delay_.InMilliseconds())); | 28 static_cast<int>(timer_task->delay_.InMilliseconds())); |
236 } | 29 } |
237 | 30 |
238 } // namespace base | 31 } // namespace base |
OLD | NEW |