Add partial pre-read functionality to browser startup (Windows).
[chromium-blink-merge.git] / base / timer.h
blob9c9bec777975e136a18d43519685ee5e6851eec6
1 // Copyright (c) 2012 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.
5 // 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.
16 // Sample RepeatingTimer usage:
18 // class MyClass {
19 // public:
20 // void StartDoingStuff() {
21 // timer_.Start(FROM_HERE, TimeDelta::FromSeconds(1),
22 // this, &MyClass::DoStuff);
23 // }
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 // };
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.
42 #ifndef BASE_TIMER_H_
43 #define BASE_TIMER_H_
44 #pragma once
46 // 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.
51 #include "base/base_export.h"
52 #include "base/location.h"
53 #include "base/logging.h"
54 #include "base/time.h"
56 class MessageLoop;
58 namespace base {
60 //-----------------------------------------------------------------------------
61 // This class is an implementation detail of OneShotTimer and RepeatingTimer.
62 // Please do not use this class directly.
64 // This class exists to share code between BaseTimer<T> template instantiations.
66 class BASE_EXPORT BaseTimer_Helper {
67 public:
68 // Stops the timer.
69 ~BaseTimer_Helper() {
70 OrphanDelayedTask();
73 // Returns true if the timer is running (i.e., not stopped).
74 bool IsRunning() const {
75 return delayed_task_ != NULL;
78 // 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_;
85 protected:
86 BaseTimer_Helper() : delayed_task_(NULL) {}
88 // We have access to the timer_ member so we can orphan this task.
89 class TimerTask {
90 public:
91 TimerTask(const tracked_objects::Location& posted_from,
92 TimeDelta delay)
93 : posted_from_(posted_from),
94 timer_(NULL),
95 delay_(delay) {
97 virtual ~TimerTask() {}
98 virtual void Run() = 0;
99 tracked_objects::Location posted_from_;
100 BaseTimer_Helper* timer_;
101 TimeDelta delay_;
104 // Used to orphan delayed_task_ so that when it runs it does nothing.
105 void OrphanDelayedTask();
107 // 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);
111 TimerTask* delayed_task_;
113 DISALLOW_COPY_AND_ASSIGN(BaseTimer_Helper);
116 //-----------------------------------------------------------------------------
117 // This class is an implementation detail of OneShotTimer and RepeatingTimer.
118 // Please do not use this class directly.
119 template <class Receiver, bool kIsRepeating>
120 class BaseTimer : public BaseTimer_Helper {
121 public:
122 typedef void (Receiver::*ReceiverMethod)();
124 // Call this method to start the timer. It is an error to call this method
125 // while the timer is already running.
126 void Start(const tracked_objects::Location& posted_from,
127 TimeDelta delay,
128 Receiver* receiver,
129 ReceiverMethod method) {
130 DCHECK(!IsRunning());
131 InitiateDelayedTask(new TimerTask(posted_from, delay, receiver, method));
134 // Call this method to stop the timer. It is a no-op if the timer is not
135 // running.
136 void Stop() {
137 OrphanDelayedTask();
140 // Call this method to reset the timer delay of an already running timer.
141 void Reset() {
142 DCHECK(IsRunning());
143 InitiateDelayedTask(static_cast<TimerTask*>(delayed_task_)->Clone());
146 private:
147 typedef BaseTimer<Receiver, kIsRepeating> SelfType;
149 class TimerTask : public BaseTimer_Helper::TimerTask {
150 public:
151 TimerTask(const tracked_objects::Location& posted_from,
152 TimeDelta delay,
153 Receiver* receiver,
154 ReceiverMethod method)
155 : BaseTimer_Helper::TimerTask(posted_from, delay),
156 receiver_(receiver),
157 method_(method) {
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();
167 virtual void Run() {
168 if (!timer_) // timer_ is null if we were orphaned.
169 return;
170 if (kIsRepeating)
171 ResetBaseTimer();
172 else
173 ClearBaseTimer();
174 (receiver_->*method_)();
177 TimerTask* Clone() const {
178 return new TimerTask(posted_from_, delay_, receiver_, method_);
181 private:
182 // 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;
191 // 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
193 // for us in its destructor.
194 timer_ = NULL;
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();
206 Receiver* receiver_;
207 ReceiverMethod method_;
211 //-----------------------------------------------------------------------------
212 // A simple, one-shot timer. See usage notes at the top of the file.
213 template <class Receiver>
214 class OneShotTimer : public BaseTimer<Receiver, false> {};
216 //-----------------------------------------------------------------------------
217 // A simple, repeating timer. See usage notes at the top of the file.
218 template <class Receiver>
219 class RepeatingTimer : public BaseTimer<Receiver, true> {};
221 //-----------------------------------------------------------------------------
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.
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.
230 // If destroyed, the timeout is canceled and will not occur even if already
231 // inflight.
232 template <class Receiver>
233 class DelayTimer {
234 public:
235 typedef void (Receiver::*ReceiverMethod)();
237 DelayTimer(const tracked_objects::Location& posted_from,
238 TimeDelta delay,
239 Receiver* receiver,
240 ReceiverMethod method)
241 : posted_from_(posted_from),
242 receiver_(receiver),
243 method_(method),
244 delay_(delay) {
247 void Reset() {
248 DelayFor(delay_);
251 private:
252 void DelayFor(TimeDelta delay) {
253 trigger_time_ = TimeTicks::Now() + delay;
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;
260 // The timer isn't running, or will expire too late, so restart it.
261 timer_.Stop();
262 timer_.Start(posted_from_, delay, this, &DelayTimer<Receiver>::Check);
265 void Check() {
266 if (trigger_time_.is_null())
267 return;
269 // If we have not waited long enough, then wait some more.
270 const TimeTicks now = TimeTicks::Now();
271 if (now < trigger_time_) {
272 DelayFor(trigger_time_ - now);
273 return;
276 (receiver_->*method_)();
279 tracked_objects::Location posted_from_;
280 Receiver *const receiver_;
281 const ReceiverMethod method_;
282 const TimeDelta delay_;
284 OneShotTimer<DelayTimer<Receiver> > timer_;
285 TimeTicks trigger_time_;
288 } // namespace base
290 #endif // BASE_TIMER_H_