// 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 <stdint.h> #include <sys/timerfd.h> #include <algorithm> #include <utility> #include "base/bind.h" #include "base/debug/task_annotator.h" #include "base/files/file_util.h" #include "base/logging.h" #include "base/memory/ptr_util.h" #include "base/pending_task.h" #include "base/trace_event/trace_event.h" namespace timers { AlarmTimer::AlarmTimer(bool retain_user_task, bool is_repeating) : base::Timer(retain_user_task, is_repeating), alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)), weak_factory_(this) {} AlarmTimer::~AlarmTimer() { DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); Stop(); } void AlarmTimer::Stop() { DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); if (!base::Timer::is_running()) return; if (!CanWakeFromSuspend()) { base::Timer::Stop(); return; } // Cancel any previous callbacks. weak_factory_.InvalidateWeakPtrs(); base::Timer::set_is_running(false); alarm_fd_watcher_.reset(); pending_task_.reset(); if (!base::Timer::retain_user_task()) base::Timer::set_user_task(base::Closure()); } void AlarmTimer::Reset() { DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); DCHECK(!base::Timer::user_task().is_null()); if (!CanWakeFromSuspend()) { base::Timer::Reset(); return; } // Cancel any previous callbacks and stop watching |alarm_fd_|. weak_factory_.InvalidateWeakPtrs(); alarm_fd_watcher_.reset(); // Ensure that the delay is not negative. const base::TimeDelta delay = std::max(base::TimeDelta(), base::Timer::GetCurrentDelay()); // Set up the pending task. base::Timer::set_desired_run_time( delay.is_zero() ? base::TimeTicks() : base::TimeTicks::Now() + delay); pending_task_ = base::MakeUnique<base::PendingTask>( base::Timer::posted_from(), base::Timer::user_task(), base::Timer::desired_run_time(), true /* nestable */); // Set |alarm_fd_| to be signaled when the delay expires. If the delay is // zero, |alarm_fd_| will never be signaled. This overrides the previous // delay, 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"; // The timer is running. base::Timer::set_is_running(true); // If the delay is zero, post the task now. if (delay.is_zero()) { origin_task_runner_->PostTask( FROM_HERE, base::Bind(&AlarmTimer::OnTimerFired, weak_factory_.GetWeakPtr())); } else { // Otherwise, if the delay is not zero, generate a tracing event to indicate // that the task was posted and watch |alarm_fd_|. base::debug::TaskAnnotator().DidQueueTask("AlarmTimer::Reset", *pending_task_); alarm_fd_watcher_ = base::FileDescriptorWatcher::WatchReadable( alarm_fd_, base::Bind(&AlarmTimer::OnAlarmFdReadableWithoutBlocking, weak_factory_.GetWeakPtr())); } } void AlarmTimer::OnAlarmFdReadableWithoutBlocking() { DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); DCHECK(base::Timer::IsRunning()); // Read from |alarm_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."; OnTimerFired(); } void AlarmTimer::OnTimerFired() { DCHECK(origin_task_runner_->RunsTasksOnCurrentThread()); DCHECK(base::Timer::IsRunning()); DCHECK(pending_task_.get()); // Take ownership of the PendingTask to prevent it from being deleted if the // AlarmTimer is deleted. const auto pending_user_task = std::move(pending_task_); base::WeakPtr<AlarmTimer> weak_ptr = weak_factory_.GetWeakPtr(); // Run the task. TRACE_TASK_EXECUTION("AlarmTimer::OnTimerFired", *pending_user_task); base::debug::TaskAnnotator().RunTask("AlarmTimer::Reset", pending_user_task.get()); // If the timer wasn't deleted, stopped or reset by the callback, reset or // stop it. if (weak_ptr.get()) { if (base::Timer::is_repeating()) Reset(); else Stop(); } } bool AlarmTimer::CanWakeFromSuspend() const { return alarm_fd_ != -1; } OneShotAlarmTimer::OneShotAlarmTimer() : AlarmTimer(false, false) { } OneShotAlarmTimer::~OneShotAlarmTimer() { } RepeatingAlarmTimer::RepeatingAlarmTimer() : AlarmTimer(true, true) { } RepeatingAlarmTimer::~RepeatingAlarmTimer() { } SimpleAlarmTimer::SimpleAlarmTimer() : AlarmTimer(true, false) { } SimpleAlarmTimer::~SimpleAlarmTimer() { } } // namespace timers