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