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 #ifndef FOLLY_EXPERIMENTAL_FUNCTION_SCHEDULER_H_
18 #define FOLLY_EXPERIMENTAL_FUNCTION_SCHEDULER_H_
20 #include <folly/Range.h>
22 #include <condition_variable>
31 * Schedules any number of functions to run at various intervals. E.g.,
33 * FunctionScheduler fs;
35 * fs.addFunction([&] { LOG(INFO) << "tick..."; }, seconds(1), "ticker");
36 * fs.addFunction(std::bind(&TestClass::doStuff, this), minutes(5), "stuff");
39 * fs.cancelFunction("ticker");
40 * fs.addFunction([&] { LOG(INFO) << "tock..."; }, minutes(3), "tocker");
45 * Note: the class uses only one thread - if you want to use more than one
46 * thread use multiple FunctionScheduler objects
48 * start() schedules the functions, while shutdown() terminates further
51 class FunctionScheduler {
57 * By default steady is false, meaning schedules may lag behind overtime.
58 * This could be due to long running tasks or time drift because of randomness
59 * in thread wakeup time.
60 * By setting steady to true, FunctionScheduler will attempt to catch up.
61 * i.e. more like a cronjob
63 * NOTE: it's only safe to set this before calling start()
65 void setSteady(bool steady) { steady_ = steady; }
67 struct LatencyDistribution {
71 LatencyDistribution(bool poisson,
80 * Adds a new function to the FunctionScheduler.
82 * Functions will not be run until start() is called. When start() is
83 * called, each function will be run after its specified startDelay.
84 * Functions may also be added after start() has been called, in which case
85 * startDelay is still honored.
87 * Throws an exception on error. In particular, each function must have a
88 * unique name--two functions cannot be added with the same name.
90 void addFunction(const std::function<void()>& cb,
91 std::chrono::milliseconds interval,
92 StringPiece nameID = StringPiece(),
93 std::chrono::milliseconds startDelay =
94 std::chrono::milliseconds(0));
97 * Cancels the function with the specified name, so it will no longer be run.
99 * Returns false if no function exists with the specified name.
101 bool cancelFunction(StringPiece nameID);
104 * All functions registered will be canceled.
106 void cancelAllFunctions();
109 * Starts the scheduler.
111 * Returns false if the scheduler was already running.
116 * Stops the FunctionScheduler.
118 * It may be restarted later by calling start() again.
123 * Set the name of the worker thread.
125 void setThreadName(StringPiece threadName);
129 void addFunctionInternal(const std::function<void()>& cb,
130 std::chrono::milliseconds interval,
131 const LatencyDistribution& latencyDistr,
132 StringPiece nameID = StringPiece(),
133 std::chrono::milliseconds startDelay =
134 std::chrono::milliseconds(0));
136 std::function<void()> cb;
137 std::chrono::milliseconds timeInterval;
138 std::chrono::milliseconds lastRunTime;
140 std::chrono::milliseconds startDelay;
142 std::default_random_engine generator;
143 std::poisson_distribution<int> poisson_random;
145 RepeatFunc(const std::function<void()>& cback,
146 std::chrono::milliseconds interval,
147 const std::string& nameID,
148 std::chrono::milliseconds delay,
149 bool poisson = false,
150 double meanPoisson = 1.0)
152 timeInterval(interval),
156 isPoissonDistr(poisson),
157 poisson_random(meanPoisson) {
160 std::chrono::milliseconds getNextRunTime() const {
161 return lastRunTime + timeInterval;
163 void setNextRunTime(std::chrono::milliseconds time) {
164 lastRunTime = time - timeInterval;
166 void setTimeIntervalPoissonDistr() {
167 if (isPoissonDistr) {
168 timeInterval = std::chrono::milliseconds(poisson_random(generator));
172 // Simply reset cb to an empty function.
173 cb = std::function<void()>();
175 bool isValid() const {
179 struct RunTimeOrder {
180 bool operator()(const RepeatFunc& f1, const RepeatFunc& f2) const {
181 return f1.getNextRunTime() > f2.getNextRunTime();
184 typedef std::vector<RepeatFunc> FunctionHeap;
187 void runOneFunction(std::unique_lock<std::mutex>& lock,
188 std::chrono::milliseconds now);
189 void cancelFunction(const std::unique_lock<std::mutex> &lock,
190 FunctionHeap::iterator it);
194 // Mutex to protect our member variables.
196 bool running_{false};
198 // The functions to run.
199 // This is a heap, ordered by next run time.
200 FunctionHeap functions_;
203 // The function currently being invoked by the running thread.
204 // This is null when the running thread is idle
205 RepeatFunc* currentFunction_{nullptr};
207 // Condition variable that is signalled whenever a new function is added
208 // or when the FunctionScheduler is stopped.
209 std::condition_variable runningCondvar_;
211 std::string threadName_;