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 | |
27 | using namespace folly; |
28 | |
29 | TEST(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 | |
46 | TEST(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 | |
59 | TEST(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 | |
73 | TEST(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 | |
84 | TEST(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 | |
101 | TEST(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 | |
116 | TEST(ManualExecutor, clockStartsAt0) { |
117 | ManualExecutor x; |
118 | EXPECT_EQ(x.now(), x.now().min()); |
119 | } |
120 | |
121 | TEST(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 | |
130 | TEST(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 | |
139 | TEST(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 | |
149 | TEST(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 | |
159 | TEST(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 | |
175 | TEST(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 | |
191 | TEST(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 | |
204 | TEST(ManualExecutor, drainsOnDestruction) { |
205 | size_t count = 0; |
206 | { |
207 | ManualExecutor x; |
208 | x.add([&] { ++count; }); |
209 | } |
210 | EXPECT_EQ(1, count); |
211 | } |
212 | |
213 | TEST(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 | |
227 | TEST(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 | |
241 | TEST(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 | |
256 | TEST(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 | |
268 | class CrappyExecutor : public Executor { |
269 | public: |
270 | void add(Func /* f */) override { |
271 | throw std::runtime_error("bad" ); |
272 | } |
273 | }; |
274 | |
275 | TEST(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 | |
285 | class 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 | |
295 | TEST(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 | |