Clang-format in preparation for other change
[folly.git] / folly / experimental / fibers / FiberManager.cpp
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 #include "FiberManager.h"
17
18 #include <sys/syscall.h>
19 #include <unistd.h>
20
21 #include <cassert>
22 #include <stdexcept>
23
24 #include <glog/logging.h>
25
26 #include <folly/experimental/fibers/Fiber.h>
27 #include <folly/experimental/fibers/LoopController.h>
28
29 #ifdef FOLLY_SANITIZE_ADDRESS
30
31 #include <dlfcn.h>
32
33 static void __asan_enter_fiber_weak(
34     void const* fiber_stack_base,
35     size_t fiber_stack_extent)
36     __attribute__((__weakref__("__asan_enter_fiber")));
37 static void __asan_exit_fiber_weak()
38     __attribute__((__weakref__("__asan_exit_fiber")));
39
40 typedef void (*AsanEnterFiberFuncPtr)(void const*, size_t);
41 typedef void (*AsanExitFiberFuncPtr)();
42
43 namespace folly {
44 namespace fibers {
45
46 static AsanEnterFiberFuncPtr getEnterFiberFunc();
47 static AsanExitFiberFuncPtr getExitFiberFunc();
48 }
49 }
50
51 #endif
52
53 namespace folly {
54 namespace fibers {
55
56 FOLLY_TLS FiberManager* FiberManager::currentFiberManager_ = nullptr;
57
58 FiberManager::FiberManager(
59     std::unique_ptr<LoopController> loopController,
60     Options options)
61     : FiberManager(
62           LocalType<void>(),
63           std::move(loopController),
64           std::move(options)) {}
65
66 FiberManager::~FiberManager() {
67   if (isLoopScheduled_) {
68     loopController_->cancel();
69   }
70
71   while (!fibersPool_.empty()) {
72     fibersPool_.pop_front_and_dispose([](Fiber* fiber) { delete fiber; });
73   }
74   assert(readyFibers_.empty());
75   assert(fibersActive_ == 0);
76 }
77
78 LoopController& FiberManager::loopController() {
79   return *loopController_;
80 }
81
82 const LoopController& FiberManager::loopController() const {
83   return *loopController_;
84 }
85
86 bool FiberManager::hasTasks() const {
87   return fibersActive_ > 0 || !remoteReadyQueue_.empty() ||
88       !remoteTaskQueue_.empty();
89 }
90
91 Fiber* FiberManager::getFiber() {
92   Fiber* fiber = nullptr;
93
94   if (options_.fibersPoolResizePeriodMs > 0 && !fibersPoolResizerScheduled_) {
95     fibersPoolResizer_();
96     fibersPoolResizerScheduled_ = true;
97   }
98
99   if (fibersPool_.empty()) {
100     fiber = new Fiber(*this);
101     ++fibersAllocated_;
102   } else {
103     fiber = &fibersPool_.front();
104     fibersPool_.pop_front();
105     assert(fibersPoolSize_ > 0);
106     --fibersPoolSize_;
107   }
108   assert(fiber);
109   if (++fibersActive_ > maxFibersActiveLastPeriod_) {
110     maxFibersActiveLastPeriod_ = fibersActive_;
111   }
112   ++fiberId_;
113   bool recordStack = (options_.recordStackEvery != 0) &&
114       (fiberId_ % options_.recordStackEvery == 0);
115   return fiber;
116 }
117
118 void FiberManager::setExceptionCallback(FiberManager::ExceptionCallback ec) {
119   assert(ec);
120   exceptionCallback_ = std::move(ec);
121 }
122
123 size_t FiberManager::fibersAllocated() const {
124   return fibersAllocated_;
125 }
126
127 size_t FiberManager::fibersPoolSize() const {
128   return fibersPoolSize_;
129 }
130
131 size_t FiberManager::stackHighWatermark() const {
132   return stackHighWatermark_;
133 }
134
135 void FiberManager::remoteReadyInsert(Fiber* fiber) {
136   if (observer_) {
137     observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
138   }
139   auto insertHead = [&]() { return remoteReadyQueue_.insertHead(fiber); };
140   loopController_->scheduleThreadSafe(std::ref(insertHead));
141 }
142
143 void FiberManager::setObserver(ExecutionObserver* observer) {
144   observer_ = observer;
145 }
146
147 void FiberManager::setPreemptRunner(InlineFunctionRunner* preemptRunner) {
148   preemptRunner_ = preemptRunner;
149 }
150
151 void FiberManager::doFibersPoolResizing() {
152   while (fibersAllocated_ > maxFibersActiveLastPeriod_ &&
153          fibersPoolSize_ > options_.maxFibersPoolSize) {
154     auto fiber = &fibersPool_.front();
155     assert(fiber != nullptr);
156     fibersPool_.pop_front();
157     delete fiber;
158     --fibersPoolSize_;
159     --fibersAllocated_;
160   }
161
162   maxFibersActiveLastPeriod_ = fibersActive_;
163 }
164
165 void FiberManager::FibersPoolResizer::operator()() {
166   fiberManager_.doFibersPoolResizing();
167   fiberManager_.timeoutManager_->registerTimeout(
168       *this,
169       std::chrono::milliseconds(
170           fiberManager_.options_.fibersPoolResizePeriodMs));
171 }
172
173 #ifdef FOLLY_SANITIZE_ADDRESS
174
175 void FiberManager::registerFiberActivationWithAsan(Fiber* fiber) {
176   auto context = &fiber->fcontext_;
177   void* top = context->stackBase();
178   void* bottom = context->stackLimit();
179   size_t extent = static_cast<char*>(top) - static_cast<char*>(bottom);
180
181   // Check if we can find a fiber enter function and call it if we find one
182   static AsanEnterFiberFuncPtr fn = getEnterFiberFunc();
183   if (fn == nullptr) {
184     LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
185   } else {
186     fn(bottom, extent);
187   }
188 }
189
190 void FiberManager::registerFiberDeactivationWithAsan(Fiber* fiber) {
191   (void)fiber; // currently unused
192
193   // Check if we can find a fiber exit function and call it if we find one
194   static AsanExitFiberFuncPtr fn = getExitFiberFunc();
195   if (fn == nullptr) {
196     LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
197   } else {
198     fn();
199   }
200 }
201
202 static AsanEnterFiberFuncPtr getEnterFiberFunc() {
203   AsanEnterFiberFuncPtr fn{nullptr};
204
205   // Check whether weak reference points to statically linked enter function
206   if (nullptr != (fn = &::__asan_enter_fiber_weak)) {
207     return fn;
208   }
209
210   // Check whether we can find a dynamically linked enter function
211   if (nullptr !=
212       (fn = (AsanEnterFiberFuncPtr)dlsym(RTLD_DEFAULT, "__asan_enter_fiber"))) {
213     return fn;
214   }
215
216   // Couldn't find the function at all
217   return nullptr;
218 }
219
220 static AsanExitFiberFuncPtr getExitFiberFunc() {
221   AsanExitFiberFuncPtr fn{nullptr};
222
223   // Check whether weak reference points to statically linked exit function
224   if (nullptr != (fn = &::__asan_exit_fiber_weak)) {
225     return fn;
226   }
227
228   // Check whether we can find a dynamically linked enter function
229   if (nullptr !=
230       (fn = (AsanExitFiberFuncPtr)dlsym(RTLD_DEFAULT, "__asan_exit_fiber"))) {
231     return fn;
232   }
233
234   // Couldn't find the function at all
235   return nullptr;
236 }
237
238 #endif // FOLLY_SANITIZE_ADDRESS
239 }
240 }