| 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 |