Disable the signal stack on Windows
[folly.git] / folly / fibers / FiberManager-inl.h
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 #pragma once
17
18 #include <cassert>
19
20 #include <folly/CPortability.h>
21 #include <folly/Memory.h>
22 #include <folly/Optional.h>
23 #include <folly/Portability.h>
24 #include <folly/ScopeGuard.h>
25 #ifdef __APPLE__
26 #include <folly/ThreadLocal.h>
27 #endif
28 #include <folly/fibers/Baton.h>
29 #include <folly/fibers/Fiber.h>
30 #include <folly/fibers/LoopController.h>
31 #include <folly/fibers/Promise.h>
32 #include <folly/futures/Promise.h>
33 #include <folly/Try.h>
34
35 namespace folly {
36 namespace fibers {
37
38 namespace {
39
40 inline FiberManager::Options preprocessOptions(FiberManager::Options opts) {
41 #ifdef FOLLY_SANITIZE_ADDRESS
42   /* ASAN needs a lot of extra stack space.
43      16x is a conservative estimate, 8x also worked with tests
44      where it mattered.  Note that overallocating here does not necessarily
45      increase RSS, since unused memory is pretty much free. */
46   opts.stackSize *= 16;
47 #endif
48   return opts;
49 }
50
51 } // anonymous
52
53 inline void FiberManager::ensureLoopScheduled() {
54   if (isLoopScheduled_) {
55     return;
56   }
57
58   isLoopScheduled_ = true;
59   loopController_->schedule();
60 }
61
62 inline intptr_t FiberManager::activateFiber(Fiber* fiber) {
63   DCHECK_EQ(activeFiber_, (Fiber*)nullptr);
64
65 #ifdef FOLLY_SANITIZE_ADDRESS
66   registerFiberActivationWithAsan(fiber);
67 #endif
68
69   activeFiber_ = fiber;
70   return jumpContext(&mainContext_, &fiber->fcontext_, fiber->data_);
71 }
72
73 inline intptr_t FiberManager::deactivateFiber(Fiber* fiber) {
74   DCHECK_EQ(activeFiber_, fiber);
75
76 #ifdef FOLLY_SANITIZE_ADDRESS
77   registerFiberDeactivationWithAsan(fiber);
78 #endif
79
80   activeFiber_ = nullptr;
81   return jumpContext(&fiber->fcontext_, &mainContext_, 0);
82 }
83
84 inline void FiberManager::runReadyFiber(Fiber* fiber) {
85   SCOPE_EXIT {
86     assert(currentFiber_ == nullptr);
87     assert(activeFiber_ == nullptr);
88   };
89
90   assert(
91       fiber->state_ == Fiber::NOT_STARTED ||
92       fiber->state_ == Fiber::READY_TO_RUN);
93   currentFiber_ = fiber;
94   fiber->rcontext_ = RequestContext::setContext(std::move(fiber->rcontext_));
95   if (observer_) {
96     observer_->starting(reinterpret_cast<uintptr_t>(fiber));
97   }
98
99   while (fiber->state_ == Fiber::NOT_STARTED ||
100          fiber->state_ == Fiber::READY_TO_RUN) {
101     activateFiber(fiber);
102     if (fiber->state_ == Fiber::AWAITING_IMMEDIATE) {
103       try {
104         immediateFunc_();
105       } catch (...) {
106         exceptionCallback_(std::current_exception(), "running immediateFunc_");
107       }
108       immediateFunc_ = nullptr;
109       fiber->state_ = Fiber::READY_TO_RUN;
110     }
111   }
112
113   if (fiber->state_ == Fiber::AWAITING) {
114     awaitFunc_(*fiber);
115     awaitFunc_ = nullptr;
116     if (observer_) {
117       observer_->stopped(reinterpret_cast<uintptr_t>(fiber));
118     }
119     currentFiber_ = nullptr;
120     fiber->rcontext_ = RequestContext::setContext(std::move(fiber->rcontext_));
121   } else if (fiber->state_ == Fiber::INVALID) {
122     assert(fibersActive_ > 0);
123     --fibersActive_;
124     // Making sure that task functor is deleted once task is complete.
125     // NOTE: we must do it on main context, as the fiber is not
126     // running at this point.
127     fiber->func_ = nullptr;
128     fiber->resultFunc_ = nullptr;
129     if (fiber->finallyFunc_) {
130       try {
131         fiber->finallyFunc_();
132       } catch (...) {
133         exceptionCallback_(std::current_exception(), "running finallyFunc_");
134       }
135       fiber->finallyFunc_ = nullptr;
136     }
137     // Make sure LocalData is not accessible from its destructor
138     if (observer_) {
139       observer_->stopped(reinterpret_cast<uintptr_t>(fiber));
140     }
141     currentFiber_ = nullptr;
142     fiber->rcontext_ = RequestContext::setContext(std::move(fiber->rcontext_));
143     fiber->localData_.reset();
144     fiber->rcontext_.reset();
145
146     if (fibersPoolSize_ < options_.maxFibersPoolSize ||
147         options_.fibersPoolResizePeriodMs > 0) {
148       fibersPool_.push_front(*fiber);
149       ++fibersPoolSize_;
150     } else {
151       delete fiber;
152       assert(fibersAllocated_ > 0);
153       --fibersAllocated_;
154     }
155   } else if (fiber->state_ == Fiber::YIELDED) {
156     if (observer_) {
157       observer_->stopped(reinterpret_cast<uintptr_t>(fiber));
158     }
159     currentFiber_ = nullptr;
160     fiber->rcontext_ = RequestContext::setContext(std::move(fiber->rcontext_));
161     fiber->state_ = Fiber::READY_TO_RUN;
162     yieldedFibers_.push_back(*fiber);
163   }
164 }
165
166 inline bool FiberManager::loopUntilNoReady() {
167 #ifndef _WIN32
168   if (UNLIKELY(!alternateSignalStackRegistered_)) {
169     registerAlternateSignalStack();
170   }
171 #endif
172
173   // Support nested FiberManagers
174   auto originalFiberManager = this;
175   std::swap(currentFiberManager_, originalFiberManager);
176
177   SCOPE_EXIT {
178     isLoopScheduled_ = false;
179     if (!readyFibers_.empty()) {
180       ensureLoopScheduled();
181     }
182     std::swap(currentFiberManager_, originalFiberManager);
183     CHECK_EQ(this, originalFiberManager);
184   };
185
186   bool hadRemoteFiber = true;
187   while (hadRemoteFiber) {
188     hadRemoteFiber = false;
189
190     while (!readyFibers_.empty()) {
191       auto& fiber = readyFibers_.front();
192       readyFibers_.pop_front();
193       runReadyFiber(&fiber);
194     }
195
196     remoteReadyQueue_.sweep([this, &hadRemoteFiber](Fiber* fiber) {
197       runReadyFiber(fiber);
198       hadRemoteFiber = true;
199     });
200
201     remoteTaskQueue_.sweep([this, &hadRemoteFiber](RemoteTask* taskPtr) {
202       std::unique_ptr<RemoteTask> task(taskPtr);
203       auto fiber = getFiber();
204       if (task->localData) {
205         fiber->localData_ = *task->localData;
206       }
207       fiber->rcontext_ = std::move(task->rcontext);
208
209       fiber->setFunction(std::move(task->func));
210       fiber->data_ = reinterpret_cast<intptr_t>(fiber);
211       if (observer_) {
212         observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
213       }
214       runReadyFiber(fiber);
215       hadRemoteFiber = true;
216     });
217   }
218
219   if (observer_) {
220     for (auto& yielded : yieldedFibers_) {
221       observer_->runnable(reinterpret_cast<uintptr_t>(&yielded));
222     }
223   }
224   readyFibers_.splice(readyFibers_.end(), yieldedFibers_);
225
226   return fibersActive_ > 0;
227 }
228
229 // We need this to be in a struct, not inlined in addTask, because clang crashes
230 // otherwise.
231 template <typename F>
232 struct FiberManager::AddTaskHelper {
233   class Func;
234
235   static constexpr bool allocateInBuffer =
236       sizeof(Func) <= Fiber::kUserBufferSize;
237
238   class Func {
239    public:
240     Func(F&& func, FiberManager& fm) : func_(std::forward<F>(func)), fm_(fm) {}
241
242     void operator()() {
243       try {
244         func_();
245       } catch (...) {
246         fm_.exceptionCallback_(
247             std::current_exception(), "running Func functor");
248       }
249       if (allocateInBuffer) {
250         this->~Func();
251       } else {
252         delete this;
253       }
254     }
255
256    private:
257     F func_;
258     FiberManager& fm_;
259   };
260 };
261
262 template <typename F>
263 void FiberManager::addTask(F&& func) {
264   typedef AddTaskHelper<F> Helper;
265
266   auto fiber = getFiber();
267   initLocalData(*fiber);
268
269   if (Helper::allocateInBuffer) {
270     auto funcLoc = static_cast<typename Helper::Func*>(fiber->getUserBuffer());
271     new (funcLoc) typename Helper::Func(std::forward<F>(func), *this);
272
273     fiber->setFunction(std::ref(*funcLoc));
274   } else {
275     auto funcLoc = new typename Helper::Func(std::forward<F>(func), *this);
276
277     fiber->setFunction(std::ref(*funcLoc));
278   }
279
280   fiber->data_ = reinterpret_cast<intptr_t>(fiber);
281   readyFibers_.push_back(*fiber);
282   if (observer_) {
283     observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
284   }
285
286   ensureLoopScheduled();
287 }
288
289 template <typename F>
290 auto FiberManager::addTaskFuture(F&& func) -> folly::Future<
291     typename folly::Unit::Lift<typename std::result_of<F()>::type>::type> {
292   using T = typename std::result_of<F()>::type;
293   using FutureT = typename folly::Unit::Lift<T>::type;
294
295   folly::Promise<FutureT> p;
296   auto f = p.getFuture();
297   addTaskFinally(
298       [func = std::forward<F>(func)]() mutable { return func(); },
299       [p = std::move(p)](folly::Try<T> && t) mutable {
300         p.setTry(std::move(t));
301       });
302   return f;
303 }
304
305 template <typename F>
306 void FiberManager::addTaskRemote(F&& func) {
307   auto task = [&]() {
308     auto currentFm = getFiberManagerUnsafe();
309     if (currentFm && currentFm->currentFiber_ &&
310         currentFm->localType_ == localType_) {
311       return folly::make_unique<RemoteTask>(
312           std::forward<F>(func), currentFm->currentFiber_->localData_);
313     }
314     return folly::make_unique<RemoteTask>(std::forward<F>(func));
315   }();
316   auto insertHead = [&]() {
317     return remoteTaskQueue_.insertHead(task.release());
318   };
319   loopController_->scheduleThreadSafe(std::ref(insertHead));
320 }
321
322 template <typename F>
323 auto FiberManager::addTaskRemoteFuture(F&& func) -> folly::Future<
324     typename folly::Unit::Lift<typename std::result_of<F()>::type>::type> {
325   folly::Promise<
326       typename folly::Unit::Lift<typename std::result_of<F()>::type>::type>
327       p;
328   auto f = p.getFuture();
329   addTaskRemote(
330       [ p = std::move(p), func = std::forward<F>(func), this ]() mutable {
331         auto t = folly::makeTryWith(std::forward<F>(func));
332         runInMainContext([&]() { p.setTry(std::move(t)); });
333       });
334   return f;
335 }
336
337 template <typename X>
338 struct IsRvalueRefTry {
339   static const bool value = false;
340 };
341 template <typename T>
342 struct IsRvalueRefTry<folly::Try<T>&&> {
343   static const bool value = true;
344 };
345
346 // We need this to be in a struct, not inlined in addTaskFinally, because clang
347 // crashes otherwise.
348 template <typename F, typename G>
349 struct FiberManager::AddTaskFinallyHelper {
350   class Func;
351
352   typedef typename std::result_of<F()>::type Result;
353
354   class Finally {
355    public:
356     Finally(G finally, FiberManager& fm)
357         : finally_(std::move(finally)), fm_(fm) {}
358
359     void operator()() {
360       try {
361         finally_(std::move(*result_));
362       } catch (...) {
363         fm_.exceptionCallback_(
364             std::current_exception(), "running Finally functor");
365       }
366
367       if (allocateInBuffer) {
368         this->~Finally();
369       } else {
370         delete this;
371       }
372     }
373
374    private:
375     friend class Func;
376
377     G finally_;
378     folly::Optional<folly::Try<Result>> result_;
379     FiberManager& fm_;
380   };
381
382   class Func {
383    public:
384     Func(F func, Finally& finally)
385         : func_(std::move(func)), result_(finally.result_) {}
386
387     void operator()() {
388       result_ = folly::makeTryWith(std::move(func_));
389
390       if (allocateInBuffer) {
391         this->~Func();
392       } else {
393         delete this;
394       }
395     }
396
397    private:
398     F func_;
399     folly::Optional<folly::Try<Result>>& result_;
400   };
401
402   static constexpr bool allocateInBuffer =
403       sizeof(Func) + sizeof(Finally) <= Fiber::kUserBufferSize;
404 };
405
406 template <typename F, typename G>
407 void FiberManager::addTaskFinally(F&& func, G&& finally) {
408   typedef typename std::result_of<F()>::type Result;
409
410   static_assert(
411       IsRvalueRefTry<typename FirstArgOf<G>::type>::value,
412       "finally(arg): arg must be Try<T>&&");
413   static_assert(
414       std::is_convertible<
415           Result,
416           typename std::remove_reference<
417               typename FirstArgOf<G>::type>::type::element_type>::value,
418       "finally(Try<T>&&): T must be convertible from func()'s return type");
419
420   auto fiber = getFiber();
421   initLocalData(*fiber);
422
423   typedef AddTaskFinallyHelper<
424       typename std::decay<F>::type,
425       typename std::decay<G>::type>
426       Helper;
427
428   if (Helper::allocateInBuffer) {
429     auto funcLoc = static_cast<typename Helper::Func*>(fiber->getUserBuffer());
430     auto finallyLoc =
431         static_cast<typename Helper::Finally*>(static_cast<void*>(funcLoc + 1));
432
433     new (finallyLoc) typename Helper::Finally(std::forward<G>(finally), *this);
434     new (funcLoc) typename Helper::Func(std::forward<F>(func), *finallyLoc);
435
436     fiber->setFunctionFinally(std::ref(*funcLoc), std::ref(*finallyLoc));
437   } else {
438     auto finallyLoc =
439         new typename Helper::Finally(std::forward<G>(finally), *this);
440     auto funcLoc =
441         new typename Helper::Func(std::forward<F>(func), *finallyLoc);
442
443     fiber->setFunctionFinally(std::ref(*funcLoc), std::ref(*finallyLoc));
444   }
445
446   fiber->data_ = reinterpret_cast<intptr_t>(fiber);
447   readyFibers_.push_back(*fiber);
448   if (observer_) {
449     observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
450   }
451
452   ensureLoopScheduled();
453 }
454
455 template <typename F>
456 typename std::result_of<F()>::type FiberManager::runInMainContext(F&& func) {
457   if (UNLIKELY(activeFiber_ == nullptr)) {
458     return func();
459   }
460
461   typedef typename std::result_of<F()>::type Result;
462
463   folly::Try<Result> result;
464   auto f = [&func, &result]() mutable {
465     result = folly::makeTryWith(std::forward<F>(func));
466   };
467
468   immediateFunc_ = std::ref(f);
469   activeFiber_->preempt(Fiber::AWAITING_IMMEDIATE);
470
471   return std::move(result).value();
472 }
473
474 inline FiberManager& FiberManager::getFiberManager() {
475   assert(currentFiberManager_ != nullptr);
476   return *currentFiberManager_;
477 }
478
479 inline FiberManager* FiberManager::getFiberManagerUnsafe() {
480   return currentFiberManager_;
481 }
482
483 inline bool FiberManager::hasActiveFiber() const {
484   return activeFiber_ != nullptr;
485 }
486
487 inline void FiberManager::yield() {
488   assert(currentFiberManager_ == this);
489   assert(activeFiber_ != nullptr);
490   assert(activeFiber_->state_ == Fiber::RUNNING);
491   activeFiber_->preempt(Fiber::YIELDED);
492 }
493
494 template <typename T>
495 T& FiberManager::local() {
496   if (std::type_index(typeid(T)) == localType_ && currentFiber_) {
497     return currentFiber_->localData_.get<T>();
498   }
499   return localThread<T>();
500 }
501
502 template <typename T>
503 T& FiberManager::localThread() {
504 #ifndef __APPLE__
505   static thread_local T t;
506   return t;
507 #else // osx doesn't support thread_local
508   static ThreadLocal<T> t;
509   return *t;
510 #endif
511 }
512
513 inline void FiberManager::initLocalData(Fiber& fiber) {
514   auto fm = getFiberManagerUnsafe();
515   if (fm && fm->currentFiber_ && fm->localType_ == localType_) {
516     fiber.localData_ = fm->currentFiber_->localData_;
517   }
518   fiber.rcontext_ = RequestContext::saveContext();
519 }
520
521 template <typename LocalT>
522 FiberManager::FiberManager(
523     LocalType<LocalT>,
524     std::unique_ptr<LoopController> loopController__,
525     Options options)
526     : loopController_(std::move(loopController__)),
527       stackAllocator_(options.useGuardPages),
528       options_(preprocessOptions(std::move(options))),
529       exceptionCallback_([](std::exception_ptr eptr, std::string context) {
530         try {
531           std::rethrow_exception(eptr);
532         } catch (const std::exception& e) {
533           LOG(DFATAL) << "Exception " << typeid(e).name() << " with message '"
534                       << e.what() << "' was thrown in "
535                       << "FiberManager with context '" << context << "'";
536         } catch (...) {
537           LOG(DFATAL) << "Unknown exception was thrown in FiberManager with "
538                       << "context '" << context << "'";
539         }
540       }),
541       timeoutManager_(std::make_shared<TimeoutController>(*loopController_)),
542       fibersPoolResizer_(*this),
543       localType_(typeid(LocalT)) {
544   loopController_->setFiberManager(this);
545 }
546
547 template <typename F>
548 typename FirstArgOf<F>::type::value_type inline await(F&& func) {
549   typedef typename FirstArgOf<F>::type::value_type Result;
550   typedef typename FirstArgOf<F>::type::baton_type BatonT;
551
552   return Promise<Result, BatonT>::await(std::forward<F>(func));
553 }
554 }
555 }