662 lines
18 KiB
C++
662 lines
18 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <cassert>
|
|
|
|
#include <folly/CPortability.h>
|
|
#include <folly/Memory.h>
|
|
#include <folly/Optional.h>
|
|
#include <folly/Portability.h>
|
|
#include <folly/ScopeGuard.h>
|
|
#ifdef __APPLE__
|
|
#include <folly/ThreadLocal.h>
|
|
#endif
|
|
#include <folly/Try.h>
|
|
#include <folly/fibers/Baton.h>
|
|
#include <folly/fibers/Fiber.h>
|
|
#include <folly/fibers/LoopController.h>
|
|
#include <folly/fibers/Promise.h>
|
|
#include <folly/tracing/AsyncStack.h>
|
|
|
|
namespace folly {
|
|
namespace fibers {
|
|
|
|
namespace {
|
|
|
|
inline FiberManager::Options preprocessOptions(FiberManager::Options opts) {
|
|
/**
|
|
* Adjust the stack size according to the multiplier config.
|
|
* Typically used with sanitizers, which need a lot of extra stack space.
|
|
*/
|
|
opts.stackSize *= std::exchange(opts.stackSizeMultiplier, 1);
|
|
return opts;
|
|
}
|
|
|
|
template <class F>
|
|
FOLLY_NOINLINE invoke_result_t<F> runNoInline(F&& func) {
|
|
return func();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
inline void FiberManager::ensureLoopScheduled() {
|
|
if (isLoopScheduled_) {
|
|
return;
|
|
}
|
|
|
|
isLoopScheduled_ = true;
|
|
loopController_->schedule();
|
|
}
|
|
|
|
inline void FiberManager::activateFiber(Fiber* fiber) {
|
|
DCHECK_EQ(activeFiber_, (Fiber*)nullptr);
|
|
|
|
#ifdef FOLLY_SANITIZE_ADDRESS
|
|
DCHECK(!fiber->asanMainStackBase_);
|
|
DCHECK(!fiber->asanMainStackSize_);
|
|
auto stack = fiber->getStack();
|
|
void* asanFakeStack;
|
|
registerStartSwitchStackWithAsan(&asanFakeStack, stack.first, stack.second);
|
|
SCOPE_EXIT {
|
|
registerFinishSwitchStackWithAsan(asanFakeStack, nullptr, nullptr);
|
|
fiber->asanMainStackBase_ = nullptr;
|
|
fiber->asanMainStackSize_ = 0;
|
|
};
|
|
#endif
|
|
|
|
activeFiber_ = fiber;
|
|
fiber->fiberImpl_.activate();
|
|
}
|
|
|
|
inline void FiberManager::deactivateFiber(Fiber* fiber) {
|
|
DCHECK_EQ(activeFiber_, fiber);
|
|
|
|
#ifdef FOLLY_SANITIZE_ADDRESS
|
|
DCHECK(fiber->asanMainStackBase_);
|
|
DCHECK(fiber->asanMainStackSize_);
|
|
|
|
registerStartSwitchStackWithAsan(
|
|
&fiber->asanFakeStack_,
|
|
fiber->asanMainStackBase_,
|
|
fiber->asanMainStackSize_);
|
|
SCOPE_EXIT {
|
|
registerFinishSwitchStackWithAsan(
|
|
fiber->asanFakeStack_,
|
|
&fiber->asanMainStackBase_,
|
|
&fiber->asanMainStackSize_);
|
|
fiber->asanFakeStack_ = nullptr;
|
|
};
|
|
#endif
|
|
|
|
activeFiber_ = nullptr;
|
|
fiber->fiberImpl_.deactivate();
|
|
}
|
|
|
|
inline void FiberManager::runReadyFiber(Fiber* fiber) {
|
|
SCOPE_EXIT {
|
|
assert(currentFiber_ == nullptr);
|
|
assert(activeFiber_ == nullptr);
|
|
};
|
|
|
|
assert(
|
|
fiber->state_ == Fiber::NOT_STARTED ||
|
|
fiber->state_ == Fiber::READY_TO_RUN);
|
|
currentFiber_ = fiber;
|
|
// Note: resetting the context is handled by the loop
|
|
RequestContext::setContext(std::move(fiber->rcontext_));
|
|
|
|
(void)folly::exchangeCurrentAsyncStackRoot(
|
|
std::exchange(fiber->asyncRoot_, nullptr));
|
|
|
|
if (observer_) {
|
|
observer_->starting(reinterpret_cast<uintptr_t>(fiber));
|
|
}
|
|
|
|
while (fiber->state_ == Fiber::NOT_STARTED ||
|
|
fiber->state_ == Fiber::READY_TO_RUN) {
|
|
activateFiber(fiber);
|
|
if (fiber->state_ == Fiber::AWAITING_IMMEDIATE) {
|
|
try {
|
|
immediateFunc_();
|
|
} catch (...) {
|
|
exceptionCallback_(std::current_exception(), "running immediateFunc_");
|
|
}
|
|
immediateFunc_ = nullptr;
|
|
fiber->state_ = Fiber::READY_TO_RUN;
|
|
}
|
|
}
|
|
|
|
if (fiber->state_ == Fiber::AWAITING) {
|
|
awaitFunc_(*fiber);
|
|
awaitFunc_ = nullptr;
|
|
if (observer_) {
|
|
observer_->stopped(reinterpret_cast<uintptr_t>(fiber));
|
|
}
|
|
currentFiber_ = nullptr;
|
|
fiber->rcontext_ = RequestContext::saveContext();
|
|
fiber->asyncRoot_ = folly::exchangeCurrentAsyncStackRoot(nullptr);
|
|
} else if (fiber->state_ == Fiber::INVALID) {
|
|
assert(fibersActive_ > 0);
|
|
--fibersActive_;
|
|
// Making sure that task functor is deleted once task is complete.
|
|
// NOTE: we must do it on main context, as the fiber is not
|
|
// running at this point.
|
|
fiber->func_ = nullptr;
|
|
fiber->resultFunc_ = nullptr;
|
|
fiber->taskOptions_ = TaskOptions();
|
|
if (fiber->finallyFunc_) {
|
|
try {
|
|
fiber->finallyFunc_();
|
|
} catch (...) {
|
|
exceptionCallback_(std::current_exception(), "running finallyFunc_");
|
|
}
|
|
fiber->finallyFunc_ = nullptr;
|
|
}
|
|
// Make sure LocalData is not accessible from its destructor
|
|
if (observer_) {
|
|
observer_->stopped(reinterpret_cast<uintptr_t>(fiber));
|
|
}
|
|
currentFiber_ = nullptr;
|
|
fiber->rcontext_ = RequestContext::saveContext();
|
|
// Async stack roots should have been popped by the time the
|
|
// func_() call has returned.
|
|
fiber->asyncRoot_ = folly::exchangeCurrentAsyncStackRoot(nullptr);
|
|
CHECK(fiber->asyncRoot_ == nullptr);
|
|
|
|
fiber->localData_.reset();
|
|
fiber->rcontext_.reset();
|
|
|
|
if (fibersPoolSize_ < options_.maxFibersPoolSize ||
|
|
options_.fibersPoolResizePeriodMs > 0) {
|
|
fibersPool_.push_front(*fiber);
|
|
++fibersPoolSize_;
|
|
} else {
|
|
delete fiber;
|
|
assert(fibersAllocated_ > 0);
|
|
--fibersAllocated_;
|
|
}
|
|
} else if (fiber->state_ == Fiber::YIELDED) {
|
|
if (observer_) {
|
|
observer_->stopped(reinterpret_cast<uintptr_t>(fiber));
|
|
}
|
|
currentFiber_ = nullptr;
|
|
fiber->rcontext_ = RequestContext::saveContext();
|
|
fiber->asyncRoot_ = folly::exchangeCurrentAsyncStackRoot(nullptr);
|
|
fiber->state_ = Fiber::READY_TO_RUN;
|
|
yieldedFibers_->push_back(*fiber);
|
|
}
|
|
}
|
|
|
|
inline void FiberManager::loopUntilNoReady() {
|
|
return loopController_->runLoop();
|
|
}
|
|
|
|
template <typename LoopFunc>
|
|
void FiberManager::runFibersHelper(LoopFunc&& loopFunc) {
|
|
if (UNLIKELY(!alternateSignalStackRegistered_)) {
|
|
maybeRegisterAlternateSignalStack();
|
|
}
|
|
|
|
// Support nested FiberManagers
|
|
auto originalFiberManager = std::exchange(getCurrentFiberManager(), this);
|
|
|
|
numUncaughtExceptions_ = uncaught_exceptions();
|
|
currentException_ = std::current_exception();
|
|
|
|
// Save current context, and reset it after executing all fibers.
|
|
// This can avoid a lot of context swapping,
|
|
// if the Fibers share the same context
|
|
auto curCtx = RequestContext::saveContext();
|
|
|
|
auto* curAsyncRoot = folly::exchangeCurrentAsyncStackRoot(nullptr);
|
|
|
|
FiberTailQueue yieldedFibers;
|
|
auto prevYieldedFibers = std::exchange(yieldedFibers_, &yieldedFibers);
|
|
|
|
SCOPE_EXIT {
|
|
// Restore the previous AsyncStackRoot and make sure that none of
|
|
// the fibers left any AsyncStackRoot pointers lying around.
|
|
auto* oldAsyncRoot = folly::exchangeCurrentAsyncStackRoot(curAsyncRoot);
|
|
CHECK(oldAsyncRoot == nullptr);
|
|
|
|
yieldedFibers_ = prevYieldedFibers;
|
|
if (observer_) {
|
|
for (auto& yielded : yieldedFibers) {
|
|
observer_->runnable(reinterpret_cast<uintptr_t>(&yielded));
|
|
}
|
|
}
|
|
readyFibers_.splice(readyFibers_.end(), yieldedFibers);
|
|
RequestContext::setContext(std::move(curCtx));
|
|
if (!readyFibers_.empty()) {
|
|
ensureLoopScheduled();
|
|
}
|
|
std::swap(getCurrentFiberManager(), originalFiberManager);
|
|
CHECK_EQ(this, originalFiberManager);
|
|
};
|
|
|
|
loopFunc();
|
|
}
|
|
|
|
inline size_t FiberManager::recordStackPosition(size_t position) {
|
|
auto newPosition = std::max(stackHighWatermark(), position);
|
|
stackHighWatermark_.store(newPosition, std::memory_order_relaxed);
|
|
return newPosition;
|
|
}
|
|
|
|
inline void FiberManager::loopUntilNoReadyImpl() {
|
|
runFibersHelper([&] {
|
|
SCOPE_EXIT { isLoopScheduled_ = false; };
|
|
|
|
bool hadRemote = true;
|
|
while (hadRemote) {
|
|
while (!readyFibers_.empty()) {
|
|
auto& fiber = readyFibers_.front();
|
|
readyFibers_.pop_front();
|
|
runReadyFiber(&fiber);
|
|
}
|
|
|
|
auto hadRemoteFiber = remoteReadyQueue_.sweepOnce(
|
|
[this](Fiber* fiber) { runReadyFiber(fiber); });
|
|
|
|
if (hadRemoteFiber) {
|
|
++remoteCount_;
|
|
}
|
|
|
|
auto hadRemoteTask =
|
|
remoteTaskQueue_.sweepOnce([this](RemoteTask* taskPtr) {
|
|
std::unique_ptr<RemoteTask> task(taskPtr);
|
|
auto fiber = getFiber();
|
|
if (task->localData) {
|
|
fiber->localData_ = *task->localData;
|
|
}
|
|
fiber->rcontext_ = std::move(task->rcontext);
|
|
|
|
fiber->setFunction(std::move(task->func), TaskOptions());
|
|
if (observer_) {
|
|
observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
|
|
}
|
|
runReadyFiber(fiber);
|
|
});
|
|
|
|
if (hadRemoteTask) {
|
|
++remoteCount_;
|
|
}
|
|
|
|
hadRemote = hadRemoteTask || hadRemoteFiber;
|
|
}
|
|
});
|
|
}
|
|
|
|
inline void FiberManager::runEagerFiber(Fiber* fiber) {
|
|
loopController_->runEagerFiber(fiber);
|
|
}
|
|
|
|
inline void FiberManager::runEagerFiberImpl(Fiber* fiber) {
|
|
folly::fibers::runInMainContext([&] {
|
|
auto prevCurrentFiber = std::exchange(currentFiber_, fiber);
|
|
SCOPE_EXIT { currentFiber_ = prevCurrentFiber; };
|
|
runFibersHelper([&] { runReadyFiber(fiber); });
|
|
});
|
|
}
|
|
|
|
inline bool FiberManager::shouldRunLoopRemote() {
|
|
--remoteCount_;
|
|
return !remoteReadyQueue_.empty() || !remoteTaskQueue_.empty();
|
|
}
|
|
|
|
inline bool FiberManager::hasReadyTasks() const {
|
|
return !readyFibers_.empty() || !remoteReadyQueue_.empty() ||
|
|
!remoteTaskQueue_.empty();
|
|
}
|
|
|
|
// We need this to be in a struct, not inlined in addTask, because clang crashes
|
|
// otherwise.
|
|
template <typename F>
|
|
struct FiberManager::AddTaskHelper {
|
|
class Func;
|
|
|
|
static constexpr bool allocateInBuffer =
|
|
sizeof(Func) <= Fiber::kUserBufferSize;
|
|
|
|
class Func {
|
|
public:
|
|
Func(F&& func, FiberManager& fm) : func_(std::forward<F>(func)), fm_(fm) {}
|
|
|
|
void operator()() {
|
|
try {
|
|
func_();
|
|
} catch (...) {
|
|
fm_.exceptionCallback_(
|
|
std::current_exception(), "running Func functor");
|
|
}
|
|
if (allocateInBuffer) {
|
|
this->~Func();
|
|
} else {
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
private:
|
|
F func_;
|
|
FiberManager& fm_;
|
|
};
|
|
};
|
|
|
|
template <typename F>
|
|
Fiber* FiberManager::createTask(F&& func, TaskOptions taskOptions) {
|
|
typedef AddTaskHelper<F> Helper;
|
|
|
|
auto fiber = getFiber();
|
|
initLocalData(*fiber);
|
|
|
|
if (Helper::allocateInBuffer) {
|
|
auto funcLoc = static_cast<typename Helper::Func*>(fiber->getUserBuffer());
|
|
new (funcLoc) typename Helper::Func(std::forward<F>(func), *this);
|
|
|
|
fiber->setFunction(std::ref(*funcLoc), std::move(taskOptions));
|
|
} else {
|
|
auto funcLoc = new typename Helper::Func(std::forward<F>(func), *this);
|
|
|
|
fiber->setFunction(std::ref(*funcLoc), std::move(taskOptions));
|
|
}
|
|
|
|
if (observer_) {
|
|
observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
|
|
}
|
|
|
|
return fiber;
|
|
}
|
|
|
|
template <typename F>
|
|
void FiberManager::addTask(F&& func, TaskOptions taskOptions) {
|
|
readyFibers_.push_back(
|
|
*createTask(std::forward<F>(func), std::move(taskOptions)));
|
|
ensureLoopScheduled();
|
|
}
|
|
|
|
template <typename F>
|
|
void FiberManager::addTaskEager(F&& func) {
|
|
runEagerFiber(createTask(std::forward<F>(func), TaskOptions()));
|
|
}
|
|
|
|
template <typename F>
|
|
void FiberManager::addTaskRemote(F&& func) {
|
|
auto task = [&]() {
|
|
auto currentFm = getFiberManagerUnsafe();
|
|
if (currentFm && currentFm->currentFiber_ &&
|
|
currentFm->localType_ == localType_) {
|
|
return std::make_unique<RemoteTask>(
|
|
std::forward<F>(func), currentFm->currentFiber_->localData_);
|
|
}
|
|
return std::make_unique<RemoteTask>(std::forward<F>(func));
|
|
}();
|
|
if (remoteTaskQueue_.insertHead(task.release())) {
|
|
loopController_->scheduleThreadSafe();
|
|
}
|
|
}
|
|
|
|
template <typename X>
|
|
struct IsRvalueRefTry {
|
|
static const bool value = false;
|
|
};
|
|
template <typename T>
|
|
struct IsRvalueRefTry<folly::Try<T>&&> {
|
|
static const bool value = true;
|
|
};
|
|
|
|
// We need this to be in a struct, not inlined in addTaskFinally, because clang
|
|
// crashes otherwise.
|
|
template <typename F, typename G>
|
|
struct FiberManager::AddTaskFinallyHelper {
|
|
class Func;
|
|
|
|
typedef invoke_result_t<F> Result;
|
|
|
|
class Finally {
|
|
public:
|
|
Finally(G finally, FiberManager& fm)
|
|
: finally_(std::move(finally)), fm_(fm) {}
|
|
|
|
void operator()() {
|
|
try {
|
|
finally_(std::move(result_));
|
|
} catch (...) {
|
|
fm_.exceptionCallback_(
|
|
std::current_exception(), "running Finally functor");
|
|
}
|
|
|
|
if (allocateInBuffer) {
|
|
this->~Finally();
|
|
} else {
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
private:
|
|
friend class Func;
|
|
|
|
G finally_;
|
|
folly::Try<Result> result_;
|
|
FiberManager& fm_;
|
|
};
|
|
|
|
class Func {
|
|
public:
|
|
Func(F func, Finally& finally)
|
|
: func_(std::move(func)), result_(finally.result_) {}
|
|
|
|
void operator()() {
|
|
folly::tryEmplaceWith(result_, std::move(func_));
|
|
|
|
if (allocateInBuffer) {
|
|
this->~Func();
|
|
} else {
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
private:
|
|
F func_;
|
|
folly::Try<Result>& result_;
|
|
};
|
|
|
|
static constexpr bool allocateInBuffer =
|
|
sizeof(Func) + sizeof(Finally) <= Fiber::kUserBufferSize;
|
|
};
|
|
|
|
template <typename F, typename G>
|
|
Fiber* FiberManager::createTaskFinally(F&& func, G&& finally) {
|
|
typedef invoke_result_t<F> Result;
|
|
|
|
static_assert(
|
|
IsRvalueRefTry<typename FirstArgOf<G>::type>::value,
|
|
"finally(arg): arg must be Try<T>&&");
|
|
static_assert(
|
|
std::is_convertible<
|
|
Result,
|
|
typename std::remove_reference<
|
|
typename FirstArgOf<G>::type>::type::element_type>::value,
|
|
"finally(Try<T>&&): T must be convertible from func()'s return type");
|
|
|
|
auto fiber = getFiber();
|
|
initLocalData(*fiber);
|
|
|
|
typedef AddTaskFinallyHelper<
|
|
typename std::decay<F>::type,
|
|
typename std::decay<G>::type>
|
|
Helper;
|
|
|
|
if (Helper::allocateInBuffer) {
|
|
auto funcLoc = static_cast<typename Helper::Func*>(fiber->getUserBuffer());
|
|
auto finallyLoc =
|
|
static_cast<typename Helper::Finally*>(static_cast<void*>(funcLoc + 1));
|
|
|
|
new (finallyLoc) typename Helper::Finally(std::forward<G>(finally), *this);
|
|
new (funcLoc) typename Helper::Func(std::forward<F>(func), *finallyLoc);
|
|
|
|
fiber->setFunctionFinally(std::ref(*funcLoc), std::ref(*finallyLoc));
|
|
} else {
|
|
auto finallyLoc =
|
|
new typename Helper::Finally(std::forward<G>(finally), *this);
|
|
auto funcLoc =
|
|
new typename Helper::Func(std::forward<F>(func), *finallyLoc);
|
|
|
|
fiber->setFunctionFinally(std::ref(*funcLoc), std::ref(*finallyLoc));
|
|
}
|
|
|
|
if (observer_) {
|
|
observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
|
|
}
|
|
|
|
return fiber;
|
|
}
|
|
|
|
template <typename F, typename G>
|
|
void FiberManager::addTaskFinally(F&& func, G&& finally) {
|
|
readyFibers_.push_back(
|
|
*createTaskFinally(std::forward<F>(func), std::forward<G>(finally)));
|
|
ensureLoopScheduled();
|
|
}
|
|
|
|
template <typename F, typename G>
|
|
void FiberManager::addTaskFinallyEager(F&& func, G&& finally) {
|
|
runEagerFiber(
|
|
createTaskFinally(std::forward<F>(func), std::forward<G>(finally)));
|
|
}
|
|
|
|
template <typename F>
|
|
invoke_result_t<F> FiberManager::runInMainContext(F&& func) {
|
|
if (UNLIKELY(activeFiber_ == nullptr)) {
|
|
return runNoInline(std::forward<F>(func));
|
|
}
|
|
|
|
typedef invoke_result_t<F> Result;
|
|
|
|
folly::Try<Result> result;
|
|
auto f = [&func, &result]() mutable {
|
|
folly::tryEmplaceWith(result, std::forward<F>(func));
|
|
};
|
|
|
|
immediateFunc_ = std::ref(f);
|
|
activeFiber_->preempt(Fiber::AWAITING_IMMEDIATE);
|
|
|
|
return std::move(result).value();
|
|
}
|
|
|
|
inline FiberManager& FiberManager::getFiberManager() {
|
|
assert(getCurrentFiberManager() != nullptr);
|
|
return *getCurrentFiberManager();
|
|
}
|
|
|
|
inline FiberManager* FiberManager::getFiberManagerUnsafe() {
|
|
return getCurrentFiberManager();
|
|
}
|
|
|
|
inline bool FiberManager::hasActiveFiber() const {
|
|
return activeFiber_ != nullptr;
|
|
}
|
|
|
|
inline folly::Optional<std::chrono::nanoseconds>
|
|
FiberManager::getCurrentTaskRunningTime() const {
|
|
if (activeFiber_ && activeFiber_->taskOptions_.logRunningTime &&
|
|
activeFiber_->state_ == Fiber::RUNNING) {
|
|
return activeFiber_->prevDuration_ + std::chrono::steady_clock::now() -
|
|
activeFiber_->currStartTime_;
|
|
}
|
|
return folly::none;
|
|
}
|
|
|
|
inline void FiberManager::yield() {
|
|
assert(getCurrentFiberManager() == this);
|
|
assert(activeFiber_ != nullptr);
|
|
assert(activeFiber_->state_ == Fiber::RUNNING);
|
|
activeFiber_->preempt(Fiber::YIELDED);
|
|
}
|
|
|
|
template <typename T>
|
|
T& FiberManager::local() {
|
|
if (std::type_index(typeid(T)) == localType_ && currentFiber_) {
|
|
return currentFiber_->localData_.get<T>();
|
|
}
|
|
return localThread<T>();
|
|
}
|
|
|
|
template <typename T>
|
|
T& FiberManager::localThread() {
|
|
#ifndef __APPLE__
|
|
static thread_local T t;
|
|
return t;
|
|
#else // osx doesn't support thread_local
|
|
static ThreadLocal<T> t;
|
|
return *t;
|
|
#endif
|
|
}
|
|
|
|
inline void FiberManager::initLocalData(Fiber& fiber) {
|
|
auto fm = getFiberManagerUnsafe();
|
|
if (fm && fm->currentFiber_ && fm->localType_ == localType_) {
|
|
fiber.localData_ = fm->currentFiber_->localData_;
|
|
}
|
|
fiber.rcontext_ = RequestContext::saveContext();
|
|
}
|
|
|
|
template <typename LocalT>
|
|
FiberManager::FiberManager(
|
|
LocalType<LocalT>,
|
|
std::unique_ptr<LoopController> loopController__,
|
|
Options options)
|
|
: loopController_(std::move(loopController__)),
|
|
stackAllocator_(options.guardPagesPerStack),
|
|
options_(preprocessOptions(std::move(options))),
|
|
exceptionCallback_([](std::exception_ptr eptr, std::string context) {
|
|
try {
|
|
std::rethrow_exception(eptr);
|
|
} catch (const std::exception& e) {
|
|
LOG(DFATAL) << "Exception " << typeid(e).name() << " with message '"
|
|
<< e.what() << "' was thrown in "
|
|
<< "FiberManager with context '" << context << "'";
|
|
} catch (...) {
|
|
LOG(DFATAL) << "Unknown exception was thrown in FiberManager with "
|
|
<< "context '" << context << "'";
|
|
}
|
|
}),
|
|
fibersPoolResizer_(*this),
|
|
localType_(typeid(LocalT)) {
|
|
loopController_->setFiberManager(this);
|
|
}
|
|
|
|
template <typename F>
|
|
typename FirstArgOf<F>::type::value_type inline await_async(F&& func) {
|
|
typedef typename FirstArgOf<F>::type::value_type Result;
|
|
typedef typename FirstArgOf<F>::type::baton_type BatonT;
|
|
|
|
return Promise<Result, BatonT>::await_async(std::forward<F>(func));
|
|
}
|
|
|
|
template <typename F>
|
|
invoke_result_t<F> inline runInMainContext(F&& func) {
|
|
auto fm = FiberManager::getFiberManagerUnsafe();
|
|
if (UNLIKELY(fm == nullptr)) {
|
|
return runNoInline(std::forward<F>(func));
|
|
}
|
|
return fm->runInMainContext(std::forward<F>(func));
|
|
}
|
|
|
|
} // namespace fibers
|
|
} // namespace folly
|