ipc/chromium/src/base/timer.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
michael@0 2 // Use of this source code is governed by a BSD-style license that can be
michael@0 3 // found in the LICENSE file.
michael@0 4
michael@0 5 // OneShotTimer and RepeatingTimer provide a simple timer API. As the names
michael@0 6 // suggest, OneShotTimer calls you back once after a time delay expires.
michael@0 7 // RepeatingTimer on the other hand calls you back periodically with the
michael@0 8 // prescribed time interval.
michael@0 9 //
michael@0 10 // OneShotTimer and RepeatingTimer both cancel the timer when they go out of
michael@0 11 // scope, which makes it easy to ensure that you do not get called when your
michael@0 12 // object has gone out of scope. Just instantiate a OneShotTimer or
michael@0 13 // RepeatingTimer as a member variable of the class for which you wish to
michael@0 14 // receive timer events.
michael@0 15 //
michael@0 16 // Sample RepeatingTimer usage:
michael@0 17 //
michael@0 18 // class MyClass {
michael@0 19 // public:
michael@0 20 // void StartDoingStuff() {
michael@0 21 // timer_.Start(TimeDelta::FromSeconds(1), this, &MyClass::DoStuff);
michael@0 22 // }
michael@0 23 // void StopDoingStuff() {
michael@0 24 // timer_.Stop();
michael@0 25 // }
michael@0 26 // private:
michael@0 27 // void DoStuff() {
michael@0 28 // // This method is called every second to do stuff.
michael@0 29 // ...
michael@0 30 // }
michael@0 31 // base::RepeatingTimer<MyClass> timer_;
michael@0 32 // };
michael@0 33 //
michael@0 34 // Both OneShotTimer and RepeatingTimer also support a Reset method, which
michael@0 35 // allows you to easily defer the timer event until the timer delay passes once
michael@0 36 // again. So, in the above example, if 0.5 seconds have already passed,
michael@0 37 // calling Reset on timer_ would postpone DoStuff by another 1 second. In
michael@0 38 // other words, Reset is shorthand for calling Stop and then Start again with
michael@0 39 // the same arguments.
michael@0 40
michael@0 41 #ifndef BASE_TIMER_H_
michael@0 42 #define BASE_TIMER_H_
michael@0 43
michael@0 44 // IMPORTANT: If you change timer code, make sure that all tests (including
michael@0 45 // disabled ones) from timer_unittests.cc pass locally. Some are disabled
michael@0 46 // because they're flaky on the buildbot, but when you run them locally you
michael@0 47 // should be able to tell the difference.
michael@0 48
michael@0 49 #include "base/logging.h"
michael@0 50 #include "base/task.h"
michael@0 51 #include "base/time.h"
michael@0 52
michael@0 53 class MessageLoop;
michael@0 54
michael@0 55 namespace base {
michael@0 56
michael@0 57 //-----------------------------------------------------------------------------
michael@0 58 // This class is an implementation detail of OneShotTimer and RepeatingTimer.
michael@0 59 // Please do not use this class directly.
michael@0 60 //
michael@0 61 // This class exists to share code between BaseTimer<T> template instantiations.
michael@0 62 //
michael@0 63 class BaseTimer_Helper {
michael@0 64 public:
michael@0 65 // Stops the timer.
michael@0 66 ~BaseTimer_Helper() {
michael@0 67 OrphanDelayedTask();
michael@0 68 }
michael@0 69
michael@0 70 // Returns true if the timer is running (i.e., not stopped).
michael@0 71 bool IsRunning() const {
michael@0 72 return delayed_task_ != NULL;
michael@0 73 }
michael@0 74
michael@0 75 // Returns the current delay for this timer. May only call this method when
michael@0 76 // the timer is running!
michael@0 77 TimeDelta GetCurrentDelay() const {
michael@0 78 DCHECK(IsRunning());
michael@0 79 return delayed_task_->delay_;
michael@0 80 }
michael@0 81
michael@0 82 protected:
michael@0 83 BaseTimer_Helper() : delayed_task_(NULL) {}
michael@0 84
michael@0 85 // We have access to the timer_ member so we can orphan this task.
michael@0 86 class TimerTask : public Task {
michael@0 87 public:
michael@0 88 TimerTask(TimeDelta delay) : delay_(delay) {
michael@0 89 // timer_ is set in InitiateDelayedTask.
michael@0 90 }
michael@0 91 virtual ~TimerTask() {}
michael@0 92 BaseTimer_Helper* timer_;
michael@0 93 TimeDelta delay_;
michael@0 94 };
michael@0 95
michael@0 96 // Used to orphan delayed_task_ so that when it runs it does nothing.
michael@0 97 void OrphanDelayedTask();
michael@0 98
michael@0 99 // Used to initiated a new delayed task. This has the side-effect of
michael@0 100 // orphaning delayed_task_ if it is non-null.
michael@0 101 void InitiateDelayedTask(TimerTask* timer_task);
michael@0 102
michael@0 103 TimerTask* delayed_task_;
michael@0 104
michael@0 105 DISALLOW_COPY_AND_ASSIGN(BaseTimer_Helper);
michael@0 106 };
michael@0 107
michael@0 108 //-----------------------------------------------------------------------------
michael@0 109 // This class is an implementation detail of OneShotTimer and RepeatingTimer.
michael@0 110 // Please do not use this class directly.
michael@0 111 template <class Receiver, bool kIsRepeating>
michael@0 112 class BaseTimer : public BaseTimer_Helper {
michael@0 113 public:
michael@0 114 typedef void (Receiver::*ReceiverMethod)();
michael@0 115
michael@0 116 // Call this method to start the timer. It is an error to call this method
michael@0 117 // while the timer is already running.
michael@0 118 void Start(TimeDelta delay, Receiver* receiver, ReceiverMethod method) {
michael@0 119 DCHECK(!IsRunning());
michael@0 120 InitiateDelayedTask(new TimerTask(delay, receiver, method));
michael@0 121 }
michael@0 122
michael@0 123 // Call this method to stop the timer. It is a no-op if the timer is not
michael@0 124 // running.
michael@0 125 void Stop() {
michael@0 126 OrphanDelayedTask();
michael@0 127 }
michael@0 128
michael@0 129 // Call this method to reset the timer delay of an already running timer.
michael@0 130 void Reset() {
michael@0 131 DCHECK(IsRunning());
michael@0 132 InitiateDelayedTask(static_cast<TimerTask*>(delayed_task_)->Clone());
michael@0 133 }
michael@0 134
michael@0 135 private:
michael@0 136 typedef BaseTimer<Receiver, kIsRepeating> SelfType;
michael@0 137
michael@0 138 class TimerTask : public BaseTimer_Helper::TimerTask {
michael@0 139 public:
michael@0 140 TimerTask(TimeDelta delay, Receiver* receiver, ReceiverMethod method)
michael@0 141 : BaseTimer_Helper::TimerTask(delay),
michael@0 142 receiver_(receiver),
michael@0 143 method_(method) {
michael@0 144 }
michael@0 145
michael@0 146 virtual ~TimerTask() {
michael@0 147 // This task may be getting cleared because the MessageLoop has been
michael@0 148 // destructed. If so, don't leave the Timer with a dangling pointer
michael@0 149 // to this now-defunct task.
michael@0 150 ClearBaseTimer();
michael@0 151 }
michael@0 152
michael@0 153 virtual void Run() {
michael@0 154 if (!timer_) // timer_ is null if we were orphaned.
michael@0 155 return;
michael@0 156 if (kIsRepeating)
michael@0 157 ResetBaseTimer();
michael@0 158 else
michael@0 159 ClearBaseTimer();
michael@0 160 DispatchToMethod(receiver_, method_, Tuple0());
michael@0 161 }
michael@0 162
michael@0 163 TimerTask* Clone() const {
michael@0 164 return new TimerTask(delay_, receiver_, method_);
michael@0 165 }
michael@0 166
michael@0 167 private:
michael@0 168 // Inform the Base that the timer is no longer active.
michael@0 169 void ClearBaseTimer() {
michael@0 170 if (timer_) {
michael@0 171 SelfType* self = static_cast<SelfType*>(timer_);
michael@0 172 // It is possible that the Timer has already been reset, and that this
michael@0 173 // Task is old. So, if the Timer points to a different task, assume
michael@0 174 // that the Timer has already taken care of properly setting the task.
michael@0 175 if (self->delayed_task_ == this)
michael@0 176 self->delayed_task_ = NULL;
michael@0 177 // By now the delayed_task_ in the Timer does not point to us anymore.
michael@0 178 // We should reset our own timer_ because the Timer can not do this
michael@0 179 // for us in its destructor.
michael@0 180 timer_ = NULL;
michael@0 181 }
michael@0 182 }
michael@0 183
michael@0 184 // Inform the Base that we're resetting the timer.
michael@0 185 void ResetBaseTimer() {
michael@0 186 DCHECK(timer_);
michael@0 187 DCHECK(kIsRepeating);
michael@0 188 SelfType* self = static_cast<SelfType*>(timer_);
michael@0 189 self->Reset();
michael@0 190 }
michael@0 191
michael@0 192 Receiver* receiver_;
michael@0 193 ReceiverMethod method_;
michael@0 194 };
michael@0 195 };
michael@0 196
michael@0 197 //-----------------------------------------------------------------------------
michael@0 198 // A simple, one-shot timer. See usage notes at the top of the file.
michael@0 199 template <class Receiver>
michael@0 200 class OneShotTimer : public BaseTimer<Receiver, false> {};
michael@0 201
michael@0 202 //-----------------------------------------------------------------------------
michael@0 203 // A simple, repeating timer. See usage notes at the top of the file.
michael@0 204 template <class Receiver>
michael@0 205 class RepeatingTimer : public BaseTimer<Receiver, true> {};
michael@0 206
michael@0 207 //-----------------------------------------------------------------------------
michael@0 208 // A Delay timer is like The Button from Lost. Once started, you have to keep
michael@0 209 // calling Reset otherwise it will call the given method in the MessageLoop
michael@0 210 // thread.
michael@0 211 //
michael@0 212 // Once created, it is inactive until Reset is called. Once |delay| seconds have
michael@0 213 // passed since the last call to Reset, the callback is made. Once the callback
michael@0 214 // has been made, it's inactive until Reset is called again.
michael@0 215 //
michael@0 216 // If destroyed, the timeout is canceled and will not occur even if already
michael@0 217 // inflight.
michael@0 218 template <class Receiver>
michael@0 219 class DelayTimer {
michael@0 220 public:
michael@0 221 typedef void (Receiver::*ReceiverMethod)();
michael@0 222
michael@0 223 DelayTimer(TimeDelta delay, Receiver* receiver, ReceiverMethod method)
michael@0 224 : receiver_(receiver),
michael@0 225 method_(method),
michael@0 226 delay_(delay) {
michael@0 227 }
michael@0 228
michael@0 229 void Reset() {
michael@0 230 DelayFor(delay_);
michael@0 231 }
michael@0 232
michael@0 233 private:
michael@0 234 void DelayFor(TimeDelta delay) {
michael@0 235 trigger_time_ = Time::Now() + delay;
michael@0 236
michael@0 237 // If we already have a timer that will expire at or before the given delay,
michael@0 238 // then we have nothing more to do now.
michael@0 239 if (timer_.IsRunning() && timer_.GetCurrentDelay() <= delay)
michael@0 240 return;
michael@0 241
michael@0 242 // The timer isn't running, or will expire too late, so restart it.
michael@0 243 timer_.Stop();
michael@0 244 timer_.Start(delay, this, &DelayTimer<Receiver>::Check);
michael@0 245 }
michael@0 246
michael@0 247 void Check() {
michael@0 248 if (trigger_time_.is_null())
michael@0 249 return;
michael@0 250
michael@0 251 // If we have not waited long enough, then wait some more.
michael@0 252 const Time now = Time::Now();
michael@0 253 if (now < trigger_time_) {
michael@0 254 DelayFor(trigger_time_ - now);
michael@0 255 return;
michael@0 256 }
michael@0 257
michael@0 258 (receiver_->*method_)();
michael@0 259 }
michael@0 260
michael@0 261 Receiver *const receiver_;
michael@0 262 const ReceiverMethod method_;
michael@0 263 const TimeDelta delay_;
michael@0 264
michael@0 265 OneShotTimer<DelayTimer<Receiver> > timer_;
michael@0 266 Time trigger_time_;
michael@0 267 };
michael@0 268
michael@0 269 } // namespace base
michael@0 270
michael@0 271 #endif // BASE_TIMER_H_

mercurial