Apply clang-format to folly/fibers/
[folly.git] / folly / fibers / FiberManager.cpp
1 /*
2  * Copyright 2017 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 "FiberManagerInternal.h"
17
18 #include <signal.h>
19
20 #include <cassert>
21 #include <stdexcept>
22
23 #include <glog/logging.h>
24
25 #include <folly/fibers/Fiber.h>
26 #include <folly/fibers/LoopController.h>
27
28 #include <folly/SingletonThreadLocal.h>
29 #include <folly/portability/SysSyscall.h>
30 #include <folly/portability/Unistd.h>
31
32 #ifdef FOLLY_SANITIZE_ADDRESS
33
34 #include <dlfcn.h>
35
36 static void __sanitizer_start_switch_fiber_weak(
37     void** fake_stack_save,
38     void const* fiber_stack_base,
39     size_t fiber_stack_extent)
40     __attribute__((__weakref__("__sanitizer_start_switch_fiber")));
41 static void __sanitizer_finish_switch_fiber_weak(
42     void* fake_stack_save,
43     void const** old_stack_base,
44     size_t* old_stack_extent)
45     __attribute__((__weakref__("__sanitizer_finish_switch_fiber")));
46 static void __asan_unpoison_memory_region_weak(
47     void const /* nolint */ volatile* addr,
48     size_t size) __attribute__((__weakref__("__asan_unpoison_memory_region")));
49
50 typedef void (*AsanStartSwitchStackFuncPtr)(void**, void const*, size_t);
51 typedef void (*AsanFinishSwitchStackFuncPtr)(void*, void const**, size_t*);
52 typedef void (*AsanUnpoisonMemoryRegionFuncPtr)(
53     void const /* nolint */ volatile*,
54     size_t);
55
56 namespace folly {
57 namespace fibers {
58
59 static AsanStartSwitchStackFuncPtr getStartSwitchStackFunc();
60 static AsanFinishSwitchStackFuncPtr getFinishSwitchStackFunc();
61 static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc();
62 }
63 }
64
65 #endif
66
67 namespace folly {
68 namespace fibers {
69
70 FOLLY_TLS FiberManager* FiberManager::currentFiberManager_ = nullptr;
71
72 FiberManager::FiberManager(
73     std::unique_ptr<LoopController> loopController,
74     Options options)
75     : FiberManager(
76           LocalType<void>(),
77           std::move(loopController),
78           std::move(options)) {}
79
80 FiberManager::~FiberManager() {
81   if (isLoopScheduled_) {
82     loopController_->cancel();
83   }
84
85   while (!fibersPool_.empty()) {
86     fibersPool_.pop_front_and_dispose([](Fiber* fiber) { delete fiber; });
87   }
88   assert(readyFibers_.empty());
89   assert(fibersActive_ == 0);
90 }
91
92 LoopController& FiberManager::loopController() {
93   return *loopController_;
94 }
95
96 const LoopController& FiberManager::loopController() const {
97   return *loopController_;
98 }
99
100 bool FiberManager::hasTasks() const {
101   return fibersActive_ > 0 || !remoteReadyQueue_.empty() ||
102       !remoteTaskQueue_.empty();
103 }
104
105 Fiber* FiberManager::getFiber() {
106   Fiber* fiber = nullptr;
107
108   if (options_.fibersPoolResizePeriodMs > 0 && !fibersPoolResizerScheduled_) {
109     fibersPoolResizer_();
110     fibersPoolResizerScheduled_ = true;
111   }
112
113   if (fibersPool_.empty()) {
114     fiber = new Fiber(*this);
115     ++fibersAllocated_;
116   } else {
117     fiber = &fibersPool_.front();
118     fibersPool_.pop_front();
119     assert(fibersPoolSize_ > 0);
120     --fibersPoolSize_;
121   }
122   assert(fiber);
123   if (++fibersActive_ > maxFibersActiveLastPeriod_) {
124     maxFibersActiveLastPeriod_ = fibersActive_;
125   }
126   ++fiberId_;
127   bool recordStack = (options_.recordStackEvery != 0) &&
128       (fiberId_ % options_.recordStackEvery == 0);
129   fiber->init(recordStack);
130   return fiber;
131 }
132
133 void FiberManager::setExceptionCallback(FiberManager::ExceptionCallback ec) {
134   assert(ec);
135   exceptionCallback_ = std::move(ec);
136 }
137
138 size_t FiberManager::fibersAllocated() const {
139   return fibersAllocated_;
140 }
141
142 size_t FiberManager::fibersPoolSize() const {
143   return fibersPoolSize_;
144 }
145
146 size_t FiberManager::stackHighWatermark() const {
147   return stackHighWatermark_;
148 }
149
150 void FiberManager::remoteReadyInsert(Fiber* fiber) {
151   if (observer_) {
152     observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
153   }
154   auto insertHead = [&]() { return remoteReadyQueue_.insertHead(fiber); };
155   loopController_->scheduleThreadSafe(std::ref(insertHead));
156 }
157
158 void FiberManager::setObserver(ExecutionObserver* observer) {
159   observer_ = observer;
160 }
161
162 ExecutionObserver* FiberManager::getObserver() {
163   return observer_;
164 }
165
166 void FiberManager::setPreemptRunner(InlineFunctionRunner* preemptRunner) {
167   preemptRunner_ = preemptRunner;
168 }
169
170 void FiberManager::doFibersPoolResizing() {
171   while (fibersAllocated_ > maxFibersActiveLastPeriod_ &&
172          fibersPoolSize_ > options_.maxFibersPoolSize) {
173     auto fiber = &fibersPool_.front();
174     assert(fiber != nullptr);
175     fibersPool_.pop_front();
176     delete fiber;
177     --fibersPoolSize_;
178     --fibersAllocated_;
179   }
180
181   maxFibersActiveLastPeriod_ = fibersActive_;
182 }
183
184 void FiberManager::FibersPoolResizer::operator()() {
185   fiberManager_.doFibersPoolResizing();
186   fiberManager_.timeoutManager_->registerTimeout(
187       *this,
188       std::chrono::milliseconds(
189           fiberManager_.options_.fibersPoolResizePeriodMs));
190 }
191
192 #ifdef FOLLY_SANITIZE_ADDRESS
193
194 void FiberManager::registerStartSwitchStackWithAsan(
195     void** saveFakeStack,
196     const void* stackBottom,
197     size_t stackSize) {
198   // Check if we can find a fiber enter function and call it if we find one
199   static AsanStartSwitchStackFuncPtr fn = getStartSwitchStackFunc();
200   if (fn == nullptr) {
201     LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
202   } else {
203     fn(saveFakeStack, stackBottom, stackSize);
204   }
205 }
206
207 void FiberManager::registerFinishSwitchStackWithAsan(
208     void* saveFakeStack,
209     const void** saveStackBottom,
210     size_t* saveStackSize) {
211   // Check if we can find a fiber exit function and call it if we find one
212   static AsanFinishSwitchStackFuncPtr fn = getFinishSwitchStackFunc();
213   if (fn == nullptr) {
214     LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
215   } else {
216     fn(saveFakeStack, saveStackBottom, saveStackSize);
217   }
218 }
219
220 void FiberManager::freeFakeStack(void* fakeStack) {
221   static AsanStartSwitchStackFuncPtr fnStart = getStartSwitchStackFunc();
222   static AsanFinishSwitchStackFuncPtr fnFinish = getFinishSwitchStackFunc();
223   if (fnStart == nullptr || fnFinish == nullptr) {
224     LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
225   }
226
227   void* saveFakeStack;
228   const void* stackBottom;
229   size_t stackSize;
230   fnStart(&saveFakeStack, nullptr, 0);
231   fnFinish(fakeStack, &stackBottom, &stackSize);
232   fnStart(nullptr, stackBottom, stackSize);
233   fnFinish(saveFakeStack, nullptr, nullptr);
234 }
235
236 void FiberManager::unpoisonFiberStack(const Fiber* fiber) {
237   auto stack = fiber->getStack();
238
239   // Check if we can find a fiber enter function and call it if we find one
240   static AsanUnpoisonMemoryRegionFuncPtr fn = getUnpoisonMemoryRegionFunc();
241   if (fn == nullptr) {
242     LOG(FATAL) << "This version of ASAN doesn't support memory unpoisoning";
243   } else {
244     fn(stack.first, stack.second);
245   }
246 }
247
248 static AsanStartSwitchStackFuncPtr getStartSwitchStackFunc() {
249   AsanStartSwitchStackFuncPtr fn{nullptr};
250
251   // Check whether weak reference points to statically linked enter function
252   if (nullptr != (fn = &::__sanitizer_start_switch_fiber_weak)) {
253     return fn;
254   }
255
256   // Check whether we can find a dynamically linked enter function
257   if (nullptr !=
258       (fn = (AsanStartSwitchStackFuncPtr)dlsym(
259            RTLD_DEFAULT, "__sanitizer_start_switch_fiber"))) {
260     return fn;
261   }
262
263   // Couldn't find the function at all
264   return nullptr;
265 }
266
267 static AsanFinishSwitchStackFuncPtr getFinishSwitchStackFunc() {
268   AsanFinishSwitchStackFuncPtr fn{nullptr};
269
270   // Check whether weak reference points to statically linked exit function
271   if (nullptr != (fn = &::__sanitizer_finish_switch_fiber_weak)) {
272     return fn;
273   }
274
275   // Check whether we can find a dynamically linked exit function
276   if (nullptr !=
277       (fn = (AsanFinishSwitchStackFuncPtr)dlsym(
278            RTLD_DEFAULT, "__sanitizer_finish_switch_fiber"))) {
279     return fn;
280   }
281
282   // Couldn't find the function at all
283   return nullptr;
284 }
285
286 static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc() {
287   AsanUnpoisonMemoryRegionFuncPtr fn{nullptr};
288
289   // Check whether weak reference points to statically linked unpoison function
290   if (nullptr != (fn = &::__asan_unpoison_memory_region_weak)) {
291     return fn;
292   }
293
294   // Check whether we can find a dynamically linked unpoison function
295   if (nullptr !=
296       (fn = (AsanUnpoisonMemoryRegionFuncPtr)dlsym(
297            RTLD_DEFAULT, "__asan_unpoison_memory_region"))) {
298     return fn;
299   }
300
301   // Couldn't find the function at all
302   return nullptr;
303 }
304
305 #endif // FOLLY_SANITIZE_ADDRESS
306
307 #ifndef _WIN32
308 namespace {
309
310 // SIGSTKSZ (8 kB on our architectures) isn't always enough for
311 // folly::symbolizer, so allocate 32 kB.
312 constexpr size_t kAltStackSize = folly::constexpr_max(SIGSTKSZ, 32 * 1024);
313
314 bool hasAlternateStack() {
315   stack_t ss;
316   sigaltstack(nullptr, &ss);
317   return !(ss.ss_flags & SS_DISABLE);
318 }
319
320 int setAlternateStack(char* sp, size_t size) {
321   CHECK(sp);
322   stack_t ss{};
323   ss.ss_sp = sp;
324   ss.ss_size = size;
325   return sigaltstack(&ss, nullptr);
326 }
327
328 int unsetAlternateStack() {
329   stack_t ss{};
330   ss.ss_flags = SS_DISABLE;
331   return sigaltstack(&ss, nullptr);
332 }
333
334 class ScopedAlternateSignalStack {
335  public:
336   ScopedAlternateSignalStack() {
337     if (hasAlternateStack()) {
338       return;
339     }
340
341     stack_ = std::make_unique<AltStackBuffer>();
342
343     setAlternateStack(stack_->data(), stack_->size());
344   }
345
346   ~ScopedAlternateSignalStack() {
347     if (stack_) {
348       unsetAlternateStack();
349     }
350   }
351
352  private:
353   using AltStackBuffer = std::array<char, kAltStackSize>;
354   std::unique_ptr<AltStackBuffer> stack_;
355 };
356 }
357
358 void FiberManager::registerAlternateSignalStack() {
359   static folly::SingletonThreadLocal<ScopedAlternateSignalStack> singleton;
360   singleton.get();
361
362   alternateSignalStackRegistered_ = true;
363 }
364 #endif
365 }
366 }