• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SimpleAlarmTimer()23 SimpleAlarmTimer::SimpleAlarmTimer()
24     : alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)), weak_factory_(this) {}
25 
~SimpleAlarmTimer()26 SimpleAlarmTimer::~SimpleAlarmTimer() {
27   DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
28   Stop();
29 }
30 
Stop()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 
Reset()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 
OnAlarmFdReadableWithoutBlocking()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 
OnTimerFired()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 
CanWakeFromSuspend() const139 bool SimpleAlarmTimer::CanWakeFromSuspend() const {
140   return alarm_fd_ != -1;
141 }
142 
143 }  // namespace timers
144