725 lines
22 KiB
C++
725 lines
22 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 <functional>
|
|
#include <memory>
|
|
#include <queue>
|
|
#include <thread>
|
|
#include <type_traits>
|
|
#include <typeindex>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
#include <folly/AtomicIntrusiveLinkedList.h>
|
|
#include <folly/CPortability.h>
|
|
#include <folly/Executor.h>
|
|
#include <folly/IntrusiveList.h>
|
|
#include <folly/Likely.h>
|
|
#include <folly/Portability.h>
|
|
#include <folly/Try.h>
|
|
#include <folly/functional/Invoke.h>
|
|
#include <folly/io/async/HHWheelTimer.h>
|
|
#include <folly/io/async/Request.h>
|
|
|
|
#include <folly/experimental/ExecutionObserver.h>
|
|
#include <folly/fibers/BoostContextCompatibility.h>
|
|
#include <folly/fibers/Fiber.h>
|
|
#include <folly/fibers/GuardPageAllocator.h>
|
|
#include <folly/fibers/LoopController.h>
|
|
#include <folly/fibers/traits.h>
|
|
|
|
namespace folly {
|
|
|
|
template <class T>
|
|
class Future;
|
|
|
|
namespace fibers {
|
|
|
|
class Baton;
|
|
class Fiber;
|
|
|
|
struct TaskOptions;
|
|
|
|
template <typename T>
|
|
class LocalType {};
|
|
|
|
class InlineFunctionRunner {
|
|
public:
|
|
virtual ~InlineFunctionRunner() {}
|
|
|
|
/**
|
|
* func must be executed inline and only once.
|
|
*/
|
|
virtual void run(folly::Function<void()> func) = 0;
|
|
};
|
|
|
|
/**
|
|
* @class FiberManager
|
|
* @brief Single-threaded task execution engine.
|
|
*
|
|
* FiberManager allows semi-parallel task execution on the same thread. Each
|
|
* task can notify FiberManager that it is blocked on something (via await())
|
|
* call. This will pause execution of this task and it will be resumed only
|
|
* when it is unblocked (via setData()).
|
|
*/
|
|
class FiberManager : public ::folly::Executor {
|
|
public:
|
|
struct Options {
|
|
static constexpr size_t kDefaultStackSize{16 * 1024};
|
|
|
|
/**
|
|
* Maximum stack size for fibers which will be used for executing all the
|
|
* tasks.
|
|
*/
|
|
size_t stackSize{kDefaultStackSize};
|
|
|
|
/**
|
|
* Sanitizers need a lot of extra stack space. 16x is a conservative
|
|
* estimate, but 8x also worked with tests where it mattered. Similarly,
|
|
* debug builds need extra stack space due to reduced inlining.
|
|
*
|
|
* Note that over-allocating here does not necessarily increase RSS, since
|
|
* unused memory is pretty much free.
|
|
*/
|
|
size_t stackSizeMultiplier{kIsSanitize ? 16 : (kIsDebug ? 2 : 1)};
|
|
|
|
/**
|
|
* Record exact amount of stack used.
|
|
*
|
|
* This is fairly expensive: we fill each newly allocated stack
|
|
* with some known value and find the boundary of unused stack
|
|
* with linear search every time we surrender the stack back to fibersPool.
|
|
* 0 disables stack recording.
|
|
*/
|
|
size_t recordStackEvery{0};
|
|
|
|
/**
|
|
* Keep at most this many free fibers in the pool.
|
|
* This way the total number of fibers in the system is always bounded
|
|
* by the number of active fibers + maxFibersPoolSize.
|
|
*/
|
|
size_t maxFibersPoolSize{1000};
|
|
|
|
/**
|
|
* Protect a small number of fiber stacks with this many guard pages.
|
|
*/
|
|
size_t guardPagesPerStack{1};
|
|
|
|
/**
|
|
* Free unnecessary fibers in the fibers pool every fibersPoolResizePeriodMs
|
|
* milliseconds. If value is 0, periodic resizing of the fibers pool is
|
|
* disabled.
|
|
*/
|
|
uint32_t fibersPoolResizePeriodMs{0};
|
|
|
|
constexpr Options() {}
|
|
|
|
auto hash() const {
|
|
return std::make_tuple(
|
|
stackSize,
|
|
stackSizeMultiplier,
|
|
recordStackEvery,
|
|
maxFibersPoolSize,
|
|
guardPagesPerStack,
|
|
fibersPoolResizePeriodMs);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* A (const) Options instance with a dedicated unique identifier,
|
|
* which is used as a key in FiberManagerMap.
|
|
* This is relevant if you want to run different FiberManager,
|
|
* with different Option, on the same EventBase.
|
|
*/
|
|
struct FrozenOptions {
|
|
explicit FrozenOptions(Options opts)
|
|
: options(std::move(opts)), token(create(options)) {}
|
|
|
|
const Options options;
|
|
const ssize_t token;
|
|
|
|
private:
|
|
static ssize_t create(const Options&);
|
|
};
|
|
|
|
using ExceptionCallback =
|
|
folly::Function<void(std::exception_ptr, std::string)>;
|
|
|
|
FiberManager(const FiberManager&) = delete;
|
|
FiberManager& operator=(const FiberManager&) = delete;
|
|
|
|
/**
|
|
* Initializes, but doesn't start FiberManager loop
|
|
*
|
|
* @param loopController A LoopController object
|
|
* @param options FiberManager options
|
|
*/
|
|
explicit FiberManager(
|
|
std::unique_ptr<LoopController> loopController,
|
|
Options options = Options());
|
|
|
|
/**
|
|
* Initializes, but doesn't start FiberManager loop
|
|
*
|
|
* @param loopController A LoopController object
|
|
* @param options FiberManager options
|
|
* @tparam LocalT only local of this type may be stored on fibers.
|
|
* Locals of other types will be considered thread-locals.
|
|
*/
|
|
template <typename LocalT>
|
|
FiberManager(
|
|
LocalType<LocalT>,
|
|
std::unique_ptr<LoopController> loopController,
|
|
Options options = Options());
|
|
|
|
~FiberManager() override;
|
|
|
|
/**
|
|
* Controller access.
|
|
*/
|
|
LoopController& loopController();
|
|
const LoopController& loopController() const;
|
|
|
|
/**
|
|
* Keeps running ready tasks until the list of ready tasks is empty.
|
|
*/
|
|
void loopUntilNoReady();
|
|
|
|
/**
|
|
* This should only be called by a LoopController.
|
|
*/
|
|
void loopUntilNoReadyImpl();
|
|
|
|
/**
|
|
* This should only be called by a LoopController.
|
|
*/
|
|
void runEagerFiberImpl(Fiber*);
|
|
|
|
/**
|
|
* This should only be called by a LoopController.
|
|
*/
|
|
bool shouldRunLoopRemote();
|
|
|
|
/**
|
|
* @return true if there are outstanding tasks.
|
|
*/
|
|
bool hasTasks() const;
|
|
bool isRemoteScheduled() const;
|
|
|
|
/**
|
|
* @return The number of currently active fibers (ready to run or blocked).
|
|
* Does not include the number of remotely enqueued tasks that have not been
|
|
* run yet.
|
|
*/
|
|
size_t numActiveTasks() const noexcept { return fibersActive_; }
|
|
|
|
/**
|
|
* @return true if there are tasks ready to run.
|
|
*/
|
|
bool hasReadyTasks() const;
|
|
|
|
/**
|
|
* Sets exception callback which will be called if any of the tasks throws an
|
|
* exception.
|
|
*
|
|
* @param ec An ExceptionCallback object.
|
|
*/
|
|
void setExceptionCallback(ExceptionCallback ec);
|
|
|
|
/**
|
|
* Add a new task to be executed. Must be called from FiberManager's thread.
|
|
*
|
|
* @param func Task functor; must have a signature of `void func()`.
|
|
* The object will be destroyed once task execution is complete.
|
|
* @param taskOptions Task specific configs.
|
|
*/
|
|
template <typename F>
|
|
void addTask(F&& func, TaskOptions taskOptions = TaskOptions());
|
|
|
|
/**
|
|
* Add a new task to be executed and return a future that will be set on
|
|
* return from func. Must be called from FiberManager's thread.
|
|
*
|
|
* @param func Task functor; must have a signature of `void func()`.
|
|
* The object will be destroyed once task execution is complete.
|
|
*/
|
|
template <typename F>
|
|
auto addTaskFuture(F&& func)
|
|
-> folly::Future<folly::lift_unit_t<invoke_result_t<F>>>;
|
|
|
|
/**
|
|
* Add a new task to be executed. Must be called from FiberManager's thread.
|
|
* The new task is run eagerly. addTaskEager will return only once the new
|
|
* task reaches its first suspension point or is completed.
|
|
*
|
|
* @param func Task functor; must have a signature of `void func()`.
|
|
* The object will be destroyed once task execution is complete.
|
|
*/
|
|
template <typename F>
|
|
void addTaskEager(F&& func);
|
|
|
|
/**
|
|
* Add a new task to be executed and return a future that will be set on
|
|
* return from func. Must be called from FiberManager's thread.
|
|
* The new task is run eagerly. addTaskEager will return only once the new
|
|
* task reaches its first suspension point or is completed.
|
|
*
|
|
* @param func Task functor; must have a signature of `void func()`.
|
|
* The object will be destroyed once task execution is complete.
|
|
*/
|
|
template <typename F>
|
|
auto addTaskEagerFuture(F&& func)
|
|
-> folly::Future<folly::lift_unit_t<invoke_result_t<F>>>;
|
|
|
|
/**
|
|
* Add a new task to be executed. Safe to call from other threads.
|
|
*
|
|
* @param func Task function; must have a signature of `void func()`.
|
|
* The object will be destroyed once task execution is complete.
|
|
*/
|
|
template <typename F>
|
|
void addTaskRemote(F&& func);
|
|
|
|
/**
|
|
* Add a new task to be executed and return a future that will be set on
|
|
* return from func. Safe to call from other threads.
|
|
*
|
|
* @param func Task function; must have a signature of `void func()`.
|
|
* The object will be destroyed once task execution is complete.
|
|
*/
|
|
template <typename F>
|
|
auto addTaskRemoteFuture(F&& func)
|
|
-> folly::Future<folly::lift_unit_t<invoke_result_t<F>>>;
|
|
|
|
// Executor interface calls addTaskRemote
|
|
void add(folly::Func f) override { addTaskRemote(std::move(f)); }
|
|
|
|
/**
|
|
* Add a new task. When the task is complete, execute finally(Try<Result>&&)
|
|
* on the main context.
|
|
*
|
|
* @param func Task functor; must have a signature of `T func()` for some T.
|
|
* @param finally Finally functor; must have a signature of
|
|
* `void finally(Try<T>&&)` and will be passed
|
|
* the result of func() (including the exception if occurred).
|
|
*/
|
|
template <typename F, typename G>
|
|
void addTaskFinally(F&& func, G&& finally);
|
|
|
|
/**
|
|
* Add a new task. When the task is complete, execute finally(Try<Result>&&)
|
|
* on the main context.
|
|
* The new task is run eagerly. addTaskEager will return only once the new
|
|
* task reaches its first suspension point or is completed.
|
|
*
|
|
* @param func Task functor; must have a signature of `T func()` for some T.
|
|
* @param finally Finally functor; must have a signature of
|
|
* `void finally(Try<T>&&)` and will be passed
|
|
* the result of func() (including the exception if occurred).
|
|
*/
|
|
template <typename F, typename G>
|
|
void addTaskFinallyEager(F&& func, G&& finally);
|
|
|
|
/**
|
|
* If called from a fiber, immediately switches to the FiberManager's context
|
|
* and runs func(), going back to the Fiber's context after completion.
|
|
* Outside a fiber, just calls func() directly.
|
|
*
|
|
* @return value returned by func().
|
|
*/
|
|
template <typename F>
|
|
invoke_result_t<F> runInMainContext(F&& func);
|
|
|
|
/**
|
|
* Returns a refference to a fiber-local context for given Fiber. Should be
|
|
* always called with the same T for each fiber. Fiber-local context is lazily
|
|
* default-constructed on first request.
|
|
* When new task is scheduled via addTask / addTaskRemote from a fiber its
|
|
* fiber-local context is copied into the new fiber.
|
|
*/
|
|
template <typename T>
|
|
T& local();
|
|
|
|
template <typename T>
|
|
FOLLY_EXPORT static T& localThread();
|
|
|
|
/**
|
|
* @return How many fiber objects (and stacks) has this manager allocated.
|
|
*/
|
|
size_t fibersAllocated() const;
|
|
|
|
/**
|
|
* @return How many of the allocated fiber objects are currently
|
|
* in the free pool.
|
|
*/
|
|
size_t fibersPoolSize() const;
|
|
|
|
/**
|
|
* @return true if running activeFiber_ is not nullptr.
|
|
*/
|
|
bool hasActiveFiber() const;
|
|
|
|
/**
|
|
* @return How long has the currently running task on the fiber ran, in
|
|
* terms of wallclock time. This excludes the time spent in preempted or
|
|
* waiting stages. This only works if TaskOptions.logRunningTime is true
|
|
* during addTask().
|
|
*/
|
|
folly::Optional<std::chrono::nanoseconds> getCurrentTaskRunningTime() const;
|
|
|
|
/**
|
|
* @return The currently running fiber or null if no fiber is executing.
|
|
*/
|
|
Fiber* currentFiber() const { return currentFiber_; }
|
|
|
|
/**
|
|
* @return What was the most observed fiber stack usage (in bytes).
|
|
*/
|
|
size_t stackHighWatermark() const;
|
|
|
|
/**
|
|
* Yield execution of the currently running fiber. Must only be called from a
|
|
* fiber executing on this FiberManager. The calling fiber will be scheduled
|
|
* when all other fibers have had a chance to run and the event loop is
|
|
* serviced.
|
|
*/
|
|
void yield();
|
|
|
|
/**
|
|
* Setup fibers execution observation/instrumentation. Fiber locals are
|
|
* available to observer.
|
|
*
|
|
* @param observer Fiber's execution observer.
|
|
*/
|
|
void setObserver(ExecutionObserver* observer);
|
|
|
|
/**
|
|
* @return Current observer for this FiberManager. Returns nullptr
|
|
* if no observer has been set.
|
|
*/
|
|
ExecutionObserver* getObserver();
|
|
|
|
/**
|
|
* Setup fibers preempt runner.
|
|
*/
|
|
void setPreemptRunner(InlineFunctionRunner* preemptRunner);
|
|
|
|
/**
|
|
* Returns an estimate of the number of fibers which are waiting to run (does
|
|
* not include fibers or tasks scheduled remotely).
|
|
*/
|
|
size_t runQueueSize() const {
|
|
return readyFibers_.size() + (yieldedFibers_ ? yieldedFibers_->size() : 0);
|
|
}
|
|
|
|
static FiberManager& getFiberManager();
|
|
static FiberManager* getFiberManagerUnsafe();
|
|
|
|
const Options& getOptions() const { return options_; }
|
|
|
|
private:
|
|
friend class Baton;
|
|
friend class Fiber;
|
|
template <typename F>
|
|
struct AddTaskHelper;
|
|
template <typename F, typename G>
|
|
struct AddTaskFinallyHelper;
|
|
|
|
struct RemoteTask {
|
|
template <typename F>
|
|
explicit RemoteTask(F&& f)
|
|
: func(std::forward<F>(f)), rcontext(RequestContext::saveContext()) {}
|
|
template <typename F>
|
|
RemoteTask(F&& f, const Fiber::LocalData& localData_)
|
|
: func(std::forward<F>(f)),
|
|
localData(std::make_unique<Fiber::LocalData>(localData_)),
|
|
rcontext(RequestContext::saveContext()) {}
|
|
folly::Function<void()> func;
|
|
std::unique_ptr<Fiber::LocalData> localData;
|
|
std::shared_ptr<RequestContext> rcontext;
|
|
AtomicIntrusiveLinkedListHook<RemoteTask> nextRemoteTask;
|
|
};
|
|
|
|
template <typename F>
|
|
Fiber* createTask(F&& func, TaskOptions taskOptions);
|
|
|
|
template <typename F, typename G>
|
|
Fiber* createTaskFinally(F&& func, G&& finally);
|
|
|
|
void runEagerFiber(Fiber* fiber);
|
|
|
|
void activateFiber(Fiber* fiber);
|
|
void deactivateFiber(Fiber* fiber);
|
|
|
|
template <typename LoopFunc>
|
|
void runFibersHelper(LoopFunc&& loopFunc);
|
|
|
|
size_t recordStackPosition(size_t position);
|
|
|
|
typedef folly::IntrusiveList<Fiber, &Fiber::listHook_> FiberTailQueue;
|
|
typedef folly::IntrusiveList<Fiber, &Fiber::globalListHook_>
|
|
GlobalFiberTailQueue;
|
|
|
|
Fiber* activeFiber_{nullptr}; /**< active fiber, nullptr on main context */
|
|
/**
|
|
* Same as active fiber, but also set for functions run from fiber on main
|
|
* context.
|
|
*/
|
|
Fiber* currentFiber_{nullptr};
|
|
|
|
FiberTailQueue readyFibers_; /**< queue of fibers ready to be executed */
|
|
FiberTailQueue* yieldedFibers_{nullptr}; /**< queue of fibers which have
|
|
yielded execution */
|
|
FiberTailQueue fibersPool_; /**< pool of uninitialized Fiber objects */
|
|
|
|
GlobalFiberTailQueue allFibers_; /**< list of all Fiber objects owned */
|
|
|
|
// total number of fibers allocated
|
|
std::atomic<size_t> fibersAllocated_{0};
|
|
// total number of fibers in the free pool
|
|
std::atomic<size_t> fibersPoolSize_{0};
|
|
size_t fibersActive_{0}; /**< number of running or blocked fibers */
|
|
size_t fiberId_{0}; /**< id of last fiber used */
|
|
|
|
/**
|
|
* Maximum number of active fibers in the last period lasting
|
|
* Options::fibersPoolResizePeriod milliseconds.
|
|
*/
|
|
size_t maxFibersActiveLastPeriod_{0};
|
|
|
|
std::unique_ptr<LoopController> loopController_;
|
|
bool isLoopScheduled_{false}; /**< was the ready loop scheduled to run? */
|
|
|
|
/**
|
|
* When we are inside FiberManager loop this points to FiberManager. Otherwise
|
|
* it's nullptr
|
|
*/
|
|
static FiberManager*& getCurrentFiberManager();
|
|
|
|
/**
|
|
* Allocator used to allocate stack for Fibers in the pool.
|
|
* Allocates stack on the stack of the main context.
|
|
*/
|
|
GuardPageAllocator stackAllocator_;
|
|
|
|
const Options options_; /**< FiberManager options */
|
|
|
|
/**
|
|
* Largest observed individual Fiber stack usage in bytes.
|
|
*/
|
|
std::atomic<size_t> stackHighWatermark_{0};
|
|
|
|
/**
|
|
* Schedules a loop with loopController (unless already scheduled before).
|
|
*/
|
|
void ensureLoopScheduled();
|
|
|
|
/**
|
|
* @return An initialized Fiber object from the pool
|
|
*/
|
|
Fiber* getFiber();
|
|
|
|
/**
|
|
* Sets local data for given fiber if all conditions are met.
|
|
*/
|
|
void initLocalData(Fiber& fiber);
|
|
|
|
/**
|
|
* Function passed to the await call.
|
|
*/
|
|
folly::Function<void(Fiber&)> awaitFunc_;
|
|
|
|
/**
|
|
* Function passed to the runInMainContext call.
|
|
*/
|
|
folly::Function<void()> immediateFunc_;
|
|
|
|
/**
|
|
* Preempt runner.
|
|
*/
|
|
InlineFunctionRunner* preemptRunner_{nullptr};
|
|
|
|
/**
|
|
* Fiber's execution observer.
|
|
*/
|
|
ExecutionObserver* observer_{nullptr};
|
|
|
|
ExceptionCallback exceptionCallback_; /**< task exception callback */
|
|
|
|
folly::AtomicIntrusiveLinkedList<Fiber, &Fiber::nextRemoteReady_>
|
|
remoteReadyQueue_;
|
|
|
|
folly::AtomicIntrusiveLinkedList<RemoteTask, &RemoteTask::nextRemoteTask>
|
|
remoteTaskQueue_;
|
|
|
|
ssize_t remoteCount_{0};
|
|
|
|
/**
|
|
* Number of uncaught exceptions when FiberManager loop was called.
|
|
*/
|
|
ssize_t numUncaughtExceptions_{0};
|
|
/**
|
|
* Current exception when FiberManager loop was called.
|
|
*/
|
|
std::exception_ptr currentException_;
|
|
|
|
class FibersPoolResizer final : private HHWheelTimer::Callback {
|
|
public:
|
|
explicit FibersPoolResizer(FiberManager& fm) : fiberManager_(fm) {}
|
|
void run();
|
|
|
|
private:
|
|
FiberManager& fiberManager_;
|
|
void timeoutExpired() noexcept override { run(); }
|
|
void callbackCanceled() noexcept override {}
|
|
};
|
|
|
|
FibersPoolResizer fibersPoolResizer_;
|
|
bool fibersPoolResizerScheduled_{false};
|
|
|
|
void doFibersPoolResizing();
|
|
|
|
/**
|
|
* Only local of this type will be available for fibers.
|
|
*/
|
|
std::type_index localType_;
|
|
|
|
void runReadyFiber(Fiber* fiber);
|
|
void remoteReadyInsert(Fiber* fiber);
|
|
|
|
#ifdef FOLLY_SANITIZE_ADDRESS
|
|
|
|
// These methods notify ASAN when a fiber is entered/exited so that ASAN can
|
|
// find the right stack extents when it needs to poison/unpoison the stack.
|
|
|
|
void registerStartSwitchStackWithAsan(
|
|
void** saveFakeStack, const void* stackBase, size_t stackSize);
|
|
void registerFinishSwitchStackWithAsan(
|
|
void* fakeStack, const void** saveStackBase, size_t* saveStackSize);
|
|
void freeFakeStack(void* fakeStack);
|
|
void unpoisonFiberStack(const Fiber* fiber);
|
|
|
|
#endif // FOLLY_SANITIZE_ADDRESS
|
|
|
|
bool alternateSignalStackRegistered_{false};
|
|
|
|
void maybeRegisterAlternateSignalStack();
|
|
};
|
|
|
|
/**
|
|
* @return true iff we are running in a fiber's context
|
|
*/
|
|
inline bool onFiber() {
|
|
auto fm = FiberManager::getFiberManagerUnsafe();
|
|
return fm ? fm->hasActiveFiber() : false;
|
|
}
|
|
|
|
/**
|
|
* Add a new task to be executed.
|
|
*
|
|
* @param func Task functor; must have a signature of `void func()`.
|
|
* The object will be destroyed once task execution is complete.
|
|
*/
|
|
template <typename F>
|
|
inline void addTask(F&& func) {
|
|
return FiberManager::getFiberManager().addTask(std::forward<F>(func));
|
|
}
|
|
|
|
template <typename F>
|
|
inline void addTaskEager(F&& func) {
|
|
return FiberManager::getFiberManager().addTaskEager(std::forward<F>(func));
|
|
}
|
|
|
|
/**
|
|
* Add a new task. When the task is complete, execute finally(Try<Result>&&)
|
|
* on the main context.
|
|
* Task functor is run and destroyed on the fiber context.
|
|
* Finally functor is run and destroyed on the main context.
|
|
*
|
|
* @param func Task functor; must have a signature of `T func()` for some T.
|
|
* @param finally Finally functor; must have a signature of
|
|
* `void finally(Try<T>&&)` and will be passed
|
|
* the result of func() (including the exception if occurred).
|
|
*/
|
|
template <typename F, typename G>
|
|
inline void addTaskFinally(F&& func, G&& finally) {
|
|
return FiberManager::getFiberManager().addTaskFinally(
|
|
std::forward<F>(func), std::forward<G>(finally));
|
|
}
|
|
|
|
template <typename F, typename G>
|
|
inline void addTaskFinallyEager(F&& func, G&& finally) {
|
|
return FiberManager::getFiberManager().addTaskFinallyEager(
|
|
std::forward<F>(func), std::forward<G>(finally));
|
|
}
|
|
|
|
/**
|
|
* Blocks task execution until given promise is fulfilled.
|
|
*
|
|
* Calls function passing in a Promise<T>, which has to be fulfilled.
|
|
*
|
|
* @return data which was used to fulfill the promise.
|
|
*/
|
|
template <typename F>
|
|
typename FirstArgOf<F>::type::value_type inline await_async(F&& func);
|
|
#if !defined(_MSC_VER)
|
|
template <typename F>
|
|
FOLLY_ERASE typename FirstArgOf<F>::type::value_type await(F&& func) {
|
|
return await_async(static_cast<F&&>(func));
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* If called from a fiber, immediately switches to the FiberManager's context
|
|
* and runs func(), going back to the Fiber's context after completion.
|
|
* Outside a fiber, just calls func() directly.
|
|
*
|
|
* @return value returned by func().
|
|
*/
|
|
template <typename F>
|
|
invoke_result_t<F> inline runInMainContext(F&& func);
|
|
|
|
/**
|
|
* Returns a refference to a fiber-local context for given Fiber. Should be
|
|
* always called with the same T for each fiber. Fiber-local context is lazily
|
|
* default-constructed on first request.
|
|
* When new task is scheduled via addTask / addTaskRemote from a fiber its
|
|
* fiber-local context is copied into the new fiber.
|
|
*/
|
|
template <typename T>
|
|
T& local() {
|
|
auto fm = FiberManager::getFiberManagerUnsafe();
|
|
if (fm) {
|
|
return fm->local<T>();
|
|
}
|
|
return FiberManager::localThread<T>();
|
|
}
|
|
|
|
inline void yield() {
|
|
auto fm = FiberManager::getFiberManagerUnsafe();
|
|
if (fm) {
|
|
fm->yield();
|
|
} else {
|
|
std::this_thread::yield();
|
|
}
|
|
}
|
|
} // namespace fibers
|
|
} // namespace folly
|
|
|
|
#include <folly/fibers/FiberManagerInternal-inl.h>
|