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/executors/InlineExecutor.h>
18#include <folly/executors/ManualExecutor.h>
19#include <folly/executors/QueuedImmediateExecutor.h>
20#include <folly/futures/Future.h>
21#include <folly/portability/GTest.h>
22#include <folly/synchronization/Baton.h>
23
24// TODO(jsedgwick) move this test to executors/test/ once the tested executors
25// have all moved
26
27using namespace folly;
28
29TEST(ManualExecutor, runIsStable) {
30 ManualExecutor x;
31 size_t count = 0;
32 auto f1 = [&]() { count++; };
33 auto f2 = [&]() {
34 x.add(f1);
35 x.add(f1);
36 };
37 x.add(f2);
38 x.run();
39 EXPECT_EQ(count, 0);
40
41 // ManualExecutor's destructor drains, so explicitly clear the two added by
42 // f2.
43 EXPECT_EQ(2, x.clear());
44}
45
46TEST(ManualExecutor, drainIsNotStable) {
47 ManualExecutor x;
48 size_t count = 0;
49 auto f1 = [&]() { count++; };
50 auto f2 = [&]() {
51 x.add(f1);
52 x.add(f1);
53 };
54 x.add(f2);
55 x.drain();
56 EXPECT_EQ(count, 2);
57}
58
59TEST(ManualExecutor, scheduleDur) {
60 ManualExecutor x;
61 size_t count = 0;
62 std::chrono::milliseconds dur{10};
63 x.schedule([&] { count++; }, dur);
64 EXPECT_EQ(count, 0);
65 x.run();
66 EXPECT_EQ(count, 0);
67 x.advance(dur / 2);
68 EXPECT_EQ(count, 0);
69 x.advance(dur / 2);
70 EXPECT_EQ(count, 1);
71}
72
73TEST(ManualExecutor, laterThingsDontBlockEarlierOnes) {
74 ManualExecutor x;
75 auto first = false;
76 std::chrono::milliseconds dur{10};
77 x.schedule([&] { first = true; }, dur);
78 x.schedule([] {}, 2 * dur);
79 EXPECT_FALSE(first);
80 x.advance(dur);
81 EXPECT_TRUE(first);
82}
83
84TEST(ManualExecutor, orderWillNotBeQuestioned) {
85 ManualExecutor x;
86 auto first = false;
87 auto second = false;
88 std::chrono::milliseconds dur{10};
89 x.schedule([&] { first = true; }, dur);
90 x.schedule([&] { second = true; }, 2 * dur);
91 EXPECT_FALSE(first);
92 EXPECT_FALSE(second);
93 x.advance(dur);
94 EXPECT_TRUE(first);
95 EXPECT_FALSE(second);
96 x.advance(dur);
97 EXPECT_TRUE(first);
98 EXPECT_TRUE(second);
99}
100
101TEST(ManualExecutor, evenWhenYouSkipAheadEventsRunInProperOrder) {
102 ManualExecutor x;
103 auto counter = 0;
104 auto first = 0;
105 auto second = 0;
106 std::chrono::milliseconds dur{10};
107 x.schedule([&] { first = ++counter; }, dur);
108 x.schedule([&] { second = ++counter; }, 2 * dur);
109 EXPECT_EQ(first, 0);
110 EXPECT_EQ(second, 0);
111 x.advance(3 * dur);
112 EXPECT_EQ(first, 1);
113 EXPECT_EQ(second, 2);
114}
115
116TEST(ManualExecutor, clockStartsAt0) {
117 ManualExecutor x;
118 EXPECT_EQ(x.now(), x.now().min());
119}
120
121TEST(ManualExecutor, scheduleAbs) {
122 ManualExecutor x;
123 size_t count = 0;
124 x.scheduleAt([&] { count++; }, x.now() + std::chrono::milliseconds(10));
125 EXPECT_EQ(count, 0);
126 x.advance(std::chrono::milliseconds(10));
127 EXPECT_EQ(count, 1);
128}
129
130TEST(ManualExecutor, advanceTo) {
131 ManualExecutor x;
132 size_t count = 0;
133 x.scheduleAt([&] { count++; }, std::chrono::steady_clock::now());
134 EXPECT_EQ(count, 0);
135 x.advanceTo(std::chrono::steady_clock::now());
136 EXPECT_EQ(count, 1);
137}
138
139TEST(ManualExecutor, advanceBack) {
140 ManualExecutor x;
141 size_t count = 0;
142 x.advance(std::chrono::microseconds(5));
143 x.schedule([&] { count++; }, std::chrono::microseconds(6));
144 EXPECT_EQ(count, 0);
145 x.advanceTo(x.now() - std::chrono::microseconds(1));
146 EXPECT_EQ(count, 0);
147}
148
149TEST(ManualExecutor, advanceNeg) {
150 ManualExecutor x;
151 size_t count = 0;
152 x.advance(std::chrono::microseconds(5));
153 x.schedule([&] { count++; }, std::chrono::microseconds(6));
154 EXPECT_EQ(count, 0);
155 x.advance(std::chrono::microseconds(-1));
156 EXPECT_EQ(count, 0);
157}
158
159TEST(ManualExecutor, waitForDoesNotDeadlock) {
160 ManualExecutor east, west;
161 folly::Baton<> baton;
162 auto f = makeFuture()
163 .via(&east)
164 .then([](Try<Unit>) { return makeFuture(); })
165 .via(&west);
166 std::thread t([&] {
167 baton.post();
168 west.waitFor(f);
169 });
170 baton.wait();
171 east.run();
172 t.join();
173}
174
175TEST(ManualExecutor, getViaDoesNotDeadlock) {
176 ManualExecutor east, west;
177 folly::Baton<> baton;
178 auto f = makeFuture()
179 .via(&east)
180 .then([](Try<Unit>) { return makeFuture(); })
181 .via(&west);
182 std::thread t([&] {
183 baton.post();
184 f.getVia(&west);
185 });
186 baton.wait();
187 east.run();
188 t.join();
189}
190
191TEST(ManualExecutor, clear) {
192 ManualExecutor x;
193 size_t count = 0;
194 x.add([&] { ++count; });
195 x.scheduleAt([&] { ++count; }, x.now() + std::chrono::milliseconds(10));
196 EXPECT_EQ(0, count);
197
198 x.clear();
199 x.advance(std::chrono::milliseconds(10));
200 x.run();
201 EXPECT_EQ(0, count);
202}
203
204TEST(ManualExecutor, drainsOnDestruction) {
205 size_t count = 0;
206 {
207 ManualExecutor x;
208 x.add([&] { ++count; });
209 }
210 EXPECT_EQ(1, count);
211}
212
213TEST(Executor, InlineExecutor) {
214 InlineExecutor x;
215 size_t counter = 0;
216 x.add([&] {
217 x.add([&] {
218 EXPECT_EQ(counter, 0);
219 counter++;
220 });
221 EXPECT_EQ(counter, 1);
222 counter++;
223 });
224 EXPECT_EQ(counter, 2);
225}
226
227TEST(Executor, QueuedImmediateExecutor) {
228 QueuedImmediateExecutor x;
229 size_t counter = 0;
230 x.add([&] {
231 x.add([&] {
232 EXPECT_EQ(1, counter);
233 counter++;
234 });
235 EXPECT_EQ(0, counter);
236 counter++;
237 });
238 EXPECT_EQ(2, counter);
239}
240
241TEST(Executor, Runnable) {
242 InlineExecutor x;
243 size_t counter = 0;
244 struct Runnable {
245 std::function<void()> fn;
246 void operator()() {
247 fn();
248 }
249 };
250 Runnable f;
251 f.fn = [&] { counter++; };
252 x.add(f);
253 EXPECT_EQ(counter, 1);
254}
255
256TEST(Executor, ThrowableThen) {
257 InlineExecutor x;
258 auto f = Future<Unit>().thenValue(
259 [](auto&&) { throw std::runtime_error("Faildog"); });
260
261 /*
262 auto f = Future<Unit>().via(&x).then([](){
263 throw std::runtime_error("Faildog");
264 });*/
265 EXPECT_THROW(f.value(), std::exception);
266}
267
268class CrappyExecutor : public Executor {
269 public:
270 void add(Func /* f */) override {
271 throw std::runtime_error("bad");
272 }
273};
274
275TEST(Executor, CrappyExecutor) {
276 CrappyExecutor x;
277 bool flag = false;
278 auto f = folly::via(&x).onError([&](std::runtime_error& e) {
279 EXPECT_STREQ("bad", e.what());
280 flag = true;
281 });
282 EXPECT_TRUE(flag);
283}
284
285class DoNothingExecutor : public Executor {
286 public:
287 void add(Func f) override {
288 storedFunc_ = std::move(f);
289 }
290
291 private:
292 Func storedFunc_;
293};
294
295TEST(Executor, DoNothingExecutor) {
296 DoNothingExecutor x;
297
298 // Submit future callback to DoNothingExecutor
299 auto f = folly::via(&x).thenValue([](auto&&) { return 42; });
300
301 // Callback function is stored in DoNothingExecutor, but not executed.
302 EXPECT_FALSE(f.isReady());
303
304 // Destroy the function stored in DoNothingExecutor. The future callback
305 // will never get executed.
306 x.add({});
307
308 EXPECT_TRUE(f.isReady());
309 EXPECT_THROW(std::move(f).get(), folly::BrokenPromise);
310}
311