blob: 9c9bec777975e136a18d43519685ee5e6851eec6 [file] [log] [blame]
[email protected]e0d22e82012-01-04 00:46:571// Copyright (c) 2012 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commitd7cae122008-07-26 21:49:384
[email protected]aeab57ea2008-08-28 20:50:125// OneShotTimer and RepeatingTimer provide a simple timer API. As the names
6// suggest, OneShotTimer calls you back once after a time delay expires.
7// RepeatingTimer on the other hand calls you back periodically with the
8// prescribed time interval.
9//
10// OneShotTimer and RepeatingTimer both cancel the timer when they go out of
11// scope, which makes it easy to ensure that you do not get called when your
12// object has gone out of scope. Just instantiate a OneShotTimer or
13// RepeatingTimer as a member variable of the class for which you wish to
14// receive timer events.
15//
16// Sample RepeatingTimer usage:
17//
18// class MyClass {
19// public:
20// void StartDoingStuff() {
[email protected]e0d22e82012-01-04 00:46:5721// timer_.Start(FROM_HERE, TimeDelta::FromSeconds(1),
22// this, &MyClass::DoStuff);
[email protected]aeab57ea2008-08-28 20:50:1223// }
24// void StopDoingStuff() {
25// timer_.Stop();
26// }
27// private:
28// void DoStuff() {
29// // This method is called every second to do stuff.
30// ...
31// }
32// base::RepeatingTimer<MyClass> timer_;
33// };
34//
35// Both OneShotTimer and RepeatingTimer also support a Reset method, which
36// allows you to easily defer the timer event until the timer delay passes once
37// again. So, in the above example, if 0.5 seconds have already passed,
38// calling Reset on timer_ would postpone DoStuff by another 1 second. In
39// other words, Reset is shorthand for calling Stop and then Start again with
40// the same arguments.
[email protected]aeab57ea2008-08-28 20:50:1241
initial.commitd7cae122008-07-26 21:49:3842#ifndef BASE_TIMER_H_
43#define BASE_TIMER_H_
[email protected]32b76ef2010-07-26 23:08:2444#pragma once
initial.commitd7cae122008-07-26 21:49:3845
[email protected]7d8776812009-03-09 19:54:1646// IMPORTANT: If you change timer code, make sure that all tests (including
47// disabled ones) from timer_unittests.cc pass locally. Some are disabled
48// because they're flaky on the buildbot, but when you run them locally you
49// should be able to tell the difference.
50
[email protected]0bea7252011-08-05 15:34:0051#include "base/base_export.h"
[email protected]c62dd9d2011-09-21 18:05:4152#include "base/location.h"
[email protected]56d01f62009-03-12 22:41:5453#include "base/logging.h"
initial.commitd7cae122008-07-26 21:49:3854#include "base/time.h"
55
[email protected]a5b94a92008-08-12 23:25:4356class MessageLoop;
[email protected]aeab57ea2008-08-28 20:50:1257
58namespace base {
59
[email protected]a5b94a92008-08-12 23:25:4360//-----------------------------------------------------------------------------
[email protected]aeab57ea2008-08-28 20:50:1261// This class is an implementation detail of OneShotTimer and RepeatingTimer.
62// Please do not use this class directly.
63//
64// This class exists to share code between BaseTimer<T> template instantiations.
65//
[email protected]0bea7252011-08-05 15:34:0066class BASE_EXPORT BaseTimer_Helper {
initial.commitd7cae122008-07-26 21:49:3867 public:
68 // Stops the timer.
[email protected]aeab57ea2008-08-28 20:50:1269 ~BaseTimer_Helper() {
70 OrphanDelayedTask();
initial.commitd7cae122008-07-26 21:49:3871 }
72
[email protected]aeab57ea2008-08-28 20:50:1273 // Returns true if the timer is running (i.e., not stopped).
74 bool IsRunning() const {
75 return delayed_task_ != NULL;
initial.commitd7cae122008-07-26 21:49:3876 }
77
[email protected]7e2fa032008-09-25 21:35:1078 // Returns the current delay for this timer. May only call this method when
79 // the timer is running!
80 TimeDelta GetCurrentDelay() const {
81 DCHECK(IsRunning());
82 return delayed_task_->delay_;
83 }
84
initial.commitd7cae122008-07-26 21:49:3885 protected:
[email protected]2d316662008-09-03 18:18:1486 BaseTimer_Helper() : delayed_task_(NULL) {}
initial.commitd7cae122008-07-26 21:49:3887
[email protected]aeab57ea2008-08-28 20:50:1288 // We have access to the timer_ member so we can orphan this task.
[email protected]8380ebee2011-12-15 19:35:4989 class TimerTask {
[email protected]aeab57ea2008-08-28 20:50:1290 public:
[email protected]d323a172011-09-02 18:23:0291 TimerTask(const tracked_objects::Location& posted_from,
92 TimeDelta delay)
93 : posted_from_(posted_from),
94 timer_(NULL),
95 delay_(delay) {
[email protected]2d316662008-09-03 18:18:1496 }
[email protected]a0287cbd2008-12-02 23:16:5597 virtual ~TimerTask() {}
[email protected]8380ebee2011-12-15 19:35:4998 virtual void Run() = 0;
[email protected]d323a172011-09-02 18:23:0299 tracked_objects::Location posted_from_;
[email protected]aeab57ea2008-08-28 20:50:12100 BaseTimer_Helper* timer_;
[email protected]2d316662008-09-03 18:18:14101 TimeDelta delay_;
[email protected]aeab57ea2008-08-28 20:50:12102 };
initial.commitd7cae122008-07-26 21:49:38103
[email protected]aeab57ea2008-08-28 20:50:12104 // Used to orphan delayed_task_ so that when it runs it does nothing.
105 void OrphanDelayedTask();
[email protected]e68e62fa2009-02-20 02:00:04106
[email protected]aeab57ea2008-08-28 20:50:12107 // Used to initiated a new delayed task. This has the side-effect of
108 // orphaning delayed_task_ if it is non-null.
109 void InitiateDelayedTask(TimerTask* timer_task);
initial.commitd7cae122008-07-26 21:49:38110
[email protected]aeab57ea2008-08-28 20:50:12111 TimerTask* delayed_task_;
[email protected]aeab57ea2008-08-28 20:50:12112
113 DISALLOW_COPY_AND_ASSIGN(BaseTimer_Helper);
initial.commitd7cae122008-07-26 21:49:38114};
115
[email protected]a5b94a92008-08-12 23:25:43116//-----------------------------------------------------------------------------
[email protected]aeab57ea2008-08-28 20:50:12117// This class is an implementation detail of OneShotTimer and RepeatingTimer.
118// Please do not use this class directly.
[email protected]2d316662008-09-03 18:18:14119template <class Receiver, bool kIsRepeating>
[email protected]aeab57ea2008-08-28 20:50:12120class BaseTimer : public BaseTimer_Helper {
initial.commitd7cae122008-07-26 21:49:38121 public:
[email protected]aeab57ea2008-08-28 20:50:12122 typedef void (Receiver::*ReceiverMethod)();
123
[email protected]aeab57ea2008-08-28 20:50:12124 // Call this method to start the timer. It is an error to call this method
125 // while the timer is already running.
[email protected]d323a172011-09-02 18:23:02126 void Start(const tracked_objects::Location& posted_from,
127 TimeDelta delay,
128 Receiver* receiver,
129 ReceiverMethod method) {
[email protected]aeab57ea2008-08-28 20:50:12130 DCHECK(!IsRunning());
[email protected]d323a172011-09-02 18:23:02131 InitiateDelayedTask(new TimerTask(posted_from, delay, receiver, method));
initial.commitd7cae122008-07-26 21:49:38132 }
[email protected]aeab57ea2008-08-28 20:50:12133
134 // Call this method to stop the timer. It is a no-op if the timer is not
135 // running.
136 void Stop() {
[email protected]aeab57ea2008-08-28 20:50:12137 OrphanDelayedTask();
138 }
139
140 // Call this method to reset the timer delay of an already running timer.
141 void Reset() {
142 DCHECK(IsRunning());
[email protected]2d316662008-09-03 18:18:14143 InitiateDelayedTask(static_cast<TimerTask*>(delayed_task_)->Clone());
[email protected]aeab57ea2008-08-28 20:50:12144 }
145
initial.commitd7cae122008-07-26 21:49:38146 private:
[email protected]2d316662008-09-03 18:18:14147 typedef BaseTimer<Receiver, kIsRepeating> SelfType;
[email protected]e68e62fa2009-02-20 02:00:04148
[email protected]aeab57ea2008-08-28 20:50:12149 class TimerTask : public BaseTimer_Helper::TimerTask {
150 public:
[email protected]d323a172011-09-02 18:23:02151 TimerTask(const tracked_objects::Location& posted_from,
152 TimeDelta delay,
153 Receiver* receiver,
154 ReceiverMethod method)
155 : BaseTimer_Helper::TimerTask(posted_from, delay),
[email protected]2d316662008-09-03 18:18:14156 receiver_(receiver),
157 method_(method) {
158 }
[email protected]a0287cbd2008-12-02 23:16:55159
160 virtual ~TimerTask() {
161 // This task may be getting cleared because the MessageLoop has been
162 // destructed. If so, don't leave the Timer with a dangling pointer
163 // to this now-defunct task.
164 ClearBaseTimer();
165 }
166
[email protected]aeab57ea2008-08-28 20:50:12167 virtual void Run() {
168 if (!timer_) // timer_ is null if we were orphaned.
169 return;
[email protected]a0287cbd2008-12-02 23:16:55170 if (kIsRepeating)
171 ResetBaseTimer();
172 else
173 ClearBaseTimer();
[email protected]8380ebee2011-12-15 19:35:49174 (receiver_->*method_)();
[email protected]aeab57ea2008-08-28 20:50:12175 }
[email protected]a0287cbd2008-12-02 23:16:55176
[email protected]2d316662008-09-03 18:18:14177 TimerTask* Clone() const {
[email protected]d323a172011-09-02 18:23:02178 return new TimerTask(posted_from_, delay_, receiver_, method_);
[email protected]2d316662008-09-03 18:18:14179 }
[email protected]a0287cbd2008-12-02 23:16:55180
[email protected]2d316662008-09-03 18:18:14181 private:
[email protected]a0287cbd2008-12-02 23:16:55182 // Inform the Base that the timer is no longer active.
183 void ClearBaseTimer() {
184 if (timer_) {
185 SelfType* self = static_cast<SelfType*>(timer_);
186 // It is possible that the Timer has already been reset, and that this
187 // Task is old. So, if the Timer points to a different task, assume
188 // that the Timer has already taken care of properly setting the task.
189 if (self->delayed_task_ == this)
190 self->delayed_task_ = NULL;
[email protected]95284322009-02-07 00:37:01191 // By now the delayed_task_ in the Timer does not point to us anymore.
192 // We should reset our own timer_ because the Timer can not do this
[email protected]52a261f2009-03-03 15:01:12193 // for us in its destructor.
[email protected]95284322009-02-07 00:37:01194 timer_ = NULL;
[email protected]a0287cbd2008-12-02 23:16:55195 }
196 }
197
198 // Inform the Base that we're resetting the timer.
199 void ResetBaseTimer() {
200 DCHECK(timer_);
201 DCHECK(kIsRepeating);
202 SelfType* self = static_cast<SelfType*>(timer_);
203 self->Reset();
204 }
205
[email protected]2d316662008-09-03 18:18:14206 Receiver* receiver_;
207 ReceiverMethod method_;
[email protected]aeab57ea2008-08-28 20:50:12208 };
initial.commitd7cae122008-07-26 21:49:38209};
210
[email protected]a5b94a92008-08-12 23:25:43211//-----------------------------------------------------------------------------
[email protected]aeab57ea2008-08-28 20:50:12212// A simple, one-shot timer. See usage notes at the top of the file.
213template <class Receiver>
[email protected]2d316662008-09-03 18:18:14214class OneShotTimer : public BaseTimer<Receiver, false> {};
initial.commitd7cae122008-07-26 21:49:38215
[email protected]aeab57ea2008-08-28 20:50:12216//-----------------------------------------------------------------------------
217// A simple, repeating timer. See usage notes at the top of the file.
218template <class Receiver>
[email protected]2d316662008-09-03 18:18:14219class RepeatingTimer : public BaseTimer<Receiver, true> {};
[email protected]aeab57ea2008-08-28 20:50:12220
[email protected]e68e62fa2009-02-20 02:00:04221//-----------------------------------------------------------------------------
222// A Delay timer is like The Button from Lost. Once started, you have to keep
223// calling Reset otherwise it will call the given method in the MessageLoop
224// thread.
225//
226// Once created, it is inactive until Reset is called. Once |delay| seconds have
227// passed since the last call to Reset, the callback is made. Once the callback
228// has been made, it's inactive until Reset is called again.
[email protected]02bf7f272009-02-26 23:17:56229//
230// If destroyed, the timeout is canceled and will not occur even if already
231// inflight.
[email protected]e68e62fa2009-02-20 02:00:04232template <class Receiver>
233class DelayTimer {
234 public:
235 typedef void (Receiver::*ReceiverMethod)();
236
[email protected]d323a172011-09-02 18:23:02237 DelayTimer(const tracked_objects::Location& posted_from,
238 TimeDelta delay,
239 Receiver* receiver,
240 ReceiverMethod method)
241 : posted_from_(posted_from),
242 receiver_(receiver),
[email protected]e68e62fa2009-02-20 02:00:04243 method_(method),
244 delay_(delay) {
245 }
246
247 void Reset() {
248 DelayFor(delay_);
249 }
250
251 private:
252 void DelayFor(TimeDelta delay) {
[email protected]e8a49b32011-03-20 07:19:45253 trigger_time_ = TimeTicks::Now() + delay;
[email protected]e68e62fa2009-02-20 02:00:04254
255 // If we already have a timer that will expire at or before the given delay,
256 // then we have nothing more to do now.
257 if (timer_.IsRunning() && timer_.GetCurrentDelay() <= delay)
258 return;
259
260 // The timer isn't running, or will expire too late, so restart it.
261 timer_.Stop();
[email protected]d323a172011-09-02 18:23:02262 timer_.Start(posted_from_, delay, this, &DelayTimer<Receiver>::Check);
[email protected]e68e62fa2009-02-20 02:00:04263 }
264
265 void Check() {
266 if (trigger_time_.is_null())
267 return;
268
269 // If we have not waited long enough, then wait some more.
[email protected]e8a49b32011-03-20 07:19:45270 const TimeTicks now = TimeTicks::Now();
[email protected]e68e62fa2009-02-20 02:00:04271 if (now < trigger_time_) {
272 DelayFor(trigger_time_ - now);
273 return;
274 }
275
276 (receiver_->*method_)();
277 }
278
[email protected]d323a172011-09-02 18:23:02279 tracked_objects::Location posted_from_;
[email protected]e68e62fa2009-02-20 02:00:04280 Receiver *const receiver_;
281 const ReceiverMethod method_;
282 const TimeDelta delay_;
283
284 OneShotTimer<DelayTimer<Receiver> > timer_;
[email protected]e8a49b32011-03-20 07:19:45285 TimeTicks trigger_time_;
[email protected]e68e62fa2009-02-20 02:00:04286};
287
[email protected]aeab57ea2008-08-28 20:50:12288} // namespace base
289
initial.commitd7cae122008-07-26 21:49:38290#endif // BASE_TIMER_H_