2 * Copyright 2016 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include "FiberManagerInternal.h"
23 #include <glog/logging.h>
25 #include <folly/fibers/Fiber.h>
26 #include <folly/fibers/LoopController.h>
28 #include <folly/SingletonThreadLocal.h>
29 #include <folly/portability/SysSyscall.h>
30 #include <folly/portability/Unistd.h>
32 #ifdef FOLLY_SANITIZE_ADDRESS
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")));
46 typedef void (*AsanEnterFiberFuncPtr)(void const*, size_t);
47 typedef void (*AsanExitFiberFuncPtr)();
48 typedef void (*AsanUnpoisonMemoryRegionFuncPtr)(
49 void const /* nolint */ volatile*,
55 static AsanEnterFiberFuncPtr getEnterFiberFunc();
56 static AsanExitFiberFuncPtr getExitFiberFunc();
57 static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc();
66 FOLLY_TLS FiberManager* FiberManager::currentFiberManager_ = nullptr;
68 FiberManager::FiberManager(
69 std::unique_ptr<LoopController> loopController,
73 std::move(loopController),
74 std::move(options)) {}
76 FiberManager::~FiberManager() {
77 if (isLoopScheduled_) {
78 loopController_->cancel();
81 while (!fibersPool_.empty()) {
82 fibersPool_.pop_front_and_dispose([](Fiber* fiber) { delete fiber; });
84 assert(readyFibers_.empty());
85 assert(fibersActive_ == 0);
88 LoopController& FiberManager::loopController() {
89 return *loopController_;
92 const LoopController& FiberManager::loopController() const {
93 return *loopController_;
96 bool FiberManager::hasTasks() const {
97 return fibersActive_ > 0 || !remoteReadyQueue_.empty() ||
98 !remoteTaskQueue_.empty();
101 Fiber* FiberManager::getFiber() {
102 Fiber* fiber = nullptr;
104 if (options_.fibersPoolResizePeriodMs > 0 && !fibersPoolResizerScheduled_) {
105 fibersPoolResizer_();
106 fibersPoolResizerScheduled_ = true;
109 if (fibersPool_.empty()) {
110 fiber = new Fiber(*this);
113 fiber = &fibersPool_.front();
114 fibersPool_.pop_front();
115 assert(fibersPoolSize_ > 0);
119 if (++fibersActive_ > maxFibersActiveLastPeriod_) {
120 maxFibersActiveLastPeriod_ = fibersActive_;
123 bool recordStack = (options_.recordStackEvery != 0) &&
124 (fiberId_ % options_.recordStackEvery == 0);
125 fiber->init(recordStack);
129 void FiberManager::setExceptionCallback(FiberManager::ExceptionCallback ec) {
131 exceptionCallback_ = std::move(ec);
134 size_t FiberManager::fibersAllocated() const {
135 return fibersAllocated_;
138 size_t FiberManager::fibersPoolSize() const {
139 return fibersPoolSize_;
142 size_t FiberManager::stackHighWatermark() const {
143 return stackHighWatermark_;
146 void FiberManager::remoteReadyInsert(Fiber* fiber) {
148 observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
150 auto insertHead = [&]() { return remoteReadyQueue_.insertHead(fiber); };
151 loopController_->scheduleThreadSafe(std::ref(insertHead));
154 void FiberManager::setObserver(ExecutionObserver* observer) {
155 observer_ = observer;
158 ExecutionObserver* FiberManager::getObserver() {
162 void FiberManager::setPreemptRunner(InlineFunctionRunner* preemptRunner) {
163 preemptRunner_ = preemptRunner;
166 void FiberManager::doFibersPoolResizing() {
167 while (fibersAllocated_ > maxFibersActiveLastPeriod_ &&
168 fibersPoolSize_ > options_.maxFibersPoolSize) {
169 auto fiber = &fibersPool_.front();
170 assert(fiber != nullptr);
171 fibersPool_.pop_front();
177 maxFibersActiveLastPeriod_ = fibersActive_;
180 void FiberManager::FibersPoolResizer::operator()() {
181 fiberManager_.doFibersPoolResizing();
182 fiberManager_.timeoutManager_->registerTimeout(
184 std::chrono::milliseconds(
185 fiberManager_.options_.fibersPoolResizePeriodMs));
188 #ifdef FOLLY_SANITIZE_ADDRESS
190 void FiberManager::registerFiberActivationWithAsan(Fiber* fiber) {
191 auto context = &fiber->fcontext_;
192 void* top = context->stackBase();
193 void* bottom = context->stackLimit();
194 size_t extent = static_cast<char*>(top) - static_cast<char*>(bottom);
196 // Check if we can find a fiber enter function and call it if we find one
197 static AsanEnterFiberFuncPtr fn = getEnterFiberFunc();
199 LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
205 void FiberManager::registerFiberDeactivationWithAsan(Fiber* fiber) {
206 (void)fiber; // currently unused
208 // Check if we can find a fiber exit function and call it if we find one
209 static AsanExitFiberFuncPtr fn = getExitFiberFunc();
211 LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
217 void FiberManager::unpoisonFiberStack(const Fiber* fiber) {
218 auto stack = fiber->getStack();
220 // Check if we can find a fiber enter function and call it if we find one
221 static AsanUnpoisonMemoryRegionFuncPtr fn = getUnpoisonMemoryRegionFunc();
223 LOG(FATAL) << "This version of ASAN doesn't support memory unpoisoning";
225 fn(stack.first, stack.second);
229 static AsanEnterFiberFuncPtr getEnterFiberFunc() {
230 AsanEnterFiberFuncPtr fn{nullptr};
232 // Check whether weak reference points to statically linked enter function
233 if (nullptr != (fn = &::__asan_enter_fiber_weak)) {
237 // Check whether we can find a dynamically linked enter function
239 (fn = (AsanEnterFiberFuncPtr)dlsym(RTLD_DEFAULT, "__asan_enter_fiber"))) {
243 // Couldn't find the function at all
247 static AsanExitFiberFuncPtr getExitFiberFunc() {
248 AsanExitFiberFuncPtr fn{nullptr};
250 // Check whether weak reference points to statically linked exit function
251 if (nullptr != (fn = &::__asan_exit_fiber_weak)) {
255 // Check whether we can find a dynamically linked exit function
257 (fn = (AsanExitFiberFuncPtr)dlsym(RTLD_DEFAULT, "__asan_exit_fiber"))) {
261 // Couldn't find the function at all
265 static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc() {
266 AsanUnpoisonMemoryRegionFuncPtr fn{nullptr};
268 // Check whether weak reference points to statically linked unpoison function
269 if (nullptr != (fn = &::__asan_unpoison_memory_region_weak)) {
273 // Check whether we can find a dynamically linked unpoison function
274 if (nullptr != (fn = (AsanUnpoisonMemoryRegionFuncPtr)dlsym(
275 RTLD_DEFAULT, "__asan_unpoison_memory_region"))) {
279 // Couldn't find the function at all
283 #endif // FOLLY_SANITIZE_ADDRESS
288 // SIGSTKSZ (8 kB on our architectures) isn't always enough for
289 // folly::symbolizer, so allocate 32 kB.
290 constexpr size_t kAltStackSize = folly::constexpr_max(SIGSTKSZ, 32 * 1024);
292 bool hasAlternateStack() {
294 sigaltstack(nullptr, &ss);
295 return !(ss.ss_flags & SS_DISABLE);
298 int setAlternateStack(char* sp, size_t size) {
303 return sigaltstack(&ss, nullptr);
306 int unsetAlternateStack() {
308 ss.ss_flags = SS_DISABLE;
309 return sigaltstack(&ss, nullptr);
312 class ScopedAlternateSignalStack {
314 ScopedAlternateSignalStack() {
315 if (hasAlternateStack()) {
319 stack_ = folly::make_unique<AltStackBuffer>();
321 setAlternateStack(stack_->data(), stack_->size());
324 ~ScopedAlternateSignalStack() {
326 unsetAlternateStack();
331 using AltStackBuffer = std::array<char, kAltStackSize>;
332 std::unique_ptr<AltStackBuffer> stack_;
336 void FiberManager::registerAlternateSignalStack() {
337 static folly::SingletonThreadLocal<ScopedAlternateSignalStack> singleton;
340 alternateSignalStackRegistered_ = true;