Simplify activateFiber()/deactivateFiber()
[folly.git] / folly / fibers / Fiber.h
1 /*
2  * Copyright 2016 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 <thread>
20 #include <typeinfo>
21
22 #include <folly/AtomicIntrusiveLinkedList.h>
23 #include <folly/CPortability.h>
24 #include <folly/Function.h>
25 #include <folly/IntrusiveList.h>
26 #include <folly/Portability.h>
27 #include <folly/fibers/BoostContextCompatibility.h>
28 #include <folly/io/async/Request.h>
29
30 namespace folly {
31 namespace fibers {
32
33 class Baton;
34 class FiberManager;
35
36 /**
37  * @class Fiber
38  * @brief Fiber object used by FiberManager to execute tasks.
39  *
40  * Each Fiber object can be executing at most one task at a time. In active
41  * phase it is running the task function and keeps its context.
42  * Fiber is also used to pass data to blocked task and thus unblock it.
43  * Each Fiber may be associated with a single FiberManager.
44  */
45 class Fiber {
46  public:
47   /**
48    * Resume the blocked task
49    */
50   void resume();
51
52   Fiber(const Fiber&) = delete;
53   Fiber& operator=(const Fiber&) = delete;
54
55   ~Fiber();
56
57   /**
58    * Retrieve this fiber's base stack and stack size.
59    *
60    * @return This fiber's stack pointer and stack size.
61    */
62   std::pair<void*, size_t> getStack() const {
63     void* const stack =
64         std::min<void*>(fcontext_.stackLimit(), fcontext_.stackBase());
65     const size_t size = std::abs(
66         reinterpret_cast<intptr_t>(fcontext_.stackBase()) -
67         reinterpret_cast<intptr_t>(fcontext_.stackLimit()));
68     return {stack, size};
69   }
70
71  private:
72   enum State {
73     INVALID, /**< Does't have task function */
74     NOT_STARTED, /**< Has task function, not started */
75     READY_TO_RUN, /**< Was started, blocked, then unblocked */
76     RUNNING, /**< Is running right now */
77     AWAITING, /**< Is currently blocked */
78     AWAITING_IMMEDIATE, /**< Was preempted to run an immediate function,
79                              and will be resumed right away */
80     YIELDED, /**< The fiber yielded execution voluntarily */
81   };
82
83   State state_{INVALID}; /**< current Fiber state */
84
85   friend class Baton;
86   friend class FiberManager;
87
88   explicit Fiber(FiberManager& fiberManager);
89
90   void init(bool recordStackUsed);
91
92   template <typename F>
93   void setFunction(F&& func);
94
95   template <typename F, typename G>
96   void setFunctionFinally(F&& func, G&& finally);
97
98   static void fiberFuncHelper(intptr_t fiber);
99   void fiberFunc();
100
101   /**
102    * Switch out of fiber context into the main context,
103    * performing necessary housekeeping for the new state.
104    *
105    * @param state New state, must not be RUNNING.
106    */
107   void preempt(State state);
108
109   /**
110    * Examines how much of the stack we used at this moment and
111    * registers with the FiberManager (for monitoring).
112    */
113   void recordStackPosition();
114
115   FiberManager& fiberManager_; /**< Associated FiberManager */
116   FContext fcontext_; /**< current task execution context */
117   std::shared_ptr<RequestContext> rcontext_; /**< current RequestContext */
118   folly::Function<void()> func_; /**< task function */
119   bool recordStackUsed_{false};
120   bool stackFilledWithMagic_{false};
121
122   /**
123    * Points to next fiber in remote ready list
124    */
125   folly::AtomicIntrusiveLinkedListHook<Fiber> nextRemoteReady_;
126
127   static constexpr size_t kUserBufferSize = 256;
128   std::aligned_storage<kUserBufferSize>::type userBuffer_;
129
130   void* getUserBuffer();
131
132   folly::Function<void()> resultFunc_;
133   folly::Function<void()> finallyFunc_;
134
135   class LocalData {
136    public:
137     LocalData() {}
138     LocalData(const LocalData& other);
139     LocalData& operator=(const LocalData& other);
140
141     template <typename T>
142     T& get() {
143       if (data_) {
144         assert(*dataType_ == typeid(T));
145         return *reinterpret_cast<T*>(data_);
146       }
147       return getSlow<T>();
148     }
149
150     void reset();
151
152     // private:
153     template <typename T>
154     FOLLY_NOINLINE T& getSlow();
155
156     static void* allocateHeapBuffer(size_t size);
157     static void freeHeapBuffer(void* buffer);
158
159     template <typename T>
160     static void dataCopyConstructor(void*, const void*);
161     template <typename T>
162     static void dataBufferDestructor(void*);
163     template <typename T>
164     static void dataHeapDestructor(void*);
165
166     static constexpr size_t kBufferSize = 128;
167     std::aligned_storage<kBufferSize>::type buffer_;
168     size_t dataSize_;
169
170     const std::type_info* dataType_;
171     void (*dataDestructor_)(void*);
172     void (*dataCopyConstructor_)(void*, const void*);
173     void* data_{nullptr};
174   };
175
176   LocalData localData_;
177
178   folly::IntrusiveListHook listHook_; /**< list hook for different FiberManager
179                                            queues */
180   folly::IntrusiveListHook globalListHook_; /**< list hook for global list */
181   std::thread::id threadId_{};
182
183 #ifdef FOLLY_SANITIZE_ADDRESS
184   void* asanFakeStack_{nullptr};
185   const void* asanMainStackBase_{nullptr};
186   size_t asanMainStackSize_{0};
187 #endif
188 };
189 }
190 }
191
192 #include <folly/fibers/Fiber-inl.h>