Modification to futures to remove deadlock in certain use cases for getVia(executor).
[folly.git] / folly / futures / test / ExecutorTest.cpp
1 /*
2  * Copyright 2016 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 <gtest/gtest.h>
18
19 #include <folly/futures/Future.h>
20 #include <folly/futures/InlineExecutor.h>
21 #include <folly/futures/ManualExecutor.h>
22 #include <folly/futures/QueuedImmediateExecutor.h>
23 #include <folly/Baton.h>
24
25 using namespace folly;
26
27 TEST(ManualExecutor, runIsStable) {
28   ManualExecutor x;
29   size_t count = 0;
30   auto f1 = [&]() { count++; };
31   auto f2 = [&]() { x.add(f1); x.add(f1); };
32   x.add(f2);
33   x.run();
34 }
35
36 TEST(ManualExecutor, scheduleDur) {
37   ManualExecutor x;
38   size_t count = 0;
39   std::chrono::milliseconds dur {10};
40   x.schedule([&]{ count++; }, dur);
41   EXPECT_EQ(count, 0);
42   x.run();
43   EXPECT_EQ(count, 0);
44   x.advance(dur/2);
45   EXPECT_EQ(count, 0);
46   x.advance(dur/2);
47   EXPECT_EQ(count, 1);
48 }
49
50 TEST(ManualExecutor, clockStartsAt0) {
51   ManualExecutor x;
52   EXPECT_EQ(x.now(), x.now().min());
53 }
54
55 TEST(ManualExecutor, scheduleAbs) {
56   ManualExecutor x;
57   size_t count = 0;
58   x.scheduleAt([&]{ count++; }, x.now() + std::chrono::milliseconds(10));
59   EXPECT_EQ(count, 0);
60   x.advance(std::chrono::milliseconds(10));
61   EXPECT_EQ(count, 1);
62 }
63
64 TEST(ManualExecutor, advanceTo) {
65   ManualExecutor x;
66   size_t count = 0;
67   x.scheduleAt([&]{ count++; }, std::chrono::steady_clock::now());
68   EXPECT_EQ(count, 0);
69   x.advanceTo(std::chrono::steady_clock::now());
70   EXPECT_EQ(count, 1);
71 }
72
73 TEST(ManualExecutor, advanceBack) {
74   ManualExecutor x;
75   size_t count = 0;
76   x.advance(std::chrono::microseconds(5));
77   x.schedule([&]{ count++; }, std::chrono::microseconds(6));
78   EXPECT_EQ(count, 0);
79   x.advanceTo(x.now() - std::chrono::microseconds(1));
80   EXPECT_EQ(count, 0);
81 }
82
83 TEST(ManualExecutor, advanceNeg) {
84   ManualExecutor x;
85   size_t count = 0;
86   x.advance(std::chrono::microseconds(5));
87   x.schedule([&]{ count++; }, std::chrono::microseconds(6));
88   EXPECT_EQ(count, 0);
89   x.advance(std::chrono::microseconds(-1));
90   EXPECT_EQ(count, 0);
91 }
92
93 TEST(ManualExecutor, waitForDoesNotDeadlock) {
94   ManualExecutor east, west;
95   folly::Baton<> baton;
96   auto f = makeFuture()
97     .via(&east)
98     .then([](Try<Unit>){ return makeFuture(); })
99     .via(&west);
100   std::thread t([&]{
101     baton.post();
102     west.waitFor(f);
103   });
104   baton.wait();
105   east.run();
106   t.join();
107 }
108
109 TEST(ManualExecutor, getViaDoesNotDeadlock) {
110   ManualExecutor east, west;
111   folly::Baton<> baton;
112   auto f = makeFuture().via(&east).then([](Try<Unit>) {
113     return makeFuture();
114   }).via(&west);
115   std::thread t([&] {
116     baton.post();
117     f.getVia(&west);
118   });
119   baton.wait();
120   east.run();
121   t.join();
122 }
123
124 TEST(Executor, InlineExecutor) {
125   InlineExecutor x;
126   size_t counter = 0;
127   x.add([&]{
128     x.add([&]{
129       EXPECT_EQ(counter, 0);
130       counter++;
131     });
132     EXPECT_EQ(counter, 1);
133     counter++;
134   });
135   EXPECT_EQ(counter, 2);
136 }
137
138 TEST(Executor, QueuedImmediateExecutor) {
139   QueuedImmediateExecutor x;
140   size_t counter = 0;
141   x.add([&]{
142     x.add([&]{
143       EXPECT_EQ(1, counter);
144       counter++;
145     });
146     EXPECT_EQ(0, counter);
147     counter++;
148   });
149   EXPECT_EQ(2, counter);
150 }
151
152 TEST(Executor, Runnable) {
153   InlineExecutor x;
154   size_t counter = 0;
155   struct Runnable {
156     std::function<void()> fn;
157     void operator()() { fn(); }
158   };
159   Runnable f;
160   f.fn = [&]{ counter++; };
161   x.add(f);
162   EXPECT_EQ(counter, 1);
163 }
164
165 TEST(Executor, RunnablePtr) {
166   InlineExecutor x;
167   struct Runnable {
168     std::function<void()> fn;
169     void operator()() { fn(); }
170   };
171   size_t counter = 0;
172   auto fnp = std::make_shared<Runnable>();
173   fnp->fn = [&]{ counter++; };
174   x.addPtr(fnp);
175   EXPECT_EQ(counter, 1);
176 }
177
178 TEST(Executor, ThrowableThen) {
179   InlineExecutor x;
180   auto f = Future<Unit>().via(&x).then([](){
181     throw std::runtime_error("Faildog");
182   });
183   EXPECT_THROW(f.value(), std::exception);
184 }
185
186 class CrappyExecutor : public Executor {
187  public:
188   void add(Func /* f */) override { throw std::runtime_error("bad"); }
189 };
190
191 TEST(Executor, CrappyExecutor) {
192   CrappyExecutor x;
193   bool flag = false;
194   auto f = folly::via(&x).onError([&](std::runtime_error& e) {
195     EXPECT_STREQ("bad", e.what());
196     flag = true;
197   });
198   EXPECT_TRUE(flag);
199 }