普通文本  |  170行  |  4.93 KB

// 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