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.
23 #include <type_traits>
24 #include <unordered_set>
27 #include <folly/AtomicLinkedList.h>
28 #include <folly/Executor.h>
29 #include <folly/Likely.h>
30 #include <folly/IntrusiveList.h>
31 #include <folly/io/async/Request.h>
32 #include <folly/futures/Try.h>
34 #include <folly/experimental/ExecutionObserver.h>
35 #include <folly/experimental/fibers/BoostContextCompatibility.h>
36 #include <folly/experimental/fibers/Fiber.h>
37 #include <folly/experimental/fibers/GuardPageAllocator.h>
38 #include <folly/experimental/fibers/TimeoutController.h>
39 #include <folly/experimental/fibers/traits.h>
51 class TimeoutController;
57 class InlineFunctionRunner {
59 virtual ~InlineFunctionRunner() {}
62 * func must be executed inline and only once.
64 virtual void run(folly::Function<void()> func) = 0;
69 * @brief Single-threaded task execution engine.
71 * FiberManager allows semi-parallel task execution on the same thread. Each
72 * task can notify FiberManager that it is blocked on something (via await())
73 * call. This will pause execution of this task and it will be resumed only
74 * when it is unblocked (via setData()).
76 class FiberManager : public ::folly::Executor {
79 static constexpr size_t kDefaultStackSize{16 * 1024};
82 * Maximum stack size for fibers which will be used for executing all the
85 size_t stackSize{kDefaultStackSize};
88 * Record exact amount of stack used.
90 * This is fairly expensive: we fill each newly allocated stack
91 * with some known value and find the boundary of unused stack
92 * with linear search every time we surrender the stack back to fibersPool.
93 * 0 disables stack recording.
95 size_t recordStackEvery{0};
98 * Keep at most this many free fibers in the pool.
99 * This way the total number of fibers in the system is always bounded
100 * by the number of active fibers + maxFibersPoolSize.
102 size_t maxFibersPoolSize{1000};
105 * Protect limited amount of fiber stacks with guard pages.
107 bool useGuardPages{true};
110 * Free unnecessary fibers in the fibers pool every fibersPoolResizePeriodMs
111 * milliseconds. If value is 0, periodic resizing of the fibers pool is
114 uint32_t fibersPoolResizePeriodMs{0};
116 constexpr Options() {}
119 using ExceptionCallback =
120 folly::Function<void(std::exception_ptr, std::string)>;
122 FiberManager(const FiberManager&) = delete;
123 FiberManager& operator=(const FiberManager&) = delete;
126 * Initializes, but doesn't start FiberManager loop
128 * @param loopController
129 * @param options FiberManager options
131 explicit FiberManager(std::unique_ptr<LoopController> loopController,
132 Options options = Options());
135 * Initializes, but doesn't start FiberManager loop
137 * @param loopController
138 * @param options FiberManager options
139 * @tparam LocalT only local of this type may be stored on fibers.
140 * Locals of other types will be considered thread-locals.
142 template <typename LocalT>
143 FiberManager(LocalType<LocalT>,
144 std::unique_ptr<LoopController> loopController,
145 Options options = Options());
153 LoopController& loopController();
154 const LoopController& loopController() const;
157 * Keeps running ready tasks until the list of ready tasks is empty.
159 * @return True if there are any waiting tasks remaining.
161 bool loopUntilNoReady();
164 * @return true if there are outstanding tasks.
166 bool hasTasks() const;
169 * Sets exception callback which will be called if any of the tasks throws an
174 void setExceptionCallback(ExceptionCallback ec);
177 * Add a new task to be executed. Must be called from FiberManager's thread.
179 * @param func Task functor; must have a signature of `void func()`.
180 * The object will be destroyed once task execution is complete.
182 template <typename F>
183 void addTask(F&& func);
186 * Add a new task to be executed and return a future that will be set on
187 * return from func. Must be called from FiberManager's thread.
189 * @param func Task functor; must have a signature of `void func()`.
190 * The object will be destroyed once task execution is complete.
192 template <typename F>
193 auto addTaskFuture(F&& func)
194 -> folly::Future<typename std::result_of<F()>::type>;
196 * Add a new task to be executed. Safe to call from other threads.
198 * @param func Task function; must have a signature of `void func()`.
199 * The object will be destroyed once task execution is complete.
201 template <typename F>
202 void addTaskRemote(F&& func);
205 * Add a new task to be executed and return a future that will be set on
206 * return from func. Safe to call from other threads.
208 * @param func Task function; must have a signature of `void func()`.
209 * The object will be destroyed once task execution is complete.
211 template <typename F>
212 auto addTaskRemoteFuture(F&& func)
213 -> folly::Future<typename std::result_of<F()>::type>;
215 // Executor interface calls addTaskRemote
216 void add(folly::Func f) override {
217 addTaskRemote(std::move(f));
221 * Add a new task. When the task is complete, execute finally(Try<Result>&&)
222 * on the main context.
224 * @param func Task functor; must have a signature of `T func()` for some T.
225 * @param finally Finally functor; must have a signature of
226 * `void finally(Try<T>&&)` and will be passed
227 * the result of func() (including the exception if occurred).
229 template <typename F, typename G>
230 void addTaskFinally(F&& func, G&& finally);
233 * If called from a fiber, immediately switches to the FiberManager's context
234 * and runs func(), going back to the Fiber's context after completion.
235 * Outside a fiber, just calls func() directly.
237 * @return value returned by func().
239 template <typename F>
240 typename std::result_of<F()>::type
241 runInMainContext(F&& func);
244 * Returns a refference to a fiber-local context for given Fiber. Should be
245 * always called with the same T for each fiber. Fiber-local context is lazily
246 * default-constructed on first request.
247 * When new task is scheduled via addTask / addTaskRemote from a fiber its
248 * fiber-local context is copied into the new fiber.
250 template <typename T>
253 template <typename T>
254 static T& localThread();
257 * @return How many fiber objects (and stacks) has this manager allocated.
259 size_t fibersAllocated() const;
262 * @return How many of the allocated fiber objects are currently
265 size_t fibersPoolSize() const;
268 * return true if running activeFiber_ is not nullptr.
270 bool hasActiveFiber() const;
273 * @return The currently running fiber or null if no fiber is executing.
275 Fiber* currentFiber() const {
276 return currentFiber_;
280 * @return What was the most observed fiber stack usage (in bytes).
282 size_t stackHighWatermark() const;
285 * Yield execution of the currently running fiber. Must only be called from a
286 * fiber executing on this FiberManager. The calling fiber will be scheduled
287 * when all other fibers have had a chance to run and the event loop is
293 * Setup fibers execution observation/instrumentation. Fiber locals are
294 * available to observer.
296 * @param observer Fiber's execution observer.
298 void setObserver(ExecutionObserver* observer);
301 * Setup fibers preempt runner.
303 void setPreemptRunner(InlineFunctionRunner* preemptRunner);
306 * Returns an estimate of the number of fibers which are waiting to run (does
307 * not include fibers or tasks scheduled remotely).
309 size_t runQueueSize() const {
310 return readyFibers_.size() + yieldedFibers_.size();
313 static FiberManager& getFiberManager();
314 static FiberManager* getFiberManagerUnsafe();
319 template <typename F>
320 struct AddTaskHelper;
321 template <typename F, typename G>
322 struct AddTaskFinallyHelper;
325 template <typename F>
326 explicit RemoteTask(F&& f) :
327 func(std::forward<F>(f)),
328 rcontext(RequestContext::saveContext()) {}
329 template <typename F>
330 RemoteTask(F&& f, const Fiber::LocalData& localData_) :
331 func(std::forward<F>(f)),
332 localData(folly::make_unique<Fiber::LocalData>(localData_)),
333 rcontext(RequestContext::saveContext()) {}
334 folly::Function<void()> func;
335 std::unique_ptr<Fiber::LocalData> localData;
336 std::shared_ptr<RequestContext> rcontext;
337 AtomicLinkedListHook<RemoteTask> nextRemoteTask;
340 intptr_t activateFiber(Fiber* fiber);
341 intptr_t deactivateFiber(Fiber* fiber);
343 typedef folly::IntrusiveList<Fiber, &Fiber::listHook_> FiberTailQueue;
344 typedef folly::IntrusiveList<Fiber, &Fiber::globalListHook_>
345 GlobalFiberTailQueue;
347 Fiber* activeFiber_{nullptr}; /**< active fiber, nullptr on main context */
349 * Same as active fiber, but also set for functions run from fiber on main
352 Fiber* currentFiber_{nullptr};
354 FiberTailQueue readyFibers_; /**< queue of fibers ready to be executed */
355 FiberTailQueue yieldedFibers_; /**< queue of fibers which have yielded
357 FiberTailQueue fibersPool_; /**< pool of unitialized Fiber objects */
359 GlobalFiberTailQueue allFibers_; /**< list of all Fiber objects owned */
361 size_t fibersAllocated_{0}; /**< total number of fibers allocated */
362 size_t fibersPoolSize_{0}; /**< total number of fibers in the free pool */
363 size_t fibersActive_{0}; /**< number of running or blocked fibers */
364 size_t fiberId_{0}; /**< id of last fiber used */
367 * Maximum number of active fibers in the last period lasting
368 * Options::fibersPoolResizePeriod milliseconds.
370 size_t maxFibersActiveLastPeriod_{0};
372 FContext::ContextStruct mainContext_; /**< stores loop function context */
374 std::unique_ptr<LoopController> loopController_;
375 bool isLoopScheduled_{false}; /**< was the ready loop scheduled to run? */
378 * When we are inside FiberManager loop this points to FiberManager. Otherwise
381 static FOLLY_TLS FiberManager* currentFiberManager_;
384 * Allocator used to allocate stack for Fibers in the pool.
385 * Allocates stack on the stack of the main context.
387 GuardPageAllocator stackAllocator_;
389 const Options options_; /**< FiberManager options */
392 * Largest observed individual Fiber stack usage in bytes.
394 size_t stackHighWatermark_{0};
397 * Schedules a loop with loopController (unless already scheduled before).
399 void ensureLoopScheduled();
402 * @return An initialized Fiber object from the pool
407 * Sets local data for given fiber if all conditions are met.
409 void initLocalData(Fiber& fiber);
412 * Function passed to the await call.
414 folly::Function<void(Fiber&)> awaitFunc_;
417 * Function passed to the runInMainContext call.
419 folly::Function<void()> immediateFunc_;
424 InlineFunctionRunner* preemptRunner_{nullptr};
427 * Fiber's execution observer.
429 ExecutionObserver* observer_{nullptr};
431 ExceptionCallback exceptionCallback_; /**< task exception callback */
433 folly::AtomicLinkedList<Fiber, &Fiber::nextRemoteReady_> remoteReadyQueue_;
435 folly::AtomicLinkedList<RemoteTask, &RemoteTask::nextRemoteTask>
438 std::shared_ptr<TimeoutController> timeoutManager_;
440 struct FibersPoolResizer {
441 explicit FibersPoolResizer(FiberManager& fm) :
445 FiberManager& fiberManager_;
448 FibersPoolResizer fibersPoolResizer_;
449 bool fibersPoolResizerScheduled_{false};
451 void doFibersPoolResizing();
454 * Only local of this type will be available for fibers.
456 std::type_index localType_;
458 void runReadyFiber(Fiber* fiber);
459 void remoteReadyInsert(Fiber* fiber);
461 #ifdef FOLLY_SANITIZE_ADDRESS
463 // These methods notify ASAN when a fiber is entered/exited so that ASAN can
464 // find the right stack extents when it needs to poison/unpoison the stack.
466 void registerFiberActivationWithAsan(Fiber* fiber);
467 void registerFiberDeactivationWithAsan(Fiber* fiber);
469 #endif // FOLLY_SANITIZE_ADDRESS
473 * @return true iff we are running in a fiber's context
475 inline bool onFiber() {
476 auto fm = FiberManager::getFiberManagerUnsafe();
477 return fm ? fm->hasActiveFiber() : false;
481 * Add a new task to be executed.
483 * @param func Task functor; must have a signature of `void func()`.
484 * The object will be destroyed once task execution is complete.
486 template <typename F>
487 inline void addTask(F&& func) {
488 return FiberManager::getFiberManager().addTask(std::forward<F>(func));
492 * Add a new task. When the task is complete, execute finally(Try<Result>&&)
493 * on the main context.
494 * Task functor is run and destroyed on the fiber context.
495 * Finally functor is run and destroyed on the main context.
497 * @param func Task functor; must have a signature of `T func()` for some T.
498 * @param finally Finally functor; must have a signature of
499 * `void finally(Try<T>&&)` and will be passed
500 * the result of func() (including the exception if occurred).
502 template <typename F, typename G>
503 inline void addTaskFinally(F&& func, G&& finally) {
504 return FiberManager::getFiberManager().addTaskFinally(
505 std::forward<F>(func), std::forward<G>(finally));
509 * Blocks task execution until given promise is fulfilled.
511 * Calls function passing in a Promise<T>, which has to be fulfilled.
513 * @return data which was used to fulfill the promise.
515 template <typename F>
516 typename FirstArgOf<F>::type::value_type
517 inline await(F&& func);
520 * If called from a fiber, immediately switches to the FiberManager's context
521 * and runs func(), going back to the Fiber's context after completion.
522 * Outside a fiber, just calls func() directly.
524 * @return value returned by func().
526 template <typename F>
527 typename std::result_of<F()>::type
528 inline runInMainContext(F&& func) {
529 auto fm = FiberManager::getFiberManagerUnsafe();
530 if (UNLIKELY(fm == nullptr)) {
533 return fm->runInMainContext(std::forward<F>(func));
537 * Returns a refference to a fiber-local context for given Fiber. Should be
538 * always called with the same T for each fiber. Fiber-local context is lazily
539 * default-constructed on first request.
540 * When new task is scheduled via addTask / addTaskRemote from a fiber its
541 * fiber-local context is copied into the new fiber.
543 template <typename T>
545 auto fm = FiberManager::getFiberManagerUnsafe();
547 return fm->local<T>();
549 return FiberManager::localThread<T>();
552 inline void yield() {
553 auto fm = FiberManager::getFiberManagerUnsafe();
557 std::this_thread::yield();
563 #include "FiberManager-inl.h"