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/Function.h>
20 #include <folly/Range.h>
22 #include <condition_variable>
30 * Schedules any number of functions to run at various intervals. E.g.,
32 * FunctionScheduler fs;
34 * fs.addFunction([&] { LOG(INFO) << "tick..."; }, seconds(1), "ticker");
35 * fs.addFunction(std::bind(&TestClass::doStuff, this), minutes(5), "stuff");
38 * fs.cancelFunction("ticker");
39 * fs.addFunction([&] { LOG(INFO) << "tock..."; }, minutes(3), "tocker");
44 * Note: the class uses only one thread - if you want to use more than one
45 * thread, either use multiple FunctionScheduler objects, or check out
46 * ThreadedRepeatingFunctionRunner.h for a much simpler contract of
47 * "run each function periodically in its own thread".
49 * start() schedules the functions, while shutdown() terminates further
52 class FunctionScheduler {
58 * By default steady is false, meaning schedules may lag behind overtime.
59 * This could be due to long running tasks or time drift because of randomness
60 * in thread wakeup time.
61 * By setting steady to true, FunctionScheduler will attempt to catch up.
62 * i.e. more like a cronjob
64 * NOTE: it's only safe to set this before calling start()
66 void setSteady(bool steady) { steady_ = steady; }
69 * Parameters to control the function interval.
71 * If isPoisson is true, then use std::poisson_distribution to pick the
72 * interval between each invocation of the function.
74 * If isPoisson os false, then always use fixed the interval specified to
77 struct LatencyDistribution {
81 LatencyDistribution(bool poisson, double mean)
88 * Adds a new function to the FunctionScheduler.
90 * Functions will not be run until start() is called. When start() is
91 * called, each function will be run after its specified startDelay.
92 * Functions may also be added after start() has been called, in which case
93 * startDelay is still honored.
95 * Throws an exception on error. In particular, each function must have a
96 * unique name--two functions cannot be added with the same name.
98 void addFunction(Function<void()>&& cb,
99 std::chrono::milliseconds interval,
100 StringPiece nameID = StringPiece(),
101 std::chrono::milliseconds startDelay =
102 std::chrono::milliseconds(0));
105 * Add a new function to the FunctionScheduler with a specified
106 * LatencyDistribution
109 Function<void()>&& cb,
110 std::chrono::milliseconds interval,
111 const LatencyDistribution& latencyDistr,
112 StringPiece nameID = StringPiece(),
113 std::chrono::milliseconds startDelay = std::chrono::milliseconds(0));
116 * Adds a new function to the FunctionScheduler to run only once.
118 void addFunctionOnce(
119 Function<void()>&& cb,
120 StringPiece nameID = StringPiece(),
121 std::chrono::milliseconds startDelay = std::chrono::milliseconds(0));
124 * Add a new function to the FunctionScheduler with the time
125 * interval being distributed uniformly within the given interval
126 * [minInterval, maxInterval].
128 void addFunctionUniformDistribution(Function<void()>&& cb,
129 std::chrono::milliseconds minInterval,
130 std::chrono::milliseconds maxInterval,
132 std::chrono::milliseconds startDelay);
135 * A type alias for function that is called to determine the time
136 * interval for the next scheduled run.
138 using IntervalDistributionFunc = Function<std::chrono::milliseconds()>;
141 * Add a new function to the FunctionScheduler. The scheduling interval
142 * is determined by the interval distribution functor, which is called
143 * every time the next function execution is scheduled. This allows
144 * for supporting custom interval distribution algorithms in addition
145 * to built in constant interval; and Poisson and jitter distributions
146 * (@see FunctionScheduler::addFunction and
147 * @see FunctionScheduler::addFunctionJitterInterval).
149 void addFunctionGenericDistribution(
150 Function<void()>&& cb,
151 IntervalDistributionFunc&& intervalFunc,
152 const std::string& nameID,
153 const std::string& intervalDescr,
154 std::chrono::milliseconds startDelay);
157 * Cancels the function with the specified name, so it will no longer be run.
159 * Returns false if no function exists with the specified name.
161 bool cancelFunction(StringPiece nameID);
162 bool cancelFunctionAndWait(StringPiece nameID);
165 * All functions registered will be canceled.
167 void cancelAllFunctions();
168 void cancelAllFunctionsAndWait();
171 * Resets the specified function's timer.
172 * When resetFunctionTimer is called, the specified function's timer will
173 * be reset with the same parameters it was passed initially, including
174 * its startDelay. If the startDelay was 0, the function will be invoked
177 * Returns false if no function exists with the specified name.
179 bool resetFunctionTimer(StringPiece nameID);
182 * Starts the scheduler.
184 * Returns false if the scheduler was already running.
189 * Stops the FunctionScheduler.
191 * It may be restarted later by calling start() again.
192 * Returns false if the scheduler was not running.
197 * Set the name of the worker thread.
199 void setThreadName(StringPiece threadName);
204 IntervalDistributionFunc intervalFunc;
205 std::chrono::steady_clock::time_point nextRunTime;
207 std::chrono::milliseconds startDelay;
208 std::string intervalDescr;
212 Function<void()>&& cback,
213 IntervalDistributionFunc&& intervalFn,
214 const std::string& nameID,
215 const std::string& intervalDistDescription,
216 std::chrono::milliseconds delay,
218 : cb(std::move(cback)),
219 intervalFunc(std::move(intervalFn)),
223 intervalDescr(intervalDistDescription),
226 std::chrono::steady_clock::time_point getNextRunTime() const {
229 void setNextRunTimeStrict(std::chrono::steady_clock::time_point curTime) {
230 nextRunTime = curTime + intervalFunc();
232 void setNextRunTimeSteady() { nextRunTime += intervalFunc(); }
233 void resetNextRunTime(std::chrono::steady_clock::time_point curTime) {
234 nextRunTime = curTime + startDelay;
237 // Simply reset cb to an empty function.
240 bool isValid() const { return bool(cb); }
243 struct RunTimeOrder {
244 bool operator()(const RepeatFunc& f1, const RepeatFunc& f2) const {
245 return f1.getNextRunTime() > f2.getNextRunTime();
249 typedef std::vector<RepeatFunc> FunctionHeap;
252 void runOneFunction(std::unique_lock<std::mutex>& lock,
253 std::chrono::steady_clock::time_point now);
254 void cancelFunction(const std::unique_lock<std::mutex>& lock,
255 FunctionHeap::iterator it);
256 void addFunctionToHeap(const std::unique_lock<std::mutex>& lock,
259 void addFunctionInternal(
260 Function<void()>&& cb,
261 IntervalDistributionFunc&& intervalFunc,
262 const std::string& nameID,
263 const std::string& intervalDescr,
264 std::chrono::milliseconds startDelay,
269 // Mutex to protect our member variables.
271 bool running_{false};
273 // The functions to run.
274 // This is a heap, ordered by next run time.
275 FunctionHeap functions_;
278 // The function currently being invoked by the running thread.
279 // This is null when the running thread is idle
280 RepeatFunc* currentFunction_{nullptr};
282 // Condition variable that is signalled whenever a new function is added
283 // or when the FunctionScheduler is stopped.
284 std::condition_variable runningCondvar_;
286 std::string threadName_;