tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

timer.h (8775B)


      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  ~BaseTimer_Helper() { OrphanDelayedTask(); }
     69 
     70  // Returns true if the timer is running (i.e., not stopped).
     71  bool IsRunning() const { return !!delayed_task_; }
     72 
     73  // Returns the current delay for this timer.  May only call this method when
     74  // the timer is running!
     75  TimeDelta GetCurrentDelay() const {
     76    DCHECK(IsRunning());
     77    return delayed_task_->delay_;
     78  }
     79 
     80 protected:
     81  BaseTimer_Helper() {}
     82 
     83  // We have access to the timer_ member so we can orphan this task.
     84  class TimerTask : public mozilla::Runnable {
     85   public:
     86    explicit TimerTask(TimeDelta delay)
     87        : mozilla::Runnable("base::BaseTimer_Helper::TimerTask"),
     88          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  RefPtr<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() { OrphanDelayedTask(); }
    126 
    127  // Call this method to reset the timer delay of an already running timer.
    128  void Reset() {
    129    DCHECK(IsRunning());
    130    InitiateDelayedTask(static_cast<TimerTask*>(delayed_task_.get())->Clone());
    131  }
    132 
    133 private:
    134  typedef BaseTimer<Receiver, kIsRepeating> SelfType;
    135 
    136  class TimerTask : public BaseTimer_Helper::TimerTask {
    137   public:
    138    TimerTask(TimeDelta delay, Receiver* receiver, ReceiverMethod method)
    139        : BaseTimer_Helper::TimerTask(delay),
    140          receiver_(receiver),
    141          method_(method) {}
    142 
    143    virtual ~TimerTask() {
    144      // This task may be getting cleared because the MessageLoop has been
    145      // destructed.  If so, don't leave the Timer with a dangling pointer
    146      // to this now-defunct task.
    147      ClearBaseTimer();
    148    }
    149 
    150    NS_IMETHOD Run() override {
    151      if (!timer_)  // timer_ is null if we were orphaned.
    152        return NS_OK;
    153      if (kIsRepeating)
    154        ResetBaseTimer();
    155      else
    156        ClearBaseTimer();
    157      DispatchToMethod(receiver_, method_, Tuple0());
    158      return NS_OK;
    159    }
    160 
    161    TimerTask* Clone() const {
    162      return new TimerTask(delay_, receiver_, method_);
    163    }
    164 
    165   private:
    166    // Inform the Base that the timer is no longer active.
    167    void ClearBaseTimer() {
    168      if (timer_) {
    169        SelfType* self = static_cast<SelfType*>(timer_);
    170        // It is possible that the Timer has already been reset, and that this
    171        // Task is old.  So, if the Timer points to a different task, assume
    172        // that the Timer has already taken care of properly setting the task.
    173        if (self->delayed_task_ == this) self->delayed_task_ = nullptr;
    174        // By now the delayed_task_ in the Timer does not point to us anymore.
    175        // We should reset our own timer_ because the Timer can not do this
    176        // for us in its destructor.
    177        timer_ = NULL;
    178      }
    179    }
    180 
    181    // Inform the Base that we're resetting the timer.
    182    void ResetBaseTimer() {
    183      DCHECK(timer_);
    184      DCHECK(kIsRepeating);
    185      SelfType* self = static_cast<SelfType*>(timer_);
    186      self->Reset();
    187    }
    188 
    189    Receiver* receiver_;
    190    ReceiverMethod method_;
    191  };
    192 };
    193 
    194 //-----------------------------------------------------------------------------
    195 // A simple, one-shot timer.  See usage notes at the top of the file.
    196 template <class Receiver>
    197 class OneShotTimer : public BaseTimer<Receiver, false> {};
    198 
    199 //-----------------------------------------------------------------------------
    200 // A simple, repeating timer.  See usage notes at the top of the file.
    201 template <class Receiver>
    202 class RepeatingTimer : public BaseTimer<Receiver, true> {};
    203 
    204 //-----------------------------------------------------------------------------
    205 // A Delay timer is like The Button from Lost. Once started, you have to keep
    206 // calling Reset otherwise it will call the given method in the MessageLoop
    207 // thread.
    208 //
    209 // Once created, it is inactive until Reset is called. Once |delay| seconds have
    210 // passed since the last call to Reset, the callback is made. Once the callback
    211 // has been made, it's inactive until Reset is called again.
    212 //
    213 // If destroyed, the timeout is canceled and will not occur even if already
    214 // inflight.
    215 template <class Receiver>
    216 class DelayTimer {
    217 public:
    218  typedef void (Receiver::*ReceiverMethod)();
    219 
    220  DelayTimer(TimeDelta delay, Receiver* receiver, ReceiverMethod method)
    221      : receiver_(receiver), method_(method), delay_(delay) {}
    222 
    223  void Reset() { DelayFor(delay_); }
    224 
    225 private:
    226  void DelayFor(TimeDelta delay) {
    227    trigger_time_ = Time::Now() + delay;
    228 
    229    // If we already have a timer that will expire at or before the given delay,
    230    // then we have nothing more to do now.
    231    if (timer_.IsRunning() && timer_.GetCurrentDelay() <= delay) return;
    232 
    233    // The timer isn't running, or will expire too late, so restart it.
    234    timer_.Stop();
    235    timer_.Start(delay, this, &DelayTimer<Receiver>::Check);
    236  }
    237 
    238  void Check() {
    239    if (trigger_time_.is_null()) return;
    240 
    241    // If we have not waited long enough, then wait some more.
    242    const Time now = Time::Now();
    243    if (now < trigger_time_) {
    244      DelayFor(trigger_time_ - now);
    245      return;
    246    }
    247 
    248    (receiver_->*method_)();
    249  }
    250 
    251  Receiver* const receiver_;
    252  const ReceiverMethod method_;
    253  const TimeDelta delay_;
    254 
    255  OneShotTimer<DelayTimer<Receiver> > timer_;
    256  Time trigger_time_;
    257 };
    258 
    259 }  // namespace base
    260 
    261 #endif  // BASE_TIMER_H_