1 /*
2 * Copyright (C) 2017 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/base/unix_task_runner.h"
18
19 #include "gtest/gtest.h"
20 #include "perfetto/base/build_config.h"
21 #include "perfetto/base/scoped_file.h"
22
23 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
24 !PERFETTO_BUILDFLAG(PERFETTO_EMBEDDER_BUILD)
25 #include "perfetto/base/android_task_runner.h"
26 #endif
27
28 #include <thread>
29
30 #include "perfetto/base/file_utils.h"
31 #include "perfetto/base/pipe.h"
32 #include "src/base/test/gtest_test_suite.h"
33
34 namespace perfetto {
35 namespace base {
36 namespace {
37
38 template <typename T>
39 class TaskRunnerTest : public ::testing::Test {
40 public:
41 T task_runner;
42 };
43
44 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
45 !PERFETTO_BUILDFLAG(PERFETTO_EMBEDDER_BUILD)
46 using TaskRunnerTypes = ::testing::Types<AndroidTaskRunner, UnixTaskRunner>;
47 #else
48 using TaskRunnerTypes = ::testing::Types<UnixTaskRunner>;
49 #endif
50
51 TYPED_TEST_SUITE(TaskRunnerTest, TaskRunnerTypes);
52
53 struct TestPipe : Pipe {
TestPipeperfetto::base::__anon9e0d2f920111::TestPipe54 TestPipe() : Pipe(Pipe::Create()) {
55 // Make the pipe initially readable.
56 Write();
57 }
58
Readperfetto::base::__anon9e0d2f920111::TestPipe59 void Read() {
60 char b;
61 ssize_t rd = read(*this->rd, &b, 1);
62 PERFETTO_DCHECK(rd == 1);
63 }
64
Writeperfetto::base::__anon9e0d2f920111::TestPipe65 void Write() {
66 const char b = '?';
67 ssize_t wr = WriteAll(*this->wr, &b, 1);
68 PERFETTO_DCHECK(wr == 1);
69 }
70 };
71
TYPED_TEST(TaskRunnerTest,PostImmediateTask)72 TYPED_TEST(TaskRunnerTest, PostImmediateTask) {
73 auto& task_runner = this->task_runner;
74 int counter = 0;
75 task_runner.PostTask([&counter] { counter = (counter << 4) | 1; });
76 task_runner.PostTask([&counter] { counter = (counter << 4) | 2; });
77 task_runner.PostTask([&counter] { counter = (counter << 4) | 3; });
78 task_runner.PostTask([&counter] { counter = (counter << 4) | 4; });
79 task_runner.PostTask([&task_runner] { task_runner.Quit(); });
80 task_runner.Run();
81 EXPECT_EQ(0x1234, counter);
82 }
83
TYPED_TEST(TaskRunnerTest,PostDelayedTask)84 TYPED_TEST(TaskRunnerTest, PostDelayedTask) {
85 auto& task_runner = this->task_runner;
86 int counter = 0;
87 task_runner.PostDelayedTask([&counter] { counter = (counter << 4) | 1; }, 5);
88 task_runner.PostDelayedTask([&counter] { counter = (counter << 4) | 2; }, 10);
89 task_runner.PostDelayedTask([&counter] { counter = (counter << 4) | 3; }, 15);
90 task_runner.PostDelayedTask([&counter] { counter = (counter << 4) | 4; }, 15);
91 task_runner.PostDelayedTask([&task_runner] { task_runner.Quit(); }, 20);
92 task_runner.Run();
93 EXPECT_EQ(0x1234, counter);
94 }
95
TYPED_TEST(TaskRunnerTest,PostImmediateTaskFromTask)96 TYPED_TEST(TaskRunnerTest, PostImmediateTaskFromTask) {
97 auto& task_runner = this->task_runner;
98 task_runner.PostTask([&task_runner] {
99 task_runner.PostTask([&task_runner] { task_runner.Quit(); });
100 });
101 task_runner.Run();
102 }
103
TYPED_TEST(TaskRunnerTest,PostDelayedTaskFromTask)104 TYPED_TEST(TaskRunnerTest, PostDelayedTaskFromTask) {
105 auto& task_runner = this->task_runner;
106 task_runner.PostTask([&task_runner] {
107 task_runner.PostDelayedTask([&task_runner] { task_runner.Quit(); }, 10);
108 });
109 task_runner.Run();
110 }
111
TYPED_TEST(TaskRunnerTest,PostImmediateTaskFromOtherThread)112 TYPED_TEST(TaskRunnerTest, PostImmediateTaskFromOtherThread) {
113 auto& task_runner = this->task_runner;
114 ThreadChecker thread_checker;
115 int counter = 0;
116 std::thread thread([&task_runner, &counter, &thread_checker] {
117 task_runner.PostTask([&thread_checker] {
118 EXPECT_TRUE(thread_checker.CalledOnValidThread());
119 });
120 task_runner.PostTask([&counter] { counter = (counter << 4) | 1; });
121 task_runner.PostTask([&counter] { counter = (counter << 4) | 2; });
122 task_runner.PostTask([&counter] { counter = (counter << 4) | 3; });
123 task_runner.PostTask([&counter] { counter = (counter << 4) | 4; });
124 task_runner.PostTask([&task_runner] { task_runner.Quit(); });
125 });
126 task_runner.Run();
127 thread.join();
128 EXPECT_EQ(0x1234, counter);
129 }
130
TYPED_TEST(TaskRunnerTest,PostDelayedTaskFromOtherThread)131 TYPED_TEST(TaskRunnerTest, PostDelayedTaskFromOtherThread) {
132 auto& task_runner = this->task_runner;
133 std::thread thread([&task_runner] {
134 task_runner.PostDelayedTask([&task_runner] { task_runner.Quit(); }, 10);
135 });
136 task_runner.Run();
137 thread.join();
138 }
139
TYPED_TEST(TaskRunnerTest,AddFileDescriptorWatch)140 TYPED_TEST(TaskRunnerTest, AddFileDescriptorWatch) {
141 auto& task_runner = this->task_runner;
142 TestPipe pipe;
143 task_runner.AddFileDescriptorWatch(pipe.rd.get(),
144 [&task_runner] { task_runner.Quit(); });
145 task_runner.Run();
146 }
147
TYPED_TEST(TaskRunnerTest,RemoveFileDescriptorWatch)148 TYPED_TEST(TaskRunnerTest, RemoveFileDescriptorWatch) {
149 auto& task_runner = this->task_runner;
150 TestPipe pipe;
151
152 bool watch_ran = false;
153 task_runner.AddFileDescriptorWatch(pipe.rd.get(),
154 [&watch_ran] { watch_ran = true; });
155 task_runner.RemoveFileDescriptorWatch(pipe.rd.get());
156 task_runner.PostDelayedTask([&task_runner] { task_runner.Quit(); }, 10);
157 task_runner.Run();
158
159 EXPECT_FALSE(watch_ran);
160 }
161
TYPED_TEST(TaskRunnerTest,RemoveFileDescriptorWatchFromTask)162 TYPED_TEST(TaskRunnerTest, RemoveFileDescriptorWatchFromTask) {
163 auto& task_runner = this->task_runner;
164 TestPipe pipe;
165
166 bool watch_ran = false;
167 task_runner.PostTask([&task_runner, &pipe] {
168 task_runner.RemoveFileDescriptorWatch(pipe.rd.get());
169 });
170 task_runner.AddFileDescriptorWatch(pipe.rd.get(),
171 [&watch_ran] { watch_ran = true; });
172 task_runner.PostDelayedTask([&task_runner] { task_runner.Quit(); }, 10);
173 task_runner.Run();
174
175 EXPECT_FALSE(watch_ran);
176 }
177
TYPED_TEST(TaskRunnerTest,AddFileDescriptorWatchFromAnotherWatch)178 TYPED_TEST(TaskRunnerTest, AddFileDescriptorWatchFromAnotherWatch) {
179 auto& task_runner = this->task_runner;
180 TestPipe pipe;
181 TestPipe pipe2;
182
183 task_runner.AddFileDescriptorWatch(
184 pipe.rd.get(), [&task_runner, &pipe, &pipe2] {
185 pipe.Read();
186 task_runner.AddFileDescriptorWatch(
187 pipe2.rd.get(), [&task_runner] { task_runner.Quit(); });
188 });
189 task_runner.Run();
190 }
191
TYPED_TEST(TaskRunnerTest,RemoveFileDescriptorWatchFromAnotherWatch)192 TYPED_TEST(TaskRunnerTest, RemoveFileDescriptorWatchFromAnotherWatch) {
193 auto& task_runner = this->task_runner;
194 TestPipe pipe;
195 TestPipe pipe2;
196
197 bool watch_ran = false;
198 task_runner.AddFileDescriptorWatch(
199 pipe.rd.get(), [&task_runner, &pipe, &pipe2] {
200 pipe.Read();
201 task_runner.RemoveFileDescriptorWatch(pipe2.rd.get());
202 });
203 task_runner.AddFileDescriptorWatch(pipe2.rd.get(),
204 [&watch_ran] { watch_ran = true; });
205 task_runner.PostDelayedTask([&task_runner] { task_runner.Quit(); }, 10);
206 task_runner.Run();
207
208 EXPECT_FALSE(watch_ran);
209 }
210
TYPED_TEST(TaskRunnerTest,ReplaceFileDescriptorWatchFromAnotherWatch)211 TYPED_TEST(TaskRunnerTest, ReplaceFileDescriptorWatchFromAnotherWatch) {
212 auto& task_runner = this->task_runner;
213 TestPipe pipe;
214 TestPipe pipe2;
215
216 bool watch_ran = false;
217 task_runner.AddFileDescriptorWatch(pipe.rd.get(), [&task_runner, &pipe2] {
218 task_runner.RemoveFileDescriptorWatch(pipe2.rd.get());
219 task_runner.AddFileDescriptorWatch(pipe2.rd.get(),
220 [&task_runner] { task_runner.Quit(); });
221 });
222 task_runner.AddFileDescriptorWatch(pipe2.rd.get(),
223 [&watch_ran] { watch_ran = true; });
224 task_runner.Run();
225
226 EXPECT_FALSE(watch_ran);
227 }
228
TYPED_TEST(TaskRunnerTest,AddFileDescriptorWatchFromAnotherThread)229 TYPED_TEST(TaskRunnerTest, AddFileDescriptorWatchFromAnotherThread) {
230 auto& task_runner = this->task_runner;
231 TestPipe pipe;
232
233 std::thread thread([&task_runner, &pipe] {
234 task_runner.AddFileDescriptorWatch(pipe.rd.get(),
235 [&task_runner] { task_runner.Quit(); });
236 });
237 task_runner.Run();
238 thread.join();
239 }
240
TYPED_TEST(TaskRunnerTest,FileDescriptorWatchWithMultipleEvents)241 TYPED_TEST(TaskRunnerTest, FileDescriptorWatchWithMultipleEvents) {
242 auto& task_runner = this->task_runner;
243 TestPipe pipe;
244
245 int event_count = 0;
246 task_runner.AddFileDescriptorWatch(pipe.rd.get(),
247 [&task_runner, &pipe, &event_count] {
248 if (++event_count == 3) {
249 task_runner.Quit();
250 return;
251 }
252 pipe.Read();
253 });
254 task_runner.PostTask([&pipe] { pipe.Write(); });
255 task_runner.PostTask([&pipe] { pipe.Write(); });
256 task_runner.Run();
257 }
258
TYPED_TEST(TaskRunnerTest,FileDescriptorClosedEvent)259 TYPED_TEST(TaskRunnerTest, FileDescriptorClosedEvent) {
260 auto& task_runner = this->task_runner;
261 TestPipe pipe;
262 pipe.wr.reset();
263 task_runner.AddFileDescriptorWatch(pipe.rd.get(),
264 [&task_runner] { task_runner.Quit(); });
265 task_runner.Run();
266 }
267
TYPED_TEST(TaskRunnerTest,PostManyDelayedTasks)268 TYPED_TEST(TaskRunnerTest, PostManyDelayedTasks) {
269 // Check that PostTask doesn't start failing if there are too many scheduled
270 // wake-ups.
271 auto& task_runner = this->task_runner;
272 for (int i = 0; i < 0x1000; i++)
273 task_runner.PostDelayedTask([] {}, 0);
274 task_runner.PostDelayedTask([&task_runner] { task_runner.Quit(); }, 10);
275 task_runner.Run();
276 }
277
TYPED_TEST(TaskRunnerTest,RunAgain)278 TYPED_TEST(TaskRunnerTest, RunAgain) {
279 auto& task_runner = this->task_runner;
280 int counter = 0;
281 task_runner.PostTask([&task_runner, &counter] {
282 counter++;
283 task_runner.Quit();
284 });
285 task_runner.Run();
286 task_runner.PostTask([&task_runner, &counter] {
287 counter++;
288 task_runner.Quit();
289 });
290 task_runner.Run();
291 EXPECT_EQ(2, counter);
292 }
293
294 template <typename TaskRunner>
RepeatingTask(TaskRunner * task_runner)295 void RepeatingTask(TaskRunner* task_runner) {
296 task_runner->PostTask(std::bind(&RepeatingTask<TaskRunner>, task_runner));
297 }
298
TYPED_TEST(TaskRunnerTest,FileDescriptorWatchesNotStarved)299 TYPED_TEST(TaskRunnerTest, FileDescriptorWatchesNotStarved) {
300 auto& task_runner = this->task_runner;
301 TestPipe pipe;
302 task_runner.PostTask(std::bind(&RepeatingTask<TypeParam>, &task_runner));
303 task_runner.AddFileDescriptorWatch(pipe.rd.get(),
304 [&task_runner] { task_runner.Quit(); });
305 task_runner.Run();
306 }
307
308 template <typename TaskRunner>
CountdownTask(TaskRunner * task_runner,int * counter)309 void CountdownTask(TaskRunner* task_runner, int* counter) {
310 if (!--(*counter)) {
311 task_runner->Quit();
312 return;
313 }
314 task_runner->PostTask(
315 std::bind(&CountdownTask<TaskRunner>, task_runner, counter));
316 }
317
TYPED_TEST(TaskRunnerTest,NoDuplicateFileDescriptorWatchCallbacks)318 TYPED_TEST(TaskRunnerTest, NoDuplicateFileDescriptorWatchCallbacks) {
319 auto& task_runner = this->task_runner;
320 TestPipe pipe;
321 bool watch_called = 0;
322 int counter = 10;
323 task_runner.AddFileDescriptorWatch(pipe.rd.get(), [&pipe, &watch_called] {
324 ASSERT_FALSE(watch_called);
325 pipe.Read();
326 watch_called = true;
327 });
328 task_runner.PostTask(
329 std::bind(&CountdownTask<TypeParam>, &task_runner, &counter));
330 task_runner.Run();
331 }
332
TYPED_TEST(TaskRunnerTest,ReplaceFileDescriptorWatchFromOtherThread)333 TYPED_TEST(TaskRunnerTest, ReplaceFileDescriptorWatchFromOtherThread) {
334 auto& task_runner = this->task_runner;
335 TestPipe pipe;
336
337 // The two watch tasks here race each other. We don't particularly care which
338 // wins as long as one of them runs.
339 task_runner.AddFileDescriptorWatch(pipe.rd.get(),
340 [&task_runner] { task_runner.Quit(); });
341
342 std::thread thread([&task_runner, &pipe] {
343 task_runner.RemoveFileDescriptorWatch(pipe.rd.get());
344 task_runner.AddFileDescriptorWatch(pipe.rd.get(),
345 [&task_runner] { task_runner.Quit(); });
346 });
347
348 task_runner.Run();
349 thread.join();
350 }
351
TYPED_TEST(TaskRunnerTest,IsIdleForTesting)352 TYPED_TEST(TaskRunnerTest, IsIdleForTesting) {
353 auto& task_runner = this->task_runner;
354 task_runner.PostTask(
355 [&task_runner] { EXPECT_FALSE(task_runner.IsIdleForTesting()); });
356 task_runner.PostTask([&task_runner] {
357 EXPECT_TRUE(task_runner.IsIdleForTesting());
358 task_runner.Quit();
359 });
360 task_runner.Run();
361 }
362
TYPED_TEST(TaskRunnerTest,RunsTasksOnCurrentThread)363 TYPED_TEST(TaskRunnerTest, RunsTasksOnCurrentThread) {
364 auto& main_tr = this->task_runner;
365
366 EXPECT_TRUE(main_tr.RunsTasksOnCurrentThread());
367 std::thread thread([&main_tr] {
368 typename std::remove_reference<decltype(main_tr)>::type second_tr;
369 second_tr.PostTask([&main_tr, &second_tr] {
370 EXPECT_FALSE(main_tr.RunsTasksOnCurrentThread());
371 EXPECT_TRUE(second_tr.RunsTasksOnCurrentThread());
372 second_tr.Quit();
373 });
374 second_tr.Run();
375 });
376 thread.join();
377 }
378
379 } // namespace
380 } // namespace base
381 } // namespace perfetto
382