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