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.
23 #include <unordered_set>
26 #include <folly/AtomicIntrusiveLinkedList.h>
27 #include <folly/Executor.h>
28 #include <folly/Likely.h>
29 #include <folly/IntrusiveList.h>
30 #include <folly/io/async/Request.h>
31 #include <folly/futures/Try.h>
33 #include <folly/experimental/ExecutionObserver.h>
34 #include <folly/experimental/fibers/BoostContextCompatibility.h>
35 #include <folly/experimental/fibers/Fiber.h>
36 #include <folly/experimental/fibers/GuardPageAllocator.h>
37 #include <folly/experimental/fibers/TimeoutController.h>
38 #include <folly/experimental/fibers/traits.h>
40 namespace folly { namespace fibers {
45 class TimeoutController;
51 class InlineFunctionRunner {
53 virtual ~InlineFunctionRunner() {}
56 * func must be executed inline and only once.
58 virtual void run(std::function<void()> func) = 0;
63 * @brief Single-threaded task execution engine.
65 * FiberManager allows semi-parallel task execution on the same thread. Each
66 * task can notify FiberManager that it is blocked on something (via await())
67 * call. This will pause execution of this task and it will be resumed only
68 * when it is unblocked (via setData()).
70 class FiberManager : public ::folly::Executor {
73 static constexpr size_t kDefaultStackSize{16 * 1024};
76 * Maximum stack size for fibers which will be used for executing all the
79 size_t stackSize{kDefaultStackSize};
82 * Record exact amount of stack used.
84 * This is fairly expensive: we fill each newly allocated stack
85 * with some known value and find the boundary of unused stack
86 * with linear search every time we surrender the stack back to fibersPool.
87 * 0 disables stack recording.
89 size_t recordStackEvery{0};
92 * Keep at most this many free fibers in the pool.
93 * This way the total number of fibers in the system is always bounded
94 * by the number of active fibers + maxFibersPoolSize.
96 size_t maxFibersPoolSize{1000};
99 * Protect limited amount of fiber stacks with guard pages.
101 bool useGuardPages{false};
104 * Free unnecessary fibers in the fibers pool every fibersPoolResizePeriodMs
105 * milliseconds. If value is 0, periodic resizing of the fibers pool is
108 uint32_t fibersPoolResizePeriodMs{0};
110 constexpr Options() {}
113 typedef std::function<void(std::exception_ptr, std::string)>
116 FiberManager(const FiberManager&) = delete;
117 FiberManager& operator=(const FiberManager&) = delete;
120 * Initializes, but doesn't start FiberManager loop
122 * @param loopController
123 * @param options FiberManager options
125 explicit FiberManager(std::unique_ptr<LoopController> loopController,
126 Options options = Options());
129 * Initializes, but doesn't start FiberManager loop
131 * @param loopController
132 * @param options FiberManager options
133 * @tparam LocalT only local of this type may be stored on fibers.
134 * Locals of other types will be considered thread-locals.
136 template <typename LocalT>
137 FiberManager(LocalType<LocalT>,
138 std::unique_ptr<LoopController> loopController,
139 Options options = Options());
147 LoopController& loopController();
148 const LoopController& loopController() const;
151 * Keeps running ready tasks until the list of ready tasks is empty.
153 * @return True if there are any waiting tasks remaining.
155 bool loopUntilNoReady();
158 * @return true if there are outstanding tasks.
160 bool hasTasks() const;
163 * Sets exception callback which will be called if any of the tasks throws an
168 void setExceptionCallback(ExceptionCallback ec);
171 * Add a new task to be executed. Must be called from FiberManager's thread.
173 * @param func Task functor; must have a signature of `void func()`.
174 * The object will be destroyed once task execution is complete.
176 template <typename F>
177 void addTask(F&& func);
180 * Add a new task to be executed. Safe to call from other threads.
182 * @param func Task function; must have a signature of `void func()`.
183 * The object will be destroyed once task execution is complete.
185 template <typename F>
186 void addTaskRemote(F&& func);
188 // Executor interface calls addTaskRemote
189 void add(std::function<void()> f) {
190 addTaskRemote(std::move(f));
194 * Add a new task. When the task is complete, execute finally(Try<Result>&&)
195 * on the main context.
197 * @param func Task functor; must have a signature of `T func()` for some T.
198 * @param finally Finally functor; must have a signature of
199 * `void finally(Try<T>&&)` and will be passed
200 * the result of func() (including the exception if occurred).
202 template <typename F, typename G>
203 void addTaskFinally(F&& func, G&& finally);
206 * If called from a fiber, immediately switches to the FiberManager's context
207 * and runs func(), going back to the Fiber's context after completion.
208 * Outside a fiber, just calls func() directly.
210 * @return value returned by func().
212 template <typename F>
213 typename std::result_of<F()>::type
214 runInMainContext(F&& func);
217 * Returns a refference to a fiber-local context for given Fiber. Should be
218 * always called with the same T for each fiber. Fiber-local context is lazily
219 * default-constructed on first request.
220 * When new task is scheduled via addTask / addTaskRemote from a fiber its
221 * fiber-local context is copied into the new fiber.
223 template <typename T>
226 template <typename T>
227 static T& localThread();
230 * @return How many fiber objects (and stacks) has this manager allocated.
232 size_t fibersAllocated() const;
235 * @return How many of the allocated fiber objects are currently
238 size_t fibersPoolSize() const;
241 * return true if running activeFiber_ is not nullptr.
243 bool hasActiveFiber() const;
246 * @return The currently running fiber or null if no fiber is executing.
248 Fiber* currentFiber() const {
249 return currentFiber_;
253 * @return What was the most observed fiber stack usage (in bytes).
255 size_t stackHighWatermark() const;
258 * Yield execution of the currently running fiber. Must only be called from a
259 * fiber executing on this FiberManager. The calling fiber will be scheduled
260 * when all other fibers have had a chance to run and the event loop is
266 * Setup fibers execution observation/instrumentation. Fiber locals are
267 * available to observer.
269 * @param observer Fiber's execution observer.
271 void setObserver(ExecutionObserver* observer);
274 * Setup fibers preempt runner.
276 void setPreemptRunner(InlineFunctionRunner* preemptRunner);
279 * Returns an estimate of the number of fibers which are waiting to run (does
280 * not include fibers or tasks scheduled remotely).
282 size_t runQueueSize() const {
283 return readyFibers_.size() + yieldedFibers_.size();
286 static FiberManager& getFiberManager();
287 static FiberManager* getFiberManagerUnsafe();
292 template <typename F>
293 struct AddTaskHelper;
294 template <typename F, typename G>
295 struct AddTaskFinallyHelper;
298 template <typename F>
299 explicit RemoteTask(F&& f) :
300 func(std::forward<F>(f)),
301 rcontext(RequestContext::saveContext()) {}
302 template <typename F>
303 RemoteTask(F&& f, const Fiber::LocalData& localData_) :
304 func(std::forward<F>(f)),
305 localData(folly::make_unique<Fiber::LocalData>(localData_)),
306 rcontext(RequestContext::saveContext()) {}
307 std::function<void()> func;
308 std::unique_ptr<Fiber::LocalData> localData;
309 std::shared_ptr<RequestContext> rcontext;
310 AtomicIntrusiveLinkedListHook<RemoteTask> nextRemoteTask;
313 typedef folly::IntrusiveList<Fiber, &Fiber::listHook_> FiberTailQueue;
315 Fiber* activeFiber_{nullptr}; /**< active fiber, nullptr on main context */
317 * Same as active fiber, but also set for functions run from fiber on main
320 Fiber* currentFiber_{nullptr};
322 FiberTailQueue readyFibers_; /**< queue of fibers ready to be executed */
323 FiberTailQueue yieldedFibers_; /**< queue of fibers which have yielded
325 FiberTailQueue fibersPool_; /**< pool of unitialized Fiber objects */
327 size_t fibersAllocated_{0}; /**< total number of fibers allocated */
328 size_t fibersPoolSize_{0}; /**< total number of fibers in the free pool */
329 size_t fibersActive_{0}; /**< number of running or blocked fibers */
330 size_t fiberId_{0}; /**< id of last fiber used */
333 * Maximum number of active fibers in the last period lasting
334 * Options::fibersPoolResizePeriod milliseconds.
336 size_t maxFibersActiveLastPeriod_{0};
338 FContext::ContextStruct mainContext_; /**< stores loop function context */
340 std::unique_ptr<LoopController> loopController_;
341 bool isLoopScheduled_{false}; /**< was the ready loop scheduled to run? */
344 * When we are inside FiberManager loop this points to FiberManager. Otherwise
347 static FOLLY_TLS FiberManager* currentFiberManager_;
350 * runInMainContext implementation for non-void functions.
352 template <typename F>
353 typename std::enable_if<
354 !std::is_same<typename std::result_of<F()>::type, void>::value,
355 typename std::result_of<F()>::type>::type
356 runInMainContextHelper(F&& func);
359 * runInMainContext implementation for void functions
361 template <typename F>
362 typename std::enable_if<
363 std::is_same<typename std::result_of<F()>::type, void>::value,
365 runInMainContextHelper(F&& func);
368 * Allocator used to allocate stack for Fibers in the pool.
369 * Allocates stack on the stack of the main context.
371 GuardPageAllocator stackAllocator_;
373 const Options options_; /**< FiberManager options */
376 * Largest observed individual Fiber stack usage in bytes.
378 size_t stackHighWatermark_{0};
381 * Schedules a loop with loopController (unless already scheduled before).
383 void ensureLoopScheduled();
386 * @return An initialized Fiber object from the pool
391 * Sets local data for given fiber if all conditions are met.
393 void initLocalData(Fiber& fiber);
396 * Function passed to the await call.
398 std::function<void(Fiber&)> awaitFunc_;
401 * Function passed to the runInMainContext call.
403 std::function<void()> immediateFunc_;
408 InlineFunctionRunner* preemptRunner_{nullptr};
411 * Fiber's execution observer.
413 ExecutionObserver* observer_{nullptr};
415 ExceptionCallback exceptionCallback_; /**< task exception callback */
417 folly::AtomicIntrusiveLinkedList<Fiber, &Fiber::nextRemoteReady_>
420 folly::AtomicIntrusiveLinkedList<RemoteTask, &RemoteTask::nextRemoteTask>
423 std::shared_ptr<TimeoutController> timeoutManager_;
425 struct FibersPoolResizer {
426 explicit FibersPoolResizer(FiberManager& fm) :
430 FiberManager& fiberManager_;
433 FibersPoolResizer fibersPoolResizer_;
434 bool fibersPoolResizerScheduled_{false};
436 void doFibersPoolResizing();
439 * Only local of this type will be available for fibers.
441 std::type_index localType_;
443 void runReadyFiber(Fiber* fiber);
444 void remoteReadyInsert(Fiber* fiber);
448 * @return true iff we are running in a fiber's context
450 inline bool onFiber() {
451 auto fm = FiberManager::getFiberManagerUnsafe();
452 return fm ? fm->hasActiveFiber() : false;
456 * Add a new task to be executed.
458 * @param func Task functor; must have a signature of `void func()`.
459 * The object will be destroyed once task execution is complete.
461 template <typename F>
462 inline void addTask(F&& func) {
463 return FiberManager::getFiberManager().addTask(std::forward<F>(func));
467 * Add a new task. When the task is complete, execute finally(Try<Result>&&)
468 * on the main context.
469 * Task functor is run and destroyed on the fiber context.
470 * Finally functor is run and destroyed on the main context.
472 * @param func Task functor; must have a signature of `T func()` for some T.
473 * @param finally Finally functor; must have a signature of
474 * `void finally(Try<T>&&)` and will be passed
475 * the result of func() (including the exception if occurred).
477 template <typename F, typename G>
478 inline void addTaskFinally(F&& func, G&& finally) {
479 return FiberManager::getFiberManager().addTaskFinally(
480 std::forward<F>(func), std::forward<G>(finally));
484 * Blocks task execution until given promise is fulfilled.
486 * Calls function passing in a Promise<T>, which has to be fulfilled.
488 * @return data which was used to fulfill the promise.
490 template <typename F>
491 typename FirstArgOf<F>::type::value_type
492 inline await(F&& func);
495 * If called from a fiber, immediately switches to the FiberManager's context
496 * and runs func(), going back to the Fiber's context after completion.
497 * Outside a fiber, just calls func() directly.
499 * @return value returned by func().
501 template <typename F>
502 typename std::result_of<F()>::type
503 inline runInMainContext(F&& func) {
504 auto fm = FiberManager::getFiberManagerUnsafe();
505 if (UNLIKELY(fm == nullptr)) {
508 return fm->runInMainContext(std::forward<F>(func));
512 * Returns a refference to a fiber-local context for given Fiber. Should be
513 * always called with the same T for each fiber. Fiber-local context is lazily
514 * default-constructed on first request.
515 * When new task is scheduled via addTask / addTaskRemote from a fiber its
516 * fiber-local context is copied into the new fiber.
518 template <typename T>
520 auto fm = FiberManager::getFiberManagerUnsafe();
522 return fm->local<T>();
524 return FiberManager::localThread<T>();
527 inline void yield() {
528 auto fm = FiberManager::getFiberManagerUnsafe();
532 std::this_thread::yield();
538 #include "FiberManager-inl.h"