2 * Copyright 2015 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.
17 #include <gtest/gtest.h>
19 #include <folly/experimental/FunctionScheduler.h>
24 * Helper functions for controlling how long this test takes.
26 * Using larger intervals here will make the tests less flaky when run on
27 * heavily loaded systems. However, this will also make the tests take longer
30 static const auto timeFactor = std::chrono::milliseconds(100);
31 std::chrono::milliseconds testInterval(int n) {
32 return n * timeFactor;
35 std::chrono::microseconds usec(n * timeFactor);
39 TEST(FunctionScheduler, SimpleAdd) {
42 fs.addFunction([&] { total += 2; }, testInterval(2), "add2");
51 TEST(FunctionScheduler, AddCancel) {
54 fs.addFunction([&] { total += 2; }, testInterval(2), "add2");
60 EXPECT_TRUE(fs.cancelFunction("add2"));
61 EXPECT_FALSE(fs.cancelFunction("NO SUCH FUNC"));
64 fs.addFunction([&] { total += 1; }, testInterval(2), "add2");
65 EXPECT_FALSE(fs.start()); // already running
73 TEST(FunctionScheduler, AddCancel2) {
77 // Test adds and cancels while the scheduler is stopped
78 EXPECT_FALSE(fs.cancelFunction("add2"));
79 fs.addFunction([&] { total += 1; }, testInterval(2), "add2");
80 EXPECT_TRUE(fs.cancelFunction("add2"));
81 EXPECT_FALSE(fs.cancelFunction("add2"));
82 fs.addFunction([&] { total += 2; }, testInterval(2), "add2");
83 fs.addFunction([&] { total += 3; }, testInterval(3), "add3");
90 // Cancel add2 while the scheduler is running
91 EXPECT_TRUE(fs.cancelFunction("add2"));
92 EXPECT_FALSE(fs.cancelFunction("add2"));
93 EXPECT_FALSE(fs.cancelFunction("bogus"));
97 EXPECT_TRUE(fs.cancelFunction("add3"));
99 // Test a function that cancels itself
100 int selfCancelCount = 0;
104 if (selfCancelCount > 2) {
105 fs.cancelFunction("selfCancel");
108 testInterval(1), "selfCancel", testInterval(1));
110 EXPECT_EQ(3, selfCancelCount);
111 EXPECT_FALSE(fs.cancelFunction("selfCancel"));
113 // Test a function that schedules another function
116 auto fn2 = [&] { ++fn2Count; };
119 if (adderCount == 2) {
120 fs.addFunction(fn2, testInterval(3), "fn2", testInterval(2));
123 fs.addFunction(fnAdder, testInterval(4), "adder");
126 EXPECT_EQ(1, adderCount);
127 EXPECT_EQ(0, fn2Count);
128 // t4: adder fires, schedules fn2
130 EXPECT_EQ(2, adderCount);
131 EXPECT_EQ(0, fn2Count);
134 EXPECT_EQ(2, adderCount);
135 EXPECT_EQ(1, fn2Count);
139 EXPECT_EQ(3, adderCount);
140 EXPECT_EQ(2, fn2Count);
141 EXPECT_TRUE(fs.cancelFunction("fn2"));
142 EXPECT_TRUE(fs.cancelFunction("adder"));
144 EXPECT_EQ(3, adderCount);
145 EXPECT_EQ(2, fn2Count);
148 EXPECT_EQ(3, selfCancelCount);
151 TEST(FunctionScheduler, AddMultiple) {
153 FunctionScheduler fs;
154 fs.addFunction([&] { total += 2; }, testInterval(2), "add2");
155 fs.addFunction([&] { total += 3; }, testInterval(3), "add3");
156 // function name already exists
157 EXPECT_THROW(fs.addFunction([&] { total += 2; }, testInterval(2),
158 "add2"), std::exception);
164 EXPECT_EQ(12, total);
165 EXPECT_TRUE(fs.cancelFunction("add2"));
167 EXPECT_EQ(15, total);
170 EXPECT_EQ(15, total);
174 TEST(FunctionScheduler, AddAfterStart) {
176 FunctionScheduler fs;
177 fs.addFunction([&] { total += 2; }, testInterval(2), "add2");
178 fs.addFunction([&] { total += 3; }, testInterval(2), "add3");
181 EXPECT_EQ(10, total);
182 fs.addFunction([&] { total += 2; }, testInterval(3), "add22");
184 EXPECT_EQ(17, total);
187 TEST(FunctionScheduler, ShutdownStart) {
189 FunctionScheduler fs;
190 fs.addFunction([&] { total += 2; }, testInterval(2), "add2");
197 EXPECT_FALSE(fs.cancelFunction("add3")); // non existing
202 TEST(FunctionScheduler, AddInvalid) {
204 FunctionScheduler fs;
205 // interval may not be negative
206 EXPECT_THROW(fs.addFunction([&] { total += 2; }, testInterval(-1), "add2"),
208 EXPECT_FALSE(fs.cancelFunction("addNoFunc"));
211 TEST(FunctionScheduler, NoFunctions) {
212 FunctionScheduler fs;
213 EXPECT_TRUE(fs.start());
215 FunctionScheduler fs2;
219 TEST(FunctionScheduler, AddWhileRunning) {
221 FunctionScheduler fs;
224 fs.addFunction([&] { total += 2; }, testInterval(2), "add2");
225 // The function should be invoked nearly immediately when we add it
226 // and the FunctionScheduler is already running
233 TEST(FunctionScheduler, NoShutdown) {
236 FunctionScheduler fs;
237 fs.addFunction([&] { total += 2; }, testInterval(1), "add2");
242 // Destroyed the FunctionScheduler without calling shutdown.
243 // Everything should have been cleaned up, and the function will no longer
249 TEST(FunctionScheduler, StartDelay) {
251 FunctionScheduler fs;
252 fs.addFunction([&] { total += 2; }, testInterval(2), "add2",
254 fs.addFunction([&] { total += 3; }, testInterval(3), "add3",
256 EXPECT_THROW(fs.addFunction([&] { total += 2; }, testInterval(3),
257 "addX", testInterval(-1)), std::exception);
265 // t4 : add2: total=7
266 // t5 : add3: total=10
267 // t6 : add2: total=12
269 EXPECT_EQ(12, total);
270 fs.cancelFunction("add2");
271 // t8 : add3: total=15
273 EXPECT_EQ(15, total);
276 EXPECT_EQ(15, total);
280 TEST(FunctionScheduler, NoSteadyCatchup) {
281 std::atomic<int> ticks(0);
282 FunctionScheduler fs;
283 fs.setThreadName("NoSteadyCatchup");
284 // fs.setSteady(false); is the default
285 fs.addFunction([&ticks] {
287 std::this_thread::sleep_for(
288 std::chrono::milliseconds(200));
291 std::chrono::milliseconds(5));
293 std::this_thread::sleep_for(std::chrono::milliseconds(500));
295 // no steady catch up means we'd tick once for 200ms, then remaining
296 // 300ms / 5 = 60 times
297 EXPECT_LE(ticks.load(), 61);
300 TEST(FunctionScheduler, SteadyCatchup) {
301 std::atomic<int> ticks(0);
302 FunctionScheduler fs;
303 fs.setThreadName("SteadyCatchup");
305 fs.addFunction([&ticks] {
307 std::this_thread::sleep_for(
308 std::chrono::milliseconds(200));
311 std::chrono::milliseconds(5));
314 std::this_thread::sleep_for(std::chrono::milliseconds(500));
316 // tick every 5ms. Despite tick == 2 is slow, later ticks should be fast
317 // enough to catch back up to schedule
318 EXPECT_NEAR(100, ticks.load(), 10);