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/AtomicLinkedList.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/traits.h>
39 namespace folly { namespace fibers {
44 class TimeoutController;
52 * @brief Single-threaded task execution engine.
54 * FiberManager allows semi-parallel task execution on the same thread. Each
55 * task can notify FiberManager that it is blocked on something (via await())
56 * call. This will pause execution of this task and it will be resumed only
57 * when it is unblocked (via setData()).
59 class FiberManager : public ::folly::Executor {
62 static constexpr size_t kDefaultStackSize{16 * 1024};
65 * Maximum stack size for fibers which will be used for executing all the
68 size_t stackSize{kDefaultStackSize};
71 * Record exact amount of stack used.
73 * This is fairly expensive: we fill each newly allocated stack
74 * with some known value and find the boundary of unused stack
75 * with linear search every time we surrender the stack back to fibersPool.
76 * 0 disables stack recording.
78 size_t recordStackEvery{0};
81 * Keep at most this many free fibers in the pool.
82 * This way the total number of fibers in the system is always bounded
83 * by the number of active fibers + maxFibersPoolSize.
85 size_t maxFibersPoolSize{1000};
88 * Protect limited amount of fiber stacks with guard pages.
90 bool useGuardPages{false};
92 constexpr Options() {}
95 typedef std::function<void(std::exception_ptr, std::string)>
98 FiberManager(const FiberManager&) = delete;
99 FiberManager& operator=(const FiberManager&) = delete;
102 * Initializes, but doesn't start FiberManager loop
104 * @param loopController
105 * @param options FiberManager options
107 explicit FiberManager(std::unique_ptr<LoopController> loopController,
108 Options options = Options());
111 * Initializes, but doesn't start FiberManager loop
113 * @param loopController
114 * @param options FiberManager options
115 * @tparam LocalT only local of this type may be stored on fibers.
116 * Locals of other types will be considered thread-locals.
118 template <typename LocalT>
119 FiberManager(LocalType<LocalT>,
120 std::unique_ptr<LoopController> loopController,
121 Options options = Options());
129 LoopController& loopController();
130 const LoopController& loopController() const;
133 * Keeps running ready tasks until the list of ready tasks is empty.
135 * @return True if there are any waiting tasks remaining.
137 bool loopUntilNoReady();
140 * @return true if there are outstanding tasks.
142 bool hasTasks() const;
145 * Sets exception callback which will be called if any of the tasks throws an
150 void setExceptionCallback(ExceptionCallback ec);
153 * Add a new task to be executed. Must be called from FiberManager's thread.
155 * @param func Task functor; must have a signature of `void func()`.
156 * The object will be destroyed once task execution is complete.
158 template <typename F>
159 void addTask(F&& func);
162 * Add a new task to be executed. Safe to call from other threads.
164 * @param func Task function; must have a signature of `void func()`.
165 * The object will be destroyed once task execution is complete.
167 template <typename F>
168 void addTaskRemote(F&& func);
170 // Executor interface calls addTaskRemote
171 void add(std::function<void()> f) {
172 addTaskRemote(std::move(f));
176 * Add a new task. When the task is complete, execute finally(Try<Result>&&)
177 * on the main context.
179 * @param func Task functor; must have a signature of `T func()` for some T.
180 * @param finally Finally functor; must have a signature of
181 * `void finally(Try<T>&&)` and will be passed
182 * the result of func() (including the exception if occurred).
184 template <typename F, typename G>
185 void addTaskFinally(F&& func, G&& finally);
188 * If called from a fiber, immediately switches to the FiberManager's context
189 * and runs func(), going back to the Fiber's context after completion.
190 * Outside a fiber, just calls func() directly.
192 * @return value returned by func().
194 template <typename F>
195 typename std::result_of<F()>::type
196 runInMainContext(F&& func);
199 * Returns a refference to a fiber-local context for given Fiber. Should be
200 * always called with the same T for each fiber. Fiber-local context is lazily
201 * default-constructed on first request.
202 * When new task is scheduled via addTask / addTaskRemote from a fiber its
203 * fiber-local context is copied into the new fiber.
205 template <typename T>
208 template <typename T>
209 static T& localThread();
212 * @return How many fiber objects (and stacks) has this manager allocated.
214 size_t fibersAllocated() const;
217 * @return How many of the allocated fiber objects are currently
220 size_t fibersPoolSize() const;
223 * return true if running activeFiber_ is not nullptr.
225 bool hasActiveFiber() const;
228 * @return The currently running fiber or null if no fiber is executing.
230 Fiber* currentFiber() const {
231 return currentFiber_;
235 * @return What was the most observed fiber stack usage (in bytes).
237 size_t stackHighWatermark() const;
240 * Yield execution of the currently running fiber. Must only be called from a
241 * fiber executing on this FiberManager. The calling fiber will be scheduled
242 * when all other fibers have had a chance to run and the event loop is
248 * Setup fibers execution observation/instrumentation. Fiber locals are
249 * available to observer.
251 * @param observer Fiber's execution observer.
253 void setObserver(ExecutionObserver* observer);
256 * Returns an estimate of the number of fibers which are waiting to run (does
257 * not include fibers or tasks scheduled remotely).
259 size_t runQueueSize() const {
260 return readyFibers_.size() + yieldedFibers_.size();
263 static FiberManager& getFiberManager();
264 static FiberManager* getFiberManagerUnsafe();
269 template <typename F>
270 struct AddTaskHelper;
271 template <typename F, typename G>
272 struct AddTaskFinallyHelper;
275 template <typename F>
276 explicit RemoteTask(F&& f) :
277 func(std::forward<F>(f)),
278 rcontext(RequestContext::saveContext()) {}
279 template <typename F>
280 RemoteTask(F&& f, const Fiber::LocalData& localData_) :
281 func(std::forward<F>(f)),
282 localData(folly::make_unique<Fiber::LocalData>(localData_)),
283 rcontext(RequestContext::saveContext()) {}
284 std::function<void()> func;
285 std::unique_ptr<Fiber::LocalData> localData;
286 std::shared_ptr<RequestContext> rcontext;
287 AtomicLinkedListHook<RemoteTask> nextRemoteTask;
290 typedef folly::IntrusiveList<Fiber, &Fiber::listHook_> FiberTailQueue;
292 Fiber* activeFiber_{nullptr}; /**< active fiber, nullptr on main context */
294 * Same as active fiber, but also set for functions run from fiber on main
297 Fiber* currentFiber_{nullptr};
299 FiberTailQueue readyFibers_; /**< queue of fibers ready to be executed */
300 FiberTailQueue yieldedFibers_; /**< queue of fibers which have yielded
302 FiberTailQueue fibersPool_; /**< pool of unitialized Fiber objects */
304 size_t fibersAllocated_{0}; /**< total number of fibers allocated */
305 size_t fibersPoolSize_{0}; /**< total number of fibers in the free pool */
306 size_t fibersActive_{0}; /**< number of running or blocked fibers */
307 size_t fiberId_{0}; /**< id of last fiber used */
309 FContext::ContextStruct mainContext_; /**< stores loop function context */
311 std::unique_ptr<LoopController> loopController_;
312 bool isLoopScheduled_{false}; /**< was the ready loop scheduled to run? */
315 * When we are inside FiberManager loop this points to FiberManager. Otherwise
318 static FOLLY_TLS FiberManager* currentFiberManager_;
321 * runInMainContext implementation for non-void functions.
323 template <typename F>
324 typename std::enable_if<
325 !std::is_same<typename std::result_of<F()>::type, void>::value,
326 typename std::result_of<F()>::type>::type
327 runInMainContextHelper(F&& func);
330 * runInMainContext implementation for void functions
332 template <typename F>
333 typename std::enable_if<
334 std::is_same<typename std::result_of<F()>::type, void>::value,
336 runInMainContextHelper(F&& func);
339 * Allocator used to allocate stack for Fibers in the pool.
340 * Allocates stack on the stack of the main context.
342 GuardPageAllocator stackAllocator_;
344 const Options options_; /**< FiberManager options */
347 * Largest observed individual Fiber stack usage in bytes.
349 size_t stackHighWatermark_{0};
352 * Schedules a loop with loopController (unless already scheduled before).
354 void ensureLoopScheduled();
357 * @return An initialized Fiber object from the pool
362 * Sets local data for given fiber if all conditions are met.
364 void initLocalData(Fiber& fiber);
367 * Function passed to the await call.
369 std::function<void(Fiber&)> awaitFunc_;
372 * Function passed to the runInMainContext call.
374 std::function<void()> immediateFunc_;
377 * Fiber's execution observer.
379 ExecutionObserver* observer_{nullptr};
381 ExceptionCallback exceptionCallback_; /**< task exception callback */
383 folly::AtomicLinkedList<Fiber, &Fiber::nextRemoteReady_> remoteReadyQueue_;
385 folly::AtomicLinkedList<RemoteTask, &RemoteTask::nextRemoteTask>
388 std::shared_ptr<TimeoutController> timeoutManager_;
391 * Only local of this type will be available for fibers.
393 std::type_index localType_;
395 void runReadyFiber(Fiber* fiber);
396 void remoteReadyInsert(Fiber* fiber);
400 * @return true iff we are running in a fiber's context
402 inline bool onFiber() {
403 auto fm = FiberManager::getFiberManagerUnsafe();
404 return fm ? fm->hasActiveFiber() : false;
408 * Add a new task to be executed.
410 * @param func Task functor; must have a signature of `void func()`.
411 * The object will be destroyed once task execution is complete.
413 template <typename F>
414 inline void addTask(F&& func) {
415 return FiberManager::getFiberManager().addTask(std::forward<F>(func));
419 * Add a new task. When the task is complete, execute finally(Try<Result>&&)
420 * on the main context.
421 * Task functor is run and destroyed on the fiber context.
422 * Finally functor is run and destroyed on the main context.
424 * @param func Task functor; must have a signature of `T func()` for some T.
425 * @param finally Finally functor; must have a signature of
426 * `void finally(Try<T>&&)` and will be passed
427 * the result of func() (including the exception if occurred).
429 template <typename F, typename G>
430 inline void addTaskFinally(F&& func, G&& finally) {
431 return FiberManager::getFiberManager().addTaskFinally(
432 std::forward<F>(func), std::forward<G>(finally));
436 * Blocks task execution until given promise is fulfilled.
438 * Calls function passing in a Promise<T>, which has to be fulfilled.
440 * @return data which was used to fulfill the promise.
442 template <typename F>
443 typename FirstArgOf<F>::type::value_type
444 inline await(F&& func);
447 * If called from a fiber, immediately switches to the FiberManager's context
448 * and runs func(), going back to the Fiber's context after completion.
449 * Outside a fiber, just calls func() directly.
451 * @return value returned by func().
453 template <typename F>
454 typename std::result_of<F()>::type
455 inline runInMainContext(F&& func) {
456 auto fm = FiberManager::getFiberManagerUnsafe();
457 if (UNLIKELY(fm == nullptr)) {
460 return fm->runInMainContext(std::forward<F>(func));
464 * Returns a refference to a fiber-local context for given Fiber. Should be
465 * always called with the same T for each fiber. Fiber-local context is lazily
466 * default-constructed on first request.
467 * When new task is scheduled via addTask / addTaskRemote from a fiber its
468 * fiber-local context is copied into the new fiber.
470 template <typename T>
472 auto fm = FiberManager::getFiberManagerUnsafe();
474 return fm->local<T>();
476 return FiberManager::localThread<T>();
479 inline void yield() {
480 auto fm = FiberManager::getFiberManagerUnsafe();
484 std::this_thread::yield();
490 #include "FiberManager-inl.h"