diff options
author | Daniel Erat <derat@google.com> | 2015-08-24 15:00:23 -0600 |
---|---|---|
committer | Daniel Erat <derat@google.com> | 2015-08-24 15:06:48 -0600 |
commit | 742125f404a0a2b549f0be7820c2b33be8712b6f (patch) | |
tree | 48d8debfa35e9e454751584f253b6d24ca4d2656 /components | |
parent | 6d178983b5b73aeb67ec44ac0caf72bc412a94e9 (diff) | |
download | libchrome-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.gn | 14 | ||||
-rw-r--r-- | components/timers/DEPS | 10 | ||||
-rw-r--r-- | components/timers/OWNERS | 2 | ||||
-rw-r--r-- | components/timers/README | 3 | ||||
-rw-r--r-- | components/timers/alarm_timer.cc | 127 | ||||
-rw-r--r-- | components/timers/alarm_timer.h | 105 | ||||
-rw-r--r-- | components/timers/alarm_timer_chromeos.cc | 467 | ||||
-rw-r--r-- | components/timers/alarm_timer_chromeos.h | 146 | ||||
-rw-r--r-- | components/timers/alarm_timer_unittest.cc | 512 | ||||
-rw-r--r-- | components/timers/rtc_alarm.cc | 186 | ||||
-rw-r--r-- | components/timers/rtc_alarm.h | 95 |
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_ |