From 163a570ea0a11a82f1d81288f0314c9f4544cf0a Mon Sep 17 00:00:00 2001 From: Brian Watling Date: Tue, 7 Apr 2015 05:34:06 -0700 Subject: [PATCH] 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 --- folly/experimental/fibers/Fiber.h | 1 + folly/experimental/fibers/FiberManager-inl.h | 15 +++++++++++++++ folly/experimental/fibers/FiberManager.h | 20 ++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/folly/experimental/fibers/Fiber.h b/folly/experimental/fibers/Fiber.h index 6ab48af4..2c56efa9 100644 --- a/folly/experimental/fibers/Fiber.h +++ b/folly/experimental/fibers/Fiber.h @@ -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 */ diff --git a/folly/experimental/fibers/FiberManager-inl.h b/folly/experimental/fibers/FiberManager-inl.h index f56b0640..554d7c28 100644 --- a/folly/experimental/fibers/FiberManager-inl.h +++ b/folly/experimental/fibers/FiberManager-inl.h @@ -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 T& FiberManager::local() { if (std::type_index(typeid(T)) == localType_ && currentFiber_) { diff --git a/folly/experimental/fibers/FiberManager.h b/folly/experimental/fibers/FiberManager.h index 94e6c289..49ccfa4a 100644 --- a/folly/experimental/fibers/FiberManager.h +++ b/folly/experimental/fibers/FiberManager.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -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(); } +inline void yield() { + auto fm = FiberManager::getFiberManagerUnsafe(); + if (fm) { + fm->yield(); + } else { + std::this_thread::yield(); + } +} + }} #include "FiberManager-inl.h" -- 2.34.1