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