1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 */
8
9 #include <quic/common/FunctionLooper.h>
10
11 #include <gtest/gtest.h>
12
13 using namespace std;
14 using namespace folly;
15 using namespace testing;
16
17 namespace quic {
18 namespace test {
19
20 class FunctionLooperTest : public Test {};
21
TEST(FunctionLooperTest,LooperNotRunning)22 TEST(FunctionLooperTest, LooperNotRunning) {
23 EventBase evb;
24 bool called = false;
25 auto func = [&](bool) { called = true; };
26 FunctionLooper::Ptr looper(
27 new FunctionLooper(&evb, std::move(func), LooperType::ReadLooper));
28 evb.loopOnce();
29 EXPECT_FALSE(called);
30 evb.loopOnce();
31 EXPECT_FALSE(called);
32 EXPECT_FALSE(looper->isRunning());
33 }
34
TEST(FunctionLooperTest,LooperStarted)35 TEST(FunctionLooperTest, LooperStarted) {
36 EventBase evb;
37 bool called = false;
38 auto func = [&](bool) { called = true; };
39 FunctionLooper::Ptr looper(
40 new FunctionLooper(&evb, std::move(func), LooperType::ReadLooper));
41 looper->run();
42 EXPECT_TRUE(looper->isRunning());
43 evb.loopOnce();
44 EXPECT_TRUE(called);
45 called = false;
46 evb.loopOnce();
47 EXPECT_TRUE(called);
48 }
49
TEST(FunctionLooperTest,LooperStopped)50 TEST(FunctionLooperTest, LooperStopped) {
51 EventBase evb;
52 bool called = false;
53 auto func = [&](bool) { called = true; };
54 FunctionLooper::Ptr looper(
55 new FunctionLooper(&evb, std::move(func), LooperType::ReadLooper));
56 looper->run();
57 evb.loopOnce();
58 EXPECT_TRUE(called);
59 called = false;
60 looper->stop();
61 EXPECT_FALSE(looper->isRunning());
62 evb.loopOnce();
63 EXPECT_FALSE(called);
64 }
65
TEST(FunctionLooperTest,LooperRestarted)66 TEST(FunctionLooperTest, LooperRestarted) {
67 EventBase evb;
68 bool called = false;
69 auto func = [&](bool) { called = true; };
70 FunctionLooper::Ptr looper(
71 new FunctionLooper(&evb, std::move(func), LooperType::ReadLooper));
72 looper->run();
73 evb.loopOnce();
74 EXPECT_TRUE(called);
75 called = false;
76 looper->stop();
77 evb.loopOnce();
78 EXPECT_FALSE(called);
79 looper->run();
80 EXPECT_TRUE(looper->isRunning());
81 evb.loopOnce();
82 EXPECT_TRUE(called);
83 }
84
TEST(FunctionLooperTest,DestroyLooperDuringFunc)85 TEST(FunctionLooperTest, DestroyLooperDuringFunc) {
86 EventBase evb;
87 bool called = false;
88 FunctionLooper::Ptr* looperPtr = nullptr;
89
90 auto func = [&](bool) {
91 called = true;
92 *looperPtr = nullptr;
93 };
94 FunctionLooper::Ptr looper(
95 new FunctionLooper(&evb, std::move(func), LooperType::ReadLooper));
96 looperPtr = &looper;
97
98 looper->run();
99 evb.loopOnce();
100 EXPECT_TRUE(called);
101 EXPECT_EQ(looper, nullptr);
102 }
103
TEST(FunctionLooperTest,StopLooperDuringFunc)104 TEST(FunctionLooperTest, StopLooperDuringFunc) {
105 EventBase evb;
106 bool called = false;
107 FunctionLooper::Ptr* looperPtr = nullptr;
108
109 auto func = [&](bool) {
110 called = true;
111 (*looperPtr)->stop();
112 };
113 FunctionLooper::Ptr looper(
114 new FunctionLooper(&evb, std::move(func), LooperType::ReadLooper));
115 looperPtr = &looper;
116
117 looper->run();
118 evb.loopOnce();
119 EXPECT_TRUE(called);
120 called = false;
121 evb.loopOnce();
122 EXPECT_FALSE(called);
123 }
124
TEST(FunctionLooperTest,RunLooperDuringFunc)125 TEST(FunctionLooperTest, RunLooperDuringFunc) {
126 EventBase evb;
127 bool called = false;
128 FunctionLooper::Ptr* looperPtr = nullptr;
129
130 auto func = [&](bool) {
131 called = true;
132 (*looperPtr)->run();
133 };
134 FunctionLooper::Ptr looper(
135 new FunctionLooper(&evb, std::move(func), LooperType::ReadLooper));
136 looperPtr = &looper;
137
138 looper->run();
139 evb.loopOnce();
140 EXPECT_TRUE(called);
141 called = false;
142 evb.loopOnce();
143 EXPECT_TRUE(called);
144 }
145
TEST(FunctionLooperTest,DetachStopsLooper)146 TEST(FunctionLooperTest, DetachStopsLooper) {
147 EventBase evb;
148 bool called = false;
149 auto func = [&](bool) { called = true; };
150 FunctionLooper::Ptr looper(
151 new FunctionLooper(&evb, std::move(func), LooperType::ReadLooper));
152 looper->run();
153 EXPECT_TRUE(looper->isRunning());
154 looper->detachEventBase();
155 EXPECT_FALSE(looper->isRunning());
156 looper->attachEventBase(&evb);
157 EXPECT_FALSE(looper->isRunning());
158 }
159
TEST(FunctionLooperTest,PacingOnce)160 TEST(FunctionLooperTest, PacingOnce) {
161 EventBase evb;
162 TimerHighRes::SharedPtr pacingTimer(TimerHighRes::newTimer(&evb, 1ms));
163 std::vector<bool> fromTimerVec;
164 auto func = [&](bool fromTimer) { fromTimerVec.push_back(fromTimer); };
165 bool firstTime = true;
166 auto pacingFunc = [&]() -> auto {
167 if (firstTime) {
168 firstTime = false;
169 return 3600000ms;
170 }
171 return std::chrono::milliseconds::zero();
172 };
173 FunctionLooper::Ptr looper(
174 new FunctionLooper(&evb, std::move(func), LooperType::ReadLooper));
175 looper->setPacingTimer(std::move(pacingTimer));
176 looper->setPacingFunction(std::move(pacingFunc));
177 looper->run();
178 evb.loopOnce();
179 EXPECT_EQ(1, fromTimerVec.size());
180 EXPECT_FALSE(fromTimerVec.back());
181 EXPECT_TRUE(looper->isScheduled());
182 looper->timeoutExpired();
183 EXPECT_EQ(2, fromTimerVec.size());
184 EXPECT_TRUE(fromTimerVec.back());
185 looper->stop();
186 }
187
TEST(FunctionLooperTest,KeepPacing)188 TEST(FunctionLooperTest, KeepPacing) {
189 EventBase evb;
190 TimerHighRes::SharedPtr pacingTimer(TimerHighRes::newTimer(&evb, 1ms));
191 std::vector<bool> fromTimerVec;
192 auto func = [&](bool fromTimer) { fromTimerVec.push_back(fromTimer); };
193 bool stopPacing = false;
194 auto pacingFunc = [&]() -> auto {
195 if (stopPacing) {
196 return std::chrono::milliseconds::zero();
197 }
198 return 3600000ms;
199 };
200 FunctionLooper::Ptr looper(
201 new FunctionLooper(&evb, std::move(func), LooperType::ReadLooper));
202 looper->setPacingTimer(std::move(pacingTimer));
203 looper->setPacingFunction(std::move(pacingFunc));
204 looper->run();
205 evb.loopOnce();
206 EXPECT_EQ(1, fromTimerVec.size());
207 EXPECT_FALSE(fromTimerVec.back());
208 EXPECT_TRUE(looper->isScheduled());
209
210 looper->cancelTimeout();
211 EXPECT_FALSE(looper->isScheduled());
212 looper->timeoutExpired();
213 EXPECT_EQ(2, fromTimerVec.size());
214 EXPECT_TRUE(fromTimerVec.back());
215 EXPECT_TRUE(looper->isScheduled());
216
217 looper->cancelTimeout();
218 EXPECT_FALSE(looper->isScheduled());
219 looper->timeoutExpired();
220 EXPECT_EQ(3, fromTimerVec.size());
221 EXPECT_TRUE(fromTimerVec.back());
222 EXPECT_TRUE(looper->isScheduled());
223
224 stopPacing = true;
225 looper->cancelTimeout();
226 EXPECT_FALSE(looper->isScheduled());
227 looper->timeoutExpired();
228 EXPECT_EQ(4, fromTimerVec.size());
229 EXPECT_TRUE(fromTimerVec.back());
230 EXPECT_FALSE(looper->isScheduled());
231
232 looper->stop();
233 }
234
TEST(FunctionLooperTest,TimerTickSize)235 TEST(FunctionLooperTest, TimerTickSize) {
236 EventBase evb;
237 TimerHighRes::SharedPtr pacingTimer(TimerHighRes::newTimer(&evb, 123ms));
238 FunctionLooper::Ptr looper(new FunctionLooper(
239 &evb, [&](bool) {}, LooperType::ReadLooper));
240 looper->setPacingTimer(std::move(pacingTimer));
241 EXPECT_EQ(123ms, looper->getTimerTickInterval());
242 }
243
TEST(FunctionLooperTest,TimerTickSizeAfterNewEvb)244 TEST(FunctionLooperTest, TimerTickSizeAfterNewEvb) {
245 EventBase evb;
246 TimerHighRes::SharedPtr pacingTimer(TimerHighRes::newTimer(&evb, 123ms));
247 FunctionLooper::Ptr looper(new FunctionLooper(
248 &evb, [&](bool) {}, LooperType::ReadLooper));
249 looper->setPacingTimer(std::move(pacingTimer));
250 EXPECT_EQ(123ms, looper->getTimerTickInterval());
251 looper->detachEventBase();
252 EventBase evb2;
253 looper->attachEventBase(&evb2);
254 EXPECT_EQ(123ms, looper->getTimerTickInterval());
255 }
256
TEST(FunctionLooperTest,NoLoopCallbackInPacingMode)257 TEST(FunctionLooperTest, NoLoopCallbackInPacingMode) {
258 EventBase evb;
259 TimerHighRes::SharedPtr pacingTimer(TimerHighRes::newTimer(&evb, 1ms));
260 uint32_t loopCallbackRunCounter = 0, pacingRunCounter = 0;
261 auto runFunc = [&](bool fromTimer) {
262 if (!fromTimer) {
263 loopCallbackRunCounter++;
264 } else {
265 pacingRunCounter++;
266 }
267 };
268 auto pacingFunc = [&]() { return 3600000ms; };
269 FunctionLooper::Ptr looper(
270 new FunctionLooper(&evb, std::move(runFunc), LooperType::ReadLooper));
271 looper->setPacingTimer(std::move(pacingTimer));
272 looper->setPacingFunction(std::move(pacingFunc));
273 // bootstrap the looper
274 looper->run();
275 // this loop will schedule pacer not looper:
276 evb.loopOnce();
277 EXPECT_TRUE(looper->isScheduled());
278 EXPECT_FALSE(looper->isLoopCallbackScheduled());
279 looper->stop();
280 }
281
TEST(FunctionLooperTest,RunConditions)282 TEST(FunctionLooperTest, RunConditions) {
283 EventBase evb;
284 TimerHighRes::SharedPtr pacingTimer(TimerHighRes::newTimer(&evb, 1ms));
285 uint32_t loopCallbackRunCounter = 0, pacingRunCounter = 0;
286 FunctionLooper::Ptr* looperPtr = nullptr;
287 auto runFunc = [&](bool fromTimer) {
288 if (!fromTimer) {
289 loopCallbackRunCounter++;
290 } else {
291 pacingRunCounter++;
292 }
293 (*looperPtr)->run();
294 };
295 auto pacingFunc = [&]() { return 3600000ms; };
296 FunctionLooper::Ptr looper(
297 new FunctionLooper(&evb, std::move(runFunc), LooperType::ReadLooper));
298 looperPtr = &looper;
299 looper->setPacingTimer(std::move(pacingTimer));
300 looper->setPacingFunction(std::move(pacingFunc));
301 // Nothing scheduled yet, this run will loop
302 looper->run();
303 evb.loopOnce();
304 EXPECT_EQ(0, pacingRunCounter);
305 EXPECT_EQ(1, loopCallbackRunCounter);
306
307 // run() inside runFunc didn't have effect. Loop again won't run anything:
308 evb.loopOnce();
309 EXPECT_EQ(0, pacingRunCounter);
310 EXPECT_EQ(1, loopCallbackRunCounter);
311
312 // Since pacing is scheduled, explicit run() outside of runFunc won't run
313 // either:
314 looper->run();
315 evb.loopOnce();
316 EXPECT_EQ(0, pacingRunCounter);
317 EXPECT_EQ(1, loopCallbackRunCounter);
318
319 looper->stop();
320 }
321 } // namespace test
322 } // namespace quic
323