Coverage Report

Created: 2025-12-31 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/perfetto/src/base/periodic_task.cc
Line
Count
Source
1
/*
2
 * Copyright (C) 2021 The Android Open Source Project
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#include "perfetto/ext/base/periodic_task.h"
18
19
#include <limits>
20
21
#include "perfetto/base/build_config.h"
22
#include "perfetto/base/logging.h"
23
#include "perfetto/base/task_runner.h"
24
#include "perfetto/base/time.h"
25
#include "perfetto/ext/base/file_utils.h"
26
27
#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
28
    (PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && __ANDROID_API__ >= 19)
29
#include <sys/timerfd.h>
30
#endif
31
32
namespace perfetto {
33
namespace base {
34
35
namespace {
36
37
uint32_t GetNextDelayMs(const TimeMillis& now_ms,
38
300
                        const PeriodicTask::Args& args) {
39
300
  if (args.one_shot)
40
0
    return args.period_ms;
41
42
300
  return args.period_ms -
43
300
         static_cast<uint32_t>(now_ms.count() % args.period_ms);
44
300
}
45
46
0
ScopedPlatformHandle CreateTimerFd(const PeriodicTask::Args& args) {
47
0
#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX_BUT_NOT_QNX) || \
48
0
    (PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && __ANDROID_API__ >= 19)
49
0
  ScopedPlatformHandle tfd(
50
0
      timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK));
51
0
  uint32_t phase_ms = GetNextDelayMs(GetBootTimeMs(), args);
52
53
0
  struct itimerspec its {};
54
  // The "1 +" is to make sure that we never pass a zero it_value in the
55
  // unlikely case of phase_ms being 0. That would cause the timer to be
56
  // considered disarmed by timerfd_settime.
57
0
  its.it_value.tv_sec = static_cast<time_t>(phase_ms / 1000u);
58
0
  its.it_value.tv_nsec = 1 + static_cast<long>((phase_ms % 1000u) * 1000000u);
59
0
  if (args.one_shot) {
60
0
    its.it_interval.tv_sec = 0;
61
0
    its.it_interval.tv_nsec = 0;
62
0
  } else {
63
0
    const uint32_t period_ms = args.period_ms;
64
0
    its.it_interval.tv_sec = static_cast<time_t>(period_ms / 1000u);
65
0
    its.it_interval.tv_nsec = static_cast<long>((period_ms % 1000u) * 1000000u);
66
0
  }
67
0
  if (timerfd_settime(*tfd, 0, &its, nullptr) < 0)
68
0
    return ScopedPlatformHandle();
69
0
  return tfd;
70
#else
71
  ignore_result(args);
72
  return ScopedPlatformHandle();
73
#endif
74
0
}
75
76
}  // namespace
77
78
PeriodicTask::PeriodicTask(TaskRunner* task_runner)
79
600
    : task_runner_(task_runner), weak_ptr_factory_(this) {}
80
81
600
PeriodicTask::~PeriodicTask() {
82
600
  Reset();
83
600
}
84
85
300
void PeriodicTask::Start(Args args) {
86
300
  PERFETTO_DCHECK_THREAD(thread_checker_);
87
300
  Reset();
88
300
  if (args.period_ms == 0 || !args.task) {
89
0
    PERFETTO_DCHECK(args.period_ms > 0);
90
0
    PERFETTO_DCHECK(args.task);
91
0
    return;
92
0
  }
93
300
  args_ = std::move(args);
94
300
  if (args_.use_suspend_aware_timer) {
95
0
    timer_fd_ = CreateTimerFd(args_);
96
0
    if (timer_fd_) {
97
0
      auto weak_this = weak_ptr_factory_.GetWeakPtr();
98
0
      task_runner_->AddFileDescriptorWatch(
99
0
          *timer_fd_,
100
0
          std::bind(PeriodicTask::RunTaskAndPostNext, weak_this, generation_));
101
0
    } else {
102
0
      PERFETTO_DPLOG("timerfd not supported, falling back on PostDelayedTask");
103
0
    }
104
0
  }  // if (use_suspend_aware_timer).
105
106
300
  if (!timer_fd_)
107
300
    PostNextTask();
108
109
300
  if (args_.start_first_task_immediately)
110
300
    args_.task();
111
300
}
112
113
300
void PeriodicTask::PostNextTask() {
114
300
  PERFETTO_DCHECK_THREAD(thread_checker_);
115
300
  PERFETTO_DCHECK(args_.period_ms > 0);
116
300
  PERFETTO_DCHECK(!timer_fd_);
117
300
  uint32_t delay_ms = GetNextDelayMs(GetWallTimeMs(), args_);
118
300
  auto weak_this = weak_ptr_factory_.GetWeakPtr();
119
300
  task_runner_->PostDelayedTask(
120
300
      std::bind(PeriodicTask::RunTaskAndPostNext, weak_this, generation_),
121
300
      delay_ms);
122
300
}
123
124
// static
125
// This function can be called in two ways (both from the TaskRunner):
126
// 1. When using a timerfd, this task is registered as a FD watch.
127
// 2. When using PostDelayedTask, this is the task posted on the TaskRunner.
128
void PeriodicTask::RunTaskAndPostNext(WeakPtr<PeriodicTask> thiz,
129
0
                                      uint32_t generation) {
130
0
  if (!thiz || !thiz->args_.task || generation != thiz->generation_)
131
0
    return;  // Destroyed or Reset() in the meanwhile.
132
0
  PERFETTO_DCHECK_THREAD(thiz->thread_checker_);
133
0
  if (thiz->timer_fd_) {
134
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
135
    PERFETTO_FATAL("timerfd for periodic tasks unsupported on Windows");
136
#else
137
    // If we are using a timerfd there is no need to repeatedly call
138
    // PostDelayedTask(). The kernel will wakeup the timer fd periodically. We
139
    // just need to read() it.
140
0
    uint64_t ignored = 0;
141
0
    errno = 0;
142
0
    auto rsize = Read(*thiz->timer_fd_, &ignored, sizeof(&ignored));
143
0
    if (rsize != sizeof(uint64_t)) {
144
0
      if (errno == EAGAIN)
145
0
        return;  // A spurious wakeup. Rare, but can happen, just ignore.
146
0
      PERFETTO_PLOG("read(timerfd) failed, falling back on PostDelayedTask");
147
0
      thiz->ResetTimerFd();
148
0
    }
149
0
#endif
150
0
  }
151
152
  // Create a copy of the task to deal with either:
153
  // 1. one_shot causing a Reset().
154
  // 2. task() invoking internally Reset().
155
  // That would cause a reset of the args_.task itself, which would invalidate
156
  // the task bind state while we are invoking it.
157
0
  auto task = thiz->args_.task;
158
159
  // The repetition of the if() is to deal with the ResetTimerFd() case above.
160
0
  if (thiz->args_.one_shot) {
161
0
    thiz->Reset();
162
0
  } else if (!thiz->timer_fd_) {
163
0
    thiz->PostNextTask();
164
0
  }
165
166
0
  task();
167
0
}
168
169
1.20k
void PeriodicTask::Reset() {
170
1.20k
  PERFETTO_DCHECK_THREAD(thread_checker_);
171
1.20k
  ++generation_;
172
1.20k
  args_ = Args();
173
1.20k
  PERFETTO_DCHECK(!args_.task);
174
1.20k
  ResetTimerFd();
175
1.20k
}
176
177
1.20k
void PeriodicTask::ResetTimerFd() {
178
1.20k
  if (!timer_fd_)
179
1.20k
    return;
180
0
  task_runner_->RemoveFileDescriptorWatch(*timer_fd_);
181
0
  timer_fd_.reset();
182
0
}
183
184
}  // namespace base
185
}  // namespace perfetto