1 | /* |
2 | * Copyright 2014-present Facebook, Inc. |
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 <folly/Singleton.h> |
18 | #include <folly/executors/ManualExecutor.h> |
19 | #include <folly/futures/Future.h> |
20 | #include <folly/futures/ThreadWheelTimekeeper.h> |
21 | #include <folly/portability/GTest.h> |
22 | |
23 | using namespace folly; |
24 | using std::chrono::milliseconds; |
25 | |
26 | std::chrono::milliseconds const zero_ms(0); |
27 | std::chrono::milliseconds const one_ms(1); |
28 | std::chrono::milliseconds const awhile(10); |
29 | std::chrono::seconds const too_long(10); |
30 | |
31 | std::chrono::steady_clock::time_point now() { |
32 | return std::chrono::steady_clock::now(); |
33 | } |
34 | |
35 | struct TimekeeperFixture : public testing::Test { |
36 | TimekeeperFixture() : timeLord_(folly::detail::getTimekeeperSingleton()) {} |
37 | |
38 | std::shared_ptr<Timekeeper> timeLord_; |
39 | }; |
40 | |
41 | TEST_F(TimekeeperFixture, after) { |
42 | auto t1 = now(); |
43 | auto f = timeLord_->after(awhile); |
44 | EXPECT_FALSE(f.isReady()); |
45 | std::move(f).get(); |
46 | auto t2 = now(); |
47 | |
48 | EXPECT_GE(t2 - t1, awhile); |
49 | } |
50 | |
51 | TEST(Timekeeper, futureGet) { |
52 | Promise<int> p; |
53 | auto t = std::thread([&] { p.setValue(42); }); |
54 | EXPECT_EQ(42, p.getFuture().get()); |
55 | t.join(); |
56 | } |
57 | |
58 | TEST(Timekeeper, futureGetBeforeTimeout) { |
59 | Promise<int> p; |
60 | auto t = std::thread([&] { p.setValue(42); }); |
61 | // Technically this is a race and if the test server is REALLY overloaded |
62 | // and it takes more than a second to do that thread it could be flaky. But |
63 | // I want a low timeout (in human terms) so if this regresses and someone |
64 | // runs it by hand they're not sitting there forever wondering why it's |
65 | // blocked, and get a useful error message instead. If it does get flaky, |
66 | // empirically increase the timeout to the point where it's very improbable. |
67 | EXPECT_EQ(42, p.getFuture().get(std::chrono::seconds(2))); |
68 | t.join(); |
69 | } |
70 | |
71 | TEST(Timekeeper, futureGetTimeout) { |
72 | Promise<int> p; |
73 | EXPECT_THROW(p.getFuture().get(one_ms), folly::FutureTimeout); |
74 | } |
75 | |
76 | TEST(Timekeeper, futureSleep) { |
77 | auto t1 = now(); |
78 | futures::sleep(one_ms).get(); |
79 | EXPECT_GE(now() - t1, one_ms); |
80 | } |
81 | |
82 | TEST(Timekeeper, futureSleepHandlesNullTimekeeperSingleton) { |
83 | Singleton<ThreadWheelTimekeeper>::make_mock([] { return nullptr; }); |
84 | SCOPE_EXIT { |
85 | Singleton<ThreadWheelTimekeeper>::make_mock(); |
86 | }; |
87 | EXPECT_THROW(futures::sleep(one_ms).get(), FutureNoTimekeeper); |
88 | } |
89 | |
90 | TEST(Timekeeper, futureWithinHandlesNullTimekeeperSingleton) { |
91 | Singleton<ThreadWheelTimekeeper>::make_mock([] { return nullptr; }); |
92 | SCOPE_EXIT { |
93 | Singleton<ThreadWheelTimekeeper>::make_mock(); |
94 | }; |
95 | Promise<int> p; |
96 | auto f = p.getFuture().within(one_ms); |
97 | EXPECT_THROW(std::move(f).get(), FutureNoTimekeeper); |
98 | } |
99 | |
100 | TEST(Timekeeper, semiFutureWithinHandlesNullTimekeeperSingleton) { |
101 | Singleton<ThreadWheelTimekeeper>::make_mock([] { return nullptr; }); |
102 | SCOPE_EXIT { |
103 | Singleton<ThreadWheelTimekeeper>::make_mock(); |
104 | }; |
105 | Promise<int> p; |
106 | auto f = p.getSemiFuture().within(one_ms); |
107 | EXPECT_THROW(std::move(f).get(), FutureNoTimekeeper); |
108 | } |
109 | |
110 | TEST(Timekeeper, futureDelayed) { |
111 | auto t1 = now(); |
112 | auto dur = makeFuture() |
113 | .delayed(one_ms) |
114 | .thenValue([=](auto&&) { return now() - t1; }) |
115 | .get(); |
116 | |
117 | EXPECT_GE(dur, one_ms); |
118 | } |
119 | |
120 | TEST(Timekeeper, semiFutureDelayed) { |
121 | auto t1 = now(); |
122 | auto dur = makeSemiFuture() |
123 | .delayed(one_ms) |
124 | .toUnsafeFuture() |
125 | .thenValue([=](auto&&) { return now() - t1; }) |
126 | .get(); |
127 | |
128 | EXPECT_GE(dur, one_ms); |
129 | } |
130 | |
131 | TEST(Timekeeper, futureDelayedUnsafe) { |
132 | auto t1 = now(); |
133 | auto dur = makeFuture() |
134 | .delayedUnsafe(one_ms) |
135 | .thenValue([=](auto&&) { return now() - t1; }) |
136 | .get(); |
137 | |
138 | EXPECT_GE(dur, one_ms); |
139 | } |
140 | |
141 | TEST(Timekeeper, futureDelayedStickyExecutor) { |
142 | // Check that delayed without an executor binds the inline executor. |
143 | { |
144 | auto t1 = now(); |
145 | class TimekeeperHelper : public ThreadWheelTimekeeper { |
146 | public: |
147 | std::thread::id get_thread_id() { |
148 | return thread_.get_id(); |
149 | } |
150 | }; |
151 | TimekeeperHelper tk; |
152 | std::thread::id timekeeper_thread_id = tk.get_thread_id(); |
153 | std::thread::id task_thread_id{}; |
154 | auto dur = makeFuture() |
155 | .delayed(one_ms, &tk) |
156 | .thenValue([=, &task_thread_id](auto&&) { |
157 | task_thread_id = std::this_thread::get_id(); |
158 | return now() - t1; |
159 | }) |
160 | .get(); |
161 | |
162 | EXPECT_GE(dur, one_ms); |
163 | EXPECT_EQ(timekeeper_thread_id, task_thread_id); |
164 | } |
165 | |
166 | // Check that delayed applied to an executor returns a future that binds |
167 | // to the same executor as was input. |
168 | { |
169 | auto t1 = now(); |
170 | std::thread::id driver_thread_id{}; |
171 | std::thread::id first_task_thread_id{}; |
172 | std::thread::id second_task_thread_id{}; |
173 | folly::ManualExecutor me; |
174 | std::atomic<bool> stop_signal{false}; |
175 | std::thread me_driver{[&me, &driver_thread_id, &stop_signal] { |
176 | driver_thread_id = std::this_thread::get_id(); |
177 | while (!stop_signal) { |
178 | me.run(); |
179 | } |
180 | }}; |
181 | auto dur = makeSemiFuture() |
182 | .via(&me) |
183 | .thenValue([&first_task_thread_id](auto&&) { |
184 | first_task_thread_id = std::this_thread::get_id(); |
185 | }) |
186 | .delayed(one_ms) |
187 | .thenValue([=, &second_task_thread_id](auto&&) { |
188 | second_task_thread_id = std::this_thread::get_id(); |
189 | return now() - t1; |
190 | }) |
191 | .get(); |
192 | stop_signal = true; |
193 | me_driver.join(); |
194 | EXPECT_GE(dur, one_ms); |
195 | EXPECT_EQ(driver_thread_id, first_task_thread_id); |
196 | EXPECT_EQ(driver_thread_id, second_task_thread_id); |
197 | } |
198 | } |
199 | |
200 | TEST(Timekeeper, futureWithinThrows) { |
201 | Promise<int> p; |
202 | auto f = |
203 | p.getFuture().within(one_ms).onError([](FutureTimeout&) { return -1; }); |
204 | |
205 | EXPECT_EQ(-1, std::move(f).get()); |
206 | } |
207 | |
208 | TEST(Timekeeper, semiFutureWithinThrows) { |
209 | Promise<int> p; |
210 | auto f = p.getSemiFuture().within(one_ms).toUnsafeFuture().onError( |
211 | [](FutureTimeout&) { return -1; }); |
212 | |
213 | EXPECT_EQ(-1, std::move(f).get()); |
214 | } |
215 | |
216 | TEST(Timekeeper, futureWithinAlreadyComplete) { |
217 | auto f = |
218 | makeFuture(42).within(one_ms).onError([&](FutureTimeout&) { return -1; }); |
219 | |
220 | EXPECT_EQ(42, std::move(f).get()); |
221 | } |
222 | |
223 | TEST(Timekeeper, semiFutureWithinAlreadyComplete) { |
224 | auto f = makeSemiFuture(42).within(one_ms).toUnsafeFuture().onError( |
225 | [&](FutureTimeout&) { return -1; }); |
226 | |
227 | EXPECT_EQ(42, std::move(f).get()); |
228 | } |
229 | |
230 | TEST(Timekeeper, futureWithinFinishesInTime) { |
231 | Promise<int> p; |
232 | auto f = p.getFuture() |
233 | .within(std::chrono::minutes(1)) |
234 | .onError([&](FutureTimeout&) { return -1; }); |
235 | p.setValue(42); |
236 | |
237 | EXPECT_EQ(42, std::move(f).get()); |
238 | } |
239 | |
240 | TEST(Timekeeper, semiFutureWithinFinishesInTime) { |
241 | Promise<int> p; |
242 | auto f = p.getSemiFuture() |
243 | .within(std::chrono::minutes(1)) |
244 | .toUnsafeFuture() |
245 | .onError([&](FutureTimeout&) { return -1; }); |
246 | p.setValue(42); |
247 | |
248 | EXPECT_EQ(42, std::move(f).get()); |
249 | } |
250 | |
251 | TEST(Timekeeper, futureWithinVoidSpecialization) { |
252 | makeFuture().within(one_ms); |
253 | } |
254 | |
255 | TEST(Timekeeper, semiFutureWithinVoidSpecialization) { |
256 | makeSemiFuture().within(one_ms); |
257 | } |
258 | |
259 | TEST(Timekeeper, futureWithinException) { |
260 | Promise<Unit> p; |
261 | auto f = p.getFuture().within(awhile, std::runtime_error("expected" )); |
262 | EXPECT_THROW(std::move(f).get(), std::runtime_error); |
263 | } |
264 | |
265 | TEST(Timekeeper, semiFutureWithinException) { |
266 | Promise<Unit> p; |
267 | auto f = p.getSemiFuture().within(awhile, std::runtime_error("expected" )); |
268 | EXPECT_THROW(std::move(f).get(), std::runtime_error); |
269 | } |
270 | |
271 | TEST(Timekeeper, onTimeout) { |
272 | bool flag = false; |
273 | makeFuture(42) |
274 | .delayed(10 * one_ms) |
275 | .onTimeout( |
276 | zero_ms, |
277 | [&] { |
278 | flag = true; |
279 | return -1; |
280 | }) |
281 | .get(); |
282 | EXPECT_TRUE(flag); |
283 | } |
284 | |
285 | TEST(Timekeeper, onTimeoutComplete) { |
286 | bool flag = false; |
287 | makeFuture(42) |
288 | .onTimeout( |
289 | zero_ms, |
290 | [&] { |
291 | flag = true; |
292 | return -1; |
293 | }) |
294 | .get(); |
295 | EXPECT_FALSE(flag); |
296 | } |
297 | |
298 | TEST(Timekeeper, onTimeoutReturnsFuture) { |
299 | bool flag = false; |
300 | makeFuture(42) |
301 | .delayed(10 * one_ms) |
302 | .onTimeout( |
303 | zero_ms, |
304 | [&] { |
305 | flag = true; |
306 | return makeFuture(-1); |
307 | }) |
308 | .get(); |
309 | EXPECT_TRUE(flag); |
310 | } |
311 | |
312 | TEST(Timekeeper, onTimeoutVoid) { |
313 | makeFuture().delayed(one_ms).onTimeout(zero_ms, [&] {}); |
314 | makeFuture().delayed(one_ms).onTimeout(zero_ms, [&] { |
315 | return makeFuture<Unit>(std::runtime_error("expected" )); |
316 | }); |
317 | // just testing compilation here |
318 | } |
319 | |
320 | TEST(Timekeeper, interruptDoesntCrash) { |
321 | auto f = futures::sleep(too_long); |
322 | f.cancel(); |
323 | } |
324 | |
325 | TEST(Timekeeper, chainedInterruptTest) { |
326 | bool test = false; |
327 | auto f = |
328 | futures::sleep(milliseconds(100)).thenValue([&](auto&&) { test = true; }); |
329 | f.cancel(); |
330 | f.wait(); |
331 | EXPECT_FALSE(test); |
332 | } |
333 | |
334 | TEST(Timekeeper, futureWithinChainedInterruptTest) { |
335 | bool test = false; |
336 | Promise<Unit> p; |
337 | p.setInterruptHandler([&test, &p](const exception_wrapper& ex) { |
338 | ex.handle( |
339 | [&test](const FutureCancellation& /* cancellation */) { test = true; }); |
340 | p.setException(ex); |
341 | }); |
342 | auto f = p.getFuture().within(milliseconds(100)); |
343 | EXPECT_FALSE(test) << "Sanity check" ; |
344 | f.cancel(); |
345 | f.wait(); |
346 | EXPECT_TRUE(test); |
347 | } |
348 | |
349 | TEST(Timekeeper, semiFutureWithinChainedInterruptTest) { |
350 | bool test = false; |
351 | Promise<Unit> p; |
352 | p.setInterruptHandler([&test, &p](const exception_wrapper& ex) { |
353 | ex.handle( |
354 | [&test](const FutureCancellation& /* cancellation */) { test = true; }); |
355 | p.setException(ex); |
356 | }); |
357 | auto f = p.getSemiFuture().within(milliseconds(100)); |
358 | EXPECT_FALSE(test) << "Sanity check" ; |
359 | f.cancel(); |
360 | f.wait(); |
361 | EXPECT_TRUE(test); |
362 | } |
363 | |
364 | TEST(Timekeeper, executor) { |
365 | class ExecutorTester : public Executor { |
366 | public: |
367 | void add(Func f) override { |
368 | count++; |
369 | f(); |
370 | } |
371 | std::atomic<int> count{0}; |
372 | }; |
373 | |
374 | Promise<Unit> p; |
375 | ExecutorTester tester; |
376 | auto f = p.getFuture().via(&tester).within(one_ms).thenValue([&](auto&&) {}); |
377 | p.setValue(); |
378 | f.wait(); |
379 | EXPECT_EQ(2, tester.count); |
380 | } |
381 | |
382 | // TODO(5921764) |
383 | /* |
384 | TEST(Timekeeper, onTimeoutPropagates) { |
385 | bool flag = false; |
386 | EXPECT_THROW( |
387 | makeFuture(42).delayed(one_ms) |
388 | .onTimeout(zero_ms, [&]{ flag = true; }) |
389 | .get(), |
390 | FutureTimeout); |
391 | EXPECT_TRUE(flag); |
392 | } |
393 | */ |
394 | |
395 | TEST_F(TimekeeperFixture, atBeforeNow) { |
396 | auto f = timeLord_->at(now() - too_long); |
397 | EXPECT_TRUE(f.isReady()); |
398 | EXPECT_FALSE(f.hasException()); |
399 | } |
400 | |
401 | TEST_F(TimekeeperFixture, howToCastDuration) { |
402 | // I'm not sure whether this rounds up or down but it's irrelevant for the |
403 | // purpose of this example. |
404 | auto f = timeLord_->after( |
405 | std::chrono::duration_cast<Duration>(std::chrono::nanoseconds(1))); |
406 | } |
407 | |
408 | TEST_F(TimekeeperFixture, destruction) { |
409 | folly::Optional<ThreadWheelTimekeeper> tk; |
410 | tk.emplace(); |
411 | auto f = tk->after(std::chrono::seconds(10)); |
412 | EXPECT_FALSE(f.isReady()); |
413 | tk.clear(); |
414 | EXPECT_TRUE(f.isReady()); |
415 | EXPECT_TRUE(f.hasException()); |
416 | } |
417 | |