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 <gtest/gtest.h>
21 #include <folly/Baton.h>
22 #include <folly/executors/CPUThreadPoolExecutor.h>
23 #include <folly/executors/SerialExecutor.h>
24 #include <folly/futures/InlineExecutor.h>
26 using namespace std::chrono;
27 using folly::SerialExecutor;
30 void burnMs(uint64_t ms) {
31 /* sleep override */ std::this_thread::sleep_for(milliseconds(ms));
35 void SimpleTest(std::shared_ptr<folly::Executor> const& parent) {
36 SerialExecutor executor(parent);
38 std::vector<int> values;
39 std::vector<int> expected;
41 for (int i = 0; i < 20; ++i) {
42 executor.add([i, &values] {
43 // make this extra vulnerable to concurrent execution
48 expected.push_back(i);
51 // wait until last task has executed
52 folly::Baton<> finished_baton;
53 executor.add([&finished_baton] { finished_baton.post(); });
54 finished_baton.wait();
56 EXPECT_EQ(expected, values);
59 TEST(SerialExecutor, Simple) {
60 SimpleTest(std::make_shared<folly::CPUThreadPoolExecutor>(4));
62 TEST(SerialExecutor, SimpleInline) {
63 SimpleTest(std::make_shared<folly::InlineExecutor>());
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);
73 // block executor until we call start_baton.post()
74 folly::Baton<> start_baton;
75 executor->add([&start_baton] { start_baton.wait(); });
77 std::vector<int> values;
78 std::vector<int> expected;
80 for (int i = 0; i < 20; ++i) {
81 executor->add([i, &values] {
82 // make this extra vulnerable to concurrent execution
87 expected.push_back(i);
90 folly::Baton<> finished_baton;
91 executor->add([&finished_baton] { finished_baton.post(); });
93 // destroy SerialExecutor
96 // now kick off the tasks
99 // wait until last task has executed
100 finished_baton.wait();
102 EXPECT_EQ(expected, values);
105 void RecursiveAddTest(std::shared_ptr<folly::Executor> const& parent) {
106 SerialExecutor executor(parent);
108 folly::Baton<> finished_baton;
110 std::vector<int> values;
111 std::vector<int> expected = {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}};
114 std::function<void()> lambda = [&] {
116 // make this extra vulnerable to concurrent execution
120 executor.add(lambda);
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.
127 finished_baton.post();
132 executor.add(lambda);
133 executor.add(lambda);
134 executor.add(lambda);
136 // wait until last task has executed
137 finished_baton.wait();
139 EXPECT_EQ(expected, values);
142 TEST(SerialExecutor, RecursiveAdd) {
143 RecursiveAddTest(std::make_shared<folly::CPUThreadPoolExecutor>(4));
145 TEST(SerialExecutor, RecursiveAddInline) {
146 RecursiveAddTest(std::make_shared<folly::InlineExecutor>());
149 TEST(SerialExecutor, ExecutionThrows) {
150 SerialExecutor executor(std::make_shared<folly::InlineExecutor>());
152 // an empty Func will throw std::bad_function_call when invoked,
153 // but SerialExecutor should catch that exception
154 executor.add(folly::Func{});