Move mcrouter/lib/fibers to folly/experimental/fibers
[folly.git] / folly / experimental / fibers / FiberManager.h
1 /*
2  * Copyright 2015 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 #pragma once
17
18 #include <functional>
19 #include <memory>
20 #include <queue>
21 #include <unordered_set>
22 #include <vector>
23
24 #include <folly/AtomicLinkedList.h>
25 #include <folly/Likely.h>
26 #include <folly/IntrusiveList.h>
27 #include <folly/futures/Try.h>
28
29 #include <folly/experimental/fibers/BoostContextCompatibility.h>
30 #include <folly/experimental/fibers/Fiber.h>
31 #include <folly/experimental/fibers/traits.h>
32
33 #ifdef USE_GUARD_ALLOCATOR
34 #include <folly/experimental/fibers/GuardPageAllocator.h>
35 #endif
36
37 namespace folly { namespace fibers {
38
39 class Baton;
40 class Fiber;
41 class LoopController;
42 class TimeoutController;
43
44 /**
45  * @class FiberManager
46  * @brief Single-threaded task execution engine.
47  *
48  * FiberManager allows semi-parallel task execution on the same thread. Each
49  * task can notify FiberManager that it is blocked on something (via await())
50  * call. This will pause execution of this task and it will be resumed only
51  * when it is unblocked (via setData()).
52  */
53 class FiberManager {
54  public:
55   struct Options {
56 #ifdef FOLLY_SANITIZE_ADDRESS
57     /* ASAN needs a lot of extra stack space.
58        16x is a conservative estimate, 8x also worked with tests
59        where it mattered.  Note that overallocating here does not necessarily
60        increase RSS, since unused memory is pretty much free. */
61     static constexpr size_t kDefaultStackSize{16 * 16 * 1024};
62 #else
63     static constexpr size_t kDefaultStackSize{16 * 1024};
64 #endif
65     /**
66      * Maximum stack size for fibers which will be used for executing all the
67      * tasks.
68      */
69     size_t stackSize{kDefaultStackSize};
70
71     /**
72      * Record exact amount of stack used.
73      *
74      * This is fairly expensive: we fill each newly allocated stack
75      * with some known value and find the boundary of unused stack
76      * with linear search every time we surrender the stack back to fibersPool.
77      */
78     bool debugRecordStackUsed{false};
79
80     /**
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.
84      */
85     size_t maxFibersPoolSize{1000};
86
87     constexpr Options() {}
88   };
89
90   typedef std::function<void(std::exception_ptr, std::string)>
91   ExceptionCallback;
92
93   /**
94    * Initializes, but doesn't start FiberManager loop
95    *
96    * @param options FiberManager options
97    */
98   explicit FiberManager(std::unique_ptr<LoopController> loopController,
99                         Options options = Options());
100
101   ~FiberManager();
102
103   /**
104    * Controller access.
105    */
106   LoopController& loopController();
107   const LoopController& loopController() const;
108
109   /**
110    * Keeps running ready tasks until the list of ready tasks is empty.
111    *
112    * @return True if there are any waiting tasks remaining.
113    */
114   bool loopUntilNoReady();
115
116   /**
117    * @return true if there are outstanding tasks.
118    */
119   bool hasTasks() const;
120
121   /**
122    * Sets exception callback which will be called if any of the tasks throws an
123    * exception.
124    *
125    * @param ec
126    */
127   void setExceptionCallback(ExceptionCallback ec);
128
129   /**
130    * Add a new task to be executed. Must be called from FiberManager's thread.
131    *
132    * @param func Task functor; must have a signature of `void func()`.
133    *             The object will be destroyed once task execution is complete.
134    */
135   template <typename F>
136   void addTask(F&& func);
137
138   /**
139    * Add a new task to be executed, along with a function readyFunc_ which needs
140    * to be executed just before jumping to the ready fiber
141    *
142    * @param func Task functor; must have a signature of `T func()` for some T.
143    * @param readyFunc functor that needs to be executed just before jumping to
144    *                  ready fiber on the main context. This can for example be
145    *                  used to set up state before starting or resuming a fiber.
146    */
147   template <typename F, typename G>
148   void addTaskReadyFunc(F&& func, G&& readyFunc);
149
150   /**
151    * Add a new task to be executed. Safe to call from other threads.
152    *
153    * @param func Task function; must have a signature of `void func()`.
154    *             The object will be destroyed once task execution is complete.
155    */
156   template <typename F>
157   void addTaskRemote(F&& func);
158
159   /**
160    * Add a new task. When the task is complete, execute finally(Try<Result>&&)
161    * on the main context.
162    *
163    * @param func Task functor; must have a signature of `T func()` for some T.
164    * @param finally Finally functor; must have a signature of
165    *                `void finally(Try<T>&&)` and will be passed
166    *                the result of func() (including the exception if occurred).
167    */
168   template <typename F, typename G>
169   void addTaskFinally(F&& func, G&& finally);
170
171   /**
172    * If called from a fiber, immediately switches to the FiberManager's context
173    * and runs func(), going back to the Fiber's context after completion.
174    * Outside a fiber, just calls func() directly.
175    *
176    * @return value returned by func().
177    */
178   template <typename F>
179   typename std::result_of<F()>::type
180   runInMainContext(F&& func);
181
182   /**
183    * @return How many fiber objects (and stacks) has this manager allocated.
184    */
185   size_t fibersAllocated() const;
186
187   /**
188    * @return How many of the allocated fiber objects are currently
189    * in the free pool.
190    */
191   size_t fibersPoolSize() const;
192
193   /**
194    * return     true if running activeFiber_ is not nullptr.
195    */
196   bool hasActiveFiber();
197
198   /**
199    * @return What was the most observed fiber stack usage (in bytes).
200    */
201   size_t stackHighWatermark() const;
202
203   static FiberManager& getFiberManager();
204   static FiberManager* getFiberManagerUnsafe();
205
206  private:
207   friend class Baton;
208   friend class Fiber;
209   template <typename F>
210   struct AddTaskHelper;
211   template <typename F, typename G>
212   struct AddTaskFinallyHelper;
213
214   struct RemoteTask {
215     template <typename F>
216     explicit RemoteTask(F&& f) : func(std::move(f)) {}
217     std::function<void()> func;
218     folly::AtomicLinkedListHook<RemoteTask> nextRemoteTask;
219   };
220
221   typedef folly::IntrusiveList<Fiber, &Fiber::listHook_> FiberTailQueue;
222
223   Fiber* activeFiber_{nullptr}; /**< active fiber, nullptr on main context */
224
225   FiberTailQueue readyFibers_;  /**< queue of fibers ready to be executed */
226   FiberTailQueue fibersPool_;   /**< pool of unitialized Fiber objects */
227
228   size_t fibersAllocated_{0};   /**< total number of fibers allocated */
229   size_t fibersPoolSize_{0};    /**< total number of fibers in the free pool */
230   size_t fibersActive_{0};      /**< number of running or blocked fibers */
231
232   FContext::ContextStruct mainContext_;  /**< stores loop function context */
233
234   std::unique_ptr<LoopController> loopController_;
235   bool isLoopScheduled_{false}; /**< was the ready loop scheduled to run? */
236
237   /**
238    * When we are inside FiberManager loop this points to FiberManager. Otherwise
239    * it's nullptr
240    */
241   static __thread FiberManager* currentFiberManager_;
242
243   /**
244    * runInMainContext implementation for non-void functions.
245    */
246   template <typename F>
247   typename std::enable_if<
248     !std::is_same<typename std::result_of<F()>::type, void>::value,
249     typename std::result_of<F()>::type>::type
250   runInMainContextHelper(F&& func);
251
252   /**
253    * runInMainContext implementation for void functions
254    */
255   template <typename F>
256   typename std::enable_if<
257     std::is_same<typename std::result_of<F()>::type, void>::value,
258     void>::type
259   runInMainContextHelper(F&& func);
260
261   /**
262    * Allocator used to allocate stack for Fibers in the pool.
263    * Allocates stack on the stack of the main context.
264    */
265 #ifdef USE_GUARD_ALLOCATOR
266   /* This is too slow for production use; can be fixed
267      if we allocated all stack storage once upfront */
268   GuardPageAllocator stackAllocator_;
269 #else
270   std::allocator<unsigned char> stackAllocator_;
271 #endif
272
273   const Options options_;       /**< FiberManager options */
274
275   /**
276    * Largest observed individual Fiber stack usage in bytes.
277    */
278   size_t stackHighWatermark_{0};
279
280   /**
281    * Schedules a loop with loopController (unless already scheduled before).
282    */
283   void ensureLoopScheduled();
284
285   /**
286    * @return An initialized Fiber object from the pool
287    */
288   Fiber* getFiber();
289
290   /**
291    * Function passed to the await call.
292    */
293   std::function<void(Fiber&)> awaitFunc_;
294
295   /**
296    * Function passed to the runInMainContext call.
297    */
298   std::function<void()> immediateFunc_;
299
300   ExceptionCallback exceptionCallback_; /**< task exception callback */
301
302   folly::AtomicLinkedList<Fiber, &Fiber::nextRemoteReady_> remoteReadyQueue_;
303
304   folly::AtomicLinkedList<RemoteTask, &RemoteTask::nextRemoteTask>
305       remoteTaskQueue_;
306
307   std::shared_ptr<TimeoutController> timeoutManager_;
308
309   void runReadyFiber(Fiber* fiber);
310   void remoteReadyInsert(Fiber* fiber);
311 };
312
313 /**
314  * @return      true iff we are running in a fiber's context
315  */
316 inline bool onFiber() {
317   auto fm = FiberManager::getFiberManagerUnsafe();
318   return fm ? fm->hasActiveFiber() : false;
319 }
320
321 /**
322  * Add a new task to be executed.
323  *
324  * @param func Task functor; must have a signature of `void func()`.
325  *             The object will be destroyed once task execution is complete.
326  */
327 template <typename F>
328 inline void addTask(F&& func) {
329   return FiberManager::getFiberManager().addTask(std::forward<F>(func));
330 }
331
332 /**
333  * Add a new task. When the task is complete, execute finally(Try<Result>&&)
334  * on the main context.
335  * Task functor is run and destroyed on the fiber context.
336  * Finally functor is run and destroyed on the main context.
337  *
338  * @param func Task functor; must have a signature of `T func()` for some T.
339  * @param finally Finally functor; must have a signature of
340  *                `void finally(Try<T>&&)` and will be passed
341  *                the result of func() (including the exception if occurred).
342  */
343 template <typename F, typename G>
344 inline void addTaskFinally(F&& func, G&& finally) {
345   return FiberManager::getFiberManager().addTaskFinally(
346     std::forward<F>(func), std::forward<G>(finally));
347 }
348
349 /**
350  * Blocks task execution until given promise is fulfilled.
351  *
352  * Calls function passing in a Promise<T>, which has to be fulfilled.
353  *
354  * @return data which was used to fulfill the promise.
355  */
356 template <typename F>
357 typename FirstArgOf<F>::type::value_type
358 inline await(F&& func);
359
360 /**
361  * If called from a fiber, immediately switches to the FiberManager's context
362  * and runs func(), going back to the Fiber's context after completion.
363  * Outside a fiber, just calls func() directly.
364  *
365  * @return value returned by func().
366  */
367 template <typename F>
368 typename std::result_of<F()>::type
369 inline runInMainContext(F&& func) {
370   auto fm = FiberManager::getFiberManagerUnsafe();
371   if (UNLIKELY(fm == nullptr)) {
372     return func();
373   }
374   return fm->runInMainContext(std::forward<F>(func));
375 }
376
377 }}
378
379 #include "FiberManager-inl.h"