michael@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: // OneShotTimer and RepeatingTimer provide a simple timer API. As the names michael@0: // suggest, OneShotTimer calls you back once after a time delay expires. michael@0: // RepeatingTimer on the other hand calls you back periodically with the michael@0: // prescribed time interval. michael@0: // michael@0: // OneShotTimer and RepeatingTimer both cancel the timer when they go out of michael@0: // scope, which makes it easy to ensure that you do not get called when your michael@0: // object has gone out of scope. Just instantiate a OneShotTimer or michael@0: // RepeatingTimer as a member variable of the class for which you wish to michael@0: // receive timer events. michael@0: // michael@0: // Sample RepeatingTimer usage: michael@0: // michael@0: // class MyClass { michael@0: // public: michael@0: // void StartDoingStuff() { michael@0: // timer_.Start(TimeDelta::FromSeconds(1), this, &MyClass::DoStuff); michael@0: // } michael@0: // void StopDoingStuff() { michael@0: // timer_.Stop(); michael@0: // } michael@0: // private: michael@0: // void DoStuff() { michael@0: // // This method is called every second to do stuff. michael@0: // ... michael@0: // } michael@0: // base::RepeatingTimer timer_; michael@0: // }; michael@0: // michael@0: // Both OneShotTimer and RepeatingTimer also support a Reset method, which michael@0: // allows you to easily defer the timer event until the timer delay passes once michael@0: // again. So, in the above example, if 0.5 seconds have already passed, michael@0: // calling Reset on timer_ would postpone DoStuff by another 1 second. In michael@0: // other words, Reset is shorthand for calling Stop and then Start again with michael@0: // the same arguments. michael@0: michael@0: #ifndef BASE_TIMER_H_ michael@0: #define BASE_TIMER_H_ michael@0: michael@0: // IMPORTANT: If you change timer code, make sure that all tests (including michael@0: // disabled ones) from timer_unittests.cc pass locally. Some are disabled michael@0: // because they're flaky on the buildbot, but when you run them locally you michael@0: // should be able to tell the difference. michael@0: michael@0: #include "base/logging.h" michael@0: #include "base/task.h" michael@0: #include "base/time.h" michael@0: michael@0: class MessageLoop; michael@0: michael@0: namespace base { michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // This class is an implementation detail of OneShotTimer and RepeatingTimer. michael@0: // Please do not use this class directly. michael@0: // michael@0: // This class exists to share code between BaseTimer template instantiations. michael@0: // michael@0: class BaseTimer_Helper { michael@0: public: michael@0: // Stops the timer. michael@0: ~BaseTimer_Helper() { michael@0: OrphanDelayedTask(); michael@0: } michael@0: michael@0: // Returns true if the timer is running (i.e., not stopped). michael@0: bool IsRunning() const { michael@0: return delayed_task_ != NULL; michael@0: } michael@0: michael@0: // Returns the current delay for this timer. May only call this method when michael@0: // the timer is running! michael@0: TimeDelta GetCurrentDelay() const { michael@0: DCHECK(IsRunning()); michael@0: return delayed_task_->delay_; michael@0: } michael@0: michael@0: protected: michael@0: BaseTimer_Helper() : delayed_task_(NULL) {} michael@0: michael@0: // We have access to the timer_ member so we can orphan this task. michael@0: class TimerTask : public Task { michael@0: public: michael@0: TimerTask(TimeDelta delay) : delay_(delay) { michael@0: // timer_ is set in InitiateDelayedTask. michael@0: } michael@0: virtual ~TimerTask() {} michael@0: BaseTimer_Helper* timer_; michael@0: TimeDelta delay_; michael@0: }; michael@0: michael@0: // Used to orphan delayed_task_ so that when it runs it does nothing. michael@0: void OrphanDelayedTask(); michael@0: michael@0: // Used to initiated a new delayed task. This has the side-effect of michael@0: // orphaning delayed_task_ if it is non-null. michael@0: void InitiateDelayedTask(TimerTask* timer_task); michael@0: michael@0: TimerTask* delayed_task_; michael@0: michael@0: DISALLOW_COPY_AND_ASSIGN(BaseTimer_Helper); michael@0: }; michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // This class is an implementation detail of OneShotTimer and RepeatingTimer. michael@0: // Please do not use this class directly. michael@0: template michael@0: class BaseTimer : public BaseTimer_Helper { michael@0: public: michael@0: typedef void (Receiver::*ReceiverMethod)(); michael@0: michael@0: // Call this method to start the timer. It is an error to call this method michael@0: // while the timer is already running. michael@0: void Start(TimeDelta delay, Receiver* receiver, ReceiverMethod method) { michael@0: DCHECK(!IsRunning()); michael@0: InitiateDelayedTask(new TimerTask(delay, receiver, method)); michael@0: } michael@0: michael@0: // Call this method to stop the timer. It is a no-op if the timer is not michael@0: // running. michael@0: void Stop() { michael@0: OrphanDelayedTask(); michael@0: } michael@0: michael@0: // Call this method to reset the timer delay of an already running timer. michael@0: void Reset() { michael@0: DCHECK(IsRunning()); michael@0: InitiateDelayedTask(static_cast(delayed_task_)->Clone()); michael@0: } michael@0: michael@0: private: michael@0: typedef BaseTimer SelfType; michael@0: michael@0: class TimerTask : public BaseTimer_Helper::TimerTask { michael@0: public: michael@0: TimerTask(TimeDelta delay, Receiver* receiver, ReceiverMethod method) michael@0: : BaseTimer_Helper::TimerTask(delay), michael@0: receiver_(receiver), michael@0: method_(method) { michael@0: } michael@0: michael@0: virtual ~TimerTask() { michael@0: // This task may be getting cleared because the MessageLoop has been michael@0: // destructed. If so, don't leave the Timer with a dangling pointer michael@0: // to this now-defunct task. michael@0: ClearBaseTimer(); michael@0: } michael@0: michael@0: virtual void Run() { michael@0: if (!timer_) // timer_ is null if we were orphaned. michael@0: return; michael@0: if (kIsRepeating) michael@0: ResetBaseTimer(); michael@0: else michael@0: ClearBaseTimer(); michael@0: DispatchToMethod(receiver_, method_, Tuple0()); michael@0: } michael@0: michael@0: TimerTask* Clone() const { michael@0: return new TimerTask(delay_, receiver_, method_); michael@0: } michael@0: michael@0: private: michael@0: // Inform the Base that the timer is no longer active. michael@0: void ClearBaseTimer() { michael@0: if (timer_) { michael@0: SelfType* self = static_cast(timer_); michael@0: // It is possible that the Timer has already been reset, and that this michael@0: // Task is old. So, if the Timer points to a different task, assume michael@0: // that the Timer has already taken care of properly setting the task. michael@0: if (self->delayed_task_ == this) michael@0: self->delayed_task_ = NULL; michael@0: // By now the delayed_task_ in the Timer does not point to us anymore. michael@0: // We should reset our own timer_ because the Timer can not do this michael@0: // for us in its destructor. michael@0: timer_ = NULL; michael@0: } michael@0: } michael@0: michael@0: // Inform the Base that we're resetting the timer. michael@0: void ResetBaseTimer() { michael@0: DCHECK(timer_); michael@0: DCHECK(kIsRepeating); michael@0: SelfType* self = static_cast(timer_); michael@0: self->Reset(); michael@0: } michael@0: michael@0: Receiver* receiver_; michael@0: ReceiverMethod method_; michael@0: }; michael@0: }; michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // A simple, one-shot timer. See usage notes at the top of the file. michael@0: template michael@0: class OneShotTimer : public BaseTimer {}; michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // A simple, repeating timer. See usage notes at the top of the file. michael@0: template michael@0: class RepeatingTimer : public BaseTimer {}; michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // A Delay timer is like The Button from Lost. Once started, you have to keep michael@0: // calling Reset otherwise it will call the given method in the MessageLoop michael@0: // thread. michael@0: // michael@0: // Once created, it is inactive until Reset is called. Once |delay| seconds have michael@0: // passed since the last call to Reset, the callback is made. Once the callback michael@0: // has been made, it's inactive until Reset is called again. michael@0: // michael@0: // If destroyed, the timeout is canceled and will not occur even if already michael@0: // inflight. michael@0: template michael@0: class DelayTimer { michael@0: public: michael@0: typedef void (Receiver::*ReceiverMethod)(); michael@0: michael@0: DelayTimer(TimeDelta delay, Receiver* receiver, ReceiverMethod method) michael@0: : receiver_(receiver), michael@0: method_(method), michael@0: delay_(delay) { michael@0: } michael@0: michael@0: void Reset() { michael@0: DelayFor(delay_); michael@0: } michael@0: michael@0: private: michael@0: void DelayFor(TimeDelta delay) { michael@0: trigger_time_ = Time::Now() + delay; michael@0: michael@0: // If we already have a timer that will expire at or before the given delay, michael@0: // then we have nothing more to do now. michael@0: if (timer_.IsRunning() && timer_.GetCurrentDelay() <= delay) michael@0: return; michael@0: michael@0: // The timer isn't running, or will expire too late, so restart it. michael@0: timer_.Stop(); michael@0: timer_.Start(delay, this, &DelayTimer::Check); michael@0: } michael@0: michael@0: void Check() { michael@0: if (trigger_time_.is_null()) michael@0: return; michael@0: michael@0: // If we have not waited long enough, then wait some more. michael@0: const Time now = Time::Now(); michael@0: if (now < trigger_time_) { michael@0: DelayFor(trigger_time_ - now); michael@0: return; michael@0: } michael@0: michael@0: (receiver_->*method_)(); michael@0: } michael@0: michael@0: Receiver *const receiver_; michael@0: const ReceiverMethod method_; michael@0: const TimeDelta delay_; michael@0: michael@0: OneShotTimer > timer_; michael@0: Time trigger_time_; michael@0: }; michael@0: michael@0: } // namespace base michael@0: michael@0: #endif // BASE_TIMER_H_