summaryrefslogtreecommitdiffstats
path: root/base/timer
diff options
context:
space:
mode:
authoravi@chromium.org <avi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-27 18:04:56 +0000
committeravi@chromium.org <avi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-06-27 18:04:56 +0000
commit89bf27e34e49c271aa958c1795d5bccad0e63f88 (patch)
treef971f9282a07dbb991a079daade728830df3da2d /base/timer
parentaa0ec7b63637a5917ce4be9a0e6cceb26b7a3298 (diff)
downloadchromium_src-89bf27e34e49c271aa958c1795d5bccad0e63f88.zip
chromium_src-89bf27e34e49c271aa958c1795d5bccad0e63f88.tar.gz
chromium_src-89bf27e34e49c271aa958c1795d5bccad0e63f88.tar.bz2
Move timing files into base/time and base/timer, install forwarding headers.
BUG=254986 TEST=none TBR=brettw@chromium.org Review URL: https://codereview.chromium.org/18063004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@208951 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/timer')
-rw-r--r--base/timer/hi_res_timer_manager.h38
-rw-r--r--base/timer/hi_res_timer_manager_posix.cc24
-rw-r--r--base/timer/hi_res_timer_manager_unittest.cc57
-rw-r--r--base/timer/hi_res_timer_manager_win.cc35
-rw-r--r--base/timer/timer.cc186
-rw-r--r--base/timer/timer.h246
-rw-r--r--base/timer/timer_unittest.cc489
7 files changed, 1075 insertions, 0 deletions
diff --git a/base/timer/hi_res_timer_manager.h b/base/timer/hi_res_timer_manager.h
new file mode 100644
index 0000000..7fcdb0d
--- /dev/null
+++ b/base/timer/hi_res_timer_manager.h
@@ -0,0 +1,38 @@
+// Copyright (c) 2011 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.
+
+#ifndef BASE_TIMER_HI_RES_TIMER_MANAGER_H_
+#define BASE_TIMER_HI_RES_TIMER_MANAGER_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/power_monitor/power_observer.h"
+
+namespace base {
+
+// Ensures that the Windows high resolution timer is only used
+// when not running on battery power.
+class BASE_EXPORT HighResolutionTimerManager : public base::PowerObserver {
+ public:
+ HighResolutionTimerManager();
+ virtual ~HighResolutionTimerManager();
+
+ // base::PowerObserver method.
+ virtual void OnPowerStateChange(bool on_battery_power) OVERRIDE;
+
+ // Returns true if the hi resolution clock could be used right now.
+ bool hi_res_clock_available() const { return hi_res_clock_available_; }
+
+ private:
+ // Enable or disable the faster multimedia timer.
+ void UseHiResClock(bool use);
+
+ bool hi_res_clock_available_;
+
+ DISALLOW_COPY_AND_ASSIGN(HighResolutionTimerManager);
+};
+
+} // namespace base
+
+#endif // BASE_TIMER_HI_RES_TIMER_MANAGER_H_
diff --git a/base/timer/hi_res_timer_manager_posix.cc b/base/timer/hi_res_timer_manager_posix.cc
new file mode 100644
index 0000000..d2f152c
--- /dev/null
+++ b/base/timer/hi_res_timer_manager_posix.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2011 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.
+
+#include "base/timer/hi_res_timer_manager.h"
+
+// On POSIX we don't need to do anything special with the system timer.
+
+namespace base {
+
+HighResolutionTimerManager::HighResolutionTimerManager()
+ : hi_res_clock_available_(false) {
+}
+
+HighResolutionTimerManager::~HighResolutionTimerManager() {
+}
+
+void HighResolutionTimerManager::OnPowerStateChange(bool on_battery_power) {
+}
+
+void HighResolutionTimerManager::UseHiResClock(bool use) {
+}
+
+} // namespace base
diff --git a/base/timer/hi_res_timer_manager_unittest.cc b/base/timer/hi_res_timer_manager_unittest.cc
new file mode 100644
index 0000000..0017859
--- /dev/null
+++ b/base/timer/hi_res_timer_manager_unittest.cc
@@ -0,0 +1,57 @@
+// Copyright (c) 2012 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.
+
+#include "base/timer/hi_res_timer_manager.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/power_monitor/power_monitor.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+#if defined(OS_WIN)
+// http://crbug.com/114048
+TEST(HiResTimerManagerTest, DISABLED_ToggleOnOff) {
+ base::MessageLoop loop;
+ scoped_ptr<base::PowerMonitor> power_monitor(new base::PowerMonitor());
+ HighResolutionTimerManager manager;
+
+ // At this point, we don't know if the high resolution timers are on or off,
+ // it depends on what system the tests are running on (for example, if this
+ // test is running on a laptop/battery, then the PowerMonitor would have
+ // already set the PowerState to battery power; but if we're running on a
+ // desktop, then the PowerState will be non-battery power). Simulate a power
+ // level change to get to a deterministic state.
+ manager.OnPowerStateChange(/* on_battery */ false);
+
+ // Loop a few times to test power toggling.
+ for (int loop = 2; loop >= 0; --loop) {
+ // The manager has the high resolution clock enabled now.
+ EXPECT_TRUE(manager.hi_res_clock_available());
+ // But the Time class has it off, because it hasn't been activated.
+ EXPECT_FALSE(base::Time::IsHighResolutionTimerInUse());
+
+ // Activate the high resolution timer.
+ base::Time::ActivateHighResolutionTimer(true);
+ EXPECT_TRUE(base::Time::IsHighResolutionTimerInUse());
+
+ // Simulate a on-battery power event.
+ manager.OnPowerStateChange(/* on_battery */ true);
+ EXPECT_FALSE(manager.hi_res_clock_available());
+ EXPECT_FALSE(base::Time::IsHighResolutionTimerInUse());
+
+ // Simulate a off-battery power event.
+ manager.OnPowerStateChange(/* on_battery */ false);
+ EXPECT_TRUE(manager.hi_res_clock_available());
+ EXPECT_TRUE(base::Time::IsHighResolutionTimerInUse());
+
+ // De-activate the high resolution timer.
+ base::Time::ActivateHighResolutionTimer(false);
+ }
+}
+#endif // defined(OS_WIN)
+
+} // namespace base
diff --git a/base/timer/hi_res_timer_manager_win.cc b/base/timer/hi_res_timer_manager_win.cc
new file mode 100644
index 0000000..3647714
--- /dev/null
+++ b/base/timer/hi_res_timer_manager_win.cc
@@ -0,0 +1,35 @@
+// Copyright (c) 2011 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.
+
+#include "base/timer/hi_res_timer_manager.h"
+
+#include "base/power_monitor/power_monitor.h"
+#include "base/time/time.h"
+
+namespace base {
+
+HighResolutionTimerManager::HighResolutionTimerManager()
+ : hi_res_clock_available_(false) {
+ base::PowerMonitor* power_monitor = base::PowerMonitor::Get();
+ power_monitor->AddObserver(this);
+ UseHiResClock(!power_monitor->BatteryPower());
+}
+
+HighResolutionTimerManager::~HighResolutionTimerManager() {
+ base::PowerMonitor::Get()->RemoveObserver(this);
+ UseHiResClock(false);
+}
+
+void HighResolutionTimerManager::OnPowerStateChange(bool on_battery_power) {
+ UseHiResClock(!on_battery_power);
+}
+
+void HighResolutionTimerManager::UseHiResClock(bool use) {
+ if (use == hi_res_clock_available_)
+ return;
+ hi_res_clock_available_ = use;
+ base::Time::EnableHighResolutionTimer(use);
+}
+
+} // namespace base
diff --git a/base/timer/timer.cc b/base/timer/timer.cc
new file mode 100644
index 0000000..c7dcce0
--- /dev/null
+++ b/base/timer/timer.cc
@@ -0,0 +1,186 @@
+// Copyright (c) 2012 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.
+
+#include "base/timer.h"
+
+#include "base/logging.h"
+#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/threading/platform_thread.h"
+
+namespace base {
+
+// BaseTimerTaskInternal is a simple delegate for scheduling a callback to
+// Timer in the thread's default task runner. It also handles the following
+// edge cases:
+// - deleted by the task runner.
+// - abandoned (orphaned) by Timer.
+class BaseTimerTaskInternal {
+ public:
+ explicit BaseTimerTaskInternal(Timer* timer)
+ : timer_(timer) {
+ }
+
+ ~BaseTimerTaskInternal() {
+ // This task may be getting cleared because the task runner has been
+ // destructed. If so, don't leave Timer with a dangling pointer
+ // to this.
+ if (timer_)
+ timer_->StopAndAbandon();
+ }
+
+ void Run() {
+ // timer_ is NULL if we were abandoned.
+ if (!timer_)
+ return;
+
+ // *this will be deleted by the task runner, so Timer needs to
+ // forget us:
+ timer_->scheduled_task_ = NULL;
+
+ // Although Timer should not call back into *this, let's clear
+ // the timer_ member first to be pedantic.
+ Timer* timer = timer_;
+ timer_ = NULL;
+ timer->RunScheduledTask();
+ }
+
+ // The task remains in the MessageLoop queue, but nothing will happen when it
+ // runs.
+ void Abandon() {
+ timer_ = NULL;
+ }
+
+ private:
+ Timer* timer_;
+};
+
+Timer::Timer(bool retain_user_task, bool is_repeating)
+ : scheduled_task_(NULL),
+ thread_id_(0),
+ is_repeating_(is_repeating),
+ retain_user_task_(retain_user_task),
+ is_running_(false) {
+}
+
+Timer::Timer(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task,
+ bool is_repeating)
+ : scheduled_task_(NULL),
+ posted_from_(posted_from),
+ delay_(delay),
+ user_task_(user_task),
+ thread_id_(0),
+ is_repeating_(is_repeating),
+ retain_user_task_(true),
+ is_running_(false) {
+}
+
+Timer::~Timer() {
+ StopAndAbandon();
+}
+
+void Timer::Start(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task) {
+ SetTaskInfo(posted_from, delay, user_task);
+ Reset();
+}
+
+void Timer::Stop() {
+ is_running_ = false;
+ if (!retain_user_task_)
+ user_task_.Reset();
+}
+
+void Timer::Reset() {
+ DCHECK(!user_task_.is_null());
+
+ // If there's no pending task, start one up and return.
+ if (!scheduled_task_) {
+ PostNewScheduledTask(delay_);
+ return;
+ }
+
+ // Set the new desired_run_time_.
+ desired_run_time_ = TimeTicks::Now() + delay_;
+
+ // We can use the existing scheduled task if it arrives before the new
+ // desired_run_time_.
+ if (desired_run_time_ > scheduled_run_time_) {
+ is_running_ = true;
+ return;
+ }
+
+ // We can't reuse the scheduled_task_, so abandon it and post a new one.
+ AbandonScheduledTask();
+ PostNewScheduledTask(delay_);
+}
+
+void Timer::SetTaskInfo(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task) {
+ posted_from_ = posted_from;
+ delay_ = delay;
+ user_task_ = user_task;
+}
+
+void Timer::PostNewScheduledTask(TimeDelta delay) {
+ DCHECK(scheduled_task_ == NULL);
+ is_running_ = true;
+ scheduled_task_ = new BaseTimerTaskInternal(this);
+ ThreadTaskRunnerHandle::Get()->PostDelayedTask(posted_from_,
+ base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_)),
+ delay);
+ scheduled_run_time_ = desired_run_time_ = TimeTicks::Now() + delay;
+ // Remember the thread ID that posts the first task -- this will be verified
+ // later when the task is abandoned to detect misuse from multiple threads.
+ if (!thread_id_)
+ thread_id_ = static_cast<int>(PlatformThread::CurrentId());
+}
+
+void Timer::AbandonScheduledTask() {
+ DCHECK(thread_id_ == 0 ||
+ thread_id_ == static_cast<int>(PlatformThread::CurrentId()));
+ if (scheduled_task_) {
+ scheduled_task_->Abandon();
+ scheduled_task_ = NULL;
+ }
+}
+
+void Timer::RunScheduledTask() {
+ // Task may have been disabled.
+ if (!is_running_)
+ return;
+
+ // First check if we need to delay the task because of a new target time.
+ if (desired_run_time_ > scheduled_run_time_) {
+ // TimeTicks::Now() can be expensive, so only call it if we know the user
+ // has changed the desired_run_time_.
+ TimeTicks now = TimeTicks::Now();
+ // Task runner may have called us late anyway, so only post a continuation
+ // task if the desired_run_time_ is in the future.
+ if (desired_run_time_ > now) {
+ // Post a new task to span the remaining time.
+ PostNewScheduledTask(desired_run_time_ - now);
+ return;
+ }
+ }
+
+ // Make a local copy of the task to run. The Stop method will reset the
+ // user_task_ member if retain_user_task_ is false.
+ base::Closure task = user_task_;
+
+ if (is_repeating_)
+ PostNewScheduledTask(delay_);
+ else
+ Stop();
+
+ task.Run();
+
+ // No more member accesses here: *this could be deleted at this point.
+}
+
+} // namespace base
diff --git a/base/timer/timer.h b/base/timer/timer.h
new file mode 100644
index 0000000..23b1d3c
--- /dev/null
+++ b/base/timer/timer.h
@@ -0,0 +1,246 @@
+// Copyright (c) 2012 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(FROM_HERE, 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.
+//
+// NOTE: These APIs are not thread safe. Always call from the same thread.
+
+#ifndef BASE_TIMER_TIMER_H_
+#define BASE_TIMER_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/base_export.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/time/time.h"
+
+namespace base {
+
+class BaseTimerTaskInternal;
+class MessageLoop;
+
+//-----------------------------------------------------------------------------
+// This class wraps MessageLoop::PostDelayedTask to manage delayed and repeating
+// tasks. It must be destructed on the same thread that starts tasks. There are
+// DCHECKs in place to verify this.
+//
+class BASE_EXPORT Timer {
+ public:
+ // Construct a timer in repeating or one-shot mode. Start or SetTaskInfo must
+ // be called later to set task info. |retain_user_task| determines whether the
+ // user_task is retained or reset when it runs or stops.
+ Timer(bool retain_user_task, bool is_repeating);
+
+ // Construct a timer with retained task info.
+ Timer(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task,
+ bool is_repeating);
+
+ virtual ~Timer();
+
+ // Returns true if the timer is running (i.e., not stopped).
+ bool IsRunning() const {
+ return is_running_;
+ }
+
+ // Returns the current delay for this timer.
+ TimeDelta GetCurrentDelay() const {
+ return delay_;
+ }
+
+ // Start the timer to run at the given |delay| from now. If the timer is
+ // already running, it will be replaced to call the given |user_task|.
+ void Start(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task);
+
+ // Call this method to stop and cancel the timer. It is a no-op if the timer
+ // is not running.
+ void Stop();
+
+ // Call this method to reset the timer delay. The user_task_ must be set. If
+ // the timer is not running, this will start it by posting a task.
+ void Reset();
+
+ const base::Closure& user_task() const { return user_task_; }
+ const TimeTicks& desired_run_time() const { return desired_run_time_; }
+
+ protected:
+ // Used to initiate a new delayed task. This has the side-effect of disabling
+ // scheduled_task_ if it is non-null.
+ void SetTaskInfo(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task);
+
+ private:
+ friend class BaseTimerTaskInternal;
+
+ // Allocates a new scheduled_task_ and posts it on the current MessageLoop
+ // with the given |delay|. scheduled_task_ must be NULL. scheduled_run_time_
+ // and desired_run_time_ are reset to Now() + delay.
+ void PostNewScheduledTask(TimeDelta delay);
+
+ // Disable scheduled_task_ and abandon it so that it no longer refers back to
+ // this object.
+ void AbandonScheduledTask();
+
+ // Called by BaseTimerTaskInternal when the MessageLoop runs it.
+ void RunScheduledTask();
+
+ // Stop running task (if any) and abandon scheduled task (if any).
+ void StopAndAbandon() {
+ Stop();
+ AbandonScheduledTask();
+ }
+
+ // When non-NULL, the scheduled_task_ is waiting in the MessageLoop to call
+ // RunScheduledTask() at scheduled_run_time_.
+ BaseTimerTaskInternal* scheduled_task_;
+
+ // Location in user code.
+ tracked_objects::Location posted_from_;
+ // Delay requested by user.
+ TimeDelta delay_;
+ // user_task_ is what the user wants to be run at desired_run_time_.
+ base::Closure user_task_;
+
+ // The estimated time that the MessageLoop will run the scheduled_task_ that
+ // will call RunScheduledTask().
+ TimeTicks scheduled_run_time_;
+
+ // The desired run time of user_task_. The user may update this at any time,
+ // even if their previous request has not run yet. If desired_run_time_ is
+ // greater than scheduled_run_time_, a continuation task will be posted to
+ // wait for the remaining time. This allows us to reuse the pending task so as
+ // not to flood the MessageLoop with orphaned tasks when the user code
+ // excessively Stops and Starts the timer.
+ TimeTicks desired_run_time_;
+
+ // Thread ID of current MessageLoop for verifying single-threaded usage.
+ int thread_id_;
+
+ // Repeating timers automatically post the task again before calling the task
+ // callback.
+ const bool is_repeating_;
+
+ // If true, hold on to the user_task_ closure object for reuse.
+ const bool retain_user_task_;
+
+ // If true, user_task_ is scheduled to run sometime in the future.
+ bool is_running_;
+
+ DISALLOW_COPY_AND_ASSIGN(Timer);
+};
+
+//-----------------------------------------------------------------------------
+// This class is an implementation detail of OneShotTimer and RepeatingTimer.
+// Please do not use this class directly.
+template <class Receiver, bool kIsRepeating>
+class BaseTimerMethodPointer : public Timer {
+ public:
+ typedef void (Receiver::*ReceiverMethod)();
+
+ // This is here to work around the fact that Timer::Start is "hidden" by the
+ // Start definition below, rather than being overloaded.
+ // TODO(tim): We should remove uses of BaseTimerMethodPointer::Start below
+ // and convert callers to use the base::Closure version in Timer::Start,
+ // see bug 148832.
+ using Timer::Start;
+
+ BaseTimerMethodPointer() : Timer(kIsRepeating, kIsRepeating) {}
+
+ // Start the timer to run at the given |delay| from now. If the timer is
+ // already running, it will be replaced to call a task formed from
+ // |reviewer->*method|.
+ void Start(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ Receiver* receiver,
+ ReceiverMethod method) {
+ Timer::Start(posted_from, delay,
+ base::Bind(method, base::Unretained(receiver)));
+ }
+};
+
+//-----------------------------------------------------------------------------
+// A simple, one-shot timer. See usage notes at the top of the file.
+template <class Receiver>
+class OneShotTimer : public BaseTimerMethodPointer<Receiver, false> {};
+
+//-----------------------------------------------------------------------------
+// A simple, repeating timer. See usage notes at the top of the file.
+template <class Receiver>
+class RepeatingTimer : public BaseTimerMethodPointer<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 : protected Timer {
+ public:
+ typedef void (Receiver::*ReceiverMethod)();
+
+ DelayTimer(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ Receiver* receiver,
+ ReceiverMethod method)
+ : Timer(posted_from, delay,
+ base::Bind(method, base::Unretained(receiver)),
+ false) {}
+
+ void Reset() { Timer::Reset(); }
+};
+
+} // namespace base
+
+#endif // BASE_TIMER_TIMER_H_
diff --git a/base/timer/timer_unittest.cc b/base/timer/timer_unittest.cc
new file mode 100644
index 0000000..9fe21a4
--- /dev/null
+++ b/base/timer/timer_unittest.cc
@@ -0,0 +1,489 @@
+// Copyright (c) 2012 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.
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/timer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::TimeDelta;
+
+namespace {
+
+// The message loops on which each timer should be tested.
+const base::MessageLoop::Type testing_message_loops[] = {
+ base::MessageLoop::TYPE_DEFAULT,
+ base::MessageLoop::TYPE_IO,
+#if !defined(OS_IOS) // iOS does not allow direct running of the UI loop.
+ base::MessageLoop::TYPE_UI,
+#endif
+};
+
+const int kNumTestingMessageLoops = arraysize(testing_message_loops);
+
+class OneShotTimerTester {
+ public:
+ explicit OneShotTimerTester(bool* did_run, unsigned milliseconds = 10)
+ : did_run_(did_run),
+ delay_ms_(milliseconds) {
+ }
+ void Start() {
+ timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(delay_ms_), this,
+ &OneShotTimerTester::Run);
+ }
+ private:
+ void Run() {
+ *did_run_ = true;
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
+ bool* did_run_;
+ base::OneShotTimer<OneShotTimerTester> timer_;
+ const unsigned delay_ms_;
+};
+
+class OneShotSelfDeletingTimerTester {
+ public:
+ explicit OneShotSelfDeletingTimerTester(bool* did_run) :
+ did_run_(did_run),
+ timer_(new base::OneShotTimer<OneShotSelfDeletingTimerTester>()) {
+ }
+ void Start() {
+ timer_->Start(FROM_HERE, TimeDelta::FromMilliseconds(10), this,
+ &OneShotSelfDeletingTimerTester::Run);
+ }
+ private:
+ void Run() {
+ *did_run_ = true;
+ timer_.reset();
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
+ bool* did_run_;
+ scoped_ptr<base::OneShotTimer<OneShotSelfDeletingTimerTester> > timer_;
+};
+
+class RepeatingTimerTester {
+ public:
+ explicit RepeatingTimerTester(bool* did_run)
+ : did_run_(did_run), counter_(10) {
+ }
+
+ void Start() {
+ timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(10), this,
+ &RepeatingTimerTester::Run);
+ }
+ private:
+ void Run() {
+ if (--counter_ == 0) {
+ *did_run_ = true;
+ base::MessageLoop::current()->QuitWhenIdle();
+ }
+ }
+ bool* did_run_;
+ int counter_;
+ base::RepeatingTimer<RepeatingTimerTester> timer_;
+};
+
+void RunTest_OneShotTimer(base::MessageLoop::Type message_loop_type) {
+ base::MessageLoop loop(message_loop_type);
+
+ bool did_run = false;
+ OneShotTimerTester f(&did_run);
+ f.Start();
+
+ base::MessageLoop::current()->Run();
+
+ EXPECT_TRUE(did_run);
+}
+
+void RunTest_OneShotTimer_Cancel(base::MessageLoop::Type message_loop_type) {
+ base::MessageLoop loop(message_loop_type);
+
+ bool did_run_a = false;
+ OneShotTimerTester* a = new OneShotTimerTester(&did_run_a);
+
+ // This should run before the timer expires.
+ base::MessageLoop::current()->DeleteSoon(FROM_HERE, a);
+
+ // Now start the timer.
+ a->Start();
+
+ bool did_run_b = false;
+ OneShotTimerTester b(&did_run_b);
+ b.Start();
+
+ base::MessageLoop::current()->Run();
+
+ EXPECT_FALSE(did_run_a);
+ EXPECT_TRUE(did_run_b);
+}
+
+void RunTest_OneShotSelfDeletingTimer(
+ base::MessageLoop::Type message_loop_type) {
+ base::MessageLoop loop(message_loop_type);
+
+ bool did_run = false;
+ OneShotSelfDeletingTimerTester f(&did_run);
+ f.Start();
+
+ base::MessageLoop::current()->Run();
+
+ EXPECT_TRUE(did_run);
+}
+
+void RunTest_RepeatingTimer(base::MessageLoop::Type message_loop_type) {
+ base::MessageLoop loop(message_loop_type);
+
+ bool did_run = false;
+ RepeatingTimerTester f(&did_run);
+ f.Start();
+
+ base::MessageLoop::current()->Run();
+
+ EXPECT_TRUE(did_run);
+}
+
+void RunTest_RepeatingTimer_Cancel(base::MessageLoop::Type message_loop_type) {
+ base::MessageLoop loop(message_loop_type);
+
+ bool did_run_a = false;
+ RepeatingTimerTester* a = new RepeatingTimerTester(&did_run_a);
+
+ // This should run before the timer expires.
+ base::MessageLoop::current()->DeleteSoon(FROM_HERE, a);
+
+ // Now start the timer.
+ a->Start();
+
+ bool did_run_b = false;
+ RepeatingTimerTester b(&did_run_b);
+ b.Start();
+
+ base::MessageLoop::current()->Run();
+
+ EXPECT_FALSE(did_run_a);
+ EXPECT_TRUE(did_run_b);
+}
+
+class DelayTimerTarget {
+ public:
+ DelayTimerTarget()
+ : signaled_(false) {
+ }
+
+ bool signaled() const { return signaled_; }
+
+ void Signal() {
+ ASSERT_FALSE(signaled_);
+ signaled_ = true;
+ }
+
+ private:
+ bool signaled_;
+};
+
+void RunTest_DelayTimer_NoCall(base::MessageLoop::Type message_loop_type) {
+ base::MessageLoop loop(message_loop_type);
+
+ // If Delay is never called, the timer shouldn't go off.
+ DelayTimerTarget target;
+ base::DelayTimer<DelayTimerTarget> timer(FROM_HERE,
+ TimeDelta::FromMilliseconds(1), &target, &DelayTimerTarget::Signal);
+
+ bool did_run = false;
+ OneShotTimerTester tester(&did_run);
+ tester.Start();
+ base::MessageLoop::current()->Run();
+
+ ASSERT_FALSE(target.signaled());
+}
+
+void RunTest_DelayTimer_OneCall(base::MessageLoop::Type message_loop_type) {
+ base::MessageLoop loop(message_loop_type);
+
+ DelayTimerTarget target;
+ base::DelayTimer<DelayTimerTarget> timer(FROM_HERE,
+ TimeDelta::FromMilliseconds(1), &target, &DelayTimerTarget::Signal);
+ timer.Reset();
+
+ bool did_run = false;
+ OneShotTimerTester tester(&did_run, 100 /* milliseconds */);
+ tester.Start();
+ base::MessageLoop::current()->Run();
+
+ ASSERT_TRUE(target.signaled());
+}
+
+struct ResetHelper {
+ ResetHelper(base::DelayTimer<DelayTimerTarget>* timer,
+ DelayTimerTarget* target)
+ : timer_(timer),
+ target_(target) {
+ }
+
+ void Reset() {
+ ASSERT_FALSE(target_->signaled());
+ timer_->Reset();
+ }
+
+ private:
+ base::DelayTimer<DelayTimerTarget> *const timer_;
+ DelayTimerTarget *const target_;
+};
+
+void RunTest_DelayTimer_Reset(base::MessageLoop::Type message_loop_type) {
+ base::MessageLoop loop(message_loop_type);
+
+ // If Delay is never called, the timer shouldn't go off.
+ DelayTimerTarget target;
+ base::DelayTimer<DelayTimerTarget> timer(FROM_HERE,
+ TimeDelta::FromMilliseconds(50), &target, &DelayTimerTarget::Signal);
+ timer.Reset();
+
+ ResetHelper reset_helper(&timer, &target);
+
+ base::OneShotTimer<ResetHelper> timers[20];
+ for (size_t i = 0; i < arraysize(timers); ++i) {
+ timers[i].Start(FROM_HERE, TimeDelta::FromMilliseconds(i * 10),
+ &reset_helper, &ResetHelper::Reset);
+ }
+
+ bool did_run = false;
+ OneShotTimerTester tester(&did_run, 300);
+ tester.Start();
+ base::MessageLoop::current()->Run();
+
+ ASSERT_TRUE(target.signaled());
+}
+
+class DelayTimerFatalTarget {
+ public:
+ void Signal() {
+ ASSERT_TRUE(false);
+ }
+};
+
+
+void RunTest_DelayTimer_Deleted(base::MessageLoop::Type message_loop_type) {
+ base::MessageLoop loop(message_loop_type);
+
+ DelayTimerFatalTarget target;
+
+ {
+ base::DelayTimer<DelayTimerFatalTarget> timer(
+ FROM_HERE, TimeDelta::FromMilliseconds(50), &target,
+ &DelayTimerFatalTarget::Signal);
+ timer.Reset();
+ }
+
+ // When the timer is deleted, the DelayTimerFatalTarget should never be
+ // called.
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
+}
+
+} // namespace
+
+//-----------------------------------------------------------------------------
+// Each test is run against each type of MessageLoop. That way we are sure
+// that timers work properly in all configurations.
+
+TEST(TimerTest, OneShotTimer) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_OneShotTimer(testing_message_loops[i]);
+ }
+}
+
+TEST(TimerTest, OneShotTimer_Cancel) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_OneShotTimer_Cancel(testing_message_loops[i]);
+ }
+}
+
+// If underline timer does not handle properly, we will crash or fail
+// in full page heap environment.
+TEST(TimerTest, OneShotSelfDeletingTimer) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_OneShotSelfDeletingTimer(testing_message_loops[i]);
+ }
+}
+
+TEST(TimerTest, RepeatingTimer) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_RepeatingTimer(testing_message_loops[i]);
+ }
+}
+
+TEST(TimerTest, RepeatingTimer_Cancel) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_RepeatingTimer_Cancel(testing_message_loops[i]);
+ }
+}
+
+TEST(TimerTest, DelayTimer_NoCall) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_DelayTimer_NoCall(testing_message_loops[i]);
+ }
+}
+
+TEST(TimerTest, DelayTimer_OneCall) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_DelayTimer_OneCall(testing_message_loops[i]);
+ }
+}
+
+// It's flaky on the buildbot, http://crbug.com/25038.
+TEST(TimerTest, DISABLED_DelayTimer_Reset) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_DelayTimer_Reset(testing_message_loops[i]);
+ }
+}
+
+TEST(TimerTest, DelayTimer_Deleted) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_DelayTimer_Deleted(testing_message_loops[i]);
+ }
+}
+
+TEST(TimerTest, MessageLoopShutdown) {
+ // This test is designed to verify that shutdown of the
+ // message loop does not cause crashes if there were pending
+ // timers not yet fired. It may only trigger exceptions
+ // if debug heap checking is enabled.
+ bool did_run = false;
+ {
+ OneShotTimerTester a(&did_run);
+ OneShotTimerTester b(&did_run);
+ OneShotTimerTester c(&did_run);
+ OneShotTimerTester d(&did_run);
+ {
+ base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+ a.Start();
+ b.Start();
+ } // MessageLoop destructs by falling out of scope.
+ } // OneShotTimers destruct. SHOULD NOT CRASH, of course.
+
+ EXPECT_FALSE(did_run);
+}
+
+void TimerTestCallback() {
+}
+
+TEST(TimerTest, NonRepeatIsRunning) {
+ {
+ base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+ base::Timer timer(false, false);
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Start(FROM_HERE, TimeDelta::FromDays(1),
+ base::Bind(&TimerTestCallback));
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Stop();
+ EXPECT_FALSE(timer.IsRunning());
+ EXPECT_TRUE(timer.user_task().is_null());
+ }
+
+ {
+ base::Timer timer(true, false);
+ base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Start(FROM_HERE, TimeDelta::FromDays(1),
+ base::Bind(&TimerTestCallback));
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Stop();
+ EXPECT_FALSE(timer.IsRunning());
+ ASSERT_FALSE(timer.user_task().is_null());
+ timer.Reset();
+ EXPECT_TRUE(timer.IsRunning());
+ }
+}
+
+TEST(TimerTest, NonRepeatMessageLoopDeath) {
+ base::Timer timer(false, false);
+ {
+ base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Start(FROM_HERE, TimeDelta::FromDays(1),
+ base::Bind(&TimerTestCallback));
+ EXPECT_TRUE(timer.IsRunning());
+ }
+ EXPECT_FALSE(timer.IsRunning());
+ EXPECT_TRUE(timer.user_task().is_null());
+}
+
+TEST(TimerTest, RetainRepeatIsRunning) {
+ base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+ base::Timer timer(FROM_HERE, TimeDelta::FromDays(1),
+ base::Bind(&TimerTestCallback), true);
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Reset();
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Stop();
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Reset();
+ EXPECT_TRUE(timer.IsRunning());
+}
+
+TEST(TimerTest, RetainNonRepeatIsRunning) {
+ base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+ base::Timer timer(FROM_HERE, TimeDelta::FromDays(1),
+ base::Bind(&TimerTestCallback), false);
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Reset();
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Stop();
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Reset();
+ EXPECT_TRUE(timer.IsRunning());
+}
+
+namespace {
+
+bool g_callback_happened1 = false;
+bool g_callback_happened2 = false;
+
+void ClearAllCallbackHappened() {
+ g_callback_happened1 = false;
+ g_callback_happened2 = false;
+}
+
+void SetCallbackHappened1() {
+ g_callback_happened1 = true;
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+void SetCallbackHappened2() {
+ g_callback_happened2 = true;
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+TEST(TimerTest, ContinuationStopStart) {
+ {
+ ClearAllCallbackHappened();
+ base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+ base::Timer timer(false, false);
+ timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(10),
+ base::Bind(&SetCallbackHappened1));
+ timer.Stop();
+ timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(40),
+ base::Bind(&SetCallbackHappened2));
+ base::MessageLoop::current()->Run();
+ EXPECT_FALSE(g_callback_happened1);
+ EXPECT_TRUE(g_callback_happened2);
+ }
+}
+
+TEST(TimerTest, ContinuationReset) {
+ {
+ ClearAllCallbackHappened();
+ base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+ base::Timer timer(false, false);
+ timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(10),
+ base::Bind(&SetCallbackHappened1));
+ timer.Reset();
+ // Since Reset happened before task ran, the user_task must not be cleared:
+ ASSERT_FALSE(timer.user_task().is_null());
+ base::MessageLoop::current()->Run();
+ EXPECT_TRUE(g_callback_happened1);
+ }
+}
+
+} // namespace