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 "perfetto/ext/base/file_utils.h"
20 #include "src/base/test/test_task_runner.h"
21 #include "test/gtest_and_gmock.h"
22
23 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
24 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
25 #include <unistd.h>
26 #endif
27
28 namespace perfetto {
29 namespace base {
30
31 namespace {
32
TEST(PeriodicTaskTest,PostDelayedTaskMode)33 TEST(PeriodicTaskTest, PostDelayedTaskMode) {
34 TestTaskRunner task_runner;
35 PeriodicTask pt(&task_runner);
36 uint32_t num_callbacks = 0;
37 auto quit_closure = task_runner.CreateCheckpoint("all_timers_done");
38
39 PeriodicTask::Args args;
40 args.task = [&] {
41 if (++num_callbacks == 3)
42 quit_closure();
43 };
44 args.period_ms = 1;
45 args.start_first_task_immediately = true;
46 pt.Start(std::move(args));
47 EXPECT_EQ(num_callbacks, 1u);
48 task_runner.RunUntilCheckpoint("all_timers_done");
49 EXPECT_EQ(num_callbacks, 3u);
50 }
51
52 // Call Reset() from a callback, ensure no further calls are made.
TEST(PeriodicTaskTest,ResetFromCallback)53 TEST(PeriodicTaskTest, ResetFromCallback) {
54 TestTaskRunner task_runner;
55 PeriodicTask pt(&task_runner);
56 uint32_t num_callbacks = 0;
57 PeriodicTask::Args args;
58 auto quit_closure = task_runner.CreateCheckpoint("quit_closure");
59 args.task = [&] {
60 ++num_callbacks;
61 pt.Reset();
62 task_runner.PostDelayedTask(quit_closure, 5);
63 };
64 args.period_ms = 1;
65 pt.Start(std::move(args));
66 EXPECT_EQ(num_callbacks, 0u); // No immediate execution.
67
68 task_runner.RunUntilCheckpoint("quit_closure");
69 EXPECT_EQ(num_callbacks, 1u);
70 }
71
72 // Invalidates the timerfd, by replacing it with /dev/null, in the middle of
73 // the periodic ticks. That causes the next read() to fail and fall back on
74 // PostDelayedTask().
75 // On Mac and other systems where timerfd is not supported this will fall back
76 // on PostDelayedTask() immediately (and work).
TEST(PeriodicTaskTest,FallbackIfTimerfdFails)77 TEST(PeriodicTaskTest, FallbackIfTimerfdFails) {
78 TestTaskRunner task_runner;
79 PeriodicTask pt(&task_runner);
80 uint32_t num_callbacks = 0;
81 auto quit_closure = task_runner.CreateCheckpoint("all_timers_done");
82
83 PeriodicTask::Args args;
84 args.task = [&] {
85 ++num_callbacks;
86 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
87 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
88 if (num_callbacks == 3 && pt.timer_fd_for_testing() > 0) {
89 ScopedFile dev_null = OpenFile("/dev/null", O_RDONLY);
90 dup2(*dev_null, pt.timer_fd_for_testing());
91 }
92 #else
93 EXPECT_FALSE(base::ScopedPlatformHandle::ValidityChecker::IsValid(
94 pt.timer_fd_for_testing()));
95 #endif
96 if (num_callbacks == 6)
97 quit_closure();
98 };
99 args.period_ms = 1;
100 args.use_suspend_aware_timer = true;
101 pt.Start(std::move(args));
102 task_runner.RunUntilCheckpoint("all_timers_done");
103 EXPECT_EQ(num_callbacks, 6u);
104 }
105
TEST(PeriodicTaskTest,DestroyedFromCallback)106 TEST(PeriodicTaskTest, DestroyedFromCallback) {
107 TestTaskRunner task_runner;
108 std::unique_ptr<PeriodicTask> pt(new PeriodicTask(&task_runner));
109 uint32_t num_callbacks = 0;
110 PeriodicTask::Args args;
111 auto quit_closure = task_runner.CreateCheckpoint("quit_closure");
112 args.task = [&] {
113 ++num_callbacks;
114 pt.reset();
115 task_runner.PostDelayedTask(quit_closure, 5);
116 };
117 args.period_ms = 1;
118 args.use_suspend_aware_timer = true;
119 pt->Start(std::move(args));
120
121 task_runner.RunUntilCheckpoint("quit_closure");
122 EXPECT_EQ(num_callbacks, 1u);
123 EXPECT_FALSE(pt);
124 }
125
TEST(PeriodicTaskTest,DestroyedFromAnotherTask)126 TEST(PeriodicTaskTest, DestroyedFromAnotherTask) {
127 TestTaskRunner task_runner;
128 std::unique_ptr<PeriodicTask> pt(new PeriodicTask(&task_runner));
129 uint32_t num_callbacks = 0;
130 PeriodicTask::Args args;
131 auto quit_closure = task_runner.CreateCheckpoint("quit_closure");
132 args.task = [&] {
133 if (++num_callbacks == 2) {
134 task_runner.PostTask([&] {
135 pt.reset();
136 task_runner.PostDelayedTask(quit_closure, 5);
137 });
138 }
139 };
140 args.period_ms = 1;
141 args.use_suspend_aware_timer = true;
142 pt->Start(std::move(args));
143
144 task_runner.RunUntilCheckpoint("quit_closure");
145 EXPECT_EQ(num_callbacks, 2u);
146 EXPECT_FALSE(pt);
147 }
148
149 // Checks the generation logic.
TEST(PeriodicTaskTest,RestartWhileRunning)150 TEST(PeriodicTaskTest, RestartWhileRunning) {
151 TestTaskRunner task_runner;
152 PeriodicTask pt(&task_runner);
153 uint32_t num_callbacks_a = 0;
154 uint32_t num_callbacks_b = 0;
155 auto quit_closure = task_runner.CreateCheckpoint("quit_closure");
156
157 auto reuse = [&] {
158 PeriodicTask::Args args;
159 args.period_ms = 1;
160 args.task = [&] {
161 if (++num_callbacks_b == 3)
162 quit_closure();
163 };
164 pt.Start(std::move(args));
165 };
166
167 PeriodicTask::Args args;
168 args.task = [&] {
169 if (++num_callbacks_a == 2)
170 task_runner.PostTask(reuse);
171 };
172 args.period_ms = 1;
173 args.use_suspend_aware_timer = true;
174 pt.Start(std::move(args));
175
176 task_runner.RunUntilCheckpoint("quit_closure");
177 EXPECT_EQ(num_callbacks_a, 2u);
178 EXPECT_EQ(num_callbacks_b, 3u);
179 }
180
TEST(PeriodicTaskTest,ImmediateExecution)181 TEST(PeriodicTaskTest, ImmediateExecution) {
182 TestTaskRunner task_runner;
183 PeriodicTask pt(&task_runner);
184 uint32_t num_callbacks = 0;
185
186 PeriodicTask::Args args;
187 args.task = [&] { ++num_callbacks; };
188 args.period_ms = 1;
189 pt.Start(args);
190 EXPECT_EQ(num_callbacks, 0u); // No immediate execution.
191
192 args.start_first_task_immediately = true;
193 pt.Start(args);
194 EXPECT_EQ(num_callbacks, 1u);
195 }
196
197 } // namespace
198 } // namespace base
199 } // namespace perfetto
200