Home | History | Annotate | Download | only in base
      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.
     68   ~BaseTimer_Helper() {
     69     OrphanDelayedTask();
     70   }
     71 
     72   // Returns true if the timer is running (i.e., not stopped).
     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!
     79   TimeDelta GetCurrentDelay() const {
     80     DCHECK(IsRunning());
     81     return delayed_task_->delay_;
     82   }
     83 
     84  protected:
     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:
     90     explicit TimerTask(TimeDelta delay) : timer_(NULL), delay_(delay) {
     91     }
     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.
    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.
    126   void Stop() {
    127     OrphanDelayedTask();
    128   }
    129 
    130   // Call this method to reset the timer delay of an already running timer.
    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:
    141     TimerTask(TimeDelta delay, Receiver* receiver, ReceiverMethod method)
    142         : BaseTimer_Helper::TimerTask(delay),
    143           receiver_(receiver),
    144           method_(method) {
    145     }
    146 
    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 
    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 
    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.
    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.
    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 
    224   DelayTimer(TimeDelta delay, Receiver* receiver, ReceiverMethod method)
    225       : receiver_(receiver),
    226         method_(method),
    227         delay_(delay) {
    228   }
    229 
    230   void Reset() {
    231     DelayFor(delay_);
    232   }
    233 
    234  private:
    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 
    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