huangcm
2024-12-18 9d29be7f7249789d6ffd0440067187a9f040c2cd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// 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 {
 
SimpleAlarmTimer::SimpleAlarmTimer()
    : alarm_fd_(timerfd_create(CLOCK_REALTIME_ALARM, 0)), weak_factory_(this) {}
 
SimpleAlarmTimer::~SimpleAlarmTimer() {
  DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
  Stop();
}
 
void SimpleAlarmTimer::Stop() {
  DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
 
  if (!IsRunning())
    return;
 
  if (!CanWakeFromSuspend()) {
    base::RetainingOneShotTimer::Stop();
    return;
  }
 
  // Cancel any previous callbacks.
  weak_factory_.InvalidateWeakPtrs();
 
  base::RetainingOneShotTimer::set_is_running(false);
  alarm_fd_watcher_.reset();
  pending_task_.reset();
}
 
void SimpleAlarmTimer::Reset() {
  DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
  DCHECK(!base::RetainingOneShotTimer::user_task().is_null());
 
  if (!CanWakeFromSuspend()) {
    base::RetainingOneShotTimer::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::RetainingOneShotTimer::GetCurrentDelay());
 
  // Set up the pending task.
  base::RetainingOneShotTimer::set_desired_run_time(
      delay.is_zero() ? base::TimeTicks() : base::TimeTicks::Now() + delay);
  pending_task_ = std::make_unique<base::PendingTask>(
      base::RetainingOneShotTimer::posted_from(),
      base::RetainingOneShotTimer::user_task(),
      base::RetainingOneShotTimer::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::RetainingOneShotTimer::set_is_running(true);
 
  // If the delay is zero, post the task now.
  if (delay.is_zero()) {
    origin_task_runner_->PostTask(
        FROM_HERE, base::BindOnce(&SimpleAlarmTimer::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().WillQueueTask("SimpleAlarmTimer::Reset",
                                               pending_task_.get());
    alarm_fd_watcher_ = base::FileDescriptorWatcher::WatchReadable(
        alarm_fd_,
        base::BindRepeating(&SimpleAlarmTimer::OnAlarmFdReadableWithoutBlocking,
                            weak_factory_.GetWeakPtr()));
  }
}
 
void SimpleAlarmTimer::OnAlarmFdReadableWithoutBlocking() {
  DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
  DCHECK(base::RetainingOneShotTimer::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 SimpleAlarmTimer::OnTimerFired() {
  DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
  DCHECK(base::RetainingOneShotTimer::IsRunning());
  DCHECK(pending_task_.get());
 
  // Take ownership of the PendingTask to prevent it from being deleted if the
  // SimpleAlarmTimer is deleted.
  const auto pending_user_task = std::move(pending_task_);
 
  base::WeakPtr<SimpleAlarmTimer> weak_ptr = weak_factory_.GetWeakPtr();
 
  // Run the task.
  TRACE_TASK_EXECUTION("SimpleAlarmTimer::OnTimerFired", *pending_user_task);
  base::debug::TaskAnnotator().RunTask("SimpleAlarmTimer::Reset",
                                       pending_user_task.get());
 
  // If the timer wasn't deleted, stopped or reset by the callback, stop it.
  if (weak_ptr)
    Stop();
}
 
bool SimpleAlarmTimer::CanWakeFromSuspend() const {
  return alarm_fd_ != -1;
}
 
}  // namespace timers