timer.h

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