Chromium Code Reviews| Index: base/timer/rtc_alarm_chromeos.cc |
| diff --git a/base/timer/rtc_alarm_chromeos.cc b/base/timer/rtc_alarm_chromeos.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f6228baeb4af470563223b22ddeaa538dabdcee5 |
| --- /dev/null |
| +++ b/base/timer/rtc_alarm_chromeos.cc |
| @@ -0,0 +1,183 @@ |
| +// 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 "base/timer/rtc_alarm_chromeos.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 base { |
| + |
| +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")) { |
| + bool ret = thread_->StartWithOptions( |
| + base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); |
| + |
| + CHECK(ret); |
|
Daniel Erat
2014/10/15 15:04:22
nit: just put the CHECK() around the call above?
Chirantan Ekbote
2014/10/16 21:26:38
Done.
|
| + } |
| + ~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 |
| + |
| +RtcAlarmChromeos::RtcAlarmChromeos() |
| + : alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)), |
| + origin_event_id_(0), |
| + io_event_id_(0) { |
| +} |
| + |
| +RtcAlarmChromeos::~RtcAlarmChromeos() { |
| + if (alarm_fd_ != -1) |
| + close(alarm_fd_); |
| +} |
| + |
| +bool RtcAlarmChromeos::Init(WeakPtr<AlarmTimer> parent) { |
| + parent_ = parent; |
| + |
| + return alarm_fd_ != -1; |
| +} |
| + |
| +void RtcAlarmChromeos::Stop() { |
| + // Make sure that we stop the RTC from a MessageLoopForIO. |
| + if (!MessageLoopForIO::IsCurrent()) { |
| + g_io_thread.Get()->task_runner()->PostTask( |
| + FROM_HERE, |
| + Bind(&RtcAlarmChromeos::Stop, |
| + scoped_refptr<RtcAlarmChromeos>(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 RtcAlarmChromeos::Reset(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_ = 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 separately. |
| + if (delay == TimeDelta()) { |
| + origin_message_loop_->PostTask(FROM_HERE, |
| + Bind(&RtcAlarmChromeos::OnTimerFired, |
| + scoped_refptr<RtcAlarmChromeos>(this), |
| + origin_event_id_)); |
| + return; |
| + } |
| + |
| + // Make sure that we are running on a MessageLoopForIO. |
| + if (!MessageLoopForIO::IsCurrent()) { |
| + g_io_thread.Get()->task_runner()->PostTask( |
| + FROM_HERE, |
| + Bind(&RtcAlarmChromeos::ResetImpl, |
| + scoped_refptr<RtcAlarmChromeos>(this), |
| + delay, |
| + origin_event_id_)); |
| + return; |
| + } |
| + |
| + ResetImpl(delay, origin_event_id_); |
| +} |
| + |
| +void RtcAlarmChromeos::OnFileCanReadWithoutBlocking(int fd) { |
| + DCHECK_EQ(alarm_fd_, fd); |
| + |
| + // Read from the fd to ack the event. |
| + char val[sizeof(uint64_t)]; |
| + 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, |
| + Bind(&RtcAlarmChromeos::OnTimerFired, |
| + scoped_refptr<RtcAlarmChromeos>(this), |
| + io_event_id_)); |
| +} |
| + |
| +void RtcAlarmChromeos::OnFileCanWriteWithoutBlocking(int fd) { |
| + NOTREACHED(); |
| +} |
| + |
| +void RtcAlarmChromeos::ResetImpl(TimeDelta delay, int event_id) { |
| + DCHECK(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 MessageLoopForIO::FileDescriptorWatcher); |
| + |
| + // Start watching the fd to see when the timer fires. |
| + if (!MessageLoopForIO::current()->WatchFileDescriptor( |
| + alarm_fd_, |
| + false, |
| + 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() % Time::kMicrosecondsPerSecond) * |
| + Time::kNanosecondsPerMicrosecond; |
| + if (timerfd_settime(alarm_fd_, 0, &alarm_time, NULL) < 0) |
| + PLOG(ERROR) << "Error while setting alarm time. Timer will not fire"; |
| +} |
| + |
| +void RtcAlarmChromeos::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 base |