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 { ...@@ -60,6 +60,7 @@ class Fiber {
AWAITING, /**< Is currently blocked */ AWAITING, /**< Is currently blocked */
AWAITING_IMMEDIATE, /**< Was preempted to run an immediate function, AWAITING_IMMEDIATE, /**< Was preempted to run an immediate function,
and will be resumed right away */ and will be resumed right away */
YIELDED, /**< The fiber yielded execution voluntarily */
}; };
State state_{INVALID}; /**< current Fiber state */ State state_{INVALID}; /**< current Fiber state */
......
...@@ -87,6 +87,9 @@ inline void FiberManager::runReadyFiber(Fiber* fiber) { ...@@ -87,6 +87,9 @@ inline void FiberManager::runReadyFiber(Fiber* fiber) {
assert(fibersAllocated_ > 0); assert(fibersAllocated_ > 0);
--fibersAllocated_; --fibersAllocated_;
} }
} else if (fiber->state_ == Fiber::YIELDED) {
fiber->state_ = Fiber::READY_TO_RUN;
yieldedFibers_.push_back(*fiber);
} }
currentFiber_ = nullptr; currentFiber_ = nullptr;
} }
...@@ -132,6 +135,11 @@ inline bool FiberManager::loopUntilNoReady() { ...@@ -132,6 +135,11 @@ inline bool FiberManager::loopUntilNoReady() {
); );
} }
if (!yieldedFibers_.empty()) {
readyFibers_.splice(readyFibers_.end(), yieldedFibers_);
ensureLoopScheduled();
}
return fibersActive_ > 0; return fibersActive_ > 0;
} }
...@@ -379,6 +387,13 @@ inline bool FiberManager::hasActiveFiber() const { ...@@ -379,6 +387,13 @@ inline bool FiberManager::hasActiveFiber() const {
return activeFiber_ != nullptr; 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> template <typename T>
T& FiberManager::local() { T& FiberManager::local() {
if (std::type_index(typeid(T)) == localType_ && currentFiber_) { if (std::type_index(typeid(T)) == localType_ && currentFiber_) {
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <queue> #include <queue>
#include <thread>
#include <typeindex> #include <typeindex>
#include <unordered_set> #include <unordered_set>
#include <vector> #include <vector>
...@@ -222,6 +223,14 @@ class FiberManager { ...@@ -222,6 +223,14 @@ class FiberManager {
*/ */
size_t stackHighWatermark() const; 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& getFiberManager();
static FiberManager* getFiberManagerUnsafe(); static FiberManager* getFiberManagerUnsafe();
...@@ -255,6 +264,8 @@ class FiberManager { ...@@ -255,6 +264,8 @@ class FiberManager {
Fiber* currentFiber_{nullptr}; Fiber* currentFiber_{nullptr};
FiberTailQueue readyFibers_; /**< queue of fibers ready to be executed */ 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 */ FiberTailQueue fibersPool_; /**< pool of unitialized Fiber objects */
size_t fibersAllocated_{0}; /**< total number of fibers allocated */ size_t fibersAllocated_{0}; /**< total number of fibers allocated */
...@@ -433,6 +444,15 @@ T& local() { ...@@ -433,6 +444,15 @@ T& local() {
return FiberManager::localThread<T>(); return FiberManager::localThread<T>();
} }
inline void yield() {
auto fm = FiberManager::getFiberManagerUnsafe();
if (fm) {
fm->yield();
} else {
std::this_thread::yield();
}
}
}} }}
#include "FiberManager-inl.h" #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