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