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