2 * Copyright 2016 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/Range.h>
21 #include <condition_variable>
29 * Schedules any number of functions to run at various intervals. E.g.,
31 * FunctionScheduler fs;
33 * fs.addFunction([&] { LOG(INFO) << "tick..."; }, seconds(1), "ticker");
34 * fs.addFunction(std::bind(&TestClass::doStuff, this), minutes(5), "stuff");
37 * fs.cancelFunction("ticker");
38 * fs.addFunction([&] { LOG(INFO) << "tock..."; }, minutes(3), "tocker");
43 * Note: the class uses only one thread - if you want to use more than one
44 * thread use multiple FunctionScheduler objects
46 * start() schedules the functions, while shutdown() terminates further
49 class FunctionScheduler {
55 * By default steady is false, meaning schedules may lag behind overtime.
56 * This could be due to long running tasks or time drift because of randomness
57 * in thread wakeup time.
58 * By setting steady to true, FunctionScheduler will attempt to catch up.
59 * i.e. more like a cronjob
61 * NOTE: it's only safe to set this before calling start()
63 void setSteady(bool steady) { steady_ = steady; }
66 * Parameters to control the function interval.
68 * If isPoisson is true, then use std::poisson_distribution to pick the
69 * interval between each invocation of the function.
71 * If isPoisson os false, then always use fixed the interval specified to
74 struct LatencyDistribution {
78 LatencyDistribution(bool poisson, double mean)
85 * Adds a new function to the FunctionScheduler.
87 * Functions will not be run until start() is called. When start() is
88 * called, each function will be run after its specified startDelay.
89 * Functions may also be added after start() has been called, in which case
90 * startDelay is still honored.
92 * Throws an exception on error. In particular, each function must have a
93 * unique name--two functions cannot be added with the same name.
95 void addFunction(const std::function<void()>& cb,
96 std::chrono::milliseconds interval,
97 StringPiece nameID = StringPiece(),
98 std::chrono::milliseconds startDelay =
99 std::chrono::milliseconds(0));
102 * Add a new function to the FunctionScheduler with a specified
103 * LatencyDistribution
106 const std::function<void()>& cb,
107 std::chrono::milliseconds interval,
108 const LatencyDistribution& latencyDistr,
109 StringPiece nameID = StringPiece(),
110 std::chrono::milliseconds startDelay = std::chrono::milliseconds(0));
113 * Add a new function to the FunctionScheduler with the time
114 * interval being distributed uniformly within the given interval
115 * [minInterval, maxInterval].
117 void addFunctionUniformDistribution(const std::function<void()>& cb,
118 std::chrono::milliseconds minInterval,
119 std::chrono::milliseconds maxInterval,
121 std::chrono::milliseconds startDelay);
124 * A type alias for function that is called to determine the time
125 * interval for the next scheduled run.
127 using IntervalDistributionFunc = std::function<std::chrono::milliseconds()>;
130 * Add a new function to the FunctionScheduler. The scheduling interval
131 * is determined by the interval distribution functor, which is called
132 * every time the next function execution is scheduled. This allows
133 * for supporting custom interval distribution algorithms in addition
134 * to built in constant interval; and Poisson and jitter distributions
135 * (@see FunctionScheduler::addFunction and
136 * @see FunctionScheduler::addFunctionJitterInterval).
138 void addFunctionGenericDistribution(
139 const std::function<void()>& cb,
140 const IntervalDistributionFunc& intervalFunc,
141 const std::string& nameID,
142 const std::string& intervalDescr,
143 std::chrono::milliseconds startDelay);
146 * Cancels the function with the specified name, so it will no longer be run.
148 * Returns false if no function exists with the specified name.
150 bool cancelFunction(StringPiece nameID);
153 * All functions registered will be canceled.
155 void cancelAllFunctions();
158 * Resets the specified function's timer.
159 * When resetFunctionTimer is called, the specified function's timer will
160 * be reset with the same parameters it was passed initially, including
161 * its startDelay. If the startDelay was 0, the function will be invoked
164 * Returns false if no function exists with the specified name.
166 bool resetFunctionTimer(StringPiece nameID);
169 * Starts the scheduler.
171 * Returns false if the scheduler was already running.
176 * Stops the FunctionScheduler.
178 * It may be restarted later by calling start() again.
183 * Set the name of the worker thread.
185 void setThreadName(StringPiece threadName);
189 std::function<void()> cb;
190 IntervalDistributionFunc intervalFunc;
191 std::chrono::steady_clock::time_point nextRunTime;
193 std::chrono::milliseconds startDelay;
194 std::string intervalDescr;
196 RepeatFunc(const std::function<void()>& cback,
197 const IntervalDistributionFunc& intervalFn,
198 const std::string& nameID,
199 const std::string& intervalDistDescription,
200 std::chrono::milliseconds delay)
202 intervalFunc(intervalFn),
206 intervalDescr(intervalDistDescription) {}
208 std::chrono::steady_clock::time_point getNextRunTime() const {
211 void setNextRunTimeStrict(std::chrono::steady_clock::time_point curTime) {
212 nextRunTime = curTime + intervalFunc();
214 void setNextRunTimeSteady() { nextRunTime += intervalFunc(); }
215 void resetNextRunTime(std::chrono::steady_clock::time_point curTime) {
216 nextRunTime = curTime + startDelay;
219 // Simply reset cb to an empty function.
220 cb = std::function<void()>();
222 bool isValid() const { return bool(cb); }
225 struct RunTimeOrder {
226 bool operator()(const RepeatFunc& f1, const RepeatFunc& f2) const {
227 return f1.getNextRunTime() > f2.getNextRunTime();
231 typedef std::vector<RepeatFunc> FunctionHeap;
234 void runOneFunction(std::unique_lock<std::mutex>& lock,
235 std::chrono::steady_clock::time_point now);
236 void cancelFunction(const std::unique_lock<std::mutex>& lock,
237 FunctionHeap::iterator it);
238 void addFunctionToHeap(const std::unique_lock<std::mutex>& lock,
243 // Mutex to protect our member variables.
245 bool running_{false};
247 // The functions to run.
248 // This is a heap, ordered by next run time.
249 FunctionHeap functions_;
252 // The function currently being invoked by the running thread.
253 // This is null when the running thread is idle
254 RepeatFunc* currentFunction_{nullptr};
256 // Condition variable that is signalled whenever a new function is added
257 // or when the FunctionScheduler is stopped.
258 std::condition_variable runningCondvar_;
260 std::string threadName_;