2d17a4a34dff4f79ec8bf070c7ff16229d87c909
[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::unpoisonFiberStack(const Fiber* fiber) {
221   auto stack = fiber->getStack();
222
223   // Check if we can find a fiber enter function and call it if we find one
224   static AsanUnpoisonMemoryRegionFuncPtr fn = getUnpoisonMemoryRegionFunc();
225   if (fn == nullptr) {
226     LOG(FATAL) << "This version of ASAN doesn't support memory unpoisoning";
227   } else {
228     fn(stack.first, stack.second);
229   }
230 }
231
232 static AsanStartSwitchStackFuncPtr getStartSwitchStackFunc() {
233   AsanStartSwitchStackFuncPtr fn{nullptr};
234
235   // Check whether weak reference points to statically linked enter function
236   if (nullptr != (fn = &::__sanitizer_start_switch_fiber_weak)) {
237     return fn;
238   }
239
240   // Check whether we can find a dynamically linked enter function
241   if (nullptr != (fn = (AsanStartSwitchStackFuncPtr)dlsym(
242                       RTLD_DEFAULT, "__sanitizer_start_switch_fiber"))) {
243     return fn;
244   }
245
246   // Couldn't find the function at all
247   return nullptr;
248 }
249
250 static AsanFinishSwitchStackFuncPtr getFinishSwitchStackFunc() {
251   AsanFinishSwitchStackFuncPtr fn{nullptr};
252
253   // Check whether weak reference points to statically linked exit function
254   if (nullptr != (fn = &::__sanitizer_finish_switch_fiber_weak)) {
255     return fn;
256   }
257
258   // Check whether we can find a dynamically linked exit function
259   if (nullptr != (fn = (AsanFinishSwitchStackFuncPtr)dlsym(
260                       RTLD_DEFAULT, "__sanitizer_finish_switch_fiber"))) {
261     return fn;
262   }
263
264   // Couldn't find the function at all
265   return nullptr;
266 }
267
268 static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc() {
269   AsanUnpoisonMemoryRegionFuncPtr fn{nullptr};
270
271   // Check whether weak reference points to statically linked unpoison function
272   if (nullptr != (fn = &::__asan_unpoison_memory_region_weak)) {
273     return fn;
274   }
275
276   // Check whether we can find a dynamically linked unpoison function
277   if (nullptr != (fn = (AsanUnpoisonMemoryRegionFuncPtr)dlsym(
278                       RTLD_DEFAULT, "__asan_unpoison_memory_region"))) {
279     return fn;
280   }
281
282   // Couldn't find the function at all
283   return nullptr;
284 }
285
286 #endif // FOLLY_SANITIZE_ADDRESS
287
288 #ifndef _WIN32
289 namespace {
290
291 // SIGSTKSZ (8 kB on our architectures) isn't always enough for
292 // folly::symbolizer, so allocate 32 kB.
293 constexpr size_t kAltStackSize = folly::constexpr_max(SIGSTKSZ, 32 * 1024);
294
295 bool hasAlternateStack() {
296   stack_t ss;
297   sigaltstack(nullptr, &ss);
298   return !(ss.ss_flags & SS_DISABLE);
299 }
300
301 int setAlternateStack(char* sp, size_t size) {
302   CHECK(sp);
303   stack_t ss{};
304   ss.ss_sp = sp;
305   ss.ss_size = size;
306   return sigaltstack(&ss, nullptr);
307 }
308
309 int unsetAlternateStack() {
310   stack_t ss{};
311   ss.ss_flags = SS_DISABLE;
312   return sigaltstack(&ss, nullptr);
313 }
314
315 class ScopedAlternateSignalStack {
316  public:
317   ScopedAlternateSignalStack() {
318     if (hasAlternateStack()) {
319       return;
320     }
321
322     stack_ = folly::make_unique<AltStackBuffer>();
323
324     setAlternateStack(stack_->data(), stack_->size());
325   }
326
327   ~ScopedAlternateSignalStack() {
328     if (stack_) {
329       unsetAlternateStack();
330     }
331   }
332
333  private:
334   using AltStackBuffer = std::array<char, kAltStackSize>;
335   std::unique_ptr<AltStackBuffer> stack_;
336 };
337 }
338
339 void FiberManager::registerAlternateSignalStack() {
340   static folly::SingletonThreadLocal<ScopedAlternateSignalStack> singleton;
341   singleton.get();
342
343   alternateSignalStackRegistered_ = true;
344 }
345 #endif
346 }
347 }