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
23using namespace folly;
24using std::chrono::milliseconds;
25
26std::chrono::milliseconds const zero_ms(0);
27std::chrono::milliseconds const one_ms(1);
28std::chrono::milliseconds const awhile(10);
29std::chrono::seconds const too_long(10);
30
31std::chrono::steady_clock::time_point now() {
32 return std::chrono::steady_clock::now();
33}
34
35struct TimekeeperFixture : public testing::Test {
36 TimekeeperFixture() : timeLord_(folly::detail::getTimekeeperSingleton()) {}
37
38 std::shared_ptr<Timekeeper> timeLord_;
39};
40
41TEST_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
51TEST(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
58TEST(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
71TEST(Timekeeper, futureGetTimeout) {
72 Promise<int> p;
73 EXPECT_THROW(p.getFuture().get(one_ms), folly::FutureTimeout);
74}
75
76TEST(Timekeeper, futureSleep) {
77 auto t1 = now();
78 futures::sleep(one_ms).get();
79 EXPECT_GE(now() - t1, one_ms);
80}
81
82TEST(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
90TEST(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
100TEST(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
110TEST(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
120TEST(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
131TEST(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
141TEST(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
200TEST(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
208TEST(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
216TEST(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
223TEST(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
230TEST(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
240TEST(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
251TEST(Timekeeper, futureWithinVoidSpecialization) {
252 makeFuture().within(one_ms);
253}
254
255TEST(Timekeeper, semiFutureWithinVoidSpecialization) {
256 makeSemiFuture().within(one_ms);
257}
258
259TEST(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
265TEST(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
271TEST(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
285TEST(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
298TEST(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
312TEST(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
320TEST(Timekeeper, interruptDoesntCrash) {
321 auto f = futures::sleep(too_long);
322 f.cancel();
323}
324
325TEST(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
334TEST(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
349TEST(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
364TEST(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/*
384TEST(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
395TEST_F(TimekeeperFixture, atBeforeNow) {
396 auto f = timeLord_->at(now() - too_long);
397 EXPECT_TRUE(f.isReady());
398 EXPECT_FALSE(f.hasException());
399}
400
401TEST_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
408TEST_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