summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Erat <derat@google.com>2015-07-07 09:28:14 -0600
committerDaniel Erat <derat@google.com>2015-07-21 17:01:02 -0600
commitd1fe520f7c726bd404fa860cddacb09ef609eb23 (patch)
tree0de4e4946301506aa49844315238a39b47c3ebac
parentdffedf276cb2ea1a87e75788e9eabd9acaa7f0de (diff)
downloadlibchrome-d1fe520f7c726bd404fa860cddacb09ef609eb23.tar.gz
Build base::AlarmTimer into libchrome.
Original Chrome OS commit message: crrev.com/26436e402e6e1b780f6565539ae0acb3e2976fb9 added a new timer class to chrome that is capable of waking the system from suspend. shill and powerd also need to use this timer but unfortunately the git mirror for the directory where the timer class lives has not been set up yet and it's probably too close to the branch point now to risk trying to uprev libchrome anyway. Instead we can backport that particular CL into the chrome os tree until the next time we update libchrome. This adds the components/ directory from Chrome OS's libchrome package, as pulled in by the following commits: https://chromium-review.googlesource.com/226183 https://chromium-review.googlesource.com/227460 https://chromium-review.googlesource.com/234450 Bug: 22317122 Change-Id: I23630e2eac709aa86340435c44b822f58245d36a
-rw-r--r--Android.mk2
-rw-r--r--components/timers/alarm_timer.cc127
-rw-r--r--components/timers/alarm_timer.h105
-rw-r--r--components/timers/rtc_alarm.cc186
-rw-r--r--components/timers/rtc_alarm.h95
5 files changed, 515 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
index f44d306f7b..bd1d04a5ce 100644
--- a/Android.mk
+++ b/Android.mk
@@ -202,6 +202,8 @@ LOCAL_SRC_FILES := \
base/tracking_info.cc \
base/values.cc \
base/vlog.cc \
+ components/timers/alarm_timer.cc \
+ components/timers/rtc_alarm.cc \
LOCAL_CPP_EXTENSION := $(libchromeCommonCppExtension)
LOCAL_CFLAGS := $(libchromeCommonCFlags)
diff --git a/components/timers/alarm_timer.cc b/components/timers/alarm_timer.cc
new file mode 100644
index 0000000000..51624e7770
--- /dev/null
+++ b/components/timers/alarm_timer.cc
@@ -0,0 +1,127 @@
+// 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
new file mode 100644
index 0000000000..2fb80584d2
--- /dev/null
+++ b/components/timers/alarm_timer.h
@@ -0,0 +1,105 @@
+// 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/rtc_alarm.cc b/components/timers/rtc_alarm.cc
new file mode 100644
index 0000000000..3b0f1e0d95
--- /dev/null
+++ b/components/timers/rtc_alarm.cc
@@ -0,0 +1,186 @@
+// 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
new file mode 100644
index 0000000000..417e5f6257
--- /dev/null
+++ b/components/timers/rtc_alarm.h
@@ -0,0 +1,95 @@
+// 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_