Home | History | Annotate | Download | only in timers
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "components/timers/alarm_timer_chromeos.h"
      6 
      7 #include <stdint.h>
      8 #include <sys/timerfd.h>
      9 
     10 #include <algorithm>
     11 #include <memory>
     12 #include <utility>
     13 
     14 #include "base/bind.h"
     15 #include "base/debug/task_annotator.h"
     16 #include "base/files/file_util.h"
     17 #include "base/logging.h"
     18 #include "base/pending_task.h"
     19 #include "base/trace_event/trace_event.h"
     20 
     21 namespace timers {
     22 
     23 SimpleAlarmTimer::SimpleAlarmTimer()
     24     : alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)), weak_factory_(this) {}
     25 
     26 SimpleAlarmTimer::~SimpleAlarmTimer() {
     27   DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
     28   Stop();
     29 }
     30 
     31 void SimpleAlarmTimer::Stop() {
     32   DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
     33 
     34   if (!IsRunning())
     35     return;
     36 
     37   if (!CanWakeFromSuspend()) {
     38     base::RetainingOneShotTimer::Stop();
     39     return;
     40   }
     41 
     42   // Cancel any previous callbacks.
     43   weak_factory_.InvalidateWeakPtrs();
     44 
     45   base::RetainingOneShotTimer::set_is_running(false);
     46   alarm_fd_watcher_.reset();
     47   pending_task_.reset();
     48 }
     49 
     50 void SimpleAlarmTimer::Reset() {
     51   DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
     52   DCHECK(!base::RetainingOneShotTimer::user_task().is_null());
     53 
     54   if (!CanWakeFromSuspend()) {
     55     base::RetainingOneShotTimer::Reset();
     56     return;
     57   }
     58 
     59   // Cancel any previous callbacks and stop watching |alarm_fd_|.
     60   weak_factory_.InvalidateWeakPtrs();
     61   alarm_fd_watcher_.reset();
     62 
     63   // Ensure that the delay is not negative.
     64   const base::TimeDelta delay = std::max(
     65       base::TimeDelta(), base::RetainingOneShotTimer::GetCurrentDelay());
     66 
     67   // Set up the pending task.
     68   base::RetainingOneShotTimer::set_desired_run_time(
     69       delay.is_zero() ? base::TimeTicks() : base::TimeTicks::Now() + delay);
     70   pending_task_ = std::make_unique<base::PendingTask>(
     71       base::RetainingOneShotTimer::posted_from(),
     72       base::RetainingOneShotTimer::user_task(),
     73       base::RetainingOneShotTimer::desired_run_time());
     74 
     75   // Set |alarm_fd_| to be signaled when the delay expires. If the delay is
     76   // zero, |alarm_fd_| will never be signaled. This overrides the previous
     77   // delay, if any.
     78   itimerspec alarm_time = {};
     79   alarm_time.it_value.tv_sec = delay.InSeconds();
     80   alarm_time.it_value.tv_nsec =
     81       (delay.InMicroseconds() % base::Time::kMicrosecondsPerSecond) *
     82       base::Time::kNanosecondsPerMicrosecond;
     83   if (timerfd_settime(alarm_fd_, 0, &alarm_time, NULL) < 0)
     84     PLOG(ERROR) << "Error while setting alarm time.  Timer will not fire";
     85 
     86   // The timer is running.
     87   base::RetainingOneShotTimer::set_is_running(true);
     88 
     89   // If the delay is zero, post the task now.
     90   if (delay.is_zero()) {
     91     origin_task_runner_->PostTask(
     92         FROM_HERE, base::BindOnce(&SimpleAlarmTimer::OnTimerFired,
     93                                   weak_factory_.GetWeakPtr()));
     94   } else {
     95     // Otherwise, if the delay is not zero, generate a tracing event to indicate
     96     // that the task was posted and watch |alarm_fd_|.
     97     base::debug::TaskAnnotator().WillQueueTask("SimpleAlarmTimer::Reset",
     98                                                pending_task_.get());
     99     alarm_fd_watcher_ = base::FileDescriptorWatcher::WatchReadable(
    100         alarm_fd_,
    101         base::BindRepeating(&SimpleAlarmTimer::OnAlarmFdReadableWithoutBlocking,
    102                             weak_factory_.GetWeakPtr()));
    103   }
    104 }
    105 
    106 void SimpleAlarmTimer::OnAlarmFdReadableWithoutBlocking() {
    107   DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
    108   DCHECK(base::RetainingOneShotTimer::IsRunning());
    109 
    110   // Read from |alarm_fd_| to ack the event.
    111   char val[sizeof(uint64_t)];
    112   if (!base::ReadFromFD(alarm_fd_, val, sizeof(uint64_t)))
    113     PLOG(DFATAL) << "Unable to read from timer file descriptor.";
    114 
    115   OnTimerFired();
    116 }
    117 
    118 void SimpleAlarmTimer::OnTimerFired() {
    119   DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
    120   DCHECK(base::RetainingOneShotTimer::IsRunning());
    121   DCHECK(pending_task_.get());
    122 
    123   // Take ownership of the PendingTask to prevent it from being deleted if the
    124   // SimpleAlarmTimer is deleted.
    125   const auto pending_user_task = std::move(pending_task_);
    126 
    127   base::WeakPtr<SimpleAlarmTimer> weak_ptr = weak_factory_.GetWeakPtr();
    128 
    129   // Run the task.
    130   TRACE_TASK_EXECUTION("SimpleAlarmTimer::OnTimerFired", *pending_user_task);
    131   base::debug::TaskAnnotator().RunTask("SimpleAlarmTimer::Reset",
    132                                        pending_user_task.get());
    133 
    134   // If the timer wasn't deleted, stopped or reset by the callback, stop it.
    135   if (weak_ptr)
    136     Stop();
    137 }
    138 
    139 bool SimpleAlarmTimer::CanWakeFromSuspend() const {
    140   return alarm_fd_ != -1;
    141 }
    142 
    143 }  // namespace timers
    144