]>
Commit | Line | Data |
---|---|---|
20effc67 TL |
1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
2 | // This source code is licensed under both the GPLv2 (found in the | |
3 | // COPYING file in the root directory) and Apache 2.0 License | |
4 | // (found in the LICENSE.Apache file in the root directory). | |
5 | ||
6 | #include "util/timer.h" | |
7 | ||
8 | #include "db/db_test_util.h" | |
9 | ||
10 | namespace ROCKSDB_NAMESPACE { | |
11 | ||
12 | class TimerTest : public testing::Test { | |
13 | public: | |
14 | TimerTest() : mock_env_(new MockTimeEnv(Env::Default())) {} | |
15 | ||
16 | protected: | |
17 | std::unique_ptr<MockTimeEnv> mock_env_; | |
18 | ||
19 | void SetUp() override { mock_env_->InstallTimedWaitFixCallback(); } | |
20 | ||
21 | const int kUsPerSec = 1000000; | |
22 | }; | |
23 | ||
24 | TEST_F(TimerTest, SingleScheduleOnce) { | |
25 | const int kInitDelayUs = 1 * kUsPerSec; | |
26 | Timer timer(mock_env_.get()); | |
27 | ||
28 | int count = 0; | |
29 | timer.Add([&] { count++; }, "fn_sch_test", kInitDelayUs, 0); | |
30 | ||
31 | ASSERT_TRUE(timer.Start()); | |
32 | ||
33 | ASSERT_EQ(0, count); | |
34 | // Wait for execution to finish | |
35 | timer.TEST_WaitForRun( | |
36 | [&] { mock_env_->MockSleepForMicroseconds(kInitDelayUs); }); | |
37 | ASSERT_EQ(1, count); | |
38 | ||
39 | ASSERT_TRUE(timer.Shutdown()); | |
40 | } | |
41 | ||
42 | TEST_F(TimerTest, MultipleScheduleOnce) { | |
43 | const int kInitDelay1Us = 1 * kUsPerSec; | |
44 | const int kInitDelay2Us = 3 * kUsPerSec; | |
45 | Timer timer(mock_env_.get()); | |
46 | ||
47 | int count1 = 0; | |
48 | timer.Add([&] { count1++; }, "fn_sch_test1", kInitDelay1Us, 0); | |
49 | ||
50 | int count2 = 0; | |
51 | timer.Add([&] { count2++; }, "fn_sch_test2", kInitDelay2Us, 0); | |
52 | ||
53 | ASSERT_TRUE(timer.Start()); | |
54 | ASSERT_EQ(0, count1); | |
55 | ASSERT_EQ(0, count2); | |
56 | ||
57 | timer.TEST_WaitForRun( | |
58 | [&] { mock_env_->MockSleepForMicroseconds(kInitDelay1Us); }); | |
59 | ||
60 | ASSERT_EQ(1, count1); | |
61 | ASSERT_EQ(0, count2); | |
62 | ||
63 | timer.TEST_WaitForRun([&] { | |
64 | mock_env_->MockSleepForMicroseconds(kInitDelay2Us - kInitDelay1Us); | |
65 | }); | |
66 | ||
67 | ASSERT_EQ(1, count1); | |
68 | ASSERT_EQ(1, count2); | |
69 | ||
70 | ASSERT_TRUE(timer.Shutdown()); | |
71 | } | |
72 | ||
73 | TEST_F(TimerTest, SingleScheduleRepeatedly) { | |
74 | const int kIterations = 5; | |
75 | const int kInitDelayUs = 1 * kUsPerSec; | |
76 | const int kRepeatUs = 1 * kUsPerSec; | |
77 | ||
78 | Timer timer(mock_env_.get()); | |
79 | int count = 0; | |
80 | timer.Add([&] { count++; }, "fn_sch_test", kInitDelayUs, kRepeatUs); | |
81 | ||
82 | ASSERT_TRUE(timer.Start()); | |
83 | ASSERT_EQ(0, count); | |
84 | ||
85 | timer.TEST_WaitForRun( | |
86 | [&] { mock_env_->MockSleepForMicroseconds(kInitDelayUs); }); | |
87 | ||
88 | ASSERT_EQ(1, count); | |
89 | ||
90 | // Wait for execution to finish | |
91 | for (int i = 1; i < kIterations; i++) { | |
92 | timer.TEST_WaitForRun( | |
93 | [&] { mock_env_->MockSleepForMicroseconds(kRepeatUs); }); | |
94 | } | |
95 | ASSERT_EQ(kIterations, count); | |
96 | ||
97 | ASSERT_TRUE(timer.Shutdown()); | |
98 | } | |
99 | ||
100 | TEST_F(TimerTest, MultipleScheduleRepeatedly) { | |
101 | const int kIterations = 5; | |
102 | const int kInitDelay1Us = 0 * kUsPerSec; | |
103 | const int kInitDelay2Us = 1 * kUsPerSec; | |
104 | const int kInitDelay3Us = 0 * kUsPerSec; | |
105 | const int kRepeatUs = 2 * kUsPerSec; | |
106 | const int kLargeRepeatUs = 100 * kUsPerSec; | |
107 | ||
108 | Timer timer(mock_env_.get()); | |
109 | ||
110 | int count1 = 0; | |
111 | timer.Add([&] { count1++; }, "fn_sch_test1", kInitDelay1Us, kRepeatUs); | |
112 | ||
113 | int count2 = 0; | |
114 | timer.Add([&] { count2++; }, "fn_sch_test2", kInitDelay2Us, kRepeatUs); | |
115 | ||
116 | // Add a function with relatively large repeat interval | |
117 | int count3 = 0; | |
118 | timer.Add([&] { count3++; }, "fn_sch_test3", kInitDelay3Us, kLargeRepeatUs); | |
119 | ||
120 | ASSERT_TRUE(timer.Start()); | |
121 | ||
122 | ASSERT_EQ(0, count2); | |
123 | // Wait for execution to finish | |
124 | for (int i = 1; i < kIterations * (kRepeatUs / kUsPerSec); i++) { | |
125 | timer.TEST_WaitForRun( | |
126 | [&] { mock_env_->MockSleepForMicroseconds(1 * kUsPerSec); }); | |
127 | ASSERT_EQ((i + 2) / (kRepeatUs / kUsPerSec), count1); | |
128 | ASSERT_EQ((i + 1) / (kRepeatUs / kUsPerSec), count2); | |
129 | ||
130 | // large interval function should only run once (the first one). | |
131 | ASSERT_EQ(1, count3); | |
132 | } | |
133 | ||
134 | timer.Cancel("fn_sch_test1"); | |
135 | ||
136 | // Wait for execution to finish | |
137 | timer.TEST_WaitForRun( | |
138 | [&] { mock_env_->MockSleepForMicroseconds(1 * kUsPerSec); }); | |
139 | ASSERT_EQ(kIterations, count1); | |
140 | ASSERT_EQ(kIterations, count2); | |
141 | ASSERT_EQ(1, count3); | |
142 | ||
143 | timer.Cancel("fn_sch_test2"); | |
144 | ||
145 | ASSERT_EQ(kIterations, count1); | |
146 | ASSERT_EQ(kIterations, count2); | |
147 | ||
148 | // execute the long interval one | |
149 | timer.TEST_WaitForRun([&] { | |
150 | mock_env_->MockSleepForMicroseconds( | |
151 | kLargeRepeatUs - static_cast<int>(mock_env_->NowMicros())); | |
152 | }); | |
153 | ASSERT_EQ(2, count3); | |
154 | ||
155 | ASSERT_TRUE(timer.Shutdown()); | |
156 | } | |
157 | ||
158 | TEST_F(TimerTest, AddAfterStartTest) { | |
159 | const int kIterations = 5; | |
160 | const int kInitDelayUs = 1 * kUsPerSec; | |
161 | const int kRepeatUs = 1 * kUsPerSec; | |
162 | ||
163 | // wait timer to run and then add a new job | |
164 | SyncPoint::GetInstance()->LoadDependency( | |
165 | {{"Timer::Run::Waiting", "TimerTest:AddAfterStartTest:1"}}); | |
166 | SyncPoint::GetInstance()->EnableProcessing(); | |
167 | ||
168 | Timer timer(mock_env_.get()); | |
169 | ||
170 | ASSERT_TRUE(timer.Start()); | |
171 | ||
172 | TEST_SYNC_POINT("TimerTest:AddAfterStartTest:1"); | |
173 | int count = 0; | |
174 | timer.Add([&] { count++; }, "fn_sch_test", kInitDelayUs, kRepeatUs); | |
175 | ASSERT_EQ(0, count); | |
176 | // Wait for execution to finish | |
177 | timer.TEST_WaitForRun( | |
178 | [&] { mock_env_->MockSleepForMicroseconds(kInitDelayUs); }); | |
179 | ASSERT_EQ(1, count); | |
180 | ||
181 | for (int i = 1; i < kIterations; i++) { | |
182 | timer.TEST_WaitForRun( | |
183 | [&] { mock_env_->MockSleepForMicroseconds(kRepeatUs); }); | |
184 | } | |
185 | ASSERT_EQ(kIterations, count); | |
186 | ||
187 | ASSERT_TRUE(timer.Shutdown()); | |
188 | } | |
189 | ||
190 | TEST_F(TimerTest, CancelRunningTask) { | |
191 | static constexpr char kTestFuncName[] = "test_func"; | |
192 | const int kRepeatUs = 1 * kUsPerSec; | |
193 | Timer timer(mock_env_.get()); | |
194 | ASSERT_TRUE(timer.Start()); | |
195 | int* value = new int; | |
196 | *value = 0; | |
197 | SyncPoint::GetInstance()->DisableProcessing(); | |
198 | SyncPoint::GetInstance()->LoadDependency({ | |
199 | {"TimerTest::CancelRunningTask:test_func:0", | |
200 | "TimerTest::CancelRunningTask:BeforeCancel"}, | |
201 | {"Timer::WaitForTaskCompleteIfNecessary:TaskExecuting", | |
202 | "TimerTest::CancelRunningTask:test_func:1"}, | |
203 | }); | |
204 | SyncPoint::GetInstance()->EnableProcessing(); | |
205 | timer.Add( | |
206 | [&]() { | |
207 | *value = 1; | |
208 | TEST_SYNC_POINT("TimerTest::CancelRunningTask:test_func:0"); | |
209 | TEST_SYNC_POINT("TimerTest::CancelRunningTask:test_func:1"); | |
210 | }, | |
211 | kTestFuncName, 0, kRepeatUs); | |
212 | port::Thread control_thr([&]() { | |
213 | TEST_SYNC_POINT("TimerTest::CancelRunningTask:BeforeCancel"); | |
214 | timer.Cancel(kTestFuncName); | |
215 | // Verify that *value has been set to 1. | |
216 | ASSERT_EQ(1, *value); | |
217 | delete value; | |
218 | value = nullptr; | |
219 | }); | |
220 | mock_env_->MockSleepForMicroseconds(kRepeatUs); | |
221 | control_thr.join(); | |
222 | ASSERT_TRUE(timer.Shutdown()); | |
223 | } | |
224 | ||
225 | TEST_F(TimerTest, ShutdownRunningTask) { | |
226 | const int kRepeatUs = 1 * kUsPerSec; | |
227 | constexpr char kTestFunc1Name[] = "test_func1"; | |
228 | constexpr char kTestFunc2Name[] = "test_func2"; | |
229 | Timer timer(mock_env_.get()); | |
230 | ||
231 | SyncPoint::GetInstance()->DisableProcessing(); | |
232 | SyncPoint::GetInstance()->LoadDependency({ | |
233 | {"TimerTest::ShutdownRunningTest:test_func:0", | |
234 | "TimerTest::ShutdownRunningTest:BeforeShutdown"}, | |
235 | {"Timer::WaitForTaskCompleteIfNecessary:TaskExecuting", | |
236 | "TimerTest::ShutdownRunningTest:test_func:1"}, | |
237 | }); | |
238 | SyncPoint::GetInstance()->EnableProcessing(); | |
239 | ||
240 | ASSERT_TRUE(timer.Start()); | |
241 | ||
242 | int* value = new int; | |
243 | *value = 0; | |
244 | timer.Add( | |
245 | [&]() { | |
246 | TEST_SYNC_POINT("TimerTest::ShutdownRunningTest:test_func:0"); | |
247 | *value = 1; | |
248 | TEST_SYNC_POINT("TimerTest::ShutdownRunningTest:test_func:1"); | |
249 | }, | |
250 | kTestFunc1Name, 0, kRepeatUs); | |
251 | ||
252 | timer.Add([&]() { ++(*value); }, kTestFunc2Name, 0, kRepeatUs); | |
253 | ||
254 | port::Thread control_thr([&]() { | |
255 | TEST_SYNC_POINT("TimerTest::ShutdownRunningTest:BeforeShutdown"); | |
256 | timer.Shutdown(); | |
257 | }); | |
258 | mock_env_->MockSleepForMicroseconds(kRepeatUs); | |
259 | control_thr.join(); | |
260 | delete value; | |
261 | } | |
262 | ||
263 | TEST_F(TimerTest, AddSameFuncName) { | |
264 | const int kInitDelayUs = 1 * kUsPerSec; | |
265 | const int kRepeat1Us = 5 * kUsPerSec; | |
266 | const int kRepeat2Us = 4 * kUsPerSec; | |
267 | ||
268 | Timer timer(mock_env_.get()); | |
269 | ASSERT_TRUE(timer.Start()); | |
270 | ||
271 | int func_counter1 = 0; | |
272 | timer.Add([&] { func_counter1++; }, "duplicated_func", kInitDelayUs, | |
273 | kRepeat1Us); | |
274 | ||
275 | int func2_counter = 0; | |
276 | timer.Add([&] { func2_counter++; }, "func2", kInitDelayUs, kRepeat2Us); | |
277 | ||
278 | // New function with the same name should override the existing one | |
279 | int func_counter2 = 0; | |
280 | timer.Add([&] { func_counter2++; }, "duplicated_func", kInitDelayUs, | |
281 | kRepeat1Us); | |
282 | ||
283 | ASSERT_EQ(0, func_counter1); | |
284 | ASSERT_EQ(0, func2_counter); | |
285 | ASSERT_EQ(0, func_counter2); | |
286 | ||
287 | timer.TEST_WaitForRun( | |
288 | [&] { mock_env_->MockSleepForMicroseconds(kInitDelayUs); }); | |
289 | ||
290 | ASSERT_EQ(0, func_counter1); | |
291 | ASSERT_EQ(1, func2_counter); | |
292 | ASSERT_EQ(1, func_counter2); | |
293 | ||
294 | timer.TEST_WaitForRun( | |
295 | [&] { mock_env_->MockSleepForMicroseconds(kRepeat1Us); }); | |
296 | ||
297 | ASSERT_EQ(0, func_counter1); | |
298 | ASSERT_EQ(2, func2_counter); | |
299 | ASSERT_EQ(2, func_counter2); | |
300 | ||
301 | ASSERT_TRUE(timer.Shutdown()); | |
302 | } | |
303 | ||
304 | TEST_F(TimerTest, RepeatIntervalWithFuncRunningTime) { | |
305 | const int kInitDelayUs = 1 * kUsPerSec; | |
306 | const int kRepeatUs = 5 * kUsPerSec; | |
307 | const int kFuncRunningTimeUs = 1 * kUsPerSec; | |
308 | ||
309 | Timer timer(mock_env_.get()); | |
310 | ASSERT_TRUE(timer.Start()); | |
311 | ||
312 | int func_counter = 0; | |
313 | timer.Add( | |
314 | [&] { | |
315 | mock_env_->MockSleepForMicroseconds(kFuncRunningTimeUs); | |
316 | func_counter++; | |
317 | }, | |
318 | "func", kInitDelayUs, kRepeatUs); | |
319 | ||
320 | ASSERT_EQ(0, func_counter); | |
321 | timer.TEST_WaitForRun( | |
322 | [&] { mock_env_->MockSleepForMicroseconds(kInitDelayUs); }); | |
323 | ASSERT_EQ(1, func_counter); | |
324 | ASSERT_EQ(kInitDelayUs + kFuncRunningTimeUs, mock_env_->NowMicros()); | |
325 | ||
326 | // After repeat interval time, the function is not executed, as running | |
327 | // the function takes some time (`kFuncRunningTimeSec`). The repeat interval | |
328 | // is the time between ending time of the last call and starting time of the | |
329 | // next call. | |
330 | uint64_t next_abs_interval_time_us = kInitDelayUs + kRepeatUs; | |
331 | timer.TEST_WaitForRun([&] { | |
332 | mock_env_->set_current_time(next_abs_interval_time_us / kUsPerSec); | |
333 | }); | |
334 | ASSERT_EQ(1, func_counter); | |
335 | ||
336 | // After the function running time, it's executed again | |
337 | timer.TEST_WaitForRun( | |
338 | [&] { mock_env_->MockSleepForMicroseconds(kFuncRunningTimeUs); }); | |
339 | ASSERT_EQ(2, func_counter); | |
340 | ||
341 | ASSERT_TRUE(timer.Shutdown()); | |
342 | } | |
343 | ||
344 | TEST_F(TimerTest, DestroyRunningTimer) { | |
345 | const int kInitDelayUs = 1 * kUsPerSec; | |
346 | const int kRepeatUs = 1 * kUsPerSec; | |
347 | ||
348 | auto timer_ptr = new Timer(mock_env_.get()); | |
349 | ||
350 | int count = 0; | |
351 | timer_ptr->Add([&] { count++; }, "fn_sch_test", kInitDelayUs, kRepeatUs); | |
352 | ASSERT_TRUE(timer_ptr->Start()); | |
353 | ||
354 | timer_ptr->TEST_WaitForRun( | |
355 | [&] { mock_env_->MockSleepForMicroseconds(kInitDelayUs); }); | |
356 | ||
357 | // delete a running timer should not cause any exception | |
358 | delete timer_ptr; | |
359 | } | |
360 | ||
361 | TEST_F(TimerTest, DestroyTimerWithRunningFunc) { | |
362 | const int kRepeatUs = 1 * kUsPerSec; | |
363 | auto timer_ptr = new Timer(mock_env_.get()); | |
364 | ||
365 | SyncPoint::GetInstance()->DisableProcessing(); | |
366 | SyncPoint::GetInstance()->LoadDependency({ | |
367 | {"TimerTest::DestroyTimerWithRunningFunc:test_func:0", | |
368 | "TimerTest::DestroyTimerWithRunningFunc:BeforeDelete"}, | |
369 | {"Timer::WaitForTaskCompleteIfNecessary:TaskExecuting", | |
370 | "TimerTest::DestroyTimerWithRunningFunc:test_func:1"}, | |
371 | }); | |
372 | SyncPoint::GetInstance()->EnableProcessing(); | |
373 | ||
374 | ASSERT_TRUE(timer_ptr->Start()); | |
375 | ||
376 | int count = 0; | |
377 | timer_ptr->Add( | |
378 | [&]() { | |
379 | TEST_SYNC_POINT("TimerTest::DestroyTimerWithRunningFunc:test_func:0"); | |
380 | count++; | |
381 | TEST_SYNC_POINT("TimerTest::DestroyTimerWithRunningFunc:test_func:1"); | |
382 | }, | |
383 | "fn_running_test", 0, kRepeatUs); | |
384 | ||
385 | port::Thread control_thr([&] { | |
386 | TEST_SYNC_POINT("TimerTest::DestroyTimerWithRunningFunc:BeforeDelete"); | |
387 | delete timer_ptr; | |
388 | }); | |
389 | mock_env_->MockSleepForMicroseconds(kRepeatUs); | |
390 | control_thr.join(); | |
391 | } | |
392 | ||
393 | } // namespace ROCKSDB_NAMESPACE | |
394 | ||
395 | int main(int argc, char** argv) { | |
396 | ::testing::InitGoogleTest(&argc, argv); | |
397 | ||
398 | return RUN_ALL_TESTS(); | |
399 | } |