summaryrefslogtreecommitdiff
path: root/components
diff options
context:
space:
mode:
authorDaniel Erat <derat@google.com>2015-08-24 15:00:23 -0600
committerDaniel Erat <derat@google.com>2015-08-24 15:06:48 -0600
commit742125f404a0a2b549f0be7820c2b33be8712b6f (patch)
tree48d8debfa35e9e454751584f253b6d24ca4d2656 /components
parent6d178983b5b73aeb67ec44ac0caf72bc412a94e9 (diff)
downloadlibchrome-742125f404a0a2b549f0be7820c2b33be8712b6f.tar.gz
Reset components/timers/ to r334380.
Update this directory to contain the stock code from https://chromium.googlesource.com/chromium/src/components/timers/ at 7eba1895 (r332440), as is currently built on Chrome OS. BUG=chromium:521005 Change-Id: I1b797e3ae9353d39816994e1946340d7d2f7895d
Diffstat (limited to 'components')
-rw-r--r--components/timers/BUILD.gn14
-rw-r--r--components/timers/DEPS10
-rw-r--r--components/timers/OWNERS2
-rw-r--r--components/timers/README3
-rw-r--r--components/timers/alarm_timer.cc127
-rw-r--r--components/timers/alarm_timer.h105
-rw-r--r--components/timers/alarm_timer_chromeos.cc467
-rw-r--r--components/timers/alarm_timer_chromeos.h146
-rw-r--r--components/timers/alarm_timer_unittest.cc512
-rw-r--r--components/timers/rtc_alarm.cc186
-rw-r--r--components/timers/rtc_alarm.h95
11 files changed, 1154 insertions, 513 deletions
diff --git a/components/timers/BUILD.gn b/components/timers/BUILD.gn
new file mode 100644
index 0000000000..d6a7efb064
--- /dev/null
+++ b/components/timers/BUILD.gn
@@ -0,0 +1,14 @@
+# Copyright 2014 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.
+
+static_library("timers") {
+ sources = [
+ "alarm_timer_chromeos.cc",
+ "alarm_timer_chromeos.h",
+ ]
+
+ deps = [
+ "//base",
+ ]
+}
diff --git a/components/timers/DEPS b/components/timers/DEPS
new file mode 100644
index 0000000000..413f57b96c
--- /dev/null
+++ b/components/timers/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+ # This directory is shared with Chrome OS, which only links against
+ # base/. We don't want any other dependencies to creep in.
+ "-build",
+ "-content",
+ "-library_loaders",
+ "-net",
+ "-third_party",
+ "-url",
+] \ No newline at end of file
diff --git a/components/timers/OWNERS b/components/timers/OWNERS
new file mode 100644
index 0000000000..0c43483139
--- /dev/null
+++ b/components/timers/OWNERS
@@ -0,0 +1,2 @@
+chirantan@chromium.org
+derat@chromium.org
diff --git a/components/timers/README b/components/timers/README
new file mode 100644
index 0000000000..0b2b4e37f8
--- /dev/null
+++ b/components/timers/README
@@ -0,0 +1,3 @@
+This directory hosts a timer class that is shared with Chrome OS. Code that
+lives in this directory is not allowed to depend on anything other than base/
+because Chrome OS only pulls in and depends on base/ as a library.
diff --git a/components/timers/alarm_timer.cc b/components/timers/alarm_timer.cc
deleted file mode 100644
index 51624e7770..0000000000
--- a/components/timers/alarm_timer.cc
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2014 The Chromium OS 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 "components/timers/alarm_timer.h"
-
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/files/file_util.h"
-#include "base/logging.h"
-#include "base/pending_task.h"
-#include "components/timers/rtc_alarm.h"
-
-namespace timers {
-
-AlarmTimer::AlarmTimer(bool retain_user_task, bool is_repeating)
- : base::Timer(retain_user_task, is_repeating),
- delegate_(new RtcAlarm()),
- can_wake_from_suspend_(false),
- origin_message_loop_(NULL),
- weak_factory_(this) {
- can_wake_from_suspend_ = delegate_->Init(weak_factory_.GetWeakPtr());
-}
-
-AlarmTimer::AlarmTimer(const tracked_objects::Location& posted_from,
- base::TimeDelta delay,
- const base::Closure& user_task,
- bool is_repeating)
- : base::Timer(posted_from, delay, user_task, is_repeating),
- delegate_(new RtcAlarm()),
- can_wake_from_suspend_(false),
- origin_message_loop_(NULL),
- weak_factory_(this) {
- can_wake_from_suspend_ = delegate_->Init(weak_factory_.GetWeakPtr());
-}
-
-AlarmTimer::~AlarmTimer() {
- Stop();
-}
-
-void AlarmTimer::OnTimerFired() {
- if (!base::Timer::IsRunning())
- return;
-
- DCHECK(pending_task_.get());
-
- // Take ownership of the pending user task, which is going to be cleared by
- // the Stop() or Reset() functions below.
- scoped_ptr<base::PendingTask> pending_user_task(pending_task_.Pass());
-
- // Re-schedule or stop the timer as requested.
- if (base::Timer::is_repeating())
- Reset();
- else
- Stop();
-
- // Now run the user task.
- base::MessageLoop::current()->task_annotator()->RunTask(
- "AlarmTimer::Reset", "AlarmTimer::OnTimerFired", *pending_user_task);
-}
-
-void AlarmTimer::Stop() {
- if (!can_wake_from_suspend_) {
- base::Timer::Stop();
- return;
- }
-
- // Clear the running flag, stop the delegate, and delete the pending task.
- base::Timer::set_is_running(false);
- delegate_->Stop();
- pending_task_.reset();
-
- // Stop is called when the AlarmTimer is destroyed so we need to remove
- // ourselves as a MessageLoop::DestructionObserver to prevent a segfault
- // later.
- if (origin_message_loop_) {
- origin_message_loop_->RemoveDestructionObserver(this);
- origin_message_loop_ = NULL;
- }
-
- if (!base::Timer::retain_user_task())
- base::Timer::set_user_task(base::Closure());
-}
-
-void AlarmTimer::Reset() {
- if (!can_wake_from_suspend_) {
- base::Timer::Reset();
- return;
- }
-
- DCHECK(!base::Timer::user_task().is_null());
- DCHECK(!origin_message_loop_ ||
- origin_message_loop_->task_runner()->RunsTasksOnCurrentThread());
-
- // Make sure that the timer will stop if the underlying message loop is
- // destroyed.
- if (!origin_message_loop_) {
- origin_message_loop_ = base::MessageLoop::current();
- origin_message_loop_->AddDestructionObserver(this);
- }
-
- // Set up the pending task.
- if (base::Timer::GetCurrentDelay() > base::TimeDelta::FromMicroseconds(0)) {
- base::Timer::set_desired_run_time(
- base::TimeTicks::Now() + base::Timer::GetCurrentDelay());
- pending_task_.reset(new base::PendingTask(base::Timer::posted_from(),
- base::Timer::user_task(),
- base::Timer::desired_run_time(),
- true /* nestable */));
- } else {
- base::Timer::set_desired_run_time(base::TimeTicks());
- pending_task_.reset(new base::PendingTask(base::Timer::posted_from(),
- base::Timer::user_task()));
- }
- base::MessageLoop::current()->task_annotator()->DidQueueTask(
- "AlarmTimer::Reset", *pending_task_);
-
- // Now start up the timer.
- delegate_->Reset(base::Timer::GetCurrentDelay());
- base::Timer::set_is_running(true);
-}
-
-void AlarmTimer::WillDestroyCurrentMessageLoop() {
- Stop();
-}
-
-} // namespace timers
diff --git a/components/timers/alarm_timer.h b/components/timers/alarm_timer.h
deleted file mode 100644
index 2fb80584d2..0000000000
--- a/components/timers/alarm_timer.h
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright 2014 The Chromium OS 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 COMPONENTS_TIMER_ALARM_TIMER_H_
-#define COMPONENTS_TIMER_ALARM_TIMER_H_
-
-#include "base/callback.h"
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/time/time.h"
-#include "base/timer/timer.h"
-
-namespace base {
-struct PendingTask;
-}
-
-namespace timers {
-// The class implements a timer that is capable of waking the system up from a
-// suspended state. For example, this is useful for running tasks that are
-// needed for maintaining network connectivity, like sending heartbeat messages.
-// Currently, this feature is only available on Chrome OS systems running linux
-// version 3.11 or higher. On all other platforms, the AlarmTimer behaves
-// exactly the same way as a regular Timer.
-class AlarmTimer : public base::Timer,
- public base::MessageLoop::DestructionObserver {
- public:
- // The delegate is responsible for managing the system level details for
- // actually setting up and monitoring a timer that is capable of waking the
- // system from suspend. This class is reference counted because it may need
- // to outlive the timer in order to clean up after itself.
- class Delegate : public base::RefCountedThreadSafe<Delegate> {
- public:
- // Initializes the timer. Should return true iff the system has timers that
- // can wake it up from suspend. Will only be called once.
- virtual bool Init(base::WeakPtr<AlarmTimer> timer) = 0;
-
- // Stops the currently running timer. It should be safe to call this more
- // than once.
- virtual void Stop() = 0;
-
- // Resets the timer to fire after |delay| has passed. Cancels any
- // pre-existing delay.
- virtual void Reset(base::TimeDelta delay) = 0;
-
- protected:
- virtual ~Delegate() {}
-
- private:
- friend class base::RefCountedThreadSafe<Delegate>;
- };
-
- AlarmTimer(bool retain_user_task, bool is_repeating);
-
- AlarmTimer(const tracked_objects::Location& posted_from,
- base::TimeDelta delay,
- const base::Closure& user_task,
- bool is_repeating);
-
- ~AlarmTimer() override;
-
- bool can_wake_from_suspend() const { return can_wake_from_suspend_; }
-
- // Must be called by the delegate to indicate that the timer has fired and
- // that the user task should be run.
- void OnTimerFired();
-
- // Timer overrides.
- void Stop() override;
- void Reset() override;
-
- // MessageLoop::DestructionObserver overrides.
- void WillDestroyCurrentMessageLoop() override;
-
- private:
- // Initializes the timer with the appropriate delegate.
- void Init();
-
- // Delegate that will manage actually setting the timer.
- scoped_refptr<Delegate> delegate_;
-
- // Keeps track of the user task we want to run. A new one is constructed
- // every time Reset() is called.
- scoped_ptr<base::PendingTask> pending_task_;
-
- // Tracks whether the timer has the ability to wake the system up from
- // suspend. This is a runtime check because we won't know if the system
- // supports being woken up from suspend until the delegate actually tries to
- // set it up.
- bool can_wake_from_suspend_;
-
- // Pointer to the message loop that started the timer. Used to track the
- // destruction of that message loop.
- base::MessageLoop* origin_message_loop_;
-
- base::WeakPtrFactory<AlarmTimer> weak_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(AlarmTimer);
-};
-
-} // namespace timers
-
-#endif // COMPONENTS_TIMER_ALARM_TIMER_H_
diff --git a/components/timers/alarm_timer_chromeos.cc b/components/timers/alarm_timer_chromeos.cc
new file mode 100644
index 0000000000..341818a60d
--- /dev/null
+++ b/components/timers/alarm_timer_chromeos.cc
@@ -0,0 +1,467 @@
+// Copyright 2014 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 "components/timers/alarm_timer_chromeos.h"
+
+#include <sys/timerfd.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/files/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/pending_task.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/threading/thread.h"
+
+namespace timers {
+namespace {
+// This class represents the IO thread that the AlarmTimer::Delegate may use for
+// watching file descriptors if it gets called from a thread that does not have
+// a MessageLoopForIO. It is a lazy global instance because it may not always
+// be necessary.
+class RtcAlarmIOThread : public base::Thread {
+ public:
+ RtcAlarmIOThread() : Thread("RTC Alarm IO Thread") {
+ CHECK(
+ StartWithOptions(base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
+ }
+ ~RtcAlarmIOThread() override { Stop(); }
+};
+
+base::LazyInstance<RtcAlarmIOThread> g_io_thread = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+// Watches a MessageLoop and runs a callback if that MessageLoop will be
+// destroyed.
+class AlarmTimer::MessageLoopObserver
+ : public base::MessageLoop::DestructionObserver {
+ public:
+ // Constructs a MessageLoopObserver that will observe |message_loop| and will
+ // call |on_will_be_destroyed_callback| when |message_loop| is about to be
+ // destroyed.
+ MessageLoopObserver(base::MessageLoop* message_loop,
+ base::Closure on_will_be_destroyed_callback)
+ : message_loop_(message_loop),
+ on_will_be_destroyed_callback_(on_will_be_destroyed_callback) {
+ DCHECK(message_loop_);
+ message_loop_->AddDestructionObserver(this);
+ }
+
+ ~MessageLoopObserver() override {
+ // If |message_loop_| was destroyed, then this class will have already
+ // unregistered itself. Doing it again will trigger a warning.
+ if (message_loop_)
+ message_loop_->RemoveDestructionObserver(this);
+ }
+
+ // base::MessageLoop::DestructionObserver override.
+ void WillDestroyCurrentMessageLoop() override {
+ message_loop_->RemoveDestructionObserver(this);
+ message_loop_ = NULL;
+
+ on_will_be_destroyed_callback_.Run();
+ }
+
+ private:
+ // The MessageLoop that this class should watch. Is a weak pointer.
+ base::MessageLoop* message_loop_;
+
+ // The callback to run when |message_loop_| will be destroyed.
+ base::Closure on_will_be_destroyed_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(MessageLoopObserver);
+};
+
+// This class manages a Real Time Clock (RTC) alarm, a feature that is available
+// from linux version 3.11 onwards. It creates a file descriptor for the RTC
+// alarm timer and then watches that file descriptor to see when it can be read
+// without blocking, indicating that the timer has fired.
+//
+// A major problem for this class is that watching file descriptors is only
+// available on a MessageLoopForIO but there is no guarantee the timer is going
+// to be created on one. To get around this, the timer has a dedicated thread
+// with a MessageLoopForIO that posts tasks back to the thread that started the
+// timer.
+class AlarmTimer::Delegate
+ : public base::RefCountedThreadSafe<AlarmTimer::Delegate>,
+ public base::MessageLoopForIO::Watcher {
+ public:
+ // Construct a Delegate for the AlarmTimer. It should be safe to call
+ // |on_timer_fired_callback| multiple times.
+ explicit Delegate(base::Closure on_timer_fired_callback);
+
+ // Returns true if the system timer managed by this delegate is capable of
+ // waking the system from suspend.
+ bool CanWakeFromSuspend();
+
+ // Resets the timer to fire after |delay| has passed. Cancels any
+ // pre-existing delay.
+ void Reset(base::TimeDelta delay);
+
+ // Stops the currently running timer. It should be safe to call this even if
+ // the timer is not running.
+ void Stop();
+
+ // Sets a hook that will be called when the timer fires and a task has been
+ // queued on |origin_task_runner_|. Used by tests to wait until a task is
+ // pending in the MessageLoop.
+ void SetTimerFiredCallbackForTest(base::Closure test_callback);
+
+ // base::MessageLoopForIO::Watcher overrides.
+ void OnFileCanReadWithoutBlocking(int fd) override;
+ void OnFileCanWriteWithoutBlocking(int fd) override;
+
+ private:
+ friend class base::RefCountedThreadSafe<Delegate>;
+ ~Delegate() override;
+
+ // Actually performs the system calls to set up the timer. This must be
+ // called on a MessageLoopForIO.
+ void ResetImpl(base::TimeDelta delay, int reset_sequence_number);
+
+ // Callback that is run when the timer fires. Must be run on
+ // |origin_task_runner_|.
+ void OnTimerFired(int reset_sequence_number);
+
+ // File descriptor associated with the alarm timer.
+ int alarm_fd_;
+
+ // Task runner which initially started the timer.
+ scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
+
+ // Callback that should be run when the timer fires.
+ base::Closure on_timer_fired_callback_;
+
+ // Hook used by tests to be notified when the timer has fired and a task has
+ // been queued in the MessageLoop.
+ base::Closure on_timer_fired_callback_for_test_;
+
+ // Manages watching file descriptors.
+ scoped_ptr<base::MessageLoopForIO::FileDescriptorWatcher> fd_watcher_;
+
+ // The sequence numbers of the last Reset() call handled respectively on
+ // |origin_task_runner_| and on the MessageLoopForIO used for watching the
+ // timer file descriptor. Note that these can be the same MessageLoop.
+ // OnTimerFired() runs |on_timer_fired_callback_| only if the sequence number
+ // it receives from the MessageLoopForIO matches
+ // |origin_reset_sequence_number_|.
+ int origin_reset_sequence_number_;
+ int io_reset_sequence_number_;
+
+ DISALLOW_COPY_AND_ASSIGN(Delegate);
+};
+
+AlarmTimer::Delegate::Delegate(base::Closure on_timer_fired_callback)
+ : alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)),
+ on_timer_fired_callback_(on_timer_fired_callback),
+ origin_reset_sequence_number_(0),
+ io_reset_sequence_number_(0) {
+ // The call to timerfd_create above may fail. This is the only indication
+ // that CLOCK_REALTIME_ALARM is not supported on this system.
+ DPLOG_IF(INFO, (alarm_fd_ == -1))
+ << "CLOCK_REALTIME_ALARM not supported on this system";
+}
+
+AlarmTimer::Delegate::~Delegate() {
+ if (alarm_fd_ != -1)
+ close(alarm_fd_);
+}
+
+bool AlarmTimer::Delegate::CanWakeFromSuspend() {
+ return alarm_fd_ != -1;
+}
+
+void AlarmTimer::Delegate::Reset(base::TimeDelta delay) {
+ // Get a task runner for the current message loop. When the timer fires, we
+ // will
+ // post tasks to this proxy to let the parent timer know.
+ origin_task_runner_ = base::ThreadTaskRunnerHandle::Get();
+
+ // Increment the sequence number. Used to invalidate any events that have
+ // been queued but not yet run since the last time Reset() was called.
+ origin_reset_sequence_number_++;
+
+ // Calling timerfd_settime with a zero delay actually clears the timer so if
+ // the user has requested a zero delay timer, we need to handle it
+ // differently. We queue the task here but we still go ahead and call
+ // timerfd_settime with the zero delay anyway to cancel any previous delay
+ // that might have been programmed.
+ if (delay <= base::TimeDelta::FromMicroseconds(0)) {
+ // The timerfd_settime documentation is vague on what happens when it is
+ // passed a negative delay. We can sidestep the issue by ensuring that
+ // the delay is 0.
+ delay = base::TimeDelta::FromMicroseconds(0);
+ origin_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&Delegate::OnTimerFired, scoped_refptr<Delegate>(this),
+ origin_reset_sequence_number_));
+ }
+
+ // Run ResetImpl() on a MessageLoopForIO.
+ if (base::MessageLoopForIO::IsCurrent()) {
+ ResetImpl(delay, origin_reset_sequence_number_);
+ } else {
+ g_io_thread.Pointer()->task_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&Delegate::ResetImpl, scoped_refptr<Delegate>(this), delay,
+ origin_reset_sequence_number_));
+ }
+}
+
+void AlarmTimer::Delegate::Stop() {
+ // Stop the RTC from a MessageLoopForIO.
+ if (!base::MessageLoopForIO::IsCurrent()) {
+ g_io_thread.Pointer()->task_runner()->PostTask(
+ FROM_HERE, base::Bind(&Delegate::Stop, scoped_refptr<Delegate>(this)));
+ return;
+ }
+
+ // Stop watching for events.
+ fd_watcher_.reset();
+
+ // Now clear the timer.
+ DCHECK_NE(alarm_fd_, -1);
+ itimerspec blank_time = {};
+ if (timerfd_settime(alarm_fd_, 0, &blank_time, NULL) < 0)
+ PLOG(ERROR) << "Unable to clear alarm time. Timer may still fire.";
+}
+
+void AlarmTimer::Delegate::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK_EQ(alarm_fd_, fd);
+
+ // Read from the fd to ack the event.
+ char val[sizeof(uint64_t)];
+ if (!base::ReadFromFD(alarm_fd_, val, sizeof(uint64_t)))
+ PLOG(DFATAL) << "Unable to read from timer file descriptor.";
+
+ // Make sure that the parent timer is informed on the proper message loop.
+ if (origin_task_runner_->RunsTasksOnCurrentThread()) {
+ OnTimerFired(io_reset_sequence_number_);
+ } else {
+ origin_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&Delegate::OnTimerFired, scoped_refptr<Delegate>(this),
+ io_reset_sequence_number_));
+ }
+}
+
+void AlarmTimer::Delegate::OnFileCanWriteWithoutBlocking(int fd) {
+ NOTREACHED();
+}
+
+void AlarmTimer::Delegate::SetTimerFiredCallbackForTest(
+ base::Closure test_callback) {
+ on_timer_fired_callback_for_test_ = test_callback;
+}
+
+void AlarmTimer::Delegate::ResetImpl(base::TimeDelta delay,
+ int reset_sequence_number) {
+ DCHECK(base::MessageLoopForIO::IsCurrent());
+ DCHECK_NE(alarm_fd_, -1);
+
+ // Store the sequence number in the IO thread variable. When the timer
+ // fires, we will bind this value to the OnTimerFired callback to ensure
+ // that we do the right thing if the timer gets reset.
+ io_reset_sequence_number_ = reset_sequence_number;
+
+ // If we were already watching the fd, this will stop watching it.
+ fd_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher);
+
+ // Start watching the fd to see when the timer fires.
+ if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
+ alarm_fd_, false, base::MessageLoopForIO::WATCH_READ,
+ fd_watcher_.get(), this)) {
+ LOG(ERROR) << "Error while attempting to watch file descriptor for RTC "
+ << "alarm. Timer will not fire.";
+ }
+
+ // Actually set the timer. This will also clear the pre-existing timer, if
+ // any.
+ itimerspec alarm_time = {};
+ alarm_time.it_value.tv_sec = delay.InSeconds();
+ alarm_time.it_value.tv_nsec =
+ (delay.InMicroseconds() % base::Time::kMicrosecondsPerSecond) *
+ base::Time::kNanosecondsPerMicrosecond;
+ if (timerfd_settime(alarm_fd_, 0, &alarm_time, NULL) < 0)
+ PLOG(ERROR) << "Error while setting alarm time. Timer will not fire";
+}
+
+void AlarmTimer::Delegate::OnTimerFired(int reset_sequence_number) {
+ DCHECK(origin_task_runner_->RunsTasksOnCurrentThread());
+
+ // If a test wants to be notified when this function is about to run, then
+ // re-queue this task in the MessageLoop and run the test's callback.
+ if (!on_timer_fired_callback_for_test_.is_null()) {
+ origin_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&Delegate::OnTimerFired, scoped_refptr<Delegate>(this),
+ reset_sequence_number));
+
+ on_timer_fired_callback_for_test_.Run();
+ on_timer_fired_callback_for_test_.Reset();
+ return;
+ }
+
+ // Check to make sure that the timer was not reset in the time between when
+ // this task was queued to run and now. If it was reset, then don't do
+ // anything.
+ if (reset_sequence_number != origin_reset_sequence_number_)
+ return;
+
+ on_timer_fired_callback_.Run();
+}
+
+AlarmTimer::AlarmTimer(bool retain_user_task, bool is_repeating)
+ : base::Timer(retain_user_task, is_repeating),
+ can_wake_from_suspend_(false),
+ origin_message_loop_(NULL),
+ weak_factory_(this) {
+ Init();
+}
+
+AlarmTimer::AlarmTimer(const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task,
+ bool is_repeating)
+ : base::Timer(posted_from, delay, user_task, is_repeating),
+ can_wake_from_suspend_(false),
+ origin_message_loop_(NULL),
+ weak_factory_(this) {
+ Init();
+}
+
+AlarmTimer::~AlarmTimer() {
+ Stop();
+}
+
+void AlarmTimer::SetTimerFiredCallbackForTest(base::Closure test_callback) {
+ delegate_->SetTimerFiredCallbackForTest(test_callback);
+}
+
+void AlarmTimer::Init() {
+ delegate_ = make_scoped_refptr(new AlarmTimer::Delegate(
+ base::Bind(&AlarmTimer::OnTimerFired, weak_factory_.GetWeakPtr())));
+ can_wake_from_suspend_ = delegate_->CanWakeFromSuspend();
+}
+
+void AlarmTimer::Stop() {
+ if (!can_wake_from_suspend_) {
+ base::Timer::Stop();
+ return;
+ }
+
+ // Clear the running flag, stop the delegate, and delete the pending task.
+ base::Timer::set_is_running(false);
+ delegate_->Stop();
+ pending_task_.reset();
+
+ // Stop watching |origin_message_loop_|.
+ origin_message_loop_ = NULL;
+ message_loop_observer_.reset();
+
+ if (!base::Timer::retain_user_task())
+ base::Timer::set_user_task(base::Closure());
+}
+
+void AlarmTimer::Reset() {
+ if (!can_wake_from_suspend_) {
+ base::Timer::Reset();
+ return;
+ }
+
+ DCHECK(!base::Timer::user_task().is_null());
+ DCHECK(!origin_message_loop_ ||
+ origin_message_loop_->task_runner()->RunsTasksOnCurrentThread());
+
+ // Make sure that the timer will stop if the underlying message loop is
+ // destroyed.
+ if (!origin_message_loop_) {
+ origin_message_loop_ = base::MessageLoop::current();
+ message_loop_observer_.reset(new MessageLoopObserver(
+ origin_message_loop_,
+ base::Bind(&AlarmTimer::WillDestroyCurrentMessageLoop,
+ weak_factory_.GetWeakPtr())));
+ }
+
+ // Set up the pending task.
+ if (base::Timer::GetCurrentDelay() > base::TimeDelta::FromMicroseconds(0)) {
+ base::Timer::set_desired_run_time(base::TimeTicks::Now() +
+ base::Timer::GetCurrentDelay());
+ pending_task_.reset(new base::PendingTask(
+ base::Timer::posted_from(), base::Timer::user_task(),
+ base::Timer::desired_run_time(), true /* nestable */));
+ } else {
+ base::Timer::set_desired_run_time(base::TimeTicks());
+ pending_task_.reset(new base::PendingTask(base::Timer::posted_from(),
+ base::Timer::user_task()));
+ }
+ base::MessageLoop::current()->task_annotator()->DidQueueTask(
+ "AlarmTimer::Reset", *pending_task_);
+
+ // Now start up the timer.
+ delegate_->Reset(base::Timer::GetCurrentDelay());
+ base::Timer::set_is_running(true);
+}
+
+void AlarmTimer::WillDestroyCurrentMessageLoop() {
+ Stop();
+}
+
+void AlarmTimer::OnTimerFired() {
+ if (!base::Timer::IsRunning())
+ return;
+
+ DCHECK(pending_task_.get());
+
+ // Take ownership of the pending user task, which is going to be cleared by
+ // the Stop() or Reset() functions below.
+ scoped_ptr<base::PendingTask> pending_user_task(pending_task_.Pass());
+
+ // Re-schedule or stop the timer as requested.
+ if (base::Timer::is_repeating())
+ Reset();
+ else
+ Stop();
+
+ // Now run the user task.
+ base::MessageLoop::current()->task_annotator()->RunTask(
+ "AlarmTimer::Reset", "AlarmTimer::OnTimerFired", *pending_user_task);
+}
+
+OneShotAlarmTimer::OneShotAlarmTimer() : AlarmTimer(false, false) {
+}
+
+OneShotAlarmTimer::~OneShotAlarmTimer() {
+}
+
+RepeatingAlarmTimer::RepeatingAlarmTimer() : AlarmTimer(true, true) {
+}
+
+RepeatingAlarmTimer::RepeatingAlarmTimer(
+ const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task)
+ : AlarmTimer(posted_from, delay, user_task, true) {
+}
+
+RepeatingAlarmTimer::~RepeatingAlarmTimer() {
+}
+
+SimpleAlarmTimer::SimpleAlarmTimer() : AlarmTimer(true, false) {
+}
+
+SimpleAlarmTimer::SimpleAlarmTimer(const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task)
+ : AlarmTimer(posted_from, delay, user_task, false) {
+}
+
+SimpleAlarmTimer::~SimpleAlarmTimer() {
+}
+
+} // namespace timers
diff --git a/components/timers/alarm_timer_chromeos.h b/components/timers/alarm_timer_chromeos.h
new file mode 100644
index 0000000000..2f6b0ff861
--- /dev/null
+++ b/components/timers/alarm_timer_chromeos.h
@@ -0,0 +1,146 @@
+// Copyright 2014 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 COMPONENTS_TIMERS_ALARM_TIMER_CHROMEOS_H_
+#define COMPONENTS_TIMERS_ALARM_TIMER_CHROMEOS_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+
+namespace base {
+class MessageLoop;
+struct PendingTask;
+}
+
+namespace timers {
+// The class implements a timer that is capable of waking the system up from a
+// suspended state. For example, this is useful for running tasks that are
+// needed for maintaining network connectivity, like sending heartbeat messages.
+// Currently, this feature is only available on Chrome OS systems running linux
+// version 3.11 or higher. On all other platforms, the AlarmTimer behaves
+// exactly the same way as a regular Timer.
+class AlarmTimer : public base::Timer {
+ public:
+ ~AlarmTimer() override;
+
+ bool can_wake_from_suspend() const { return can_wake_from_suspend_; }
+
+ // Sets a hook that will be called when the timer fires and a task has been
+ // queued on |origin_message_loop_|. Used by tests to wait until a task is
+ // pending in the MessageLoop.
+ void SetTimerFiredCallbackForTest(base::Closure test_callback);
+
+ // Timer overrides.
+ void Stop() override;
+ void Reset() override;
+
+ protected:
+ // The constructors for this class are protected because consumers should
+ // instantiate one of the specialized sub-classes defined below instead.
+ AlarmTimer(bool retain_user_task, bool is_repeating);
+ AlarmTimer(const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task,
+ bool is_repeating);
+
+ private:
+ // Common initialization that must be performed by both constructors. This
+ // really should live in a delegated constructor but the way base::Timer's
+ // constructors are written makes it really hard to do so.
+ void Init();
+
+ // Will be called by the delegate to indicate that the timer has fired and
+ // that the user task should be run.
+ void OnTimerFired();
+
+ // Called when |origin_message_loop_| will be destroyed.
+ void WillDestroyCurrentMessageLoop();
+
+ // Delegate that will manage actually setting the timer.
+ class Delegate;
+ scoped_refptr<Delegate> delegate_;
+
+ // Keeps track of the user task we want to run. A new one is constructed
+ // every time Reset() is called.
+ scoped_ptr<base::PendingTask> pending_task_;
+
+ // Tracks whether the timer has the ability to wake the system up from
+ // suspend. This is a runtime check because we won't know if the system
+ // supports being woken up from suspend until the delegate actually tries to
+ // set it up.
+ bool can_wake_from_suspend_;
+
+ // Pointer to the message loop that started the timer. Used to track the
+ // destruction of that message loop.
+ base::MessageLoop* origin_message_loop_;
+
+ // Observes |origin_message_loop_| and informs this class if it will be
+ // destroyed.
+ class MessageLoopObserver;
+ scoped_ptr<MessageLoopObserver> message_loop_observer_;
+
+ base::WeakPtrFactory<AlarmTimer> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AlarmTimer);
+};
+
+// As its name suggests, a OneShotAlarmTimer runs a given task once. It does
+// not remember the task that was given to it after it has fired and does not
+// repeat. Useful for fire-and-forget tasks.
+class OneShotAlarmTimer : public AlarmTimer {
+ public:
+ // Constructs a basic OneShotAlarmTimer. An AlarmTimer constructed this way
+ // requires that Start() is called before Reset() is called.
+ OneShotAlarmTimer();
+ ~OneShotAlarmTimer() override;
+};
+
+// A RepeatingAlarmTimer takes a task and delay and repeatedly runs the task
+// using the specified delay as an interval between the runs until it is
+// explicitly stopped. It remembers both the task and the delay it was given
+// after it fires.
+class RepeatingAlarmTimer : public AlarmTimer {
+ public:
+ // Constructs a basic RepeatingAlarmTimer. An AlarmTimer constructed this way
+ // requires that Start() is called before Reset() is called.
+ RepeatingAlarmTimer();
+
+ // Constructs a RepeatingAlarmTimer with pre-populated parameters but does not
+ // start it. Useful if |user_task| or |delay| are not going to change.
+ // Reset() can be called immediately after constructing an AlarmTimer in this
+ // way.
+ RepeatingAlarmTimer(const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task);
+
+ ~RepeatingAlarmTimer() override;
+};
+
+// A SimpleAlarmTimer only fires once but remembers the task that it was given
+// even after it has fired. Useful if you want to run the same task multiple
+// times but not at a regular interval.
+class SimpleAlarmTimer : public AlarmTimer {
+ public:
+ // Constructs a basic SimpleAlarmTimer. An AlarmTimer constructed this way
+ // requires that Start() is called before Reset() is called.
+ SimpleAlarmTimer();
+
+ // Constructs a SimpleAlarmTimer with pre-populated parameters but does not
+ // start it. Useful if |user_task| or |delay| are not going to change.
+ // Reset() can be called immediately after constructing an AlarmTimer in this
+ // way.
+ SimpleAlarmTimer(const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task);
+
+ ~SimpleAlarmTimer() override;
+};
+
+} // namespace timers
+
+#endif // COMPONENTS_TIMERS_ALARM_TIMER_CHROMEOS_H_
diff --git a/components/timers/alarm_timer_unittest.cc b/components/timers/alarm_timer_unittest.cc
new file mode 100644
index 0000000000..14c55a9fc7
--- /dev/null
+++ b/components/timers/alarm_timer_unittest.cc
@@ -0,0 +1,512 @@
+// Copyright 2014 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 <sys/timerfd.h>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/macros.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
+#include "components/timers/alarm_timer_chromeos.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Most of these tests have been lifted right out of timer_unittest.cc with only
+// cosmetic changes (like replacing calls to MessageLoop::current()->Run() with
+// a RunLoop). We want the AlarmTimer to be a drop-in replacement for the
+// regular Timer so it should pass the same tests as the Timer class.
+//
+// The only new tests are the .*ConcurrentResetAndTimerFired tests, which test
+// that race conditions that can come up in the AlarmTimer::Delegate are
+// properly handled.
+namespace timers {
+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);
+const base::TimeDelta kTenMilliseconds = base::TimeDelta::FromMilliseconds(10);
+
+class OneShotAlarmTimerTester {
+ public:
+ OneShotAlarmTimerTester(bool* did_run, base::TimeDelta delay)
+ : did_run_(did_run),
+ delay_(delay),
+ timer_(new timers::OneShotAlarmTimer()) {}
+ void Start() {
+ timer_->Start(FROM_HERE, delay_, base::Bind(&OneShotAlarmTimerTester::Run,
+ base::Unretained(this)));
+ }
+
+ private:
+ void Run() {
+ *did_run_ = true;
+
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
+ }
+
+ bool* did_run_;
+ const base::TimeDelta delay_;
+ scoped_ptr<timers::OneShotAlarmTimer> timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(OneShotAlarmTimerTester);
+};
+
+class OneShotSelfDeletingAlarmTimerTester {
+ public:
+ OneShotSelfDeletingAlarmTimerTester(bool* did_run, base::TimeDelta delay)
+ : did_run_(did_run),
+ delay_(delay),
+ timer_(new timers::OneShotAlarmTimer()) {}
+ void Start() {
+ timer_->Start(FROM_HERE, delay_,
+ base::Bind(&OneShotSelfDeletingAlarmTimerTester::Run,
+ base::Unretained(this)));
+ }
+
+ private:
+ void Run() {
+ *did_run_ = true;
+ timer_.reset();
+
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
+ }
+
+ bool* did_run_;
+ const base::TimeDelta delay_;
+ scoped_ptr<timers::OneShotAlarmTimer> timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(OneShotSelfDeletingAlarmTimerTester);
+};
+
+class RepeatingAlarmTimerTester {
+ public:
+ RepeatingAlarmTimerTester(bool* did_run, base::TimeDelta delay)
+ : did_run_(did_run),
+ delay_(delay),
+ counter_(10),
+ timer_(new timers::RepeatingAlarmTimer()) {}
+ void Start() {
+ timer_->Start(FROM_HERE, delay_, base::Bind(&RepeatingAlarmTimerTester::Run,
+ base::Unretained(this)));
+ }
+
+ private:
+ void Run() {
+ if (--counter_ == 0) {
+ *did_run_ = true;
+ timer_->Stop();
+
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
+ }
+ }
+
+ bool* did_run_;
+ const base::TimeDelta delay_;
+ int counter_;
+ scoped_ptr<timers::RepeatingAlarmTimer> timer_;
+
+ DISALLOW_COPY_AND_ASSIGN(RepeatingAlarmTimerTester);
+};
+
+} // namespace
+
+//-----------------------------------------------------------------------------
+// Each test is run against each type of MessageLoop. That way we are sure
+// that timers work properly in all configurations.
+
+TEST(AlarmTimerTest, OneShotAlarmTimer) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ base::MessageLoop loop(testing_message_loops[i]);
+
+ bool did_run = false;
+ OneShotAlarmTimerTester f(&did_run, kTenMilliseconds);
+ f.Start();
+
+ base::RunLoop().Run();
+
+ EXPECT_TRUE(did_run);
+ }
+}
+
+TEST(AlarmTimerTest, OneShotAlarmTimer_Cancel) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ base::MessageLoop loop(testing_message_loops[i]);
+
+ bool did_run_a = false;
+ OneShotAlarmTimerTester* a =
+ new OneShotAlarmTimerTester(&did_run_a, kTenMilliseconds);
+
+ // 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;
+ OneShotAlarmTimerTester b(&did_run_b, kTenMilliseconds);
+ b.Start();
+
+ base::RunLoop().Run();
+
+ EXPECT_FALSE(did_run_a);
+ EXPECT_TRUE(did_run_b);
+ }
+}
+
+// If underlying timer does not handle this properly, we will crash or fail
+// in full page heap environment.
+TEST(AlarmTimerTest, OneShotSelfDeletingAlarmTimer) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ base::MessageLoop loop(testing_message_loops[i]);
+
+ bool did_run = false;
+ OneShotSelfDeletingAlarmTimerTester f(&did_run, kTenMilliseconds);
+ f.Start();
+
+ base::RunLoop().Run();
+
+ EXPECT_TRUE(did_run);
+ }
+}
+
+TEST(AlarmTimerTest, RepeatingAlarmTimer) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ base::MessageLoop loop(testing_message_loops[i]);
+
+ bool did_run = false;
+ RepeatingAlarmTimerTester f(&did_run, kTenMilliseconds);
+ f.Start();
+
+ base::RunLoop().Run();
+
+ EXPECT_TRUE(did_run);
+ }
+}
+
+TEST(AlarmTimerTest, RepeatingAlarmTimer_Cancel) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ base::MessageLoop loop(testing_message_loops[i]);
+
+ bool did_run_a = false;
+ RepeatingAlarmTimerTester* a =
+ new RepeatingAlarmTimerTester(&did_run_a, kTenMilliseconds);
+
+ // 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;
+ RepeatingAlarmTimerTester b(&did_run_b, kTenMilliseconds);
+ b.Start();
+
+ base::RunLoop().Run();
+
+ EXPECT_FALSE(did_run_a);
+ EXPECT_TRUE(did_run_b);
+ }
+}
+
+TEST(AlarmTimerTest, RepeatingAlarmTimerZeroDelay) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ base::MessageLoop loop(testing_message_loops[i]);
+
+ bool did_run = false;
+ RepeatingAlarmTimerTester f(&did_run, base::TimeDelta());
+ f.Start();
+
+ base::RunLoop().Run();
+
+ EXPECT_TRUE(did_run);
+ }
+}
+
+TEST(AlarmTimerTest, RepeatingAlarmTimerZeroDelay_Cancel) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ base::MessageLoop loop(testing_message_loops[i]);
+
+ bool did_run_a = false;
+ RepeatingAlarmTimerTester* a =
+ new RepeatingAlarmTimerTester(&did_run_a, base::TimeDelta());
+
+ // 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;
+ RepeatingAlarmTimerTester b(&did_run_b, base::TimeDelta());
+ b.Start();
+
+ base::RunLoop().Run();
+
+ EXPECT_FALSE(did_run_a);
+ EXPECT_TRUE(did_run_b);
+ }
+}
+
+TEST(AlarmTimerTest, 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;
+ {
+ OneShotAlarmTimerTester a(&did_run, kTenMilliseconds);
+ OneShotAlarmTimerTester b(&did_run, kTenMilliseconds);
+ OneShotAlarmTimerTester c(&did_run, kTenMilliseconds);
+ OneShotAlarmTimerTester d(&did_run, kTenMilliseconds);
+ {
+ base::MessageLoop loop;
+ a.Start();
+ b.Start();
+ } // MessageLoop destructs by falling out of scope.
+ } // OneShotTimers destruct. SHOULD NOT CRASH, of course.
+
+ EXPECT_FALSE(did_run);
+}
+
+TEST(AlarmTimerTest, NonRepeatIsRunning) {
+ {
+ base::MessageLoop loop;
+ timers::OneShotAlarmTimer timer;
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Start(FROM_HERE, base::TimeDelta::FromDays(1),
+ base::Bind(&base::DoNothing));
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Stop();
+ EXPECT_FALSE(timer.IsRunning());
+ EXPECT_TRUE(timer.user_task().is_null());
+ }
+
+ {
+ timers::SimpleAlarmTimer timer;
+ base::MessageLoop loop;
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Start(FROM_HERE, base::TimeDelta::FromDays(1),
+ base::Bind(&base::DoNothing));
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Stop();
+ EXPECT_FALSE(timer.IsRunning());
+ ASSERT_FALSE(timer.user_task().is_null());
+ timer.Reset();
+ EXPECT_TRUE(timer.IsRunning());
+ }
+}
+
+TEST(AlarmTimerTest, NonRepeatMessageLoopDeath) {
+ timers::OneShotAlarmTimer timer;
+ {
+ base::MessageLoop loop;
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Start(FROM_HERE, base::TimeDelta::FromDays(1),
+ base::Bind(&base::DoNothing));
+ EXPECT_TRUE(timer.IsRunning());
+ }
+ EXPECT_FALSE(timer.IsRunning());
+ EXPECT_TRUE(timer.user_task().is_null());
+}
+
+TEST(AlarmTimerTest, RetainRepeatIsRunning) {
+ base::MessageLoop loop;
+ timers::RepeatingAlarmTimer timer(FROM_HERE, base::TimeDelta::FromDays(1),
+ base::Bind(&base::DoNothing));
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Reset();
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Stop();
+ EXPECT_FALSE(timer.IsRunning());
+ timer.Reset();
+ EXPECT_TRUE(timer.IsRunning());
+}
+
+TEST(AlarmTimerTest, RetainNonRepeatIsRunning) {
+ base::MessageLoop loop;
+ timers::SimpleAlarmTimer timer(FROM_HERE, base::TimeDelta::FromDays(1),
+ base::Bind(&base::DoNothing));
+ 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()->PostTask(
+ FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
+}
+
+void SetCallbackHappened2() {
+ g_callback_happened2 = true;
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
+}
+
+TEST(AlarmTimerTest, ContinuationStopStart) {
+ {
+ ClearAllCallbackHappened();
+ base::MessageLoop loop;
+ timers::OneShotAlarmTimer timer;
+ timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(10),
+ base::Bind(&SetCallbackHappened1));
+ timer.Stop();
+ timer.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(40),
+ base::Bind(&SetCallbackHappened2));
+ base::RunLoop().Run();
+ EXPECT_FALSE(g_callback_happened1);
+ EXPECT_TRUE(g_callback_happened2);
+ }
+}
+
+TEST(AlarmTimerTest, ContinuationReset) {
+ {
+ ClearAllCallbackHappened();
+ base::MessageLoop loop;
+ timers::OneShotAlarmTimer timer;
+ timer.Start(FROM_HERE, base::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::RunLoop().Run();
+ EXPECT_TRUE(g_callback_happened1);
+ }
+}
+
+} // namespace
+
+namespace {
+void TimerRanCallback(bool* did_run) {
+ *did_run = true;
+
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
+}
+
+bool IsAlarmTimerSupported() {
+ int fd = timerfd_create(CLOCK_REALTIME_ALARM, 0);
+
+ if (fd == -1) {
+ LOG(WARNING) << "CLOCK_REALTIME_ALARM is not supported on this system. "
+ << "Skipping test. Upgrade to at least linux version 3.11 to "
+ << "support this timer.";
+ return false;
+ }
+
+ close(fd);
+ return true;
+}
+
+TEST(AlarmTimerTest, OneShotTimerConcurrentResetAndTimerFired) {
+ // The "Linux ChromiumOS .*" bots are still running Ubuntu 12.04, which
+ // doesn't have a linux version high enough to support the AlarmTimer. Since
+ // this test depends on SetTimerFiredCallbackForTest(), which is specific to
+ // the AlarmTimer, we have to just skip the test to stop it from failing.
+ if (!IsAlarmTimerSupported())
+ return;
+
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ base::MessageLoop loop(testing_message_loops[i]);
+
+ timers::OneShotAlarmTimer timer;
+ bool did_run = false;
+
+ base::RunLoop run_loop;
+
+ timer.SetTimerFiredCallbackForTest(run_loop.QuitClosure());
+ timer.Start(FROM_HERE, kTenMilliseconds,
+ base::Bind(&TimerRanCallback, &did_run));
+
+ // Wait until the timer has fired and a task has been queue in the
+ // MessageLoop.
+ run_loop.Run();
+
+ // Now reset the timer. This is attempting to simulate the timer firing and
+ // being reset at the same time. The previously queued task should be
+ // removed.
+ timer.Reset();
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(did_run);
+
+ // If the previous check failed, running the message loop again will hang
+ // the test so we only do it if the callback has not run yet.
+ if (!did_run) {
+ base::RunLoop().Run();
+ EXPECT_TRUE(did_run);
+ }
+ }
+}
+
+TEST(AlarmTimerTest, RepeatingTimerConcurrentResetAndTimerFired) {
+ // The "Linux ChromiumOS .*" bots are still running Ubuntu 12.04, which
+ // doesn't have a linux version high enough to support the AlarmTimer. Since
+ // this test depends on SetTimerFiredCallbackForTest(), which is specific to
+ // the AlarmTimer, we have to just skip the test to stop it from failing.
+ if (!IsAlarmTimerSupported())
+ return;
+
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ base::MessageLoop loop(testing_message_loops[i]);
+
+ timers::RepeatingAlarmTimer timer;
+ bool did_run = false;
+
+ base::RunLoop run_loop;
+
+ timer.SetTimerFiredCallbackForTest(run_loop.QuitClosure());
+ timer.Start(FROM_HERE, kTenMilliseconds,
+ base::Bind(&TimerRanCallback, &did_run));
+
+ // Wait until the timer has fired and a task has been queue in the
+ // MessageLoop.
+ run_loop.Run();
+
+ // Now reset the timer. This is attempting to simulate the timer firing and
+ // being reset at the same time. The previously queued task should be
+ // removed.
+ timer.Reset();
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_FALSE(did_run);
+
+ // If the previous check failed, running the message loop again will hang
+ // the test so we only do it if the callback has not run yet.
+ if (!did_run) {
+ base::RunLoop().Run();
+ EXPECT_TRUE(did_run);
+ }
+ }
+}
+
+} // namespace
+
+} // namespace timers
diff --git a/components/timers/rtc_alarm.cc b/components/timers/rtc_alarm.cc
deleted file mode 100644
index 3b0f1e0d95..0000000000
--- a/components/timers/rtc_alarm.cc
+++ /dev/null
@@ -1,186 +0,0 @@
-// Copyright 2014 The Chromium OS 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 "components/timers/rtc_alarm.h"
-
-#include <sys/timerfd.h>
-
-#include "base/bind.h"
-#include "base/files/file_util.h"
-#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/message_loop/message_loop_proxy.h"
-#include "base/threading/thread.h"
-
-namespace timers {
-
-namespace {
-// Helper class to ensure that the IO thread we will use for watching file
-// descriptors is started only once.
-class IOThreadStartHelper {
- public:
- IOThreadStartHelper() : thread_(new base::Thread("RTC Alarm IO Thread")) {
- CHECK(thread_->StartWithOptions(
- base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
- }
- ~IOThreadStartHelper() {}
-
- base::Thread& operator*() const { return *thread_.get(); }
-
- base::Thread* operator->() const { return thread_.get(); }
-
- private:
- scoped_ptr<base::Thread> thread_;
-};
-
-base::LazyInstance<IOThreadStartHelper> g_io_thread = LAZY_INSTANCE_INITIALIZER;
-
-} // namespace
-
-RtcAlarm::RtcAlarm()
- : alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)),
- origin_event_id_(0),
- io_event_id_(0) {
-}
-
-RtcAlarm::~RtcAlarm() {
- if (alarm_fd_ != -1)
- close(alarm_fd_);
-}
-
-bool RtcAlarm::Init(base::WeakPtr<AlarmTimer> parent) {
- parent_ = parent;
-
- return alarm_fd_ != -1;
-}
-
-void RtcAlarm::Stop() {
- // Make sure that we stop the RTC from a MessageLoopForIO.
- if (!base::MessageLoopForIO::IsCurrent()) {
- g_io_thread.Get()->task_runner()->PostTask(
- FROM_HERE,
- base::Bind(&RtcAlarm::Stop, scoped_refptr<RtcAlarm>(this)));
- return;
- }
-
- // Stop watching for events.
- fd_watcher_.reset();
-
- // Now clear the timer.
- DCHECK_NE(alarm_fd_, -1);
- itimerspec blank_time = {};
- timerfd_settime(alarm_fd_, 0, &blank_time, NULL);
-}
-
-void RtcAlarm::Reset(base::TimeDelta delay) {
- // Get a proxy for the current message loop. When the timer fires, we will
- // post tasks to this proxy to let the parent timer know.
- origin_message_loop_ = base::MessageLoopProxy::current();
-
- // Increment the event id. Used to invalidate any events that have been
- // queued but not yet run since the last time Reset() was called.
- origin_event_id_++;
-
- // Calling timerfd_settime with a zero delay actually clears the timer so if
- // the user has requested a zero delay timer, we need to handle it
- // differently. We queue the task here but we still go ahead and call
- // timerfd_settime with the zero delay anyway to cancel any previous delay
- // that might have been programmed.
- if (delay <= base::TimeDelta::FromMicroseconds(0)) {
- // The timerfd_settime documentation is vague on what happens when it is
- // passed a negative delay. We can sidestep the issue by ensuring that the
- // delay is 0.
- delay = base::TimeDelta::FromMicroseconds(0);
- origin_message_loop_->PostTask(FROM_HERE,
- base::Bind(&RtcAlarm::OnTimerFired,
- scoped_refptr<RtcAlarm>(this),
- origin_event_id_));
- }
-
- // Make sure that we are running on a MessageLoopForIO.
- if (!base::MessageLoopForIO::IsCurrent()) {
- g_io_thread.Get()->task_runner()->PostTask(
- FROM_HERE,
- base::Bind(&RtcAlarm::ResetImpl,
- scoped_refptr<RtcAlarm>(this),
- delay,
- origin_event_id_));
- return;
- }
-
- ResetImpl(delay, origin_event_id_);
-}
-
-void RtcAlarm::OnFileCanReadWithoutBlocking(int fd) {
- DCHECK_EQ(alarm_fd_, fd);
-
- // Read from the fd to ack the event.
- char val[sizeof(uint64_t)];
- base::ReadFromFD(alarm_fd_, val, sizeof(uint64_t));
-
- // Make sure that the parent timer is informed on the proper message loop.
- if (origin_message_loop_->RunsTasksOnCurrentThread()) {
- OnTimerFired(io_event_id_);
- return;
- }
-
- origin_message_loop_->PostTask(FROM_HERE,
- base::Bind(&RtcAlarm::OnTimerFired,
- scoped_refptr<RtcAlarm>(this),
- io_event_id_));
-}
-
-void RtcAlarm::OnFileCanWriteWithoutBlocking(int fd) {
- NOTREACHED();
-}
-
-void RtcAlarm::ResetImpl(base::TimeDelta delay, int event_id) {
- DCHECK(base::MessageLoopForIO::IsCurrent());
- DCHECK_NE(alarm_fd_, -1);
-
- // Store the event id in the IO thread variable. When the timer fires, we
- // will bind this value to the OnTimerFired callback to ensure that we do the
- // right thing if the timer gets reset.
- io_event_id_ = event_id;
-
- // If we were already watching the fd, this will stop watching it.
- fd_watcher_.reset(new base::MessageLoopForIO::FileDescriptorWatcher);
-
- // Start watching the fd to see when the timer fires.
- if (!base::MessageLoopForIO::current()->WatchFileDescriptor(
- alarm_fd_,
- false,
- base::MessageLoopForIO::WATCH_READ,
- fd_watcher_.get(),
- this)) {
- LOG(ERROR) << "Error while attempting to watch file descriptor for RTC "
- << "alarm. Timer will not fire.";
- }
-
- // Actually set the timer. This will also clear the pre-existing timer, if
- // any.
- itimerspec alarm_time = {};
- alarm_time.it_value.tv_sec = delay.InSeconds();
- alarm_time.it_value.tv_nsec =
- (delay.InMicroseconds() % base::Time::kMicrosecondsPerSecond) *
- base::Time::kNanosecondsPerMicrosecond;
- if (timerfd_settime(alarm_fd_, 0, &alarm_time, NULL) < 0)
- PLOG(ERROR) << "Error while setting alarm time. Timer will not fire";
-}
-
-void RtcAlarm::OnTimerFired(int event_id) {
- DCHECK(origin_message_loop_->RunsTasksOnCurrentThread());
-
- // Check to make sure that the timer was not reset in the time between when
- // this task was queued to run and now. If it was reset, then don't do
- // anything.
- if (event_id != origin_event_id_)
- return;
-
- if (parent_)
- parent_->OnTimerFired();
-}
-
-} // namespace timers
diff --git a/components/timers/rtc_alarm.h b/components/timers/rtc_alarm.h
deleted file mode 100644
index 417e5f6257..0000000000
--- a/components/timers/rtc_alarm.h
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2014 The Chromium OS 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 COMPONENTS_TIMER_RTC_ALARM_H_
-#define COMPONENTS_TIMER_RTC_ALARM_H_
-
-#include "base/macros.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "base/time/time.h"
-#include "components/timers/alarm_timer.h"
-
-namespace base {
-class MessageLoopProxy;
-}
-
-namespace timers {
-// This class manages a Real Time Clock (RTC) alarm, a feature that is available
-// from linux version 3.11 onwards. It creates a file descriptor for the RTC
-// alarm timer and then watches that file descriptor to see when it can be read
-// without blocking, indicating that the timer has fired.
-//
-// A major problem for this class is that watching file descriptors is only
-// available on a MessageLoopForIO but there is no guarantee the timer is going
-// to be created on one. To get around this, the timer has a dedicated thread
-// with a MessageLoopForIO that posts tasks back to the thread that started the
-// timer.
-//
-// This class is designed to work together with the AlarmTimer class and is
-// tested through the AlarmTimer unit tests. DO NOT TRY TO USE THIS CLASS
-// DIRECTLY. Or use it anyway, I'm a comment not a cop.
-class RtcAlarm : public AlarmTimer::Delegate,
- public base::MessageLoopForIO::Watcher {
- public:
- RtcAlarm();
-
- // AlarmTimer::Delegate overrides.
- bool Init(base::WeakPtr<AlarmTimer> timer) override;
- void Stop() override;
- void Reset(base::TimeDelta delay) override;
-
- // base::MessageLoopForIO::Watcher overrides.
- void OnFileCanReadWithoutBlocking(int fd) override;
- void OnFileCanWriteWithoutBlocking(int fd) override;
-
- protected:
- // Needs to be protected because AlarmTimer::Delegate is a refcounted class.
- virtual ~RtcAlarm();
-
- private:
- // Actually performs the system calls to set up the timer. This must be
- // called on a MessageLoopForIO.
- void ResetImpl(base::TimeDelta delay, int event_id);
-
- // Callback that is run when the timer fires. Must be run on
- // |origin_message_loop_|.
- void OnTimerFired(int event_id);
-
- // File descriptor associated with the alarm timer.
- int alarm_fd_;
-
- // Message loop which initially started the timer.
- scoped_refptr<base::MessageLoopProxy> origin_message_loop_;
-
- // The parent timer that should be informed when the timer fires. We may end
- // up outliving the parent so we need to ensure the reference is valid before
- // we try to call it.
- base::WeakPtr<AlarmTimer> parent_;
-
- // Manages watching file descriptors.
- scoped_ptr<base::MessageLoopForIO::FileDescriptorWatcher> fd_watcher_;
-
- // These two variables are used for coordinating between the thread that
- // started the timer and the IO thread being used to watch the timer file
- // descriptor. When Reset() is called, the original thread increments
- // |origin_event_id_| and binds its value to ResetImpl(), which gets posted to
- // the IO thread. When the IO thread runs, it saves this value in
- // |io_event_id_|. Later, when the timer fires, the IO thread binds the value
- // of |io_event_id_| to OnTimerFired() and posts it to the original thread.
- // When the original thread runs OnTimerFired(), it calls
- // parent_->OnTimerFired() only if |origin_event_id_| matches the event id
- // that was passed in to it. This is used to get around a race condition
- // where the user resets the timer on the original thread, while the event is
- // being fired on the IO thread at the same time.
- int origin_event_id_;
- int io_event_id_;
-
- DISALLOW_COPY_AND_ASSIGN(RtcAlarm);
-};
-
-} // namespace timers
-#endif // COMPONENTS_TIMER_RTC_ALARM_H_