blob: 1ee52d443b47631c9a97322898832f298273baef [file] [log] [blame]
license.botf003cfe2008-08-24 09:55:55 +09001// 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.commit3f4a7322008-07-27 06:49:38 +09004
5#include "base/timer.h"
darin@google.comee6fa722008-08-13 08:25:43 +09006
7#include <math.h>
8#if defined(OS_WIN)
initial.commit3f4a7322008-07-27 06:49:38 +09009#include <mmsystem.h>
darin@google.comee6fa722008-08-13 08:25:43 +090010#endif
initial.commit3f4a7322008-07-27 06:49:38 +090011
deanm@google.comc3545282008-08-06 21:29:16 +090012#include "base/atomic_sequence_num.h"
initial.commit3f4a7322008-07-27 06:49:38 +090013#include "base/logging.h"
14#include "base/message_loop.h"
15#include "base/task.h"
16
deanm@google.comc3545282008-08-06 21:29:16 +090017// A sequence number for all allocated times (used to break ties when
18// comparing times in the TimerManager, and assure FIFO execution sequence).
19static base::AtomicSequenceNumber timer_id_counter_;
initial.commit3f4a7322008-07-27 06:49:38 +090020
21//-----------------------------------------------------------------------------
22// Timer
23
24Timer::Timer(int delay, Task* task, bool repeating)
pinkerton@google.comd5fe7bb2008-08-13 07:41:22 +090025 : task_(task),
26 delay_(delay),
initial.commit3f4a7322008-07-27 06:49:38 +090027 repeating_(repeating) {
deanm@google.comc3545282008-08-06 21:29:16 +090028 timer_id_ = timer_id_counter_.GetNext();
initial.commit3f4a7322008-07-27 06:49:38 +090029 DCHECK(delay >= 0);
30 Reset();
31}
32
33void 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
42void 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
51bool TimerPQueue::ContainsTimer(const Timer* timer) const {
52 return find(c.begin(), c.end(), timer) != c.end();
53}
54
55//-----------------------------------------------------------------------------
56// TimerManager
57
darin@google.comee6fa722008-08-13 08:25:43 +090058TimerManager::TimerManager(MessageLoop* message_loop)
59 : use_broken_delay_(false),
60 message_loop_(message_loop) {
61#if defined(OS_WIN)
initial.commit3f4a7322008-07-27 06:49:38 +090062 // 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.comee6fa722008-08-13 08:25:43 +090071#endif
initial.commit3f4a7322008-07-27 06:49:38 +090072}
73
74TimerManager::~TimerManager() {
darin@google.comee6fa722008-08-13 08:25:43 +090075#if defined(OS_WIN)
initial.commit3f4a7322008-07-27 06:49:38 +090076 // Match timeBeginPeriod() from construction.
77 timeEndPeriod(1);
darin@google.comee6fa722008-08-13 08:25:43 +090078#endif
initial.commit3f4a7322008-07-27 06:49:38 +090079
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
91Timer* TimerManager::StartTimer(int delay, Task* task, bool repeating) {
92 Timer* t = new Timer(delay, task, repeating);
93 StartTimer(t);
94 return t;
95}
96
97void 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.comee6fa722008-08-13 08:25:43 +0900106 DidChangeNextTimer();
initial.commit3f4a7322008-07-27 06:49:38 +0900107 }
108}
109
110void TimerManager::ResetTimer(Timer* timer) {
111 StopTimer(timer);
112 timer->Reset();
113 StartTimer(timer);
114}
115
116bool TimerManager::IsTimerRunning(const Timer* timer) const {
117 return timers_.ContainsTimer(timer);
118}
119
120Timer* TimerManager::PeekTopTimer() {
121 if (timers_.empty())
122 return NULL;
123 return timers_.top();
124}
125
126bool TimerManager::RunSomePendingTimers() {
127 bool did_work = false;
initial.commit3f4a7322008-07-27 06:49:38 +0900128 // 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.com6393bed2008-08-20 15:30:58 +0900133 if (timers_.empty() || timers_.top()->fire_time() > Time::Now())
initial.commit3f4a7322008-07-27 06:49:38 +0900134 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.comee6fa722008-08-13 08:25:43 +0900140
initial.commit3f4a7322008-07-27 06:49:38 +0900141 // 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.comee6fa722008-08-13 08:25:43 +0900144 if (!message_loop_->NestableTasksAllowed() &&
initial.commit3f4a7322008-07-27 06:49:38 +0900145 !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.comee6fa722008-08-13 08:25:43 +0900157 message_loop_->RunTimerTask(pending);
initial.commit3f4a7322008-07-27 06:49:38 +0900158 }
159
160 // Restart the WM_TIMER (if necessary).
161 if (did_work)
darin@google.comee6fa722008-08-13 08:25:43 +0900162 DidChangeNextTimer();
initial.commit3f4a7322008-07-27 06:49:38 +0900163
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.
170void 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.comee6fa722008-08-13 08:25:43 +0900177 if (timers_.top() == timer) // We are new head of queue.
178 DidChangeNextTimer();
initial.commit3f4a7322008-07-27 06:49:38 +0900179}
180
darin@google.com6393bed2008-08-20 15:30:58 +0900181Time TimerManager::GetNextFireTime() const {
initial.commit3f4a7322008-07-27 06:49:38 +0900182 if (timers_.empty())
darin@google.com6393bed2008-08-20 15:30:58 +0900183 return Time();
184
185 return timers_.top()->fire_time();
initial.commit3f4a7322008-07-27 06:49:38 +0900186}
187
darin@google.comee6fa722008-08-13 08:25:43 +0900188void 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.commit3f4a7322008-07-27 06:49:38 +0900197 }
darin@google.comee6fa722008-08-13 08:25:43 +0900198 message_loop_->DidChangeNextTimerExpiry();
initial.commit3f4a7322008-07-27 06:49:38 +0900199}
200
201//-----------------------------------------------------------------------------
202// SimpleTimer
203
204SimpleTimer::SimpleTimer(TimeDelta delay, Task* task, bool repeating)
205 : timer_(static_cast<int>(delay.InMilliseconds()), task, repeating),
206 owns_task_(true) {
207}
208
209SimpleTimer::~SimpleTimer() {
210 Stop();
211
212 if (owns_task_)
213 delete timer_.task();
214}
215
216void SimpleTimer::Start() {
217 DCHECK(timer_.task());
218 timer_.Reset();
219 MessageLoop::current()->timer_manager()->StartTimer(&timer_);
220}
221
222void SimpleTimer::Stop() {
223 MessageLoop::current()->timer_manager()->StopTimer(&timer_);
224}
225
226bool SimpleTimer::IsRunning() const {
227 return MessageLoop::current()->timer_manager()->IsTimerRunning(&timer_);
228}
229
230void SimpleTimer::Reset() {
231 DCHECK(timer_.task());
232 MessageLoop::current()->timer_manager()->ResetTimer(&timer_);
233}
license.botf003cfe2008-08-24 09:55:55 +0900234