• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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