|
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. |
|
4 |
|
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. |
|
15 // |
|
16 // Sample RepeatingTimer usage: |
|
17 // |
|
18 // class MyClass { |
|
19 // public: |
|
20 // void StartDoingStuff() { |
|
21 // timer_.Start(TimeDelta::FromSeconds(1), this, &MyClass::DoStuff); |
|
22 // } |
|
23 // void StopDoingStuff() { |
|
24 // timer_.Stop(); |
|
25 // } |
|
26 // private: |
|
27 // void DoStuff() { |
|
28 // // This method is called every second to do stuff. |
|
29 // ... |
|
30 // } |
|
31 // base::RepeatingTimer<MyClass> timer_; |
|
32 // }; |
|
33 // |
|
34 // Both OneShotTimer and RepeatingTimer also support a Reset method, which |
|
35 // allows you to easily defer the timer event until the timer delay passes once |
|
36 // again. So, in the above example, if 0.5 seconds have already passed, |
|
37 // calling Reset on timer_ would postpone DoStuff by another 1 second. In |
|
38 // other words, Reset is shorthand for calling Stop and then Start again with |
|
39 // the same arguments. |
|
40 |
|
41 #ifndef BASE_TIMER_H_ |
|
42 #define BASE_TIMER_H_ |
|
43 |
|
44 // IMPORTANT: If you change timer code, make sure that all tests (including |
|
45 // disabled ones) from timer_unittests.cc pass locally. Some are disabled |
|
46 // because they're flaky on the buildbot, but when you run them locally you |
|
47 // should be able to tell the difference. |
|
48 |
|
49 #include "base/logging.h" |
|
50 #include "base/task.h" |
|
51 #include "base/time.h" |
|
52 |
|
53 class MessageLoop; |
|
54 |
|
55 namespace base { |
|
56 |
|
57 //----------------------------------------------------------------------------- |
|
58 // This class is an implementation detail of OneShotTimer and RepeatingTimer. |
|
59 // Please do not use this class directly. |
|
60 // |
|
61 // This class exists to share code between BaseTimer<T> template instantiations. |
|
62 // |
|
63 class BaseTimer_Helper { |
|
64 public: |
|
65 // Stops the timer. |
|
66 ~BaseTimer_Helper() { |
|
67 OrphanDelayedTask(); |
|
68 } |
|
69 |
|
70 // Returns true if the timer is running (i.e., not stopped). |
|
71 bool IsRunning() const { |
|
72 return delayed_task_ != NULL; |
|
73 } |
|
74 |
|
75 // Returns the current delay for this timer. May only call this method when |
|
76 // the timer is running! |
|
77 TimeDelta GetCurrentDelay() const { |
|
78 DCHECK(IsRunning()); |
|
79 return delayed_task_->delay_; |
|
80 } |
|
81 |
|
82 protected: |
|
83 BaseTimer_Helper() : delayed_task_(NULL) {} |
|
84 |
|
85 // We have access to the timer_ member so we can orphan this task. |
|
86 class TimerTask : public Task { |
|
87 public: |
|
88 TimerTask(TimeDelta delay) : delay_(delay) { |
|
89 // timer_ is set in InitiateDelayedTask. |
|
90 } |
|
91 virtual ~TimerTask() {} |
|
92 BaseTimer_Helper* timer_; |
|
93 TimeDelta delay_; |
|
94 }; |
|
95 |
|
96 // Used to orphan delayed_task_ so that when it runs it does nothing. |
|
97 void OrphanDelayedTask(); |
|
98 |
|
99 // Used to initiated a new delayed task. This has the side-effect of |
|
100 // orphaning delayed_task_ if it is non-null. |
|
101 void InitiateDelayedTask(TimerTask* timer_task); |
|
102 |
|
103 TimerTask* delayed_task_; |
|
104 |
|
105 DISALLOW_COPY_AND_ASSIGN(BaseTimer_Helper); |
|
106 }; |
|
107 |
|
108 //----------------------------------------------------------------------------- |
|
109 // This class is an implementation detail of OneShotTimer and RepeatingTimer. |
|
110 // Please do not use this class directly. |
|
111 template <class Receiver, bool kIsRepeating> |
|
112 class BaseTimer : public BaseTimer_Helper { |
|
113 public: |
|
114 typedef void (Receiver::*ReceiverMethod)(); |
|
115 |
|
116 // Call this method to start the timer. It is an error to call this method |
|
117 // while the timer is already running. |
|
118 void Start(TimeDelta delay, Receiver* receiver, ReceiverMethod method) { |
|
119 DCHECK(!IsRunning()); |
|
120 InitiateDelayedTask(new TimerTask(delay, receiver, method)); |
|
121 } |
|
122 |
|
123 // Call this method to stop the timer. It is a no-op if the timer is not |
|
124 // running. |
|
125 void Stop() { |
|
126 OrphanDelayedTask(); |
|
127 } |
|
128 |
|
129 // Call this method to reset the timer delay of an already running timer. |
|
130 void Reset() { |
|
131 DCHECK(IsRunning()); |
|
132 InitiateDelayedTask(static_cast<TimerTask*>(delayed_task_)->Clone()); |
|
133 } |
|
134 |
|
135 private: |
|
136 typedef BaseTimer<Receiver, kIsRepeating> SelfType; |
|
137 |
|
138 class TimerTask : public BaseTimer_Helper::TimerTask { |
|
139 public: |
|
140 TimerTask(TimeDelta delay, Receiver* receiver, ReceiverMethod method) |
|
141 : BaseTimer_Helper::TimerTask(delay), |
|
142 receiver_(receiver), |
|
143 method_(method) { |
|
144 } |
|
145 |
|
146 virtual ~TimerTask() { |
|
147 // This task may be getting cleared because the MessageLoop has been |
|
148 // destructed. If so, don't leave the Timer with a dangling pointer |
|
149 // to this now-defunct task. |
|
150 ClearBaseTimer(); |
|
151 } |
|
152 |
|
153 virtual void Run() { |
|
154 if (!timer_) // timer_ is null if we were orphaned. |
|
155 return; |
|
156 if (kIsRepeating) |
|
157 ResetBaseTimer(); |
|
158 else |
|
159 ClearBaseTimer(); |
|
160 DispatchToMethod(receiver_, method_, Tuple0()); |
|
161 } |
|
162 |
|
163 TimerTask* Clone() const { |
|
164 return new TimerTask(delay_, receiver_, method_); |
|
165 } |
|
166 |
|
167 private: |
|
168 // Inform the Base that the timer is no longer active. |
|
169 void ClearBaseTimer() { |
|
170 if (timer_) { |
|
171 SelfType* self = static_cast<SelfType*>(timer_); |
|
172 // It is possible that the Timer has already been reset, and that this |
|
173 // Task is old. So, if the Timer points to a different task, assume |
|
174 // that the Timer has already taken care of properly setting the task. |
|
175 if (self->delayed_task_ == this) |
|
176 self->delayed_task_ = NULL; |
|
177 // By now the delayed_task_ in the Timer does not point to us anymore. |
|
178 // We should reset our own timer_ because the Timer can not do this |
|
179 // for us in its destructor. |
|
180 timer_ = NULL; |
|
181 } |
|
182 } |
|
183 |
|
184 // Inform the Base that we're resetting the timer. |
|
185 void ResetBaseTimer() { |
|
186 DCHECK(timer_); |
|
187 DCHECK(kIsRepeating); |
|
188 SelfType* self = static_cast<SelfType*>(timer_); |
|
189 self->Reset(); |
|
190 } |
|
191 |
|
192 Receiver* receiver_; |
|
193 ReceiverMethod method_; |
|
194 }; |
|
195 }; |
|
196 |
|
197 //----------------------------------------------------------------------------- |
|
198 // A simple, one-shot timer. See usage notes at the top of the file. |
|
199 template <class Receiver> |
|
200 class OneShotTimer : public BaseTimer<Receiver, false> {}; |
|
201 |
|
202 //----------------------------------------------------------------------------- |
|
203 // A simple, repeating timer. See usage notes at the top of the file. |
|
204 template <class Receiver> |
|
205 class RepeatingTimer : public BaseTimer<Receiver, true> {}; |
|
206 |
|
207 //----------------------------------------------------------------------------- |
|
208 // A Delay timer is like The Button from Lost. Once started, you have to keep |
|
209 // calling Reset otherwise it will call the given method in the MessageLoop |
|
210 // thread. |
|
211 // |
|
212 // Once created, it is inactive until Reset is called. Once |delay| seconds have |
|
213 // passed since the last call to Reset, the callback is made. Once the callback |
|
214 // has been made, it's inactive until Reset is called again. |
|
215 // |
|
216 // If destroyed, the timeout is canceled and will not occur even if already |
|
217 // inflight. |
|
218 template <class Receiver> |
|
219 class DelayTimer { |
|
220 public: |
|
221 typedef void (Receiver::*ReceiverMethod)(); |
|
222 |
|
223 DelayTimer(TimeDelta delay, Receiver* receiver, ReceiverMethod method) |
|
224 : receiver_(receiver), |
|
225 method_(method), |
|
226 delay_(delay) { |
|
227 } |
|
228 |
|
229 void Reset() { |
|
230 DelayFor(delay_); |
|
231 } |
|
232 |
|
233 private: |
|
234 void DelayFor(TimeDelta delay) { |
|
235 trigger_time_ = Time::Now() + delay; |
|
236 |
|
237 // If we already have a timer that will expire at or before the given delay, |
|
238 // then we have nothing more to do now. |
|
239 if (timer_.IsRunning() && timer_.GetCurrentDelay() <= delay) |
|
240 return; |
|
241 |
|
242 // The timer isn't running, or will expire too late, so restart it. |
|
243 timer_.Stop(); |
|
244 timer_.Start(delay, this, &DelayTimer<Receiver>::Check); |
|
245 } |
|
246 |
|
247 void Check() { |
|
248 if (trigger_time_.is_null()) |
|
249 return; |
|
250 |
|
251 // If we have not waited long enough, then wait some more. |
|
252 const Time now = Time::Now(); |
|
253 if (now < trigger_time_) { |
|
254 DelayFor(trigger_time_ - now); |
|
255 return; |
|
256 } |
|
257 |
|
258 (receiver_->*method_)(); |
|
259 } |
|
260 |
|
261 Receiver *const receiver_; |
|
262 const ReceiverMethod method_; |
|
263 const TimeDelta delay_; |
|
264 |
|
265 OneShotTimer<DelayTimer<Receiver> > timer_; |
|
266 Time trigger_time_; |
|
267 }; |
|
268 |
|
269 } // namespace base |
|
270 |
|
271 #endif // BASE_TIMER_H_ |