proxygen
FiberManager.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2014-present 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  */
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>
27 
28 #include <folly/ConstexprMath.h>
32 
33 #ifdef FOLLY_SANITIZE_ADDRESS
34 
35 #include <dlfcn.h>
36 
37 static void __sanitizer_start_switch_fiber_weak(
38  void** fake_stack_save,
39  void const* fiber_stack_base,
40  size_t fiber_stack_extent)
41  __attribute__((__weakref__("__sanitizer_start_switch_fiber")));
42 static void __sanitizer_finish_switch_fiber_weak(
43  void* fake_stack_save,
44  void const** old_stack_base,
45  size_t* old_stack_extent)
46  __attribute__((__weakref__("__sanitizer_finish_switch_fiber")));
47 static void __asan_unpoison_memory_region_weak(
48  void const /* nolint */ volatile* addr,
49  size_t size) __attribute__((__weakref__("__asan_unpoison_memory_region")));
50 
51 typedef void (*AsanStartSwitchStackFuncPtr)(void**, void const*, size_t);
52 typedef void (*AsanFinishSwitchStackFuncPtr)(void*, void const**, size_t*);
53 typedef void (*AsanUnpoisonMemoryRegionFuncPtr)(
54  void const /* nolint */ volatile*,
55  size_t);
56 
57 namespace folly {
58 namespace fibers {
59 
60 static AsanStartSwitchStackFuncPtr getStartSwitchStackFunc();
61 static AsanFinishSwitchStackFuncPtr getFinishSwitchStackFunc();
62 static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc();
63 } // namespace fibers
64 } // namespace folly
65 
66 #endif
67 
68 namespace folly {
69 namespace fibers {
70 
71 FOLLY_TLS FiberManager* FiberManager::currentFiberManager_ = nullptr;
72 
74  std::unique_ptr<LoopController> loopController,
75  Options options)
76  : FiberManager(
77  LocalType<void>(),
78  std::move(loopController),
79  std::move(options)) {}
80 
82  loopController_.reset();
83 
84  while (!fibersPool_.empty()) {
85  fibersPool_.pop_front_and_dispose([](Fiber* fiber) { delete fiber; });
86  }
87  assert(readyFibers_.empty());
88  assert(fibersActive_ == 0);
89 }
90 
92  return *loopController_;
93 }
94 
96  return *loopController_;
97 }
98 
99 bool FiberManager::hasTasks() const {
100  return fibersActive_ > 0 || !remoteReadyQueue_.empty() ||
101  !remoteTaskQueue_.empty() || remoteCount_ > 0;
102 }
103 
105  Fiber* fiber = nullptr;
106 
110  }
111 
112  if (fibersPool_.empty()) {
113  fiber = new Fiber(*this);
115  } else {
116  fiber = &fibersPool_.front();
117  fibersPool_.pop_front();
118  assert(fibersPoolSize_ > 0);
119  --fibersPoolSize_;
120  }
121  assert(fiber);
124  }
125  ++fiberId_;
126  bool recordStack = (options_.recordStackEvery != 0) &&
128  fiber->init(recordStack);
129  return fiber;
130 }
131 
133  assert(ec);
135 }
136 
138  return fibersAllocated_;
139 }
140 
142  return fibersPoolSize_;
143 }
144 
146  return stackHighWatermark_;
147 }
148 
150  if (observer_) {
151  observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
152  }
153  if (remoteReadyQueue_.insertHead(fiber)) {
154  loopController_->scheduleThreadSafe();
155  }
156 }
157 
159  observer_ = observer;
160 }
161 
163  return observer_;
164 }
165 
167  preemptRunner_ = preemptRunner;
168 }
169 
173  auto fiber = &fibersPool_.front();
174  assert(fiber != nullptr);
175  fibersPool_.pop_front();
176  delete fiber;
177  --fibersPoolSize_;
179  }
180 
182 }
183 
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::freeFakeStack(void* fakeStack) {
221  static AsanStartSwitchStackFuncPtr fnStart = getStartSwitchStackFunc();
222  static AsanFinishSwitchStackFuncPtr fnFinish = getFinishSwitchStackFunc();
223  if (fnStart == nullptr || fnFinish == nullptr) {
224  LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
225  }
226 
227  void* saveFakeStack;
228  const void* stackBottom;
229  size_t stackSize;
230  fnStart(&saveFakeStack, nullptr, 0);
231  fnFinish(fakeStack, &stackBottom, &stackSize);
232  fnStart(nullptr, stackBottom, stackSize);
233  fnFinish(saveFakeStack, nullptr, nullptr);
234 }
235 
236 void FiberManager::unpoisonFiberStack(const Fiber* fiber) {
237  auto stack = fiber->getStack();
238 
239  // Check if we can find a fiber enter function and call it if we find one
240  static AsanUnpoisonMemoryRegionFuncPtr fn = getUnpoisonMemoryRegionFunc();
241  if (fn == nullptr) {
242  LOG(FATAL) << "This version of ASAN doesn't support memory unpoisoning";
243  } else {
244  fn(stack.first, stack.second);
245  }
246 }
247 
248 static AsanStartSwitchStackFuncPtr getStartSwitchStackFunc() {
249  AsanStartSwitchStackFuncPtr fn{nullptr};
250 
251  // Check whether weak reference points to statically linked enter function
252  if (nullptr != (fn = &::__sanitizer_start_switch_fiber_weak)) {
253  return fn;
254  }
255 
256  // Check whether we can find a dynamically linked enter function
257  if (nullptr !=
258  (fn = (AsanStartSwitchStackFuncPtr)dlsym(
259  RTLD_DEFAULT, "__sanitizer_start_switch_fiber"))) {
260  return fn;
261  }
262 
263  // Couldn't find the function at all
264  return nullptr;
265 }
266 
267 static AsanFinishSwitchStackFuncPtr getFinishSwitchStackFunc() {
268  AsanFinishSwitchStackFuncPtr fn{nullptr};
269 
270  // Check whether weak reference points to statically linked exit function
271  if (nullptr != (fn = &::__sanitizer_finish_switch_fiber_weak)) {
272  return fn;
273  }
274 
275  // Check whether we can find a dynamically linked exit function
276  if (nullptr !=
277  (fn = (AsanFinishSwitchStackFuncPtr)dlsym(
278  RTLD_DEFAULT, "__sanitizer_finish_switch_fiber"))) {
279  return fn;
280  }
281 
282  // Couldn't find the function at all
283  return nullptr;
284 }
285 
286 static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc() {
287  AsanUnpoisonMemoryRegionFuncPtr fn{nullptr};
288 
289  // Check whether weak reference points to statically linked unpoison function
290  if (nullptr != (fn = &::__asan_unpoison_memory_region_weak)) {
291  return fn;
292  }
293 
294  // Check whether we can find a dynamically linked unpoison function
295  if (nullptr !=
296  (fn = (AsanUnpoisonMemoryRegionFuncPtr)dlsym(
297  RTLD_DEFAULT, "__asan_unpoison_memory_region"))) {
298  return fn;
299  }
300 
301  // Couldn't find the function at all
302  return nullptr;
303 }
304 
305 #endif // FOLLY_SANITIZE_ADDRESS
306 
307 #ifndef _WIN32
308 namespace {
309 
310 // SIGSTKSZ (8 kB on our architectures) isn't always enough for
311 // folly::symbolizer, so allocate 32 kB.
312 constexpr size_t kAltStackSize = folly::constexpr_max(SIGSTKSZ, 32 * 1024);
313 
314 bool hasAlternateStack() {
315  stack_t ss;
316  sigaltstack(nullptr, &ss);
317  return !(ss.ss_flags & SS_DISABLE);
318 }
319 
320 int setAlternateStack(char* sp, size_t size) {
321  CHECK(sp);
322  stack_t ss{};
323  ss.ss_sp = sp;
324  ss.ss_size = size;
325  return sigaltstack(&ss, nullptr);
326 }
327 
328 int unsetAlternateStack() {
329  stack_t ss{};
330  ss.ss_flags = SS_DISABLE;
331  return sigaltstack(&ss, nullptr);
332 }
333 
334 class ScopedAlternateSignalStack {
335  public:
336  ScopedAlternateSignalStack() {
337  if (hasAlternateStack()) {
338  return;
339  }
340 
341  stack_ = std::make_unique<AltStackBuffer>();
342 
343  setAlternateStack(stack_->data(), stack_->size());
344  }
345 
346  ScopedAlternateSignalStack(ScopedAlternateSignalStack&&) = default;
347  ScopedAlternateSignalStack& operator=(ScopedAlternateSignalStack&&) = default;
348 
349  ~ScopedAlternateSignalStack() {
350  if (stack_) {
351  unsetAlternateStack();
352  }
353  }
354 
355  private:
356  using AltStackBuffer = std::array<char, kAltStackSize>;
357  std::unique_ptr<AltStackBuffer> stack_;
358 };
359 } // namespace
360 
363 
365 }
366 #endif
367 } // namespace fibers
368 } // namespace folly
std::pair< void *, size_t > getStack() const
Definition: Fiber.h:62
size_t fibersAllocated() const
void setPreemptRunner(InlineFunctionRunner *preemptRunner)
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
STL namespace.
static FOLLY_TLS FiberManager * currentFiberManager_
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
virtual void runnable(uintptr_t id) noexcept=0
constexpr T constexpr_max(T a)
Definition: ConstexprMath.h:68
std::unique_ptr< LoopController > loopController_
Single-threaded task execution engine.
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
Fiber object used by FiberManager to execute tasks.
Definition: Fiber.h:45
InlineFunctionRunner * preemptRunner_
void remoteReadyInsert(Fiber *fiber)
void init(bool recordStackUsed)
Definition: Fiber.cpp:78
std::unique_ptr< AltStackBuffer > stack_
LoopController & loopController()
void setExceptionCallback(ExceptionCallback ec)
folly::AtomicIntrusiveLinkedList< Fiber,&Fiber::nextRemoteReady_ > remoteReadyQueue_
FiberManager & operator=(const FiberManager &)=delete
FOLLY_EXPORT static FOLLY_ALWAYS_INLINE T & get()
__attribute__((noinline, noclone)) VirtualBase *makeVirtual()
void setObserver(ExecutionObserver *observer)
size_t stackHighWatermark() const
folly::AtomicIntrusiveLinkedList< RemoteTask,&RemoteTask::nextRemoteTask > remoteTaskQueue_
FiberManager(const FiberManager &)=delete
ThreadPoolListHook * addr
ExecutionObserver * getObserver()
size_t fibersPoolSize() const