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