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_