Commit 163a570e authored by Brian Watling's avatar Brian Watling Committed by Viswanath Sivakumar

Add ability to yield execution of the currently running fiber

Summary: This diff allows fibers to explicitly yield execution to other fibers and the event loop

Test Plan: unit tests

Reviewed By: andrii@fb.com

Subscribers: folly-diffs@, yfeldblum, chalfant, tao-eng@

FB internal diff: D1965297

Signature: t1:1965297:1428082686:e524e9dd21b3fb951e1d3556e4cb3eedc3e6511a
parent 7b7e1fc7
......@@ -60,6 +60,7 @@ class Fiber {
AWAITING, /**< Is currently blocked */
AWAITING_IMMEDIATE, /**< Was preempted to run an immediate function,
and will be resumed right away */
YIELDED, /**< The fiber yielded execution voluntarily */
};
State state_{INVALID}; /**< current Fiber state */
......
......@@ -87,6 +87,9 @@ inline void FiberManager::runReadyFiber(Fiber* fiber) {
assert(fibersAllocated_ > 0);
--fibersAllocated_;
}
} else if (fiber->state_ == Fiber::YIELDED) {
fiber->state_ = Fiber::READY_TO_RUN;
yieldedFibers_.push_back(*fiber);
}
currentFiber_ = nullptr;
}
......@@ -132,6 +135,11 @@ inline bool FiberManager::loopUntilNoReady() {
);
}
if (!yieldedFibers_.empty()) {
readyFibers_.splice(readyFibers_.end(), yieldedFibers_);
ensureLoopScheduled();
}
return fibersActive_ > 0;
}
......@@ -379,6 +387,13 @@ inline bool FiberManager::hasActiveFiber() const {
return activeFiber_ != nullptr;
}
inline void FiberManager::yield() {
assert(currentFiberManager_ == 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_) {
......
......@@ -18,6 +18,7 @@
#include <functional>
#include <memory>
#include <queue>
#include <thread>
#include <typeindex>
#include <unordered_set>
#include <vector>
......@@ -222,6 +223,14 @@ class FiberManager {
*/
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();
static FiberManager& getFiberManager();
static FiberManager* getFiberManagerUnsafe();
......@@ -255,6 +264,8 @@ class FiberManager {
Fiber* currentFiber_{nullptr};
FiberTailQueue readyFibers_; /**< queue of fibers ready to be executed */
FiberTailQueue yieldedFibers_; /**< queue of fibers which have yielded
execution */
FiberTailQueue fibersPool_; /**< pool of unitialized Fiber objects */
size_t fibersAllocated_{0}; /**< total number of fibers allocated */
......@@ -433,6 +444,15 @@ T& local() {
return FiberManager::localThread<T>();
}
inline void yield() {
auto fm = FiberManager::getFiberManagerUnsafe();
if (fm) {
fm->yield();
} else {
std::this_thread::yield();
}
}
}}
#include "FiberManager-inl.h"
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment