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