Expose current observer from FiberManager
[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 ExecutionObserver* FiberManager::getObserver() {
158   return observer_;
159 }
160
161 void FiberManager::setPreemptRunner(InlineFunctionRunner* preemptRunner) {
162   preemptRunner_ = preemptRunner;
163 }
164
165 void FiberManager::doFibersPoolResizing() {
166   while (fibersAllocated_ > maxFibersActiveLastPeriod_ &&
167          fibersPoolSize_ > options_.maxFibersPoolSize) {
168     auto fiber = &fibersPool_.front();
169     assert(fiber != nullptr);
170     fibersPool_.pop_front();
171     delete fiber;
172     --fibersPoolSize_;
173     --fibersAllocated_;
174   }
175
176   maxFibersActiveLastPeriod_ = fibersActive_;
177 }
178
179 void FiberManager::FibersPoolResizer::operator()() {
180   fiberManager_.doFibersPoolResizing();
181   fiberManager_.timeoutManager_->registerTimeout(
182       *this,
183       std::chrono::milliseconds(
184           fiberManager_.options_.fibersPoolResizePeriodMs));
185 }
186
187 #ifdef FOLLY_SANITIZE_ADDRESS
188
189 void FiberManager::registerFiberActivationWithAsan(Fiber* fiber) {
190   auto context = &fiber->fcontext_;
191   void* top = context->stackBase();
192   void* bottom = context->stackLimit();
193   size_t extent = static_cast<char*>(top) - static_cast<char*>(bottom);
194
195   // Check if we can find a fiber enter function and call it if we find one
196   static AsanEnterFiberFuncPtr fn = getEnterFiberFunc();
197   if (fn == nullptr) {
198     LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
199   } else {
200     fn(bottom, extent);
201   }
202 }
203
204 void FiberManager::registerFiberDeactivationWithAsan(Fiber* fiber) {
205   (void)fiber; // currently unused
206
207   // Check if we can find a fiber exit function and call it if we find one
208   static AsanExitFiberFuncPtr fn = getExitFiberFunc();
209   if (fn == nullptr) {
210     LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
211   } else {
212     fn();
213   }
214 }
215
216 void FiberManager::unpoisonFiberStack(const Fiber* fiber) {
217   auto stack = fiber->getStack();
218
219   // Check if we can find a fiber enter function and call it if we find one
220   static AsanUnpoisonMemoryRegionFuncPtr fn = getUnpoisonMemoryRegionFunc();
221   if (fn == nullptr) {
222     LOG(FATAL) << "This version of ASAN doesn't support memory unpoisoning";
223   } else {
224     fn(stack.first, stack.second);
225   }
226 }
227
228 static AsanEnterFiberFuncPtr getEnterFiberFunc() {
229   AsanEnterFiberFuncPtr fn{nullptr};
230
231   // Check whether weak reference points to statically linked enter function
232   if (nullptr != (fn = &::__asan_enter_fiber_weak)) {
233     return fn;
234   }
235
236   // Check whether we can find a dynamically linked enter function
237   if (nullptr !=
238       (fn = (AsanEnterFiberFuncPtr)dlsym(RTLD_DEFAULT, "__asan_enter_fiber"))) {
239     return fn;
240   }
241
242   // Couldn't find the function at all
243   return nullptr;
244 }
245
246 static AsanExitFiberFuncPtr getExitFiberFunc() {
247   AsanExitFiberFuncPtr fn{nullptr};
248
249   // Check whether weak reference points to statically linked exit function
250   if (nullptr != (fn = &::__asan_exit_fiber_weak)) {
251     return fn;
252   }
253
254   // Check whether we can find a dynamically linked exit function
255   if (nullptr !=
256       (fn = (AsanExitFiberFuncPtr)dlsym(RTLD_DEFAULT, "__asan_exit_fiber"))) {
257     return fn;
258   }
259
260   // Couldn't find the function at all
261   return nullptr;
262 }
263
264 static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc() {
265   AsanUnpoisonMemoryRegionFuncPtr fn{nullptr};
266
267   // Check whether weak reference points to statically linked unpoison function
268   if (nullptr != (fn = &::__asan_unpoison_memory_region_weak)) {
269     return fn;
270   }
271
272   // Check whether we can find a dynamically linked unpoison function
273   if (nullptr != (fn = (AsanUnpoisonMemoryRegionFuncPtr)dlsym(
274                       RTLD_DEFAULT, "__asan_unpoison_memory_region"))) {
275     return fn;
276   }
277
278   // Couldn't find the function at all
279   return nullptr;
280 }
281
282 #endif // FOLLY_SANITIZE_ADDRESS
283
284 #ifndef _WIN32
285 namespace {
286
287 // SIGSTKSZ (8 kB on our architectures) isn't always enough for
288 // folly::symbolizer, so allocate 32 kB.
289 constexpr size_t kAltStackSize = folly::constexpr_max(SIGSTKSZ, 32 * 1024);
290
291 bool hasAlternateStack() {
292   stack_t ss;
293   sigaltstack(nullptr, &ss);
294   return !(ss.ss_flags & SS_DISABLE);
295 }
296
297 int setAlternateStack(char* sp, size_t size) {
298   CHECK(sp);
299   stack_t ss{};
300   ss.ss_sp = sp;
301   ss.ss_size = size;
302   return sigaltstack(&ss, nullptr);
303 }
304
305 int unsetAlternateStack() {
306   stack_t ss{};
307   ss.ss_flags = SS_DISABLE;
308   return sigaltstack(&ss, nullptr);
309 }
310
311 class ScopedAlternateSignalStack {
312  public:
313   ScopedAlternateSignalStack() {
314     if (hasAlternateStack()) {
315       return;
316     }
317
318     stack_ = folly::make_unique<AltStackBuffer>();
319
320     setAlternateStack(stack_->data(), stack_->size());
321   }
322
323   ~ScopedAlternateSignalStack() {
324     if (stack_) {
325       unsetAlternateStack();
326     }
327   }
328
329  private:
330   using AltStackBuffer = std::array<char, kAltStackSize>;
331   std::unique_ptr<AltStackBuffer> stack_;
332 };
333 }
334
335 void FiberManager::registerAlternateSignalStack() {
336   static folly::SingletonThreadLocal<ScopedAlternateSignalStack> singleton;
337   singleton.get();
338
339   alternateSignalStackRegistered_ = true;
340 }
341 #endif
342 }
343 }