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/futures/Try.h>
32 #include <folly/experimental/ExecutionObserver.h>
33 #include <folly/experimental/fibers/BoostContextCompatibility.h>
34 #include <folly/experimental/fibers/Fiber.h>
35 #include <folly/experimental/fibers/GuardPageAllocator.h>
36 #include <folly/experimental/fibers/traits.h>
38 namespace folly { namespace fibers {
43 class TimeoutController;
51 * @brief Single-threaded task execution engine.
53 * FiberManager allows semi-parallel task execution on the same thread. Each
54 * task can notify FiberManager that it is blocked on something (via await())
55 * call. This will pause execution of this task and it will be resumed only
56 * when it is unblocked (via setData()).
58 class FiberManager : public ::folly::Executor {
61 static constexpr size_t kDefaultStackSize{16 * 1024};
64 * Maximum stack size for fibers which will be used for executing all the
67 size_t stackSize{kDefaultStackSize};
70 * Record exact amount of stack used.
72 * This is fairly expensive: we fill each newly allocated stack
73 * with some known value and find the boundary of unused stack
74 * with linear search every time we surrender the stack back to fibersPool.
75 * 0 disables stack recording.
77 size_t recordStackEvery{0};
80 * Keep at most this many free fibers in the pool.
81 * This way the total number of fibers in the system is always bounded
82 * by the number of active fibers + maxFibersPoolSize.
84 size_t maxFibersPoolSize{1000};
86 constexpr Options() {}
89 typedef std::function<void(std::exception_ptr, std::string)>
92 FiberManager(const FiberManager&) = delete;
93 FiberManager& operator=(const FiberManager&) = delete;
96 * Initializes, but doesn't start FiberManager loop
98 * @param loopController
99 * @param options FiberManager options
101 explicit FiberManager(std::unique_ptr<LoopController> loopController,
102 Options options = Options());
105 * Initializes, but doesn't start FiberManager loop
107 * @param loopController
108 * @param options FiberManager options
109 * @tparam LocalT only local of this type may be stored on fibers.
110 * Locals of other types will be considered thread-locals.
112 template <typename LocalT>
113 FiberManager(LocalType<LocalT>,
114 std::unique_ptr<LoopController> loopController,
115 Options options = Options());
123 LoopController& loopController();
124 const LoopController& loopController() const;
127 * Keeps running ready tasks until the list of ready tasks is empty.
129 * @return True if there are any waiting tasks remaining.
131 bool loopUntilNoReady();
134 * @return true if there are outstanding tasks.
136 bool hasTasks() const;
139 * Sets exception callback which will be called if any of the tasks throws an
144 void setExceptionCallback(ExceptionCallback ec);
147 * Add a new task to be executed. Must be called from FiberManager's thread.
149 * @param func Task functor; must have a signature of `void func()`.
150 * The object will be destroyed once task execution is complete.
152 template <typename F>
153 void addTask(F&& func);
156 * Add a new task to be executed. Safe to call from other threads.
158 * @param func Task function; must have a signature of `void func()`.
159 * The object will be destroyed once task execution is complete.
161 template <typename F>
162 void addTaskRemote(F&& func);
164 // Executor interface calls addTaskRemote
165 void add(std::function<void()> f) {
166 addTaskRemote(std::move(f));
170 * Add a new task. When the task is complete, execute finally(Try<Result>&&)
171 * on the main context.
173 * @param func Task functor; must have a signature of `T func()` for some T.
174 * @param finally Finally functor; must have a signature of
175 * `void finally(Try<T>&&)` and will be passed
176 * the result of func() (including the exception if occurred).
178 template <typename F, typename G>
179 void addTaskFinally(F&& func, G&& finally);
182 * If called from a fiber, immediately switches to the FiberManager's context
183 * and runs func(), going back to the Fiber's context after completion.
184 * Outside a fiber, just calls func() directly.
186 * @return value returned by func().
188 template <typename F>
189 typename std::result_of<F()>::type
190 runInMainContext(F&& func);
193 * Returns a refference to a fiber-local context for given Fiber. Should be
194 * always called with the same T for each fiber. Fiber-local context is lazily
195 * default-constructed on first request.
196 * When new task is scheduled via addTask / addTaskRemote from a fiber its
197 * fiber-local context is copied into the new fiber.
199 template <typename T>
202 template <typename T>
203 static T& localThread();
206 * @return How many fiber objects (and stacks) has this manager allocated.
208 size_t fibersAllocated() const;
211 * @return How many of the allocated fiber objects are currently
214 size_t fibersPoolSize() const;
217 * return true if running activeFiber_ is not nullptr.
219 bool hasActiveFiber() const;
222 * @return What was the most observed fiber stack usage (in bytes).
224 size_t stackHighWatermark() const;
227 * Yield execution of the currently running fiber. Must only be called from a
228 * fiber executing on this FiberManager. The calling fiber will be scheduled
229 * when all other fibers have had a chance to run and the event loop is
235 * Setup fibers execution observation/instrumentation. Fiber locals are
236 * available to observer.
238 * @param observer Fiber's execution observer.
240 void setObserver(ExecutionObserver* observer);
242 static FiberManager& getFiberManager();
243 static FiberManager* getFiberManagerUnsafe();
248 template <typename F>
249 struct AddTaskHelper;
250 template <typename F, typename G>
251 struct AddTaskFinallyHelper;
254 template <typename F>
255 explicit RemoteTask(F&& f) : func(std::forward<F>(f)) {}
256 template <typename F>
257 RemoteTask(F&& f, const Fiber::LocalData& localData_) :
258 func(std::forward<F>(f)),
259 localData(folly::make_unique<Fiber::LocalData>(localData_)) {}
260 std::function<void()> func;
261 std::unique_ptr<Fiber::LocalData> localData;
262 AtomicLinkedListHook<RemoteTask> nextRemoteTask;
265 typedef folly::IntrusiveList<Fiber, &Fiber::listHook_> FiberTailQueue;
267 Fiber* activeFiber_{nullptr}; /**< active fiber, nullptr on main context */
269 * Same as active fiber, but also set for functions run from fiber on main
272 Fiber* currentFiber_{nullptr};
274 FiberTailQueue readyFibers_; /**< queue of fibers ready to be executed */
275 FiberTailQueue yieldedFibers_; /**< queue of fibers which have yielded
277 FiberTailQueue fibersPool_; /**< pool of unitialized Fiber objects */
279 size_t fibersAllocated_{0}; /**< total number of fibers allocated */
280 size_t fibersPoolSize_{0}; /**< total number of fibers in the free pool */
281 size_t fibersActive_{0}; /**< number of running or blocked fibers */
282 size_t fiberId_{0}; /**< id of last fiber used */
284 FContext::ContextStruct mainContext_; /**< stores loop function context */
286 std::unique_ptr<LoopController> loopController_;
287 bool isLoopScheduled_{false}; /**< was the ready loop scheduled to run? */
290 * When we are inside FiberManager loop this points to FiberManager. Otherwise
293 static __thread FiberManager* currentFiberManager_;
296 * runInMainContext implementation for non-void functions.
298 template <typename F>
299 typename std::enable_if<
300 !std::is_same<typename std::result_of<F()>::type, void>::value,
301 typename std::result_of<F()>::type>::type
302 runInMainContextHelper(F&& func);
305 * runInMainContext implementation for void functions
307 template <typename F>
308 typename std::enable_if<
309 std::is_same<typename std::result_of<F()>::type, void>::value,
311 runInMainContextHelper(F&& func);
314 * Allocator used to allocate stack for Fibers in the pool.
315 * Allocates stack on the stack of the main context.
317 GuardPageAllocator stackAllocator_;
319 const Options options_; /**< FiberManager options */
322 * Largest observed individual Fiber stack usage in bytes.
324 size_t stackHighWatermark_{0};
327 * Schedules a loop with loopController (unless already scheduled before).
329 void ensureLoopScheduled();
332 * @return An initialized Fiber object from the pool
337 * Sets local data for given fiber if all conditions are met.
339 void initLocalData(Fiber& fiber);
342 * Function passed to the await call.
344 std::function<void(Fiber&)> awaitFunc_;
347 * Function passed to the runInMainContext call.
349 std::function<void()> immediateFunc_;
352 * Fiber's execution observer.
354 ExecutionObserver* observer_{nullptr};
356 ExceptionCallback exceptionCallback_; /**< task exception callback */
358 folly::AtomicLinkedList<Fiber, &Fiber::nextRemoteReady_> remoteReadyQueue_;
360 folly::AtomicLinkedList<RemoteTask, &RemoteTask::nextRemoteTask>
363 std::shared_ptr<TimeoutController> timeoutManager_;
366 * Only local of this type will be available for fibers.
368 std::type_index localType_;
370 void runReadyFiber(Fiber* fiber);
371 void remoteReadyInsert(Fiber* fiber);
375 * @return true iff we are running in a fiber's context
377 inline bool onFiber() {
378 auto fm = FiberManager::getFiberManagerUnsafe();
379 return fm ? fm->hasActiveFiber() : false;
383 * Add a new task to be executed.
385 * @param func Task functor; must have a signature of `void func()`.
386 * The object will be destroyed once task execution is complete.
388 template <typename F>
389 inline void addTask(F&& func) {
390 return FiberManager::getFiberManager().addTask(std::forward<F>(func));
394 * Add a new task. When the task is complete, execute finally(Try<Result>&&)
395 * on the main context.
396 * Task functor is run and destroyed on the fiber context.
397 * Finally functor is run and destroyed on the main context.
399 * @param func Task functor; must have a signature of `T func()` for some T.
400 * @param finally Finally functor; must have a signature of
401 * `void finally(Try<T>&&)` and will be passed
402 * the result of func() (including the exception if occurred).
404 template <typename F, typename G>
405 inline void addTaskFinally(F&& func, G&& finally) {
406 return FiberManager::getFiberManager().addTaskFinally(
407 std::forward<F>(func), std::forward<G>(finally));
411 * Blocks task execution until given promise is fulfilled.
413 * Calls function passing in a Promise<T>, which has to be fulfilled.
415 * @return data which was used to fulfill the promise.
417 template <typename F>
418 typename FirstArgOf<F>::type::value_type
419 inline await(F&& func);
422 * If called from a fiber, immediately switches to the FiberManager's context
423 * and runs func(), going back to the Fiber's context after completion.
424 * Outside a fiber, just calls func() directly.
426 * @return value returned by func().
428 template <typename F>
429 typename std::result_of<F()>::type
430 inline runInMainContext(F&& func) {
431 auto fm = FiberManager::getFiberManagerUnsafe();
432 if (UNLIKELY(fm == nullptr)) {
435 return fm->runInMainContext(std::forward<F>(func));
439 * Returns a refference to a fiber-local context for given Fiber. Should be
440 * always called with the same T for each fiber. Fiber-local context is lazily
441 * default-constructed on first request.
442 * When new task is scheduled via addTask / addTaskRemote from a fiber its
443 * fiber-local context is copied into the new fiber.
445 template <typename T>
447 auto fm = FiberManager::getFiberManagerUnsafe();
449 return fm->local<T>();
451 return FiberManager::localThread<T>();
454 inline void yield() {
455 auto fm = FiberManager::getFiberManagerUnsafe();
459 std::this_thread::yield();
465 #include "FiberManager-inl.h"