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