blob: 17180385da43b8582dc18bbd884e799b5feb6005 [file] [log] [blame]
// 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 <memory>
#include <utility>
#include "base/bind.h"
#include "base/debug/task_annotator.h"
#include "base/files/file_util.h"
#include "base/logging.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_->RunsTasksInCurrentSequence());
Stop();
}
void AlarmTimer::Stop() {
DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
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_->RunsTasksInCurrentSequence());
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_ = std::make_unique<base::PendingTask>(
base::Timer::posted_from(), base::Timer::user_task(),
base::Timer::desired_run_time());
// 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_->RunsTasksInCurrentSequence());
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_->RunsTasksInCurrentSequence());
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