1a132ef1a835548670070ef2bc22c44b8604ffe3
[folly.git] / folly / executors / test / SerialExecutorTest.cpp
1 /*
2  * Copyright 2017 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 <chrono>
18
19 #include <gtest/gtest.h>
20
21 #include <folly/Baton.h>
22 #include <folly/executors/CPUThreadPoolExecutor.h>
23 #include <folly/executors/SerialExecutor.h>
24 #include <folly/futures/InlineExecutor.h>
25
26 using namespace std::chrono;
27 using folly::SerialExecutor;
28
29 namespace {
30 void burnMs(uint64_t ms) {
31   /* sleep override */ std::this_thread::sleep_for(milliseconds(ms));
32 }
33 } // namespace
34
35 void SimpleTest(std::shared_ptr<folly::Executor> const& parent) {
36   SerialExecutor executor(parent);
37
38   std::vector<int> values;
39   std::vector<int> expected;
40
41   for (int i = 0; i < 20; ++i) {
42     executor.add([i, &values] {
43       // make this extra vulnerable to concurrent execution
44       values.push_back(0);
45       burnMs(10);
46       values.back() = i;
47     });
48     expected.push_back(i);
49   }
50
51   // wait until last task has executed
52   folly::Baton<> finished_baton;
53   executor.add([&finished_baton] { finished_baton.post(); });
54   finished_baton.wait();
55
56   EXPECT_EQ(expected, values);
57 }
58
59 TEST(SerialExecutor, Simple) {
60   SimpleTest(std::make_shared<folly::CPUThreadPoolExecutor>(4));
61 }
62 TEST(SerialExecutor, SimpleInline) {
63   SimpleTest(std::make_shared<folly::InlineExecutor>());
64 }
65
66 // The Afterlife test only works with an asynchronous executor (not the
67 // InlineExecutor), because we want execution of tasks to happen after we
68 // destroy the SerialExecutor
69 TEST(SerialExecutor, Afterlife) {
70   auto cpu_executor = std::make_shared<folly::CPUThreadPoolExecutor>(4);
71   auto executor = std::make_unique<SerialExecutor>(cpu_executor);
72
73   // block executor until we call start_baton.post()
74   folly::Baton<> start_baton;
75   executor->add([&start_baton] { start_baton.wait(); });
76
77   std::vector<int> values;
78   std::vector<int> expected;
79
80   for (int i = 0; i < 20; ++i) {
81     executor->add([i, &values] {
82       // make this extra vulnerable to concurrent execution
83       values.push_back(0);
84       burnMs(10);
85       values.back() = i;
86     });
87     expected.push_back(i);
88   }
89
90   folly::Baton<> finished_baton;
91   executor->add([&finished_baton] { finished_baton.post(); });
92
93   // destroy SerialExecutor
94   executor.reset();
95
96   // now kick off the tasks
97   start_baton.post();
98
99   // wait until last task has executed
100   finished_baton.wait();
101
102   EXPECT_EQ(expected, values);
103 }
104
105 void RecursiveAddTest(std::shared_ptr<folly::Executor> const& parent) {
106   SerialExecutor executor(parent);
107
108   folly::Baton<> finished_baton;
109
110   std::vector<int> values;
111   std::vector<int> expected = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}};
112
113   int i = 0;
114   std::function<void()> lambda = [&] {
115     if (i < 10) {
116       // make this extra vulnerable to concurrent execution
117       values.push_back(0);
118       burnMs(10);
119       values.back() = i;
120       executor.add(lambda);
121     } else if (i < 12) {
122       // Below we will post this lambda three times to the executor. When
123       // executed, the lambda will re-post itself during the first ten
124       // executions. Afterwards we do nothing twice (this else-branch), and
125       // then on the 13th execution signal that we are finished.
126     } else {
127       finished_baton.post();
128     }
129     ++i;
130   };
131
132   executor.add(lambda);
133   executor.add(lambda);
134   executor.add(lambda);
135
136   // wait until last task has executed
137   finished_baton.wait();
138
139   EXPECT_EQ(expected, values);
140 }
141
142 TEST(SerialExecutor, RecursiveAdd) {
143   RecursiveAddTest(std::make_shared<folly::CPUThreadPoolExecutor>(4));
144 }
145 TEST(SerialExecutor, RecursiveAddInline) {
146   RecursiveAddTest(std::make_shared<folly::InlineExecutor>());
147 }
148
149 TEST(SerialExecutor, ExecutionThrows) {
150   SerialExecutor executor(std::make_shared<folly::InlineExecutor>());
151
152   // an empty Func will throw std::bad_function_call when invoked,
153   // but SerialExecutor should catch that exception
154   executor.add(folly::Func{});
155 }