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