Add ability to yield execution of the currently running fiber
authorBrian Watling <bwatling@fb.com>
Tue, 7 Apr 2015 12:34:06 +0000 (05:34 -0700)
committerViswanath Sivakumar <viswanath@fb.com>
Fri, 10 Apr 2015 03:34:15 +0000 (20:34 -0700)
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
folly/experimental/fibers/FiberManager-inl.h
folly/experimental/fibers/FiberManager.h

index 6ab48af4840cb8736f584d030be1ab1d6a8a88a6..2c56efa9a8026ea0a4eaa14df5200e1e689d9db0 100644 (file)
@@ -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 */
index f56b0640002f55d5826402271e8900fc5e8b067e..554d7c2802e55366f32d24dc835dc31ba8f0e5f6 100644 (file)
@@ -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_) {
index 94e6c289bdbf861119c1840113521874bae0bdf7..49ccfa4a4acbc35680e3c1d882fd5457df9c8c36 100644 (file)
@@ -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"