2 * Copyright 2017 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <folly/Baton.h>
20 #include <folly/executors/CPUThreadPoolExecutor.h>
21 #include <folly/executors/InlineExecutor.h>
22 #include <folly/executors/SerialExecutor.h>
23 #include <folly/portability/GTest.h>
25 using namespace std::chrono;
26 using folly::SerialExecutor;
29 void burnMs(uint64_t ms) {
30 /* sleep override */ std::this_thread::sleep_for(milliseconds(ms));
34 void SimpleTest(std::shared_ptr<folly::Executor> const& parent) {
35 SerialExecutor executor(parent);
37 std::vector<int> values;
38 std::vector<int> expected;
40 for (int i = 0; i < 20; ++i) {
41 executor.add([i, &values] {
42 // make this extra vulnerable to concurrent execution
47 expected.push_back(i);
50 // wait until last task has executed
51 folly::Baton<> finished_baton;
52 executor.add([&finished_baton] { finished_baton.post(); });
53 finished_baton.wait();
55 EXPECT_EQ(expected, values);
58 TEST(SerialExecutor, Simple) {
59 SimpleTest(std::make_shared<folly::CPUThreadPoolExecutor>(4));
61 TEST(SerialExecutor, SimpleInline) {
62 SimpleTest(std::make_shared<folly::InlineExecutor>());
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);
72 // block executor until we call start_baton.post()
73 folly::Baton<> start_baton;
74 executor->add([&start_baton] { start_baton.wait(); });
76 std::vector<int> values;
77 std::vector<int> expected;
79 for (int i = 0; i < 20; ++i) {
80 executor->add([i, &values] {
81 // make this extra vulnerable to concurrent execution
86 expected.push_back(i);
89 folly::Baton<> finished_baton;
90 executor->add([&finished_baton] { finished_baton.post(); });
92 // destroy SerialExecutor
95 // now kick off the tasks
98 // wait until last task has executed
99 finished_baton.wait();
101 EXPECT_EQ(expected, values);
104 void RecursiveAddTest(std::shared_ptr<folly::Executor> const& parent) {
105 SerialExecutor executor(parent);
107 folly::Baton<> finished_baton;
109 std::vector<int> values;
110 std::vector<int> expected = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}};
113 std::function<void()> lambda = [&] {
115 // make this extra vulnerable to concurrent execution
119 executor.add(lambda);
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.
126 finished_baton.post();
131 executor.add(lambda);
132 executor.add(lambda);
133 executor.add(lambda);
135 // wait until last task has executed
136 finished_baton.wait();
138 EXPECT_EQ(expected, values);
141 TEST(SerialExecutor, RecursiveAdd) {
142 RecursiveAddTest(std::make_shared<folly::CPUThreadPoolExecutor>(4));
144 TEST(SerialExecutor, RecursiveAddInline) {
145 RecursiveAddTest(std::make_shared<folly::InlineExecutor>());
148 TEST(SerialExecutor, ExecutionThrows) {
149 SerialExecutor executor(std::make_shared<folly::InlineExecutor>());
151 // an empty Func will throw std::bad_function_call when invoked,
152 // but SerialExecutor should catch that exception
153 executor.add(folly::Func{});