Use Fiber locals for TAO Fiber-locals
[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. Safe to call from other threads.
140    *
141    * @param func Task function; must have a signature of `void func()`.
142    *             The object will be destroyed once task execution is complete.
143    */
144   template <typename F>
145   void addTaskRemote(F&& func);
146
147   /**
148    * Add a new task. When the task is complete, execute finally(Try<Result>&&)
149    * on the main context.
150    *
151    * @param func Task functor; must have a signature of `T func()` for some T.
152    * @param finally Finally functor; must have a signature of
153    *                `void finally(Try<T>&&)` and will be passed
154    *                the result of func() (including the exception if occurred).
155    */
156   template <typename F, typename G>
157   void addTaskFinally(F&& func, G&& finally);
158
159   /**
160    * If called from a fiber, immediately switches to the FiberManager's context
161    * and runs func(), going back to the Fiber's context after completion.
162    * Outside a fiber, just calls func() directly.
163    *
164    * @return value returned by func().
165    */
166   template <typename F>
167   typename std::result_of<F()>::type
168   runInMainContext(F&& func);
169
170   /**
171    * Returns a refference to a fiber-local context for given Fiber. Should be
172    * always called with the same T for each fiber. Fiber-local context is lazily
173    * default-constructed on first request.
174    * When new task is scheduled via addTask / addTaskRemote from a fiber its
175    * fiber-local context is copied into the new fiber.
176    */
177   template <typename T>
178   T& local();
179
180   template <typename T>
181   static T& localThread();
182
183   /**
184    * @return How many fiber objects (and stacks) has this manager allocated.
185    */
186   size_t fibersAllocated() const;
187
188   /**
189    * @return How many of the allocated fiber objects are currently
190    * in the free pool.
191    */
192   size_t fibersPoolSize() const;
193
194   /**
195    * return     true if running activeFiber_ is not nullptr.
196    */
197   bool hasActiveFiber();
198
199   /**
200    * @return What was the most observed fiber stack usage (in bytes).
201    */
202   size_t stackHighWatermark() const;
203
204   static FiberManager& getFiberManager();
205   static FiberManager* getFiberManagerUnsafe();
206
207  private:
208   friend class Baton;
209   friend class Fiber;
210   template <typename F>
211   struct AddTaskHelper;
212   template <typename F, typename G>
213   struct AddTaskFinallyHelper;
214
215   struct RemoteTask {
216     template <typename F>
217     explicit RemoteTask(F&& f) : func(std::forward<F>(f)) {}
218     template <typename F>
219     RemoteTask(F&& f, const Fiber::LocalData& localData_) :
220         func(std::forward<F>(f)),
221         localData(folly::make_unique<Fiber::LocalData>(localData_)) {}
222     std::function<void()> func;
223     std::unique_ptr<Fiber::LocalData> localData;
224     AtomicLinkedListHook<RemoteTask> nextRemoteTask;
225   };
226
227   typedef folly::IntrusiveList<Fiber, &Fiber::listHook_> FiberTailQueue;
228
229   Fiber* activeFiber_{nullptr}; /**< active fiber, nullptr on main context */
230   /**
231    * Same as active fiber, but also set for functions run from fiber on main
232    * context.
233    */
234   Fiber* currentFiber_{nullptr};
235
236   FiberTailQueue readyFibers_;  /**< queue of fibers ready to be executed */
237   FiberTailQueue fibersPool_;   /**< pool of unitialized Fiber objects */
238
239   size_t fibersAllocated_{0};   /**< total number of fibers allocated */
240   size_t fibersPoolSize_{0};    /**< total number of fibers in the free pool */
241   size_t fibersActive_{0};      /**< number of running or blocked fibers */
242
243   FContext::ContextStruct mainContext_;  /**< stores loop function context */
244
245   std::unique_ptr<LoopController> loopController_;
246   bool isLoopScheduled_{false}; /**< was the ready loop scheduled to run? */
247
248   /**
249    * When we are inside FiberManager loop this points to FiberManager. Otherwise
250    * it's nullptr
251    */
252   static __thread FiberManager* currentFiberManager_;
253
254   /**
255    * runInMainContext implementation for non-void functions.
256    */
257   template <typename F>
258   typename std::enable_if<
259     !std::is_same<typename std::result_of<F()>::type, void>::value,
260     typename std::result_of<F()>::type>::type
261   runInMainContextHelper(F&& func);
262
263   /**
264    * runInMainContext implementation for void functions
265    */
266   template <typename F>
267   typename std::enable_if<
268     std::is_same<typename std::result_of<F()>::type, void>::value,
269     void>::type
270   runInMainContextHelper(F&& func);
271
272   /**
273    * Allocator used to allocate stack for Fibers in the pool.
274    * Allocates stack on the stack of the main context.
275    */
276 #ifdef USE_GUARD_ALLOCATOR
277   /* This is too slow for production use; can be fixed
278      if we allocated all stack storage once upfront */
279   GuardPageAllocator stackAllocator_;
280 #else
281   std::allocator<unsigned char> stackAllocator_;
282 #endif
283
284   const Options options_;       /**< FiberManager options */
285
286   /**
287    * Largest observed individual Fiber stack usage in bytes.
288    */
289   size_t stackHighWatermark_{0};
290
291   /**
292    * Schedules a loop with loopController (unless already scheduled before).
293    */
294   void ensureLoopScheduled();
295
296   /**
297    * @return An initialized Fiber object from the pool
298    */
299   Fiber* getFiber();
300
301   /**
302    * Function passed to the await call.
303    */
304   std::function<void(Fiber&)> awaitFunc_;
305
306   /**
307    * Function passed to the runInMainContext call.
308    */
309   std::function<void()> immediateFunc_;
310
311   ExceptionCallback exceptionCallback_; /**< task exception callback */
312
313   folly::AtomicLinkedList<Fiber, &Fiber::nextRemoteReady_> remoteReadyQueue_;
314
315   folly::AtomicLinkedList<RemoteTask, &RemoteTask::nextRemoteTask>
316       remoteTaskQueue_;
317
318   std::shared_ptr<TimeoutController> timeoutManager_;
319
320   void runReadyFiber(Fiber* fiber);
321   void remoteReadyInsert(Fiber* fiber);
322 };
323
324 /**
325  * @return      true iff we are running in a fiber's context
326  */
327 inline bool onFiber() {
328   auto fm = FiberManager::getFiberManagerUnsafe();
329   return fm ? fm->hasActiveFiber() : false;
330 }
331
332 /**
333  * Add a new task to be executed.
334  *
335  * @param func Task functor; must have a signature of `void func()`.
336  *             The object will be destroyed once task execution is complete.
337  */
338 template <typename F>
339 inline void addTask(F&& func) {
340   return FiberManager::getFiberManager().addTask(std::forward<F>(func));
341 }
342
343 /**
344  * Add a new task. When the task is complete, execute finally(Try<Result>&&)
345  * on the main context.
346  * Task functor is run and destroyed on the fiber context.
347  * Finally functor is run and destroyed on the main context.
348  *
349  * @param func Task functor; must have a signature of `T func()` for some T.
350  * @param finally Finally functor; must have a signature of
351  *                `void finally(Try<T>&&)` and will be passed
352  *                the result of func() (including the exception if occurred).
353  */
354 template <typename F, typename G>
355 inline void addTaskFinally(F&& func, G&& finally) {
356   return FiberManager::getFiberManager().addTaskFinally(
357     std::forward<F>(func), std::forward<G>(finally));
358 }
359
360 /**
361  * Blocks task execution until given promise is fulfilled.
362  *
363  * Calls function passing in a Promise<T>, which has to be fulfilled.
364  *
365  * @return data which was used to fulfill the promise.
366  */
367 template <typename F>
368 typename FirstArgOf<F>::type::value_type
369 inline await(F&& func);
370
371 /**
372  * If called from a fiber, immediately switches to the FiberManager's context
373  * and runs func(), going back to the Fiber's context after completion.
374  * Outside a fiber, just calls func() directly.
375  *
376  * @return value returned by func().
377  */
378 template <typename F>
379 typename std::result_of<F()>::type
380 inline runInMainContext(F&& func) {
381   auto fm = FiberManager::getFiberManagerUnsafe();
382   if (UNLIKELY(fm == nullptr)) {
383     return func();
384   }
385   return fm->runInMainContext(std::forward<F>(func));
386 }
387
388 /**
389  * Returns a refference to a fiber-local context for given Fiber. Should be
390  * always called with the same T for each fiber. Fiber-local context is lazily
391  * default-constructed on first request.
392  * When new task is scheduled via addTask / addTaskRemote from a fiber its
393  * fiber-local context is copied into the new fiber.
394  */
395 template <typename T>
396 T& local() {
397   auto fm = FiberManager::getFiberManagerUnsafe();
398   if (fm) {
399     return fm->local<T>();
400   }
401   return FiberManager::localThread<T>();
402 }
403
404 }}
405
406 #include "FiberManager-inl.h"