Move fibers out of experimental
authorAndrii Grynenko <andrii@fb.com>
Thu, 19 May 2016 05:57:40 +0000 (22:57 -0700)
committerFacebook Github Bot 5 <facebook-github-bot-5-bot@fb.com>
Thu, 19 May 2016 06:08:58 +0000 (23:08 -0700)
Summary: folly::fibers have been used by mcrouter for more than 2 years, so not really experimental.

Reviewed By: pavlo-fb

Differential Revision: D3320595

fbshipit-source-id: 68188f92b71a4206d57222993848521ca5437ef5

78 files changed:
folly/Makefile.am
folly/experimental/fibers/AddTasks-inl.h [deleted file]
folly/experimental/fibers/AddTasks.h [deleted file]
folly/experimental/fibers/Baton-inl.h [deleted file]
folly/experimental/fibers/Baton.cpp [deleted file]
folly/experimental/fibers/Baton.h [deleted file]
folly/experimental/fibers/BoostContextCompatibility.h [deleted file]
folly/experimental/fibers/EventBaseLoopController-inl.h [deleted file]
folly/experimental/fibers/EventBaseLoopController.h [deleted file]
folly/experimental/fibers/Fiber-inl.h [deleted file]
folly/experimental/fibers/Fiber.cpp [deleted file]
folly/experimental/fibers/Fiber.h [deleted file]
folly/experimental/fibers/FiberManager-inl.h [deleted file]
folly/experimental/fibers/FiberManager.cpp [deleted file]
folly/experimental/fibers/FiberManager.h [deleted file]
folly/experimental/fibers/FiberManagerMap.cpp [deleted file]
folly/experimental/fibers/FiberManagerMap.h [deleted file]
folly/experimental/fibers/ForEach-inl.h [deleted file]
folly/experimental/fibers/ForEach.h [deleted file]
folly/experimental/fibers/GenericBaton.h [deleted file]
folly/experimental/fibers/GuardPageAllocator.cpp [deleted file]
folly/experimental/fibers/GuardPageAllocator.h [deleted file]
folly/experimental/fibers/LoopController.h [deleted file]
folly/experimental/fibers/Promise-inl.h [deleted file]
folly/experimental/fibers/Promise.h [deleted file]
folly/experimental/fibers/README.md [deleted file]
folly/experimental/fibers/SimpleLoopController.h [deleted file]
folly/experimental/fibers/TimedMutex-inl.h [deleted file]
folly/experimental/fibers/TimedMutex.h [deleted file]
folly/experimental/fibers/TimeoutController.cpp [deleted file]
folly/experimental/fibers/TimeoutController.h [deleted file]
folly/experimental/fibers/WhenN-inl.h [deleted file]
folly/experimental/fibers/WhenN.h [deleted file]
folly/experimental/fibers/scripts/utils.gdb [deleted file]
folly/experimental/fibers/test/FibersTest.cpp [deleted file]
folly/experimental/fibers/test/FibersTestApp.cpp [deleted file]
folly/experimental/fibers/test/StackOverflow.cpp [deleted file]
folly/experimental/fibers/test/main.cpp [deleted file]
folly/experimental/fibers/traits.h [deleted file]
folly/fibers/AddTasks-inl.h [new file with mode: 0644]
folly/fibers/AddTasks.h [new file with mode: 0644]
folly/fibers/Baton-inl.h [new file with mode: 0644]
folly/fibers/Baton.cpp [new file with mode: 0644]
folly/fibers/Baton.h [new file with mode: 0644]
folly/fibers/BoostContextCompatibility.h [new file with mode: 0644]
folly/fibers/EventBaseLoopController-inl.h [new file with mode: 0644]
folly/fibers/EventBaseLoopController.h [new file with mode: 0644]
folly/fibers/Fiber-inl.h [new file with mode: 0644]
folly/fibers/Fiber.cpp [new file with mode: 0644]
folly/fibers/Fiber.h [new file with mode: 0644]
folly/fibers/FiberManager-inl.h [new file with mode: 0644]
folly/fibers/FiberManager.cpp [new file with mode: 0644]
folly/fibers/FiberManager.h [new file with mode: 0644]
folly/fibers/FiberManagerMap.cpp [new file with mode: 0644]
folly/fibers/FiberManagerMap.h [new file with mode: 0644]
folly/fibers/ForEach-inl.h [new file with mode: 0644]
folly/fibers/ForEach.h [new file with mode: 0644]
folly/fibers/GenericBaton.h [new file with mode: 0644]
folly/fibers/GuardPageAllocator.cpp [new file with mode: 0644]
folly/fibers/GuardPageAllocator.h [new file with mode: 0644]
folly/fibers/LoopController.h [new file with mode: 0644]
folly/fibers/Promise-inl.h [new file with mode: 0644]
folly/fibers/Promise.h [new file with mode: 0644]
folly/fibers/README.md [new file with mode: 0644]
folly/fibers/SimpleLoopController.h [new file with mode: 0644]
folly/fibers/TimedMutex-inl.h [new file with mode: 0644]
folly/fibers/TimedMutex.h [new file with mode: 0644]
folly/fibers/TimeoutController.cpp [new file with mode: 0644]
folly/fibers/TimeoutController.h [new file with mode: 0644]
folly/fibers/WhenN-inl.h [new file with mode: 0644]
folly/fibers/WhenN.h [new file with mode: 0644]
folly/fibers/scripts/utils.gdb [new file with mode: 0644]
folly/fibers/test/FibersTest.cpp [new file with mode: 0644]
folly/fibers/test/FibersTestApp.cpp [new file with mode: 0644]
folly/fibers/test/StackOverflow.cpp [new file with mode: 0644]
folly/fibers/test/main.cpp [new file with mode: 0644]
folly/fibers/traits.h [new file with mode: 0644]
folly/futures/Future-inl.h

index bc7e42d4d38138a938ac2b005dc93c283d22addf..1cc7a2ee193d099097fa89ff35b41a0d93977e95 100644 (file)
@@ -96,32 +96,32 @@ nobase_follyinclude_HEADERS = \
        experimental/EventCount.h \
        experimental/Instructions.h \
        experimental/bser/Bser.h \
-       experimental/fibers/AddTasks.h \
-       experimental/fibers/AddTasks-inl.h \
-       experimental/fibers/Baton.h \
-       experimental/fibers/Baton-inl.h \
-       experimental/fibers/BoostContextCompatibility.h \
-       experimental/fibers/EventBaseLoopController.h \
-       experimental/fibers/EventBaseLoopController-inl.h \
-       experimental/fibers/Fiber.h \
-       experimental/fibers/Fiber-inl.h \
-       experimental/fibers/FiberManager.h \
-       experimental/fibers/FiberManager-inl.h \
-       experimental/fibers/FiberManagerMap.h \
-       experimental/fibers/ForEach.h \
-       experimental/fibers/ForEach-inl.h \
-       experimental/fibers/GenericBaton.h \
-       experimental/fibers/GuardPageAllocator.h \
-       experimental/fibers/LoopController.h \
-       experimental/fibers/Promise.h \
-       experimental/fibers/Promise-inl.h \
-       experimental/fibers/SimpleLoopController.h \
-       experimental/fibers/TimedMutex.h \
-       experimental/fibers/TimedMutex-inl.h \
-       experimental/fibers/TimeoutController.h \
-       experimental/fibers/traits.h \
-       experimental/fibers/WhenN.h \
-       experimental/fibers/WhenN-inl.h \
+       fibers/AddTasks.h \
+       fibers/AddTasks-inl.h \
+       fibers/Baton.h \
+       fibers/Baton-inl.h \
+       fibers/BoostContextCompatibility.h \
+       fibers/EventBaseLoopController.h \
+       fibers/EventBaseLoopController-inl.h \
+       fibers/Fiber.h \
+       fibers/Fiber-inl.h \
+       fibers/FiberManager.h \
+       fibers/FiberManager-inl.h \
+       fibers/FiberManagerMap.h \
+       fibers/ForEach.h \
+       fibers/ForEach-inl.h \
+       fibers/GenericBaton.h \
+       fibers/GuardPageAllocator.h \
+       fibers/LoopController.h \
+       fibers/Promise.h \
+       fibers/Promise-inl.h \
+       fibers/SimpleLoopController.h \
+       fibers/TimedMutex.h \
+       fibers/TimedMutex-inl.h \
+       fibers/TimeoutController.h \
+       fibers/traits.h \
+       fibers/WhenN.h \
+       fibers/WhenN-inl.h \
        experimental/FunctionScheduler.h \
        experimental/FutureDAG.h \
        experimental/io/FsUtil.h \
@@ -464,12 +464,12 @@ libfolly_la_SOURCES = \
        experimental/bser/Dump.cpp \
        experimental/bser/Load.cpp \
        experimental/DynamicParser.cpp \
-       experimental/fibers/Baton.cpp \
-       experimental/fibers/Fiber.cpp \
-       experimental/fibers/FiberManager.cpp \
-       experimental/fibers/FiberManagerMap.cpp \
-       experimental/fibers/GuardPageAllocator.cpp \
-       experimental/fibers/TimeoutController.cpp \
+       fibers/Baton.cpp \
+       fibers/Fiber.cpp \
+       fibers/FiberManager.cpp \
+       fibers/FiberManagerMap.cpp \
+       fibers/GuardPageAllocator.cpp \
+       fibers/TimeoutController.cpp \
        experimental/FunctionScheduler.cpp \
        experimental/io/FsUtil.cpp \
        experimental/JSONSchema.cpp \
diff --git a/folly/experimental/fibers/AddTasks-inl.h b/folly/experimental/fibers/AddTasks-inl.h
deleted file mode 100644 (file)
index f0a712e..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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.
- */
-#include <memory>
-#include <vector>
-
-namespace folly {
-namespace fibers {
-
-template <typename T>
-TaskIterator<T>::TaskIterator(TaskIterator&& other) noexcept
-    : context_(std::move(other.context_)), id_(other.id_), fm_(other.fm_) {}
-
-template <typename T>
-inline bool TaskIterator<T>::hasCompleted() const {
-  return context_->tasksConsumed < context_->results.size();
-}
-
-template <typename T>
-inline bool TaskIterator<T>::hasPending() const {
-  return !context_.unique();
-}
-
-template <typename T>
-inline bool TaskIterator<T>::hasNext() const {
-  return hasPending() || hasCompleted();
-}
-
-template <typename T>
-folly::Try<T> TaskIterator<T>::awaitNextResult() {
-  assert(hasCompleted() || hasPending());
-  reserve(1);
-
-  size_t i = context_->tasksConsumed++;
-  id_ = context_->results[i].first;
-  return std::move(context_->results[i].second);
-}
-
-template <typename T>
-inline T TaskIterator<T>::awaitNext() {
-  return std::move(awaitNextResult().value());
-}
-
-template <>
-inline void TaskIterator<void>::awaitNext() {
-  awaitNextResult().value();
-}
-
-template <typename T>
-inline void TaskIterator<T>::reserve(size_t n) {
-  size_t tasksReady = context_->results.size() - context_->tasksConsumed;
-
-  // we don't need to do anything if there are already n or more tasks complete
-  // or if we have no tasks left to execute.
-  if (!hasPending() || tasksReady >= n) {
-    return;
-  }
-
-  n -= tasksReady;
-  size_t tasksLeft = context_->totalTasks - context_->results.size();
-  n = std::min(n, tasksLeft);
-
-  await([this, n](Promise<void> promise) {
-    context_->tasksToFulfillPromise = n;
-    context_->promise.assign(std::move(promise));
-  });
-}
-
-template <typename T>
-inline size_t TaskIterator<T>::getTaskID() const {
-  assert(id_ != static_cast<size_t>(-1));
-  return id_;
-}
-
-template <typename T>
-template <typename F>
-void TaskIterator<T>::addTask(F&& func) {
-  static_assert(
-      std::is_convertible<typename std::result_of<F()>::type, T>::value,
-      "TaskIterator<T>: T must be convertible from func()'s return type");
-
-  auto taskId = context_->totalTasks++;
-
-  fm_.addTask(
-      [ taskId, context = context_, func = std::forward<F>(func) ]() mutable {
-        context->results.emplace_back(
-            taskId, folly::makeTryWith(std::move(func)));
-
-        // Check for awaiting iterator.
-        if (context->promise.hasValue()) {
-          if (--context->tasksToFulfillPromise == 0) {
-            context->promise->setValue();
-            context->promise.clear();
-          }
-        }
-      });
-}
-
-template <class InputIterator>
-TaskIterator<typename std::result_of<
-    typename std::iterator_traits<InputIterator>::value_type()>::type>
-addTasks(InputIterator first, InputIterator last) {
-  typedef typename std::result_of<
-      typename std::iterator_traits<InputIterator>::value_type()>::type
-      ResultType;
-  typedef TaskIterator<ResultType> IteratorType;
-
-  IteratorType iterator;
-
-  for (; first != last; ++first) {
-    iterator.addTask(std::move(*first));
-  }
-
-  iterator.context_->results.reserve(iterator.context_->totalTasks);
-
-  return std::move(iterator);
-}
-}
-}
diff --git a/folly/experimental/fibers/AddTasks.h b/folly/experimental/fibers/AddTasks.h
deleted file mode 100644 (file)
index 1062b9b..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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 <vector>
-
-#include <folly/Optional.h>
-#include <folly/experimental/fibers/FiberManager.h>
-#include <folly/experimental/fibers/Promise.h>
-#include <folly/futures/Try.h>
-
-namespace folly {
-namespace fibers {
-
-template <typename T>
-class TaskIterator;
-
-/**
- * Schedules several tasks and immediately returns an iterator, that
- * allow one to traverse tasks in the order of their completion. All results
- * and exceptions thrown are stored alongside with the task id and are
- * accessible via iterator.
- *
- * @param first Range of tasks to be scheduled
- * @param last
- *
- * @return movable, non-copyable iterator
- */
-template <class InputIterator>
-TaskIterator<typename std::result_of<
-    typename std::iterator_traits<InputIterator>::value_type()>::
-                 type> inline addTasks(InputIterator first, InputIterator last);
-
-template <typename T>
-class TaskIterator {
- public:
-  typedef T value_type;
-
-  TaskIterator() : fm_(FiberManager::getFiberManager()) {}
-
-  // not copyable
-  TaskIterator(const TaskIterator& other) = delete;
-  TaskIterator& operator=(const TaskIterator& other) = delete;
-
-  // movable
-  TaskIterator(TaskIterator&& other) noexcept;
-  TaskIterator& operator=(TaskIterator&& other) = delete;
-
-  /**
-   * Add one more task to the TaskIterator.
-   *
-   * @param func task to be added, will be scheduled on current FiberManager
-   */
-  template <typename F>
-  void addTask(F&& func);
-
-  /**
-   * @return True if there are tasks immediately available to be consumed (no
-   *         need to await on them).
-   */
-  bool hasCompleted() const;
-
-  /**
-   * @return True if there are tasks pending execution (need to awaited on).
-   */
-  bool hasPending() const;
-
-  /**
-   * @return True if there are any tasks (hasCompleted() || hasPending()).
-   */
-  bool hasNext() const;
-
-  /**
-   * Await for another task to complete. Will not await if the result is
-   * already available.
-   *
-   * @return result of the task completed.
-   * @throw exception thrown by the task.
-   */
-  T awaitNext();
-
-  /**
-   * Await until the specified number of tasks completes or there are no
-   * tasks left to await for.
-   * Note: Will not await if there are already the specified number of tasks
-   * available.
-   *
-   * @param n   Number of tasks to await for completition.
-   */
-  void reserve(size_t n);
-
-  /**
-   * @return id of the last task that was processed by awaitNext().
-   */
-  size_t getTaskID() const;
-
- private:
-  template <class InputIterator>
-  friend TaskIterator<typename std::result_of<
-      typename std::iterator_traits<InputIterator>::value_type()>::type>
-  addTasks(InputIterator first, InputIterator last);
-
-  struct Context {
-    std::vector<std::pair<size_t, folly::Try<T>>> results;
-    folly::Optional<Promise<void>> promise;
-    size_t totalTasks{0};
-    size_t tasksConsumed{0};
-    size_t tasksToFulfillPromise{0};
-  };
-
-  std::shared_ptr<Context> context_{std::make_shared<Context>()};
-  size_t id_{std::numeric_limits<size_t>::max()};
-  FiberManager& fm_;
-
-  folly::Try<T> awaitNextResult();
-};
-}
-}
-
-#include <folly/experimental/fibers/AddTasks-inl.h>
diff --git a/folly/experimental/fibers/Baton-inl.h b/folly/experimental/fibers/Baton-inl.h
deleted file mode 100644 (file)
index b4933ca..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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.
- */
-#include <folly/experimental/fibers/Fiber.h>
-#include <folly/experimental/fibers/FiberManager.h>
-
-namespace folly {
-namespace fibers {
-
-inline Baton::Baton() : Baton(NO_WAITER) {
-  assert(Baton(NO_WAITER).futex_.futex == static_cast<uint32_t>(NO_WAITER));
-  assert(Baton(POSTED).futex_.futex == static_cast<uint32_t>(POSTED));
-  assert(Baton(TIMEOUT).futex_.futex == static_cast<uint32_t>(TIMEOUT));
-  assert(
-      Baton(THREAD_WAITING).futex_.futex ==
-      static_cast<uint32_t>(THREAD_WAITING));
-
-  assert(futex_.futex.is_lock_free());
-  assert(waitingFiber_.is_lock_free());
-}
-
-template <typename F>
-void Baton::wait(F&& mainContextFunc) {
-  auto fm = FiberManager::getFiberManagerUnsafe();
-  if (!fm || !fm->activeFiber_) {
-    mainContextFunc();
-    return waitThread();
-  }
-
-  return waitFiber(*fm, std::forward<F>(mainContextFunc));
-}
-
-template <typename F>
-void Baton::waitFiber(FiberManager& fm, F&& mainContextFunc) {
-  auto& waitingFiber = waitingFiber_;
-  auto f = [&mainContextFunc, &waitingFiber](Fiber& fiber) mutable {
-    auto baton_fiber = waitingFiber.load();
-    do {
-      if (LIKELY(baton_fiber == NO_WAITER)) {
-        continue;
-      } else if (baton_fiber == POSTED || baton_fiber == TIMEOUT) {
-        fiber.setData(0);
-        break;
-      } else {
-        throw std::logic_error("Some Fiber is already waiting on this Baton.");
-      }
-    } while (!waitingFiber.compare_exchange_weak(
-        baton_fiber, reinterpret_cast<intptr_t>(&fiber)));
-
-    mainContextFunc();
-  };
-
-  fm.awaitFunc_ = std::ref(f);
-  fm.activeFiber_->preempt(Fiber::AWAITING);
-}
-
-template <typename F>
-bool Baton::timed_wait(
-    TimeoutController::Duration timeout,
-    F&& mainContextFunc) {
-  auto fm = FiberManager::getFiberManagerUnsafe();
-
-  if (!fm || !fm->activeFiber_) {
-    mainContextFunc();
-    return timedWaitThread(timeout);
-  }
-
-  auto& baton = *this;
-  bool canceled = false;
-  auto timeoutFunc = [&baton, &canceled]() mutable {
-    baton.postHelper(TIMEOUT);
-    canceled = true;
-  };
-
-  auto id =
-      fm->timeoutManager_->registerTimeout(std::ref(timeoutFunc), timeout);
-
-  waitFiber(*fm, std::move(mainContextFunc));
-
-  auto posted = waitingFiber_ == POSTED;
-
-  if (!canceled) {
-    fm->timeoutManager_->cancel(id);
-  }
-
-  return posted;
-}
-
-template <typename C, typename D>
-bool Baton::timed_wait(const std::chrono::time_point<C, D>& timeout) {
-  auto now = C::now();
-
-  if (LIKELY(now <= timeout)) {
-    return timed_wait(
-        std::chrono::duration_cast<std::chrono::milliseconds>(timeout - now));
-  } else {
-    return timed_wait(TimeoutController::Duration(0));
-  }
-}
-}
-}
diff --git a/folly/experimental/fibers/Baton.cpp b/folly/experimental/fibers/Baton.cpp
deleted file mode 100644 (file)
index a378483..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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.
- */
-#include "Baton.h"
-
-#include <chrono>
-
-#include <folly/detail/MemoryIdler.h>
-#include <folly/experimental/fibers/FiberManager.h>
-#include <folly/portability/Asm.h>
-
-namespace folly {
-namespace fibers {
-
-void Baton::wait() {
-  wait([]() {});
-}
-
-void Baton::wait(TimeoutHandler& timeoutHandler) {
-  auto timeoutFunc = [this, &timeoutHandler] {
-    if (!try_wait()) {
-      postHelper(TIMEOUT);
-    }
-    timeoutHandler.timeoutPtr_ = 0;
-  };
-  timeoutHandler.timeoutFunc_ = std::ref(timeoutFunc);
-  timeoutHandler.fiberManager_ = FiberManager::getFiberManagerUnsafe();
-  wait();
-  timeoutHandler.cancelTimeout();
-}
-
-bool Baton::timed_wait(TimeoutController::Duration timeout) {
-  return timed_wait(timeout, []() {});
-}
-
-void Baton::waitThread() {
-  if (spinWaitForEarlyPost()) {
-    assert(waitingFiber_.load(std::memory_order_acquire) == POSTED);
-    return;
-  }
-
-  auto fiber = waitingFiber_.load();
-
-  if (LIKELY(
-          fiber == NO_WAITER &&
-          waitingFiber_.compare_exchange_strong(fiber, THREAD_WAITING))) {
-    do {
-      folly::detail::MemoryIdler::futexWait(futex_.futex, THREAD_WAITING);
-      fiber = waitingFiber_.load(std::memory_order_relaxed);
-    } while (fiber == THREAD_WAITING);
-  }
-
-  if (LIKELY(fiber == POSTED)) {
-    return;
-  }
-
-  // Handle errors
-  if (fiber == TIMEOUT) {
-    throw std::logic_error("Thread baton can't have timeout status");
-  }
-  if (fiber == THREAD_WAITING) {
-    throw std::logic_error("Other thread is already waiting on this baton");
-  }
-  throw std::logic_error("Other fiber is already waiting on this baton");
-}
-
-bool Baton::spinWaitForEarlyPost() {
-  static_assert(
-      PreBlockAttempts > 0,
-      "isn't this assert clearer than an uninitialized variable warning?");
-  for (int i = 0; i < PreBlockAttempts; ++i) {
-    if (try_wait()) {
-      // hooray!
-      return true;
-    }
-    // The pause instruction is the polite way to spin, but it doesn't
-    // actually affect correctness to omit it if we don't have it.
-    // Pausing donates the full capabilities of the current core to
-    // its other hyperthreads for a dozen cycles or so
-    asm_volatile_pause();
-  }
-
-  return false;
-}
-
-bool Baton::timedWaitThread(TimeoutController::Duration timeout) {
-  if (spinWaitForEarlyPost()) {
-    assert(waitingFiber_.load(std::memory_order_acquire) == POSTED);
-    return true;
-  }
-
-  auto fiber = waitingFiber_.load();
-
-  if (LIKELY(
-          fiber == NO_WAITER &&
-          waitingFiber_.compare_exchange_strong(fiber, THREAD_WAITING))) {
-    auto deadline = TimeoutController::Clock::now() + timeout;
-    do {
-      const auto wait_rv =
-          futex_.futex.futexWaitUntil(THREAD_WAITING, deadline);
-      if (wait_rv == folly::detail::FutexResult::TIMEDOUT) {
-        return false;
-      }
-      fiber = waitingFiber_.load(std::memory_order_relaxed);
-    } while (fiber == THREAD_WAITING);
-  }
-
-  if (LIKELY(fiber == POSTED)) {
-    return true;
-  }
-
-  // Handle errors
-  if (fiber == TIMEOUT) {
-    throw std::logic_error("Thread baton can't have timeout status");
-  }
-  if (fiber == THREAD_WAITING) {
-    throw std::logic_error("Other thread is already waiting on this baton");
-  }
-  throw std::logic_error("Other fiber is already waiting on this baton");
-}
-
-void Baton::post() {
-  postHelper(POSTED);
-}
-
-void Baton::postHelper(intptr_t new_value) {
-  auto fiber = waitingFiber_.load();
-
-  do {
-    if (fiber == THREAD_WAITING) {
-      assert(new_value == POSTED);
-
-      return postThread();
-    }
-
-    if (fiber == POSTED || fiber == TIMEOUT) {
-      return;
-    }
-  } while (!waitingFiber_.compare_exchange_weak(fiber, new_value));
-
-  if (fiber != NO_WAITER) {
-    reinterpret_cast<Fiber*>(fiber)->setData(0);
-  }
-}
-
-bool Baton::try_wait() {
-  auto state = waitingFiber_.load();
-  return state == POSTED;
-}
-
-void Baton::postThread() {
-  auto expected = THREAD_WAITING;
-
-  if (!waitingFiber_.compare_exchange_strong(expected, POSTED)) {
-    return;
-  }
-
-  futex_.futex.futexWake(1);
-}
-
-void Baton::reset() {
-  waitingFiber_.store(NO_WAITER, std::memory_order_relaxed);
-  ;
-}
-
-void Baton::TimeoutHandler::scheduleTimeout(
-    TimeoutController::Duration timeout) {
-  assert(fiberManager_ != nullptr);
-  assert(timeoutFunc_ != nullptr);
-  assert(timeoutPtr_ == 0);
-
-  if (timeout.count() > 0) {
-    timeoutPtr_ =
-        fiberManager_->timeoutManager_->registerTimeout(timeoutFunc_, timeout);
-  }
-}
-
-void Baton::TimeoutHandler::cancelTimeout() {
-  if (timeoutPtr_) {
-    fiberManager_->timeoutManager_->cancel(timeoutPtr_);
-  }
-}
-}
-}
diff --git a/folly/experimental/fibers/Baton.h b/folly/experimental/fibers/Baton.h
deleted file mode 100644 (file)
index c2ee704..0000000
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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 <atomic>
-
-#include <folly/detail/Futex.h>
-#include <folly/experimental/fibers/TimeoutController.h>
-
-namespace folly {
-namespace fibers {
-
-class Fiber;
-class FiberManager;
-
-/**
- * @class Baton
- *
- * Primitive which allows one to put current Fiber to sleep and wake it from
- * another Fiber/thread.
- */
-class Baton {
- public:
-  class TimeoutHandler;
-
-  Baton();
-
-  ~Baton() {}
-
-  /**
-   * Puts active fiber to sleep. Returns when post is called.
-   */
-  void wait();
-
-  /**
-   * Put active fiber to sleep indefinitely. However, timeoutHandler may
-   * be used elsewhere on the same thread in order to schedule a wakeup
-   * for the active fiber.  Users of timeoutHandler must be on the same thread
-   * as the active fiber and may only schedule one timeout, which must occur
-   * after the active fiber calls wait.
-   */
-  void wait(TimeoutHandler& timeoutHandler);
-
-  /**
-   * Puts active fiber to sleep. Returns when post is called.
-   *
-   * @param mainContextFunc this function is immediately executed on the main
-   *        context.
-   */
-  template <typename F>
-  void wait(F&& mainContextFunc);
-
-  /**
-   * This is here only not break tao/locks. Please don't use it, because it is
-   * inefficient when used on Fibers.
-   */
-  template <typename C, typename D = typename C::duration>
-  bool timed_wait(const std::chrono::time_point<C, D>& timeout);
-
-  /**
-   * Puts active fiber to sleep. Returns when post is called.
-   *
-   * @param timeout Baton will be automatically awaken if timeout is hit
-   *
-   * @return true if was posted, false if timeout expired
-   */
-  bool timed_wait(TimeoutController::Duration timeout);
-
-  /**
-   * Puts active fiber to sleep. Returns when post is called.
-   *
-   * @param timeout Baton will be automatically awaken if timeout is hit
-   * @param mainContextFunc this function is immediately executed on the main
-   *        context.
-   *
-   * @return true if was posted, false if timeout expired
-   */
-  template <typename F>
-  bool timed_wait(TimeoutController::Duration timeout, F&& mainContextFunc);
-
-  /**
-   * Checks if the baton has been posted without blocking.
-   * @return    true iff the baton has been posted.
-   */
-  bool try_wait();
-
-  /**
-   * Wakes up Fiber which was waiting on this Baton (or if no Fiber is waiting,
-   * next wait() call will return immediately).
-   */
-  void post();
-
-  /**
-   * Reset's the baton (equivalent to destroying the object and constructing
-   * another one in place).
-   * Caller is responsible for making sure no one is waiting on/posting the
-   * baton when reset() is called.
-   */
-  void reset();
-
-  /**
-   * Provides a way to schedule a wakeup for a wait()ing fiber.
-   * A TimeoutHandler must be passed to Baton::wait(TimeoutHandler&)
-   * before a timeout is scheduled. It is only safe to use the
-   * TimeoutHandler on the same thread as the wait()ing fiber.
-   * scheduleTimeout() may only be called once prior to the end of the
-   * associated Baton's life.
-   */
-  class TimeoutHandler {
-   public:
-    void scheduleTimeout(TimeoutController::Duration timeoutMs);
-
-   private:
-    friend class Baton;
-
-    void cancelTimeout();
-
-    std::function<void()> timeoutFunc_{nullptr};
-    FiberManager* fiberManager_{nullptr};
-
-    intptr_t timeoutPtr_{0};
-  };
-
- private:
-  enum {
-    /**
-     * Must be positive.  If multiple threads are actively using a
-     * higher-level data structure that uses batons internally, it is
-     * likely that the post() and wait() calls happen almost at the same
-     * time.  In this state, we lose big 50% of the time if the wait goes
-     * to sleep immediately.  On circa-2013 devbox hardware it costs about
-     * 7 usec to FUTEX_WAIT and then be awoken (half the t/iter as the
-     * posix_sem_pingpong test in BatonTests).  We can improve our chances
-     * of early post by spinning for a bit, although we have to balance
-     * this against the loss if we end up sleeping any way.  Spins on this
-     * hw take about 7 nanos (all but 0.5 nanos is the pause instruction).
-     * We give ourself 300 spins, which is about 2 usec of waiting.  As a
-     * partial consolation, since we are using the pause instruction we
-     * are giving a speed boost to the colocated hyperthread.
-     */
-    PreBlockAttempts = 300,
-  };
-
-  explicit Baton(intptr_t state) : waitingFiber_(state){};
-
-  void postHelper(intptr_t new_value);
-  void postThread();
-  void waitThread();
-
-  template <typename F>
-  inline void waitFiber(FiberManager& fm, F&& mainContextFunc);
-  /**
-   * Spin for "some time" (see discussion on PreBlockAttempts) waiting
-   * for a post.
-   * @return true if we received a post the spin wait, false otherwise. If the
-   *         function returns true then Baton state is guaranteed to be POSTED
-   */
-  bool spinWaitForEarlyPost();
-
-  bool timedWaitThread(TimeoutController::Duration timeout);
-
-  static constexpr intptr_t NO_WAITER = 0;
-  static constexpr intptr_t POSTED = -1;
-  static constexpr intptr_t TIMEOUT = -2;
-  static constexpr intptr_t THREAD_WAITING = -3;
-
-  union {
-    std::atomic<intptr_t> waitingFiber_;
-    struct {
-      folly::detail::Futex<> futex;
-      int32_t _unused_packing;
-    } futex_;
-  };
-};
-}
-}
-
-#include <folly/experimental/fibers/Baton-inl.h>
diff --git a/folly/experimental/fibers/BoostContextCompatibility.h b/folly/experimental/fibers/BoostContextCompatibility.h
deleted file mode 100644 (file)
index e243767..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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 <boost/context/all.hpp>
-#include <boost/version.hpp>
-
-/**
- * Wrappers for different versions of boost::context library
- * API reference for different versions
- * Boost 1.51:
- * http://www.boost.org/doc/libs/1_51_0/libs/context/doc/html/context/context/boost_fcontext.html
- * Boost 1.52:
- * http://www.boost.org/doc/libs/1_52_0/libs/context/doc/html/context/context/boost_fcontext.html
- * Boost 1.56:
- * http://www.boost.org/doc/libs/1_56_0/libs/context/doc/html/context/context/boost_fcontext.html
- */
-
-namespace folly {
-namespace fibers {
-
-struct FContext {
- public:
-#if BOOST_VERSION >= 105200
-  using ContextStruct = boost::context::fcontext_t;
-#else
-  using ContextStruct = boost::ctx::fcontext_t;
-#endif
-
-  void* stackLimit() const {
-    return stackLimit_;
-  }
-
-  void* stackBase() const {
-    return stackBase_;
-  }
-
- private:
-  void* stackLimit_;
-  void* stackBase_;
-
-#if BOOST_VERSION >= 105600
-  ContextStruct context_;
-#elif BOOST_VERSION >= 105200
-  ContextStruct* context_;
-#else
-  ContextStruct context_;
-#endif
-
-  friend intptr_t
-  jumpContext(FContext* oldC, FContext::ContextStruct* newC, intptr_t p);
-  friend intptr_t
-  jumpContext(FContext::ContextStruct* oldC, FContext* newC, intptr_t p);
-  friend FContext
-  makeContext(void* stackLimit, size_t stackSize, void (*fn)(intptr_t));
-};
-
-inline intptr_t
-jumpContext(FContext* oldC, FContext::ContextStruct* newC, intptr_t p) {
-#if BOOST_VERSION >= 105600
-  return boost::context::jump_fcontext(&oldC->context_, *newC, p);
-#elif BOOST_VERSION >= 105200
-  return boost::context::jump_fcontext(oldC->context_, newC, p);
-#else
-  return jump_fcontext(&oldC->context_, newC, p);
-#endif
-}
-
-inline intptr_t
-jumpContext(FContext::ContextStruct* oldC, FContext* newC, intptr_t p) {
-#if BOOST_VERSION >= 105200
-  return boost::context::jump_fcontext(oldC, newC->context_, p);
-#else
-  return jump_fcontext(oldC, &newC->context_, p);
-#endif
-}
-
-inline FContext
-makeContext(void* stackLimit, size_t stackSize, void (*fn)(intptr_t)) {
-  FContext res;
-  res.stackLimit_ = stackLimit;
-  res.stackBase_ = static_cast<unsigned char*>(stackLimit) + stackSize;
-
-#if BOOST_VERSION >= 105200
-  res.context_ = boost::context::make_fcontext(res.stackBase_, stackSize, fn);
-#else
-  res.context_.fc_stack.limit = stackLimit;
-  res.context_.fc_stack.base = res.stackBase_;
-  make_fcontext(&res.context_, fn);
-#endif
-
-  return res;
-}
-}
-} // folly::fibers
diff --git a/folly/experimental/fibers/EventBaseLoopController-inl.h b/folly/experimental/fibers/EventBaseLoopController-inl.h
deleted file mode 100644 (file)
index 30848e2..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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.
- */
-#include <folly/Memory.h>
-#include <folly/experimental/fibers/EventBaseLoopController.h>
-#include <folly/experimental/fibers/FiberManager.h>
-
-namespace folly {
-namespace fibers {
-
-inline EventBaseLoopController::EventBaseLoopController()
-    : callback_(*this), aliveWeak_(destructionCallback_.getWeak()) {}
-
-inline EventBaseLoopController::~EventBaseLoopController() {
-  callback_.cancelLoopCallback();
-}
-
-inline void EventBaseLoopController::attachEventBase(
-    folly::EventBase& eventBase) {
-  if (eventBase_ != nullptr) {
-    LOG(ERROR) << "Attempt to reattach EventBase to LoopController";
-  }
-
-  eventBase_ = &eventBase;
-  eventBase_->runOnDestruction(&destructionCallback_);
-
-  eventBaseAttached_ = true;
-
-  if (awaitingScheduling_) {
-    schedule();
-  }
-}
-
-inline void EventBaseLoopController::setFiberManager(FiberManager* fm) {
-  fm_ = fm;
-}
-
-inline void EventBaseLoopController::schedule() {
-  if (eventBase_ == nullptr) {
-    // In this case we need to postpone scheduling.
-    awaitingScheduling_ = true;
-  } else {
-    // Schedule it to run in current iteration.
-    eventBase_->runInLoop(&callback_, true);
-    awaitingScheduling_ = false;
-  }
-}
-
-inline void EventBaseLoopController::cancel() {
-  callback_.cancelLoopCallback();
-}
-
-inline void EventBaseLoopController::runLoop() {
-  fm_->loopUntilNoReady();
-}
-
-inline void EventBaseLoopController::scheduleThreadSafe(
-    std::function<bool()> func) {
-  /* The only way we could end up here is if
-     1) Fiber thread creates a fiber that awaits (which means we must
-        have already attached, fiber thread wouldn't be running).
-     2) We move the promise to another thread (this move is a memory fence)
-     3) We fulfill the promise from the other thread. */
-  assert(eventBaseAttached_);
-
-  auto alive = aliveWeak_.lock();
-
-  if (func() && alive) {
-    auto aliveWeak = aliveWeak_;
-    eventBase_->runInEventBaseThread([this, aliveWeak]() {
-      if (!aliveWeak.expired()) {
-        runLoop();
-      }
-    });
-  }
-}
-
-inline void EventBaseLoopController::timedSchedule(
-    std::function<void()> func,
-    TimePoint time) {
-  assert(eventBaseAttached_);
-
-  // We want upper bound for the cast, thus we just add 1
-  auto delay_ms =
-      std::chrono::duration_cast<std::chrono::milliseconds>(time - Clock::now())
-          .count() +
-      1;
-  // If clock is not monotonic
-  delay_ms = std::max<decltype(delay_ms)>(delay_ms, 0L);
-  eventBase_->tryRunAfterDelay(func, delay_ms);
-}
-}
-} // folly::fibers
diff --git a/folly/experimental/fibers/EventBaseLoopController.h b/folly/experimental/fibers/EventBaseLoopController.h
deleted file mode 100644 (file)
index eaedb45..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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 <folly/experimental/fibers/LoopController.h>
-#include <folly/io/async/EventBase.h>
-#include <atomic>
-#include <memory>
-
-namespace folly {
-class EventBase;
-}
-
-namespace folly {
-namespace fibers {
-
-class FiberManager;
-
-class EventBaseLoopController : public LoopController {
- public:
-  explicit EventBaseLoopController();
-  ~EventBaseLoopController();
-
-  /**
-   * Attach EventBase after LoopController was created.
-   */
-  void attachEventBase(folly::EventBase& eventBase);
-
-  folly::EventBase* getEventBase() {
-    return eventBase_;
-  }
-
- private:
-  class ControllerCallback : public folly::EventBase::LoopCallback {
-   public:
-    explicit ControllerCallback(EventBaseLoopController& controller)
-        : controller_(controller) {}
-
-    void runLoopCallback() noexcept override {
-      controller_.runLoop();
-    }
-
-   private:
-    EventBaseLoopController& controller_;
-  };
-
-  class DestructionCallback : public folly::EventBase::LoopCallback {
-   public:
-    DestructionCallback() : alive_(new int(42)) {}
-    ~DestructionCallback() {
-      reset();
-    }
-
-    void runLoopCallback() noexcept override {
-      reset();
-    }
-
-    std::weak_ptr<void> getWeak() {
-      return {alive_};
-    }
-
-   private:
-    void reset() {
-      std::weak_ptr<void> aliveWeak(alive_);
-      alive_.reset();
-
-      while (!aliveWeak.expired()) {
-        // Spin until all operations requiring EventBaseLoopController to be
-        // alive are complete.
-      }
-    }
-
-    std::shared_ptr<void> alive_;
-  };
-
-  bool awaitingScheduling_{false};
-  folly::EventBase* eventBase_{nullptr};
-  ControllerCallback callback_;
-  DestructionCallback destructionCallback_;
-  FiberManager* fm_{nullptr};
-  std::atomic<bool> eventBaseAttached_{false};
-  std::weak_ptr<void> aliveWeak_;
-
-  /* LoopController interface */
-
-  void setFiberManager(FiberManager* fm) override;
-  void schedule() override;
-  void cancel() override;
-  void runLoop();
-  void scheduleThreadSafe(std::function<bool()> func) override;
-  void timedSchedule(std::function<void()> func, TimePoint time) override;
-
-  friend class FiberManager;
-};
-}
-} // folly::fibers
-
-#include "EventBaseLoopController-inl.h"
diff --git a/folly/experimental/fibers/Fiber-inl.h b/folly/experimental/fibers/Fiber-inl.h
deleted file mode 100644 (file)
index 9c87169..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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 <cassert>
-
-namespace folly {
-namespace fibers {
-
-template <typename F>
-void Fiber::setFunction(F&& func) {
-  assert(state_ == INVALID);
-  func_ = std::forward<F>(func);
-  state_ = NOT_STARTED;
-}
-
-template <typename F, typename G>
-void Fiber::setFunctionFinally(F&& resultFunc, G&& finallyFunc) {
-  assert(state_ == INVALID);
-  resultFunc_ = std::forward<F>(resultFunc);
-  finallyFunc_ = std::forward<G>(finallyFunc);
-  state_ = NOT_STARTED;
-}
-
-inline void* Fiber::getUserBuffer() {
-  return &userBuffer_;
-}
-
-template <typename T>
-T& Fiber::LocalData::getSlow() {
-  dataSize_ = sizeof(T);
-  dataType_ = &typeid(T);
-  if (sizeof(T) <= kBufferSize) {
-    dataDestructor_ = dataBufferDestructor<T>;
-    data_ = &buffer_;
-  } else {
-    dataDestructor_ = dataHeapDestructor<T>;
-    data_ = allocateHeapBuffer(dataSize_);
-  }
-  dataCopyConstructor_ = dataCopyConstructor<T>;
-
-  new (reinterpret_cast<T*>(data_)) T();
-
-  return *reinterpret_cast<T*>(data_);
-}
-
-template <typename T>
-void Fiber::LocalData::dataCopyConstructor(void* ptr, const void* other) {
-  new (reinterpret_cast<T*>(ptr)) T(*reinterpret_cast<const T*>(other));
-}
-
-template <typename T>
-void Fiber::LocalData::dataBufferDestructor(void* ptr) {
-  reinterpret_cast<T*>(ptr)->~T();
-}
-
-template <typename T>
-void Fiber::LocalData::dataHeapDestructor(void* ptr) {
-  reinterpret_cast<T*>(ptr)->~T();
-  freeHeapBuffer(ptr);
-}
-}
-} // folly::fibers
diff --git a/folly/experimental/fibers/Fiber.cpp b/folly/experimental/fibers/Fiber.cpp
deleted file mode 100644 (file)
index 54f086d..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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.
- */
-#include "Fiber.h"
-
-#include <sys/syscall.h>
-#include <unistd.h>
-
-#include <glog/logging.h>
-#include <algorithm>
-#include <cstring>
-#include <stdexcept>
-
-#include <folly/Likely.h>
-#include <folly/Portability.h>
-#include <folly/experimental/fibers/BoostContextCompatibility.h>
-#include <folly/experimental/fibers/FiberManager.h>
-#include <folly/portability/SysSyscall.h>
-
-namespace folly {
-namespace fibers {
-
-namespace {
-static const uint64_t kMagic8Bytes = 0xfaceb00cfaceb00c;
-
-std::thread::id localThreadId() {
-  return std::this_thread::get_id();
-}
-
-/* Size of the region from p + nBytes down to the last non-magic value */
-static size_t nonMagicInBytes(const FContext& context) {
-  uint64_t* begin = static_cast<uint64_t*>(context.stackLimit());
-  uint64_t* end = static_cast<uint64_t*>(context.stackBase());
-
-  auto firstNonMagic = std::find_if(
-      begin, end, [](uint64_t val) { return val != kMagic8Bytes; });
-
-  return (end - firstNonMagic) * sizeof(uint64_t);
-}
-
-} // anonymous namespace
-
-void Fiber::setData(intptr_t data) {
-  DCHECK_EQ(state_, AWAITING);
-  data_ = data;
-  state_ = READY_TO_RUN;
-
-  if (fiberManager_.observer_) {
-    fiberManager_.observer_->runnable(reinterpret_cast<uintptr_t>(this));
-  }
-
-  if (LIKELY(threadId_ == localThreadId())) {
-    fiberManager_.readyFibers_.push_back(*this);
-    fiberManager_.ensureLoopScheduled();
-  } else {
-    fiberManager_.remoteReadyInsert(this);
-  }
-}
-
-Fiber::Fiber(FiberManager& fiberManager) : fiberManager_(fiberManager) {
-  auto size = fiberManager_.options_.stackSize;
-  auto limit = fiberManager_.stackAllocator_.allocate(size);
-
-  fcontext_ = makeContext(limit, size, &Fiber::fiberFuncHelper);
-
-  fiberManager_.allFibers_.push_back(*this);
-}
-
-void Fiber::init(bool recordStackUsed) {
-// It is necessary to disable the logic for ASAN because we change
-// the fiber's stack.
-#ifndef FOLLY_SANITIZE_ADDRESS
-  recordStackUsed_ = recordStackUsed;
-  if (UNLIKELY(recordStackUsed_ && !stackFilledWithMagic_)) {
-    auto limit = fcontext_.stackLimit();
-    auto base = fcontext_.stackBase();
-
-    std::fill(
-        static_cast<uint64_t*>(limit),
-        static_cast<uint64_t*>(base),
-        kMagic8Bytes);
-
-    // newer versions of boost allocate context on fiber stack,
-    // need to create a new one
-    auto size = fiberManager_.options_.stackSize;
-    fcontext_ = makeContext(limit, size, &Fiber::fiberFuncHelper);
-
-    stackFilledWithMagic_ = true;
-  }
-#else
-  (void)recordStackUsed;
-#endif
-}
-
-Fiber::~Fiber() {
-#ifdef FOLLY_SANITIZE_ADDRESS
-  fiberManager_.unpoisonFiberStack(this);
-#endif
-  fiberManager_.stackAllocator_.deallocate(
-      static_cast<unsigned char*>(fcontext_.stackLimit()),
-      fiberManager_.options_.stackSize);
-}
-
-void Fiber::recordStackPosition() {
-  int stackDummy;
-  auto currentPosition = static_cast<size_t>(
-      static_cast<unsigned char*>(fcontext_.stackBase()) -
-      static_cast<unsigned char*>(static_cast<void*>(&stackDummy)));
-  fiberManager_.stackHighWatermark_ =
-      std::max(fiberManager_.stackHighWatermark_, currentPosition);
-  VLOG(4) << "Stack usage: " << currentPosition;
-}
-
-void Fiber::fiberFuncHelper(intptr_t fiber) {
-  reinterpret_cast<Fiber*>(fiber)->fiberFunc();
-}
-
-void Fiber::fiberFunc() {
-  while (true) {
-    DCHECK_EQ(state_, NOT_STARTED);
-
-    threadId_ = localThreadId();
-    state_ = RUNNING;
-
-    try {
-      if (resultFunc_) {
-        DCHECK(finallyFunc_);
-        DCHECK(!func_);
-
-        resultFunc_();
-      } else {
-        DCHECK(func_);
-        func_();
-      }
-    } catch (...) {
-      fiberManager_.exceptionCallback_(
-          std::current_exception(), "running Fiber func_/resultFunc_");
-    }
-
-    if (UNLIKELY(recordStackUsed_)) {
-      fiberManager_.stackHighWatermark_ = std::max(
-          fiberManager_.stackHighWatermark_, nonMagicInBytes(fcontext_));
-      VLOG(3) << "Max stack usage: " << fiberManager_.stackHighWatermark_;
-      CHECK(
-          fiberManager_.stackHighWatermark_ <
-          fiberManager_.options_.stackSize - 64)
-          << "Fiber stack overflow";
-    }
-
-    state_ = INVALID;
-
-    auto context = fiberManager_.deactivateFiber(this);
-
-    DCHECK_EQ(reinterpret_cast<Fiber*>(context), this);
-  }
-}
-
-intptr_t Fiber::preempt(State state) {
-  intptr_t ret;
-
-  auto preemptImpl = [&]() mutable {
-    DCHECK_EQ(fiberManager_.activeFiber_, this);
-    DCHECK_EQ(state_, RUNNING);
-    DCHECK_NE(state, RUNNING);
-
-    state_ = state;
-
-    recordStackPosition();
-
-    ret = fiberManager_.deactivateFiber(this);
-
-    DCHECK_EQ(fiberManager_.activeFiber_, this);
-    DCHECK_EQ(state_, READY_TO_RUN);
-    state_ = RUNNING;
-  };
-
-  if (fiberManager_.preemptRunner_) {
-    fiberManager_.preemptRunner_->run(std::ref(preemptImpl));
-  } else {
-    preemptImpl();
-  }
-
-  return ret;
-}
-
-Fiber::LocalData::LocalData(const LocalData& other) : data_(nullptr) {
-  *this = other;
-}
-
-Fiber::LocalData& Fiber::LocalData::operator=(const LocalData& other) {
-  reset();
-  if (!other.data_) {
-    return *this;
-  }
-
-  dataSize_ = other.dataSize_;
-  dataType_ = other.dataType_;
-  dataDestructor_ = other.dataDestructor_;
-  dataCopyConstructor_ = other.dataCopyConstructor_;
-
-  if (dataSize_ <= kBufferSize) {
-    data_ = &buffer_;
-  } else {
-    data_ = allocateHeapBuffer(dataSize_);
-  }
-
-  dataCopyConstructor_(data_, other.data_);
-
-  return *this;
-}
-
-void Fiber::LocalData::reset() {
-  if (!data_) {
-    return;
-  }
-
-  dataDestructor_(data_);
-  data_ = nullptr;
-}
-
-void* Fiber::LocalData::allocateHeapBuffer(size_t size) {
-  return new char[size];
-}
-
-void Fiber::LocalData::freeHeapBuffer(void* buffer) {
-  delete[] reinterpret_cast<char*>(buffer);
-}
-}
-}
diff --git a/folly/experimental/fibers/Fiber.h b/folly/experimental/fibers/Fiber.h
deleted file mode 100644 (file)
index f68df6c..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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 <typeinfo>
-
-#include <folly/AtomicIntrusiveLinkedList.h>
-#include <folly/CPortability.h>
-#include <folly/Function.h>
-#include <folly/IntrusiveList.h>
-#include <folly/Portability.h>
-#include <folly/experimental/fibers/BoostContextCompatibility.h>
-#include <folly/io/async/Request.h>
-#include <boost/context/all.hpp>
-#include <boost/version.hpp>
-
-namespace folly {
-namespace fibers {
-
-class Baton;
-class FiberManager;
-
-/**
- * @class Fiber
- * @brief Fiber object used by FiberManager to execute tasks.
- *
- * Each Fiber object can be executing at most one task at a time. In active
- * phase it is running the task function and keeps its context.
- * Fiber is also used to pass data to blocked task and thus unblock it.
- * Each Fiber may be associated with a single FiberManager.
- */
-class Fiber {
- public:
-  /**
-   * Sets data for the blocked task
-   *
-   * @param data this data will be returned by await() when task is resumed.
-   */
-  void setData(intptr_t data);
-
-  Fiber(const Fiber&) = delete;
-  Fiber& operator=(const Fiber&) = delete;
-
-  ~Fiber();
-
-  /**
-   * Retrieve this fiber's base stack and stack size.
-   *
-   * @return This fiber's stack pointer and stack size.
-   */
-  std::pair<void*, size_t> getStack() const {
-    void* const stack =
-        std::min<void*>(fcontext_.stackLimit(), fcontext_.stackBase());
-    const size_t size = std::abs<intptr_t>(
-        reinterpret_cast<intptr_t>(fcontext_.stackBase()) -
-        reinterpret_cast<intptr_t>(fcontext_.stackLimit()));
-    return {stack, size};
-  }
-
- private:
-  enum State {
-    INVALID, /**< Does't have task function */
-    NOT_STARTED, /**< Has task function, not started */
-    READY_TO_RUN, /**< Was started, blocked, then unblocked */
-    RUNNING, /**< Is running right now */
-    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 */
-
-  friend class Baton;
-  friend class FiberManager;
-
-  explicit Fiber(FiberManager& fiberManager);
-
-  void init(bool recordStackUsed);
-
-  template <typename F>
-  void setFunction(F&& func);
-
-  template <typename F, typename G>
-  void setFunctionFinally(F&& func, G&& finally);
-
-  static void fiberFuncHelper(intptr_t fiber);
-  void fiberFunc();
-
-  /**
-   * Switch out of fiber context into the main context,
-   * performing necessary housekeeping for the new state.
-   *
-   * @param state New state, must not be RUNNING.
-   *
-   * @return The value passed back from the main context.
-   */
-  intptr_t preempt(State state);
-
-  /**
-   * Examines how much of the stack we used at this moment and
-   * registers with the FiberManager (for monitoring).
-   */
-  void recordStackPosition();
-
-  FiberManager& fiberManager_; /**< Associated FiberManager */
-  FContext fcontext_; /**< current task execution context */
-  intptr_t data_; /**< Used to keep some data with the Fiber */
-  std::shared_ptr<RequestContext> rcontext_; /**< current RequestContext */
-  folly::Function<void()> func_; /**< task function */
-  bool recordStackUsed_{false};
-  bool stackFilledWithMagic_{false};
-
-  /**
-   * Points to next fiber in remote ready list
-   */
-  folly::AtomicIntrusiveLinkedListHook<Fiber> nextRemoteReady_;
-
-  static constexpr size_t kUserBufferSize = 256;
-  std::aligned_storage<kUserBufferSize>::type userBuffer_;
-
-  void* getUserBuffer();
-
-  folly::Function<void()> resultFunc_;
-  folly::Function<void()> finallyFunc_;
-
-  class LocalData {
-   public:
-    LocalData() {}
-    LocalData(const LocalData& other);
-    LocalData& operator=(const LocalData& other);
-
-    template <typename T>
-    T& get() {
-      if (data_) {
-        assert(*dataType_ == typeid(T));
-        return *reinterpret_cast<T*>(data_);
-      }
-      return getSlow<T>();
-    }
-
-    void reset();
-
-    // private:
-    template <typename T>
-    FOLLY_NOINLINE T& getSlow();
-
-    static void* allocateHeapBuffer(size_t size);
-    static void freeHeapBuffer(void* buffer);
-
-    template <typename T>
-    static void dataCopyConstructor(void*, const void*);
-    template <typename T>
-    static void dataBufferDestructor(void*);
-    template <typename T>
-    static void dataHeapDestructor(void*);
-
-    static constexpr size_t kBufferSize = 128;
-    std::aligned_storage<kBufferSize>::type buffer_;
-    size_t dataSize_;
-
-    const std::type_info* dataType_;
-    void (*dataDestructor_)(void*);
-    void (*dataCopyConstructor_)(void*, const void*);
-    void* data_{nullptr};
-  };
-
-  LocalData localData_;
-
-  folly::IntrusiveListHook listHook_; /**< list hook for different FiberManager
-                                           queues */
-  folly::IntrusiveListHook globalListHook_; /**< list hook for global list */
-  std::thread::id threadId_{};
-};
-}
-}
-
-#include <folly/experimental/fibers/Fiber-inl.h>
diff --git a/folly/experimental/fibers/FiberManager-inl.h b/folly/experimental/fibers/FiberManager-inl.h
deleted file mode 100644 (file)
index a4783c5..0000000
+++ /dev/null
@@ -1,552 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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 <cassert>
-
-#include <folly/CPortability.h>
-#include <folly/Memory.h>
-#include <folly/Optional.h>
-#include <folly/Portability.h>
-#include <folly/ScopeGuard.h>
-#ifdef __APPLE__
-#include <folly/ThreadLocal.h>
-#endif
-#include <folly/experimental/fibers/Baton.h>
-#include <folly/experimental/fibers/Fiber.h>
-#include <folly/experimental/fibers/LoopController.h>
-#include <folly/experimental/fibers/Promise.h>
-#include <folly/futures/Promise.h>
-#include <folly/futures/Try.h>
-
-namespace folly {
-namespace fibers {
-
-namespace {
-
-inline FiberManager::Options preprocessOptions(FiberManager::Options opts) {
-#ifdef FOLLY_SANITIZE_ADDRESS
-  /* ASAN needs a lot of extra stack space.
-     16x is a conservative estimate, 8x also worked with tests
-     where it mattered.  Note that overallocating here does not necessarily
-     increase RSS, since unused memory is pretty much free. */
-  opts.stackSize *= 16;
-#endif
-  return opts;
-}
-
-} // anonymous
-
-inline void FiberManager::ensureLoopScheduled() {
-  if (isLoopScheduled_) {
-    return;
-  }
-
-  isLoopScheduled_ = true;
-  loopController_->schedule();
-}
-
-inline intptr_t FiberManager::activateFiber(Fiber* fiber) {
-  DCHECK_EQ(activeFiber_, (Fiber*)nullptr);
-
-#ifdef FOLLY_SANITIZE_ADDRESS
-  registerFiberActivationWithAsan(fiber);
-#endif
-
-  activeFiber_ = fiber;
-  return jumpContext(&mainContext_, &fiber->fcontext_, fiber->data_);
-}
-
-inline intptr_t FiberManager::deactivateFiber(Fiber* fiber) {
-  DCHECK_EQ(activeFiber_, fiber);
-
-#ifdef FOLLY_SANITIZE_ADDRESS
-  registerFiberDeactivationWithAsan(fiber);
-#endif
-
-  activeFiber_ = nullptr;
-  return jumpContext(&fiber->fcontext_, &mainContext_, 0);
-}
-
-inline void FiberManager::runReadyFiber(Fiber* fiber) {
-  SCOPE_EXIT {
-    assert(currentFiber_ == nullptr);
-    assert(activeFiber_ == nullptr);
-  };
-
-  assert(
-      fiber->state_ == Fiber::NOT_STARTED ||
-      fiber->state_ == Fiber::READY_TO_RUN);
-  currentFiber_ = fiber;
-  fiber->rcontext_ = RequestContext::setContext(std::move(fiber->rcontext_));
-  if (observer_) {
-    observer_->starting(reinterpret_cast<uintptr_t>(fiber));
-  }
-
-  while (fiber->state_ == Fiber::NOT_STARTED ||
-         fiber->state_ == Fiber::READY_TO_RUN) {
-    activateFiber(fiber);
-    if (fiber->state_ == Fiber::AWAITING_IMMEDIATE) {
-      try {
-        immediateFunc_();
-      } catch (...) {
-        exceptionCallback_(std::current_exception(), "running immediateFunc_");
-      }
-      immediateFunc_ = nullptr;
-      fiber->state_ = Fiber::READY_TO_RUN;
-    }
-  }
-
-  if (fiber->state_ == Fiber::AWAITING) {
-    awaitFunc_(*fiber);
-    awaitFunc_ = nullptr;
-    if (observer_) {
-      observer_->stopped(reinterpret_cast<uintptr_t>(fiber));
-    }
-    currentFiber_ = nullptr;
-    fiber->rcontext_ = RequestContext::setContext(std::move(fiber->rcontext_));
-  } else if (fiber->state_ == Fiber::INVALID) {
-    assert(fibersActive_ > 0);
-    --fibersActive_;
-    // Making sure that task functor is deleted once task is complete.
-    // NOTE: we must do it on main context, as the fiber is not
-    // running at this point.
-    fiber->func_ = nullptr;
-    fiber->resultFunc_ = nullptr;
-    if (fiber->finallyFunc_) {
-      try {
-        fiber->finallyFunc_();
-      } catch (...) {
-        exceptionCallback_(std::current_exception(), "running finallyFunc_");
-      }
-      fiber->finallyFunc_ = nullptr;
-    }
-    // Make sure LocalData is not accessible from its destructor
-    if (observer_) {
-      observer_->stopped(reinterpret_cast<uintptr_t>(fiber));
-    }
-    currentFiber_ = nullptr;
-    fiber->rcontext_ = RequestContext::setContext(std::move(fiber->rcontext_));
-    fiber->localData_.reset();
-    fiber->rcontext_.reset();
-
-    if (fibersPoolSize_ < options_.maxFibersPoolSize ||
-        options_.fibersPoolResizePeriodMs > 0) {
-      fibersPool_.push_front(*fiber);
-      ++fibersPoolSize_;
-    } else {
-      delete fiber;
-      assert(fibersAllocated_ > 0);
-      --fibersAllocated_;
-    }
-  } else if (fiber->state_ == Fiber::YIELDED) {
-    if (observer_) {
-      observer_->stopped(reinterpret_cast<uintptr_t>(fiber));
-    }
-    currentFiber_ = nullptr;
-    fiber->rcontext_ = RequestContext::setContext(std::move(fiber->rcontext_));
-    fiber->state_ = Fiber::READY_TO_RUN;
-    yieldedFibers_.push_back(*fiber);
-  }
-}
-
-inline bool FiberManager::loopUntilNoReady() {
-  if (UNLIKELY(!alternateSignalStackRegistered_)) {
-    registerAlternateSignalStack();
-  }
-
-  // Support nested FiberManagers
-  auto originalFiberManager = this;
-  std::swap(currentFiberManager_, originalFiberManager);
-
-  SCOPE_EXIT {
-    isLoopScheduled_ = false;
-    if (!readyFibers_.empty()) {
-      ensureLoopScheduled();
-    }
-    std::swap(currentFiberManager_, originalFiberManager);
-    CHECK_EQ(this, originalFiberManager);
-  };
-
-  bool hadRemoteFiber = true;
-  while (hadRemoteFiber) {
-    hadRemoteFiber = false;
-
-    while (!readyFibers_.empty()) {
-      auto& fiber = readyFibers_.front();
-      readyFibers_.pop_front();
-      runReadyFiber(&fiber);
-    }
-
-    remoteReadyQueue_.sweep([this, &hadRemoteFiber](Fiber* fiber) {
-      runReadyFiber(fiber);
-      hadRemoteFiber = true;
-    });
-
-    remoteTaskQueue_.sweep([this, &hadRemoteFiber](RemoteTask* taskPtr) {
-      std::unique_ptr<RemoteTask> task(taskPtr);
-      auto fiber = getFiber();
-      if (task->localData) {
-        fiber->localData_ = *task->localData;
-      }
-      fiber->rcontext_ = std::move(task->rcontext);
-
-      fiber->setFunction(std::move(task->func));
-      fiber->data_ = reinterpret_cast<intptr_t>(fiber);
-      if (observer_) {
-        observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
-      }
-      runReadyFiber(fiber);
-      hadRemoteFiber = true;
-    });
-  }
-
-  if (observer_) {
-    for (auto& yielded : yieldedFibers_) {
-      observer_->runnable(reinterpret_cast<uintptr_t>(&yielded));
-    }
-  }
-  readyFibers_.splice(readyFibers_.end(), yieldedFibers_);
-
-  return fibersActive_ > 0;
-}
-
-// We need this to be in a struct, not inlined in addTask, because clang crashes
-// otherwise.
-template <typename F>
-struct FiberManager::AddTaskHelper {
-  class Func;
-
-  static constexpr bool allocateInBuffer =
-      sizeof(Func) <= Fiber::kUserBufferSize;
-
-  class Func {
-   public:
-    Func(F&& func, FiberManager& fm) : func_(std::forward<F>(func)), fm_(fm) {}
-
-    void operator()() {
-      try {
-        func_();
-      } catch (...) {
-        fm_.exceptionCallback_(
-            std::current_exception(), "running Func functor");
-      }
-      if (allocateInBuffer) {
-        this->~Func();
-      } else {
-        delete this;
-      }
-    }
-
-   private:
-    F func_;
-    FiberManager& fm_;
-  };
-};
-
-template <typename F>
-void FiberManager::addTask(F&& func) {
-  typedef AddTaskHelper<F> Helper;
-
-  auto fiber = getFiber();
-  initLocalData(*fiber);
-
-  if (Helper::allocateInBuffer) {
-    auto funcLoc = static_cast<typename Helper::Func*>(fiber->getUserBuffer());
-    new (funcLoc) typename Helper::Func(std::forward<F>(func), *this);
-
-    fiber->setFunction(std::ref(*funcLoc));
-  } else {
-    auto funcLoc = new typename Helper::Func(std::forward<F>(func), *this);
-
-    fiber->setFunction(std::ref(*funcLoc));
-  }
-
-  fiber->data_ = reinterpret_cast<intptr_t>(fiber);
-  readyFibers_.push_back(*fiber);
-  if (observer_) {
-    observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
-  }
-
-  ensureLoopScheduled();
-}
-
-template <typename F>
-auto FiberManager::addTaskFuture(F&& func) -> folly::Future<
-    typename folly::Unit::Lift<typename std::result_of<F()>::type>::type> {
-  using T = typename std::result_of<F()>::type;
-  using FutureT = typename folly::Unit::Lift<T>::type;
-
-  folly::Promise<FutureT> p;
-  auto f = p.getFuture();
-  addTaskFinally(
-      [func = std::forward<F>(func)]() mutable { return func(); },
-      [p = std::move(p)](folly::Try<T> && t) mutable {
-        p.setTry(std::move(t));
-      });
-  return f;
-}
-
-template <typename F>
-void FiberManager::addTaskRemote(F&& func) {
-  auto task = [&]() {
-    auto currentFm = getFiberManagerUnsafe();
-    if (currentFm && currentFm->currentFiber_ &&
-        currentFm->localType_ == localType_) {
-      return folly::make_unique<RemoteTask>(
-          std::forward<F>(func), currentFm->currentFiber_->localData_);
-    }
-    return folly::make_unique<RemoteTask>(std::forward<F>(func));
-  }();
-  auto insertHead = [&]() {
-    return remoteTaskQueue_.insertHead(task.release());
-  };
-  loopController_->scheduleThreadSafe(std::ref(insertHead));
-}
-
-template <typename F>
-auto FiberManager::addTaskRemoteFuture(F&& func) -> folly::Future<
-    typename folly::Unit::Lift<typename std::result_of<F()>::type>::type> {
-  folly::Promise<
-      typename folly::Unit::Lift<typename std::result_of<F()>::type>::type>
-      p;
-  auto f = p.getFuture();
-  addTaskRemote(
-      [ p = std::move(p), func = std::forward<F>(func), this ]() mutable {
-        auto t = folly::makeTryWith(std::forward<F>(func));
-        runInMainContext([&]() { p.setTry(std::move(t)); });
-      });
-  return f;
-}
-
-template <typename X>
-struct IsRvalueRefTry {
-  static const bool value = false;
-};
-template <typename T>
-struct IsRvalueRefTry<folly::Try<T>&&> {
-  static const bool value = true;
-};
-
-// We need this to be in a struct, not inlined in addTaskFinally, because clang
-// crashes otherwise.
-template <typename F, typename G>
-struct FiberManager::AddTaskFinallyHelper {
-  class Func;
-
-  typedef typename std::result_of<F()>::type Result;
-
-  class Finally {
-   public:
-    Finally(G finally, FiberManager& fm)
-        : finally_(std::move(finally)), fm_(fm) {}
-
-    void operator()() {
-      try {
-        finally_(std::move(*result_));
-      } catch (...) {
-        fm_.exceptionCallback_(
-            std::current_exception(), "running Finally functor");
-      }
-
-      if (allocateInBuffer) {
-        this->~Finally();
-      } else {
-        delete this;
-      }
-    }
-
-   private:
-    friend class Func;
-
-    G finally_;
-    folly::Optional<folly::Try<Result>> result_;
-    FiberManager& fm_;
-  };
-
-  class Func {
-   public:
-    Func(F func, Finally& finally)
-        : func_(std::move(func)), result_(finally.result_) {}
-
-    void operator()() {
-      result_ = folly::makeTryWith(std::move(func_));
-
-      if (allocateInBuffer) {
-        this->~Func();
-      } else {
-        delete this;
-      }
-    }
-
-   private:
-    F func_;
-    folly::Optional<folly::Try<Result>>& result_;
-  };
-
-  static constexpr bool allocateInBuffer =
-      sizeof(Func) + sizeof(Finally) <= Fiber::kUserBufferSize;
-};
-
-template <typename F, typename G>
-void FiberManager::addTaskFinally(F&& func, G&& finally) {
-  typedef typename std::result_of<F()>::type Result;
-
-  static_assert(
-      IsRvalueRefTry<typename FirstArgOf<G>::type>::value,
-      "finally(arg): arg must be Try<T>&&");
-  static_assert(
-      std::is_convertible<
-          Result,
-          typename std::remove_reference<
-              typename FirstArgOf<G>::type>::type::element_type>::value,
-      "finally(Try<T>&&): T must be convertible from func()'s return type");
-
-  auto fiber = getFiber();
-  initLocalData(*fiber);
-
-  typedef AddTaskFinallyHelper<
-      typename std::decay<F>::type,
-      typename std::decay<G>::type>
-      Helper;
-
-  if (Helper::allocateInBuffer) {
-    auto funcLoc = static_cast<typename Helper::Func*>(fiber->getUserBuffer());
-    auto finallyLoc =
-        static_cast<typename Helper::Finally*>(static_cast<void*>(funcLoc + 1));
-
-    new (finallyLoc) typename Helper::Finally(std::forward<G>(finally), *this);
-    new (funcLoc) typename Helper::Func(std::forward<F>(func), *finallyLoc);
-
-    fiber->setFunctionFinally(std::ref(*funcLoc), std::ref(*finallyLoc));
-  } else {
-    auto finallyLoc =
-        new typename Helper::Finally(std::forward<G>(finally), *this);
-    auto funcLoc =
-        new typename Helper::Func(std::forward<F>(func), *finallyLoc);
-
-    fiber->setFunctionFinally(std::ref(*funcLoc), std::ref(*finallyLoc));
-  }
-
-  fiber->data_ = reinterpret_cast<intptr_t>(fiber);
-  readyFibers_.push_back(*fiber);
-  if (observer_) {
-    observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
-  }
-
-  ensureLoopScheduled();
-}
-
-template <typename F>
-typename std::result_of<F()>::type FiberManager::runInMainContext(F&& func) {
-  if (UNLIKELY(activeFiber_ == nullptr)) {
-    return func();
-  }
-
-  typedef typename std::result_of<F()>::type Result;
-
-  folly::Try<Result> result;
-  auto f = [&func, &result]() mutable {
-    result = folly::makeTryWith(std::forward<F>(func));
-  };
-
-  immediateFunc_ = std::ref(f);
-  activeFiber_->preempt(Fiber::AWAITING_IMMEDIATE);
-
-  return std::move(result).value();
-}
-
-inline FiberManager& FiberManager::getFiberManager() {
-  assert(currentFiberManager_ != nullptr);
-  return *currentFiberManager_;
-}
-
-inline FiberManager* FiberManager::getFiberManagerUnsafe() {
-  return currentFiberManager_;
-}
-
-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_) {
-    return currentFiber_->localData_.get<T>();
-  }
-  return localThread<T>();
-}
-
-template <typename T>
-T& FiberManager::localThread() {
-#ifndef __APPLE__
-  static thread_local T t;
-  return t;
-#else // osx doesn't support thread_local
-  static ThreadLocal<T> t;
-  return *t;
-#endif
-}
-
-inline void FiberManager::initLocalData(Fiber& fiber) {
-  auto fm = getFiberManagerUnsafe();
-  if (fm && fm->currentFiber_ && fm->localType_ == localType_) {
-    fiber.localData_ = fm->currentFiber_->localData_;
-  }
-  fiber.rcontext_ = RequestContext::saveContext();
-}
-
-template <typename LocalT>
-FiberManager::FiberManager(
-    LocalType<LocalT>,
-    std::unique_ptr<LoopController> loopController__,
-    Options options)
-    : loopController_(std::move(loopController__)),
-      stackAllocator_(options.useGuardPages),
-      options_(preprocessOptions(std::move(options))),
-      exceptionCallback_([](std::exception_ptr eptr, std::string context) {
-        try {
-          std::rethrow_exception(eptr);
-        } catch (const std::exception& e) {
-          LOG(DFATAL) << "Exception " << typeid(e).name() << " with message '"
-                      << e.what() << "' was thrown in "
-                      << "FiberManager with context '" << context << "'";
-        } catch (...) {
-          LOG(DFATAL) << "Unknown exception was thrown in FiberManager with "
-                      << "context '" << context << "'";
-        }
-      }),
-      timeoutManager_(std::make_shared<TimeoutController>(*loopController_)),
-      fibersPoolResizer_(*this),
-      localType_(typeid(LocalT)) {
-  loopController_->setFiberManager(this);
-}
-
-template <typename F>
-typename FirstArgOf<F>::type::value_type inline await(F&& func) {
-  typedef typename FirstArgOf<F>::type::value_type Result;
-
-  return Promise<Result>::await(std::forward<F>(func));
-}
-}
-}
diff --git a/folly/experimental/fibers/FiberManager.cpp b/folly/experimental/fibers/FiberManager.cpp
deleted file mode 100644 (file)
index 2592ee1..0000000
+++ /dev/null
@@ -1,337 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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.
- */
-#include "FiberManager.h"
-
-#include <signal.h>
-#include <sys/syscall.h>
-#include <unistd.h>
-
-#include <cassert>
-#include <stdexcept>
-
-#include <glog/logging.h>
-
-#include <folly/experimental/fibers/Fiber.h>
-#include <folly/experimental/fibers/LoopController.h>
-
-#include <folly/SingletonThreadLocal.h>
-
-#ifdef FOLLY_SANITIZE_ADDRESS
-
-#include <dlfcn.h>
-
-static void __asan_enter_fiber_weak(
-    void const* fiber_stack_base,
-    size_t fiber_stack_extent)
-    __attribute__((__weakref__("__asan_enter_fiber")));
-static void __asan_exit_fiber_weak()
-    __attribute__((__weakref__("__asan_exit_fiber")));
-static void __asan_unpoison_memory_region_weak(
-    void const /* nolint */ volatile* addr,
-    size_t size) __attribute__((__weakref__("__asan_unpoison_memory_region")));
-
-typedef void (*AsanEnterFiberFuncPtr)(void const*, size_t);
-typedef void (*AsanExitFiberFuncPtr)();
-typedef void (*AsanUnpoisonMemoryRegionFuncPtr)(
-    void const /* nolint */ volatile*,
-    size_t);
-
-namespace folly {
-namespace fibers {
-
-static AsanEnterFiberFuncPtr getEnterFiberFunc();
-static AsanExitFiberFuncPtr getExitFiberFunc();
-static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc();
-}
-}
-
-#endif
-
-namespace folly {
-namespace fibers {
-
-FOLLY_TLS FiberManager* FiberManager::currentFiberManager_ = nullptr;
-
-FiberManager::FiberManager(
-    std::unique_ptr<LoopController> loopController,
-    Options options)
-    : FiberManager(
-          LocalType<void>(),
-          std::move(loopController),
-          std::move(options)) {}
-
-FiberManager::~FiberManager() {
-  if (isLoopScheduled_) {
-    loopController_->cancel();
-  }
-
-  while (!fibersPool_.empty()) {
-    fibersPool_.pop_front_and_dispose([](Fiber* fiber) { delete fiber; });
-  }
-  assert(readyFibers_.empty());
-  assert(fibersActive_ == 0);
-}
-
-LoopController& FiberManager::loopController() {
-  return *loopController_;
-}
-
-const LoopController& FiberManager::loopController() const {
-  return *loopController_;
-}
-
-bool FiberManager::hasTasks() const {
-  return fibersActive_ > 0 || !remoteReadyQueue_.empty() ||
-      !remoteTaskQueue_.empty();
-}
-
-Fiber* FiberManager::getFiber() {
-  Fiber* fiber = nullptr;
-
-  if (options_.fibersPoolResizePeriodMs > 0 && !fibersPoolResizerScheduled_) {
-    fibersPoolResizer_();
-    fibersPoolResizerScheduled_ = true;
-  }
-
-  if (fibersPool_.empty()) {
-    fiber = new Fiber(*this);
-    ++fibersAllocated_;
-  } else {
-    fiber = &fibersPool_.front();
-    fibersPool_.pop_front();
-    assert(fibersPoolSize_ > 0);
-    --fibersPoolSize_;
-  }
-  assert(fiber);
-  if (++fibersActive_ > maxFibersActiveLastPeriod_) {
-    maxFibersActiveLastPeriod_ = fibersActive_;
-  }
-  ++fiberId_;
-  bool recordStack = (options_.recordStackEvery != 0) &&
-      (fiberId_ % options_.recordStackEvery == 0);
-  return fiber;
-}
-
-void FiberManager::setExceptionCallback(FiberManager::ExceptionCallback ec) {
-  assert(ec);
-  exceptionCallback_ = std::move(ec);
-}
-
-size_t FiberManager::fibersAllocated() const {
-  return fibersAllocated_;
-}
-
-size_t FiberManager::fibersPoolSize() const {
-  return fibersPoolSize_;
-}
-
-size_t FiberManager::stackHighWatermark() const {
-  return stackHighWatermark_;
-}
-
-void FiberManager::remoteReadyInsert(Fiber* fiber) {
-  if (observer_) {
-    observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
-  }
-  auto insertHead = [&]() { return remoteReadyQueue_.insertHead(fiber); };
-  loopController_->scheduleThreadSafe(std::ref(insertHead));
-}
-
-void FiberManager::setObserver(ExecutionObserver* observer) {
-  observer_ = observer;
-}
-
-void FiberManager::setPreemptRunner(InlineFunctionRunner* preemptRunner) {
-  preemptRunner_ = preemptRunner;
-}
-
-void FiberManager::doFibersPoolResizing() {
-  while (fibersAllocated_ > maxFibersActiveLastPeriod_ &&
-         fibersPoolSize_ > options_.maxFibersPoolSize) {
-    auto fiber = &fibersPool_.front();
-    assert(fiber != nullptr);
-    fibersPool_.pop_front();
-    delete fiber;
-    --fibersPoolSize_;
-    --fibersAllocated_;
-  }
-
-  maxFibersActiveLastPeriod_ = fibersActive_;
-}
-
-void FiberManager::FibersPoolResizer::operator()() {
-  fiberManager_.doFibersPoolResizing();
-  fiberManager_.timeoutManager_->registerTimeout(
-      *this,
-      std::chrono::milliseconds(
-          fiberManager_.options_.fibersPoolResizePeriodMs));
-}
-
-#ifdef FOLLY_SANITIZE_ADDRESS
-
-void FiberManager::registerFiberActivationWithAsan(Fiber* fiber) {
-  auto context = &fiber->fcontext_;
-  void* top = context->stackBase();
-  void* bottom = context->stackLimit();
-  size_t extent = static_cast<char*>(top) - static_cast<char*>(bottom);
-
-  // Check if we can find a fiber enter function and call it if we find one
-  static AsanEnterFiberFuncPtr fn = getEnterFiberFunc();
-  if (fn == nullptr) {
-    LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
-  } else {
-    fn(bottom, extent);
-  }
-}
-
-void FiberManager::registerFiberDeactivationWithAsan(Fiber* fiber) {
-  (void)fiber; // currently unused
-
-  // Check if we can find a fiber exit function and call it if we find one
-  static AsanExitFiberFuncPtr fn = getExitFiberFunc();
-  if (fn == nullptr) {
-    LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
-  } else {
-    fn();
-  }
-}
-
-void FiberManager::unpoisonFiberStack(const Fiber* fiber) {
-  auto stack = fiber->getStack();
-
-  // Check if we can find a fiber enter function and call it if we find one
-  static AsanUnpoisonMemoryRegionFuncPtr fn = getUnpoisonMemoryRegionFunc();
-  if (fn == nullptr) {
-    LOG(FATAL) << "This version of ASAN doesn't support memory unpoisoning";
-  } else {
-    fn(stack.first, stack.second);
-  }
-}
-
-static AsanEnterFiberFuncPtr getEnterFiberFunc() {
-  AsanEnterFiberFuncPtr fn{nullptr};
-
-  // Check whether weak reference points to statically linked enter function
-  if (nullptr != (fn = &::__asan_enter_fiber_weak)) {
-    return fn;
-  }
-
-  // Check whether we can find a dynamically linked enter function
-  if (nullptr !=
-      (fn = (AsanEnterFiberFuncPtr)dlsym(RTLD_DEFAULT, "__asan_enter_fiber"))) {
-    return fn;
-  }
-
-  // Couldn't find the function at all
-  return nullptr;
-}
-
-static AsanExitFiberFuncPtr getExitFiberFunc() {
-  AsanExitFiberFuncPtr fn{nullptr};
-
-  // Check whether weak reference points to statically linked exit function
-  if (nullptr != (fn = &::__asan_exit_fiber_weak)) {
-    return fn;
-  }
-
-  // Check whether we can find a dynamically linked exit function
-  if (nullptr !=
-      (fn = (AsanExitFiberFuncPtr)dlsym(RTLD_DEFAULT, "__asan_exit_fiber"))) {
-    return fn;
-  }
-
-  // Couldn't find the function at all
-  return nullptr;
-}
-
-static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc() {
-  AsanUnpoisonMemoryRegionFuncPtr fn{nullptr};
-
-  // Check whether weak reference points to statically linked unpoison function
-  if (nullptr != (fn = &::__asan_unpoison_memory_region_weak)) {
-    return fn;
-  }
-
-  // Check whether we can find a dynamically linked unpoison function
-  if (nullptr != (fn = (AsanUnpoisonMemoryRegionFuncPtr)dlsym(
-                      RTLD_DEFAULT, "__asan_unpoison_memory_region"))) {
-    return fn;
-  }
-
-  // Couldn't find the function at all
-  return nullptr;
-}
-
-#endif // FOLLY_SANITIZE_ADDRESS
-
-namespace {
-
-// SIGSTKSZ (8 kB on our architectures) isn't always enough for
-// folly::symbolizer, so allocate 32 kB.
-constexpr size_t kAltStackSize = folly::constexpr_max(SIGSTKSZ, 32 * 1024);
-
-bool hasAlternateStack() {
-  stack_t ss;
-  sigaltstack(nullptr, &ss);
-  return !(ss.ss_flags & SS_DISABLE);
-}
-
-int setAlternateStack(char* sp, size_t size) {
-  CHECK(sp);
-  stack_t ss{};
-  ss.ss_sp = sp;
-  ss.ss_size = size;
-  return sigaltstack(&ss, nullptr);
-}
-
-int unsetAlternateStack() {
-  stack_t ss{};
-  ss.ss_flags = SS_DISABLE;
-  return sigaltstack(&ss, nullptr);
-}
-
-class ScopedAlternateSignalStack {
- public:
-  ScopedAlternateSignalStack() {
-    if (hasAlternateStack()) {
-      return;
-    }
-
-    stack_ = folly::make_unique<AltStackBuffer>();
-
-    setAlternateStack(stack_->data(), stack_->size());
-  }
-
-  ~ScopedAlternateSignalStack() {
-    if (stack_) {
-      unsetAlternateStack();
-    }
-  }
-
- private:
-  using AltStackBuffer = std::array<char, kAltStackSize>;
-  std::unique_ptr<AltStackBuffer> stack_;
-};
-}
-
-void FiberManager::registerAlternateSignalStack() {
-  static folly::SingletonThreadLocal<ScopedAlternateSignalStack> singleton;
-  singleton.get();
-
-  alternateSignalStackRegistered_ = true;
-}
-}
-}
diff --git a/folly/experimental/fibers/FiberManager.h b/folly/experimental/fibers/FiberManager.h
deleted file mode 100644 (file)
index aa250a7..0000000
+++ /dev/null
@@ -1,565 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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/Executor.h>
-#include <folly/IntrusiveList.h>
-#include <folly/Likely.h>
-#include <folly/futures/Try.h>
-#include <folly/io/async/Request.h>
-
-#include <folly/experimental/ExecutionObserver.h>
-#include <folly/experimental/fibers/BoostContextCompatibility.h>
-#include <folly/experimental/fibers/Fiber.h>
-#include <folly/experimental/fibers/GuardPageAllocator.h>
-#include <folly/experimental/fibers/TimeoutController.h>
-#include <folly/experimental/fibers/traits.h>
-
-namespace folly {
-
-template <class T>
-class Future;
-
-namespace fibers {
-
-class Baton;
-class Fiber;
-class LoopController;
-class TimeoutController;
-
-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};
-
-    /**
-     * 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 limited amount of fiber stacks with guard pages.
-     */
-    bool useGuardPages{true};
-
-    /**
-     * 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() {}
-  };
-
-  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
-   * @param options FiberManager options
-   */
-  explicit FiberManager(
-      std::unique_ptr<LoopController> loopController,
-      Options options = Options());
-
-  /**
-   * Initializes, but doesn't start FiberManager loop
-   *
-   * @param loopController
-   * @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();
-
-  /**
-   * Controller access.
-   */
-  LoopController& loopController();
-  const LoopController& loopController() const;
-
-  /**
-   * Keeps running ready tasks until the list of ready tasks is empty.
-   *
-   * @return True if there are any waiting tasks remaining.
-   */
-  bool loopUntilNoReady();
-
-  /**
-   * @return true if there are outstanding tasks.
-   */
-  bool hasTasks() const;
-
-  /**
-   * Sets exception callback which will be called if any of the tasks throws an
-   * exception.
-   *
-   * @param ec
-   */
-  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.
-   */
-  template <typename F>
-  void addTask(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.
-   *
-   * @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<
-      typename folly::Unit::Lift<typename std::result_of<F()>::type>::type>;
-  /**
-   * 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<
-      typename folly::Unit::Lift<typename std::result_of<F()>::type>::type>;
-
-  // 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);
-
-  /**
-   * 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>
-  typename std::result_of<F()>::type 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>
-  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 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);
-
-  /**
-   * 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_.size();
-  }
-
-  static FiberManager& getFiberManager();
-  static FiberManager* getFiberManagerUnsafe();
-
- 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(folly::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;
-  };
-
-  intptr_t activateFiber(Fiber* fiber);
-  intptr_t deactivateFiber(Fiber* fiber);
-
-  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_; /**< queue of fibers which have yielded
-                                      execution */
-  FiberTailQueue fibersPool_; /**< pool of unitialized Fiber objects */
-
-  GlobalFiberTailQueue allFibers_; /**< list of all Fiber objects owned */
-
-  size_t fibersAllocated_{0}; /**< total number of fibers allocated */
-  size_t fibersPoolSize_{0}; /**< total number of fibers in the free pool */
-  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};
-
-  FContext::ContextStruct mainContext_; /**< stores loop function context */
-
-  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 FOLLY_TLS FiberManager* currentFiberManager_;
-
-  /**
-   * 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.
-   */
-  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_;
-
-  std::shared_ptr<TimeoutController> timeoutManager_;
-
-  struct FibersPoolResizer {
-    explicit FibersPoolResizer(FiberManager& fm) : fiberManager_(fm) {}
-    void operator()();
-
-   private:
-    FiberManager& fiberManager_;
-  };
-
-  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 registerFiberActivationWithAsan(Fiber* fiber);
-  void registerFiberDeactivationWithAsan(Fiber* fiber);
-  void unpoisonFiberStack(const Fiber* fiber);
-
-#endif // FOLLY_SANITIZE_ADDRESS
-
-  bool alternateSignalStackRegistered_{false};
-
-  void registerAlternateSignalStack();
-};
-
-/**
- * @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));
-}
-
-/**
- * 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));
-}
-
-/**
- * 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(F&& func);
-
-/**
- * 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>
-typename std::result_of<F()>::type inline runInMainContext(F&& func) {
-  auto fm = FiberManager::getFiberManagerUnsafe();
-  if (UNLIKELY(fm == nullptr)) {
-    return func();
-  }
-  return fm->runInMainContext(std::forward<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();
-  }
-}
-}
-}
-
-#include "FiberManager-inl.h"
diff --git a/folly/experimental/fibers/FiberManagerMap.cpp b/folly/experimental/fibers/FiberManagerMap.cpp
deleted file mode 100644 (file)
index 4548137..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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.
- */
-#include "FiberManagerMap.h"
-
-#include <memory>
-#include <unordered_map>
-
-#include <folly/Synchronized.h>
-#include <folly/ThreadLocal.h>
-
-namespace folly {
-namespace fibers {
-
-namespace {
-
-class EventBaseOnDestructionCallback : public EventBase::LoopCallback {
- public:
-  explicit EventBaseOnDestructionCallback(EventBase& evb) : evb_(evb) {}
-  void runLoopCallback() noexcept override;
-
- private:
-  EventBase& evb_;
-};
-
-class GlobalCache {
- public:
-  static FiberManager& get(EventBase& evb, const FiberManager::Options& opts) {
-    return instance().getImpl(evb, opts);
-  }
-
-  static std::unique_ptr<FiberManager> erase(EventBase& evb) {
-    return instance().eraseImpl(evb);
-  }
-
- private:
-  GlobalCache() {}
-
-  // Leak this intentionally. During shutdown, we may call getFiberManager,
-  // and want access to the fiber managers during that time.
-  static GlobalCache& instance() {
-    static auto ret = new GlobalCache();
-    return *ret;
-  }
-
-  FiberManager& getImpl(EventBase& evb, const FiberManager::Options& opts) {
-    std::lock_guard<std::mutex> lg(mutex_);
-
-    auto& fmPtrRef = map_[&evb];
-
-    if (!fmPtrRef) {
-      auto loopController = make_unique<EventBaseLoopController>();
-      loopController->attachEventBase(evb);
-      evb.runOnDestruction(new EventBaseOnDestructionCallback(evb));
-
-      fmPtrRef = make_unique<FiberManager>(std::move(loopController), opts);
-    }
-
-    return *fmPtrRef;
-  }
-
-  std::unique_ptr<FiberManager> eraseImpl(EventBase& evb) {
-    std::lock_guard<std::mutex> lg(mutex_);
-
-    DCHECK_EQ(1, map_.count(&evb));
-
-    auto ret = std::move(map_[&evb]);
-    map_.erase(&evb);
-    return ret;
-  }
-
-  std::mutex mutex_;
-  std::unordered_map<EventBase*, std::unique_ptr<FiberManager>> map_;
-};
-
-constexpr size_t kEraseListMaxSize = 64;
-
-class ThreadLocalCache {
- public:
-  static FiberManager& get(EventBase& evb, const FiberManager::Options& opts) {
-    return instance()->getImpl(evb, opts);
-  }
-
-  static void erase(EventBase& evb) {
-    for (auto& localInstance : instance().accessAllThreads()) {
-      SYNCHRONIZED(info, localInstance.eraseInfo_) {
-        if (info.eraseList.size() >= kEraseListMaxSize) {
-          info.eraseAll = true;
-        } else {
-          info.eraseList.push_back(&evb);
-        }
-        localInstance.eraseRequested_ = true;
-      }
-    }
-  }
-
- private:
-  ThreadLocalCache() {}
-
-  struct ThreadLocalCacheTag {};
-  using ThreadThreadLocalCache =
-      ThreadLocal<ThreadLocalCache, ThreadLocalCacheTag>;
-
-  // Leak this intentionally. During shutdown, we may call getFiberManager,
-  // and want access to the fiber managers during that time.
-  static ThreadThreadLocalCache& instance() {
-    static auto ret =
-        new ThreadThreadLocalCache([]() { return new ThreadLocalCache(); });
-    return *ret;
-  }
-
-  FiberManager& getImpl(EventBase& evb, const FiberManager::Options& opts) {
-    eraseImpl();
-
-    auto& fmPtrRef = map_[&evb];
-    if (!fmPtrRef) {
-      fmPtrRef = &GlobalCache::get(evb, opts);
-    }
-
-    DCHECK(fmPtrRef != nullptr);
-
-    return *fmPtrRef;
-  }
-
-  void eraseImpl() {
-    if (!eraseRequested_.load()) {
-      return;
-    }
-
-    SYNCHRONIZED(info, eraseInfo_) {
-      if (info.eraseAll) {
-        map_.clear();
-      } else {
-        for (auto evbPtr : info.eraseList) {
-          map_.erase(evbPtr);
-        }
-      }
-
-      info.eraseList.clear();
-      info.eraseAll = false;
-      eraseRequested_ = false;
-    }
-  }
-
-  std::unordered_map<EventBase*, FiberManager*> map_;
-  std::atomic<bool> eraseRequested_{false};
-
-  struct EraseInfo {
-    bool eraseAll{false};
-    std::vector<EventBase*> eraseList;
-  };
-
-  folly::Synchronized<EraseInfo> eraseInfo_;
-};
-
-void EventBaseOnDestructionCallback::runLoopCallback() noexcept {
-  auto fm = GlobalCache::erase(evb_);
-  DCHECK(fm.get() != nullptr);
-  ThreadLocalCache::erase(evb_);
-
-  while (fm->hasTasks()) {
-    fm->loopUntilNoReady();
-    evb_.loopOnce();
-  }
-
-  delete this;
-}
-
-} // namespace
-
-FiberManager& getFiberManager(
-    EventBase& evb,
-    const FiberManager::Options& opts) {
-  return ThreadLocalCache::get(evb, opts);
-}
-}
-}
diff --git a/folly/experimental/fibers/FiberManagerMap.h b/folly/experimental/fibers/FiberManagerMap.h
deleted file mode 100644 (file)
index 8d233a8..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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 <folly/experimental/fibers/EventBaseLoopController.h>
-#include <folly/experimental/fibers/FiberManager.h>
-
-namespace folly {
-namespace fibers {
-
-FiberManager& getFiberManager(
-    folly::EventBase& evb,
-    const FiberManager::Options& opts = FiberManager::Options());
-}
-}
diff --git a/folly/experimental/fibers/ForEach-inl.h b/folly/experimental/fibers/ForEach-inl.h
deleted file mode 100644 (file)
index 85333b3..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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.
- */
-#include <folly/experimental/fibers/FiberManager.h>
-
-namespace folly {
-namespace fibers {
-
-namespace {
-
-template <class F, class G>
-typename std::enable_if<
-    !std::is_same<typename std::result_of<F()>::type, void>::value,
-    void>::type inline callFuncs(F&& f, G&& g, size_t id) {
-  g(id, f());
-}
-
-template <class F, class G>
-typename std::enable_if<
-    std::is_same<typename std::result_of<F()>::type, void>::value,
-    void>::type inline callFuncs(F&& f, G&& g, size_t id) {
-  f();
-  g(id);
-}
-
-} // anonymous namespace
-
-template <class InputIterator, class F>
-inline void forEach(InputIterator first, InputIterator last, F&& f) {
-  if (first == last) {
-    return;
-  }
-
-  typedef typename std::iterator_traits<InputIterator>::value_type FuncType;
-
-  size_t tasksTodo = 1;
-  std::exception_ptr e;
-  Baton baton;
-
-#ifdef __clang__
-#pragma clang diagnostic push // ignore generalized lambda capture warning
-#pragma clang diagnostic ignored "-Wc++1y-extensions"
-#endif
-  auto taskFunc = [&tasksTodo, &e, &f, &baton](size_t id, FuncType&& func) {
-    return [
-      id,
-      &tasksTodo,
-      &e,
-      &f,
-      &baton,
-      func_ = std::forward<FuncType>(func)
-    ]() mutable {
-      try {
-        callFuncs(std::forward<FuncType>(func_), f, id);
-      } catch (...) {
-        e = std::current_exception();
-      }
-      if (--tasksTodo == 0) {
-        baton.post();
-      }
-    };
-  };
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-  auto firstTask = first;
-  ++first;
-
-  for (size_t i = 1; first != last; ++i, ++first, ++tasksTodo) {
-    addTask(taskFunc(i, std::move(*first)));
-  }
-
-  taskFunc(0, std::move(*firstTask))();
-  baton.wait();
-
-  if (e != std::exception_ptr()) {
-    std::rethrow_exception(e);
-  }
-}
-}
-} // folly::fibers
diff --git a/folly/experimental/fibers/ForEach.h b/folly/experimental/fibers/ForEach.h
deleted file mode 100644 (file)
index ecbb63c..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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
-
-namespace folly {
-namespace fibers {
-
-/**
- * Schedules several tasks and blocks until all of them are completed.
- * In the process of their successfull completion given callback would be called
- * for each of them with the index of the task and the result it returned (if
- * not void).
- * If any of these n tasks throws an exception, this exception will be
- * re-thrown, but only when all tasks are complete. If several tasks throw
- * exceptions one of them will be re-thrown. Callback won't be called for
- * tasks that throw exception.
- *
- * @param first  Range of tasks to be scheduled
- * @param last
- * @param F      callback to call for each result.
- *               In case of each task returning void it should be callable
- *                  F(size_t id)
- *               otherwise should be callable
- *                  F(size_t id, Result)
- */
-template <class InputIterator, class F>
-inline void forEach(InputIterator first, InputIterator last, F&& f);
-}
-} // folly::fibers
-
-#include <folly/experimental/fibers/ForEach-inl.h>
diff --git a/folly/experimental/fibers/GenericBaton.h b/folly/experimental/fibers/GenericBaton.h
deleted file mode 100644 (file)
index c87b584..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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 <folly/Baton.h>
-
-#include <folly/experimental/fibers/Baton.h>
-
-namespace folly {
-namespace fibers {
-
-typedef Baton GenericBaton;
-}
-}
diff --git a/folly/experimental/fibers/GuardPageAllocator.cpp b/folly/experimental/fibers/GuardPageAllocator.cpp
deleted file mode 100644 (file)
index b424e6f..0000000
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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.
- */
-#include "GuardPageAllocator.h"
-
-#include <unistd.h>
-
-#include <mutex>
-
-#include <folly/Singleton.h>
-#include <folly/SpinLock.h>
-#include <folly/portability/SysMman.h>
-
-#include <glog/logging.h>
-
-namespace folly {
-namespace fibers {
-
-/**
- * Each stack with a guard page creates two memory mappings.
- * Since this is a limited resource, we don't want to create too many of these.
- *
- * The upper bound on total number of mappings created
- * is kNumGuarded * kMaxInUse.
- */
-
-/**
- * Number of guarded stacks per allocator instance
- */
-constexpr size_t kNumGuarded = 100;
-
-/**
- * Maximum number of allocator instances with guarded stacks enabled
- */
-constexpr size_t kMaxInUse = 100;
-
-/**
- * A cache for kNumGuarded stacks of a given size
- *
- * Thread safe.
- */
-class StackCache {
- public:
-  explicit StackCache(size_t stackSize) : allocSize_(allocSize(stackSize)) {
-    auto p = ::mmap(
-        nullptr,
-        allocSize_ * kNumGuarded,
-        PROT_READ | PROT_WRITE,
-        MAP_PRIVATE | MAP_ANONYMOUS,
-        -1,
-        0);
-    PCHECK(p != (void*)(-1));
-    storage_ = reinterpret_cast<unsigned char*>(p);
-
-    /* Protect the bottommost page of every stack allocation */
-    for (size_t i = 0; i < kNumGuarded; ++i) {
-      auto allocBegin = storage_ + allocSize_ * i;
-      freeList_.emplace_back(allocBegin, /* protected= */ false);
-    }
-  }
-
-  unsigned char* borrow(size_t size) {
-    std::lock_guard<folly::SpinLock> lg(lock_);
-
-    assert(storage_);
-
-    auto as = allocSize(size);
-    if (as != allocSize_ || freeList_.empty()) {
-      return nullptr;
-    }
-
-    auto p = freeList_.back().first;
-    if (!freeList_.back().second) {
-      PCHECK(0 == ::mprotect(p, pagesize(), PROT_NONE));
-    }
-    freeList_.pop_back();
-
-    /* We allocate minimum number of pages required, plus a guard page.
-       Since we use this for stack storage, requested allocation is aligned
-       at the top of the allocated pages, while the guard page is at the bottom.
-
-               -- increasing addresses -->
-             Guard page     Normal pages
-            |xxxxxxxxxx|..........|..........|
-            <- allocSize_ ------------------->
-         p -^                <- size -------->
-                      limit -^
-    */
-    auto limit = p + allocSize_ - size;
-    assert(limit >= p + pagesize());
-    return limit;
-  }
-
-  bool giveBack(unsigned char* limit, size_t size) {
-    std::lock_guard<folly::SpinLock> lg(lock_);
-
-    assert(storage_);
-
-    auto as = allocSize(size);
-    auto p = limit + size - as;
-    if (p < storage_ || p >= storage_ + allocSize_ * kNumGuarded) {
-      /* not mine */
-      return false;
-    }
-
-    assert(as == allocSize_);
-    assert((p - storage_) % allocSize_ == 0);
-    freeList_.emplace_back(p, /* protected= */ true);
-    return true;
-  }
-
-  ~StackCache() {
-    assert(storage_);
-    PCHECK(0 == ::munmap(storage_, allocSize_ * kNumGuarded));
-  }
-
- private:
-  folly::SpinLock lock_;
-  unsigned char* storage_{nullptr};
-  size_t allocSize_{0};
-
-  /**
-   * LIFO free list. Each pair contains stack pointer and protected flag.
-   */
-  std::vector<std::pair<unsigned char*, bool>> freeList_;
-
-  static size_t pagesize() {
-    static const size_t pagesize = sysconf(_SC_PAGESIZE);
-    return pagesize;
-  }
-
-  /* Returns a multiple of pagesize() enough to store size + one guard page */
-  static size_t allocSize(size_t size) {
-    return pagesize() * ((size + pagesize() - 1) / pagesize() + 1);
-  }
-};
-
-class CacheManager {
- public:
-  static CacheManager& instance() {
-    static auto inst = new CacheManager();
-    return *inst;
-  }
-
-  std::unique_ptr<StackCacheEntry> getStackCache(size_t stackSize) {
-    std::lock_guard<folly::SpinLock> lg(lock_);
-    if (inUse_ < kMaxInUse) {
-      ++inUse_;
-      return folly::make_unique<StackCacheEntry>(stackSize);
-    }
-
-    return nullptr;
-  }
-
- private:
-  folly::SpinLock lock_;
-  size_t inUse_{0};
-
-  friend class StackCacheEntry;
-
-  void giveBack(std::unique_ptr<StackCache> /* stackCache_ */) {
-    assert(inUse_ > 0);
-    --inUse_;
-    /* Note: we can add a free list for each size bucket
-       if stack re-use is important.
-       In this case this needs to be a folly::Singleton
-       to make sure the free list is cleaned up on fork.
-
-       TODO(t7351705): fix Singleton destruction order
-    */
-  }
-};
-
-/*
- * RAII Wrapper around a StackCache that calls
- * CacheManager::giveBack() on destruction.
- */
-class StackCacheEntry {
- public:
-  explicit StackCacheEntry(size_t stackSize)
-      : stackCache_(folly::make_unique<StackCache>(stackSize)) {}
-
-  StackCache& cache() const noexcept {
-    return *stackCache_;
-  }
-
-  ~StackCacheEntry() {
-    CacheManager::instance().giveBack(std::move(stackCache_));
-  }
-
- private:
-  std::unique_ptr<StackCache> stackCache_;
-};
-
-GuardPageAllocator::GuardPageAllocator(bool useGuardPages)
-    : useGuardPages_(useGuardPages) {}
-
-GuardPageAllocator::~GuardPageAllocator() = default;
-
-unsigned char* GuardPageAllocator::allocate(size_t size) {
-  if (useGuardPages_ && !stackCache_) {
-    stackCache_ = CacheManager::instance().getStackCache(size);
-  }
-
-  if (stackCache_) {
-    auto p = stackCache_->cache().borrow(size);
-    if (p != nullptr) {
-      return p;
-    }
-  }
-  return fallbackAllocator_.allocate(size);
-}
-
-void GuardPageAllocator::deallocate(unsigned char* limit, size_t size) {
-  if (!(stackCache_ && stackCache_->cache().giveBack(limit, size))) {
-    fallbackAllocator_.deallocate(limit, size);
-  }
-}
-}
-} // folly::fibers
diff --git a/folly/experimental/fibers/GuardPageAllocator.h b/folly/experimental/fibers/GuardPageAllocator.h
deleted file mode 100644 (file)
index 21d684b..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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 <memory>
-
-namespace folly {
-namespace fibers {
-
-class StackCacheEntry;
-
-/**
- * Stack allocator that protects an extra memory page after
- * the end of the stack.
- * Will only add extra memory pages up to a certain number of allocations
- * to avoid creating too many memory maps for the process.
- */
-class GuardPageAllocator {
- public:
-  /**
-   * @param useGuardPages if true, protect limited amount of stacks with guard
-   *                      pages, otherwise acts as std::allocator.
-   */
-  explicit GuardPageAllocator(bool useGuardPages);
-  ~GuardPageAllocator();
-
-  /**
-   * @return pointer to the bottom of the allocated stack of `size' bytes.
-   */
-  unsigned char* allocate(size_t size);
-
-  /**
-   * Deallocates the previous result of an `allocate(size)' call.
-   */
-  void deallocate(unsigned char* limit, size_t size);
-
- private:
-  std::unique_ptr<StackCacheEntry> stackCache_;
-  std::allocator<unsigned char> fallbackAllocator_;
-  bool useGuardPages_{true};
-};
-}
-} // folly::fibers
diff --git a/folly/experimental/fibers/LoopController.h b/folly/experimental/fibers/LoopController.h
deleted file mode 100644 (file)
index 917a655..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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 <chrono>
-#include <functional>
-
-namespace folly {
-namespace fibers {
-
-class FiberManager;
-
-class LoopController {
- public:
-  typedef std::chrono::steady_clock Clock;
-  typedef std::chrono::time_point<Clock> TimePoint;
-
-  virtual ~LoopController() {}
-
-  /**
-   * Called by FiberManager to associate itself with the LoopController.
-   */
-  virtual void setFiberManager(FiberManager*) = 0;
-
-  /**
-   * Called by FiberManager to schedule the loop function run
-   * at some point in the future.
-   */
-  virtual void schedule() = 0;
-
-  /**
-   * Same as schedule(), but safe to call from any thread.
-   * Runs func and only schedules if func returned true.
-   */
-  virtual void scheduleThreadSafe(std::function<bool()> func) = 0;
-
-  /**
-   * Called by FiberManager to cancel a previously scheduled
-   * loop function run.
-   */
-  virtual void cancel() = 0;
-
-  /**
-   * Called by FiberManager to schedule some function to be run at some time.
-   */
-  virtual void timedSchedule(std::function<void()> func, TimePoint time) = 0;
-};
-}
-} // folly::fibers
diff --git a/folly/experimental/fibers/Promise-inl.h b/folly/experimental/fibers/Promise-inl.h
deleted file mode 100644 (file)
index a50d81c..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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.
- */
-#include <folly/experimental/fibers/Baton.h>
-
-namespace folly {
-namespace fibers {
-
-template <class T>
-Promise<T>::Promise(folly::Try<T>& value, Baton& baton)
-    : value_(&value), baton_(&baton) {}
-
-template <class T>
-Promise<T>::Promise(Promise&& other) noexcept
-    : value_(other.value_), baton_(other.baton_) {
-  other.value_ = nullptr;
-  other.baton_ = nullptr;
-}
-
-template <class T>
-Promise<T>& Promise<T>::operator=(Promise&& other) {
-  std::swap(value_, other.value_);
-  std::swap(baton_, other.baton_);
-  return *this;
-}
-
-template <class T>
-void Promise<T>::throwIfFulfilled() const {
-  if (!value_) {
-    throw std::logic_error("promise already fulfilled");
-  }
-}
-
-template <class T>
-Promise<T>::~Promise() {
-  if (value_) {
-    setException(folly::make_exception_wrapper<std::logic_error>(
-        "promise not fulfilled"));
-  }
-}
-
-template <class T>
-void Promise<T>::setException(folly::exception_wrapper e) {
-  setTry(folly::Try<T>(e));
-}
-
-template <class T>
-void Promise<T>::setTry(folly::Try<T>&& t) {
-  throwIfFulfilled();
-
-  *value_ = std::move(t);
-  value_ = nullptr;
-
-  // Baton::post has to be the last step here, since if Promise is not owned by
-  // the posting thread, it may be destroyed right after Baton::post is called.
-  baton_->post();
-}
-
-template <class T>
-template <class M>
-void Promise<T>::setValue(M&& v) {
-  static_assert(!std::is_same<T, void>::value, "Use setValue() instead");
-
-  setTry(folly::Try<T>(std::forward<M>(v)));
-}
-
-template <class T>
-void Promise<T>::setValue() {
-  static_assert(std::is_same<T, void>::value, "Use setValue(value) instead");
-
-  setTry(folly::Try<void>());
-}
-
-template <class T>
-template <class F>
-void Promise<T>::setWith(F&& func) {
-  setTry(makeTryWith(std::forward<F>(func)));
-}
-
-template <class T>
-template <class F>
-typename Promise<T>::value_type Promise<T>::await(F&& func) {
-  folly::Try<value_type> result;
-  std::exception_ptr funcException;
-
-  Baton baton;
-  baton.wait([&func, &result, &baton, &funcException]() mutable {
-    try {
-      func(Promise<value_type>(result, baton));
-    } catch (...) {
-      // Save the exception, but still wait for baton to be posted by user code
-      // or promise destructor.
-      funcException = std::current_exception();
-    }
-  });
-
-  if (UNLIKELY(funcException != nullptr)) {
-    std::rethrow_exception(funcException);
-  }
-
-  return folly::moveFromTry(result);
-}
-}
-}
diff --git a/folly/experimental/fibers/Promise.h b/folly/experimental/fibers/Promise.h
deleted file mode 100644 (file)
index 4dee609..0000000
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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 <folly/experimental/fibers/traits.h>
-#include <folly/futures/Try.h>
-
-namespace folly {
-namespace fibers {
-
-class Baton;
-
-template <typename T>
-class Promise {
- public:
-  typedef T value_type;
-
-  ~Promise();
-
-  // not copyable
-  Promise(const Promise&) = delete;
-  Promise& operator=(const Promise&) = delete;
-
-  // movable
-  Promise(Promise&&) noexcept;
-  Promise& operator=(Promise&&);
-
-  /** Fulfill this promise (only for Promise<void>) */
-  void setValue();
-
-  /** Set the value (use perfect forwarding for both move and copy) */
-  template <class M>
-  void setValue(M&& value);
-
-  /**
-   * Fulfill the promise with a given try
-   *
-   * @param t
-   */
-  void setTry(folly::Try<T>&& t);
-
-  /** Fulfill this promise with the result of a function that takes no
-    arguments and returns something implicitly convertible to T.
-    Captures exceptions. e.g.
-
-    p.setWith([] { do something that may throw; return a T; });
-  */
-  template <class F>
-  void setWith(F&& func);
-
-  /** Fulfill the Promise with an exception_wrapper, e.g.
-    auto ew = folly::try_and_catch<std::exception>([]{ ... });
-    if (ew) {
-      p.setException(std::move(ew));
-    }
-    */
-  void setException(folly::exception_wrapper);
-
-  /**
-   * 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 <class F>
-  static value_type await(F&& func);
-
- private:
-  Promise(folly::Try<T>& value, Baton& baton);
-  folly::Try<T>* value_;
-  Baton* baton_;
-
-  void throwIfFulfilled() const;
-
-  template <class F>
-  typename std::enable_if<
-      std::is_convertible<typename std::result_of<F()>::type, T>::value &&
-      !std::is_same<T, void>::value>::type
-  fulfilHelper(F&& func);
-
-  template <class F>
-  typename std::enable_if<
-      std::is_same<typename std::result_of<F()>::type, void>::value &&
-      std::is_same<T, void>::value>::type
-  fulfilHelper(F&& func);
-};
-}
-}
-
-#include <folly/experimental/fibers/Promise-inl.h>
diff --git a/folly/experimental/fibers/README.md b/folly/experimental/fibers/README.md
deleted file mode 100644 (file)
index a691d56..0000000
+++ /dev/null
@@ -1,552 +0,0 @@
-<!-- This file is generated from internal wiki guide by folly/facebook/fibers-update-readme.sh. -->
-<section class="dex_guide"><h1 class="dex_title">folly::fibers</h1><section class="dex_document"><h1></h1><p class="dex_introduction">folly::fibers is an async C++ framework, which uses fibers for parallelism.</p><h2 id="overview">Overview <a href="#overview" class="headerLink">#</a></h2>
-
-<p>Fibers (or coroutines) are lightweight application threads. Multiple fibers can be running on top of a single system thread. Unlike system threads, all the context switching between fibers is happening explicitly. Because of this every such context switch is very fast (~200 million of fiber context switches can be made per second on a single CPU core).</p>
-
-<p>folly::fibers implements a task manager (FiberManager), which executes scheduled tasks on fibers. It also provides some fiber-compatible synchronization primitives.</p>
-
-<h2 id="basic-example">Basic example <a href="#basic-example" class="headerLink">#</a></h2>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="o">...</span>
-<span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="EventBase">EventBase</span> <span class="no">evb</span><span class="o">;</span>
-<span class="no">auto</span><span class="o">&amp;</span> <span class="no">fiberManager</span> <span class="o">=</span> <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="getFiberManager">getFiberManager</span><span class="o">(</span><span class="no">evb</span><span class="o">);</span>
-<span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Baton">Baton</span> <span class="no">baton</span><span class="o">;</span>
-
-<span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
-  <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="cout">cout</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;Task 1: start&quot;</span> <span class="o">&lt;&lt;</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="endl">endl</span><span class="o">;</span>
-  <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="wait">wait</span><span class="o">();</span>
-  <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="cout">cout</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;Task 1: after baton.wait()&quot;</span> <span class="o">&lt;&lt;</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="endl">endl</span><span class="o">;</span> 
-<span class="o">&#125;);</span>
-
-<span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
-  <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="cout">cout</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;Task 2: start&quot;</span> <span class="o">&lt;&lt;</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="endl">endl</span><span class="o">;</span>
-  <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="post">post</span><span class="o">();</span>
-  <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="cout">cout</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;Task 2: after baton.post()&quot;</span> <span class="o">&lt;&lt;</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="endl">endl</span><span class="o">;</span> 
-<span class="o">&#125;);</span>
-
-<span class="no">evb</span><span class="o">.</span><span class="nf" data-symbol-name="loop">loop</span><span class="o">();</span>
-<span class="o">...</span></pre></div>
-
-<p>This would print:</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">Task</span> <span class="mi">1</span><span class="o">:</span> <span class="no">start</span>
-<span class="no">Task</span> <span class="mi">2</span><span class="o">:</span> <span class="no">start</span>
-<span class="no">Task</span> <span class="mi">2</span><span class="o">:</span> <span class="no">after</span> <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="post">post</span><span class="o">()</span>
-<span class="no">Task</span> <span class="mi">1</span><span class="o">:</span> <span class="no">after</span> <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="wait">wait</span><span class="o">()</span></pre></div>
-
-<p>It&#039;s very important to note that both tasks in this example were executed on the same system thread. Task 1 was suspended by <tt>baton.wait()</tt> call. Task 2 then started and called <tt>baton.post()</tt>, resuming Task 1.</p>
-
-<h2 id="features">Features <a href="#features" class="headerLink">#</a></h2>
-
-<ul>
-<li>Fibers creation and scheduling is performed by FiberManager</li>
-<li>Integration with any event-management system (e.g. EventBase)</li>
-<li>Low-level synchronization primitives (Baton) as well as higher-level primitives built on top of them (await, collectN, mutexes, ... )</li>
-<li>Synchronization primitives have timeout support</li>
-<li>Built-in mechanisms for fiber stack-overflow detection</li>
-<li>Optional fiber-local data (i.e. equivalent of thread locals)</li>
-</ul>
-
-<h2 id="non-features">Non-features <a href="#non-features" class="headerLink">#</a></h2>
-
-<ul>
-<li>Individual fibers scheduling can&#039;t be directly controlled by application</li>
-<li>FiberManager is not thread-safe (we recommend to keep one FiberManager per thread). Application is responsible for managing it&#039;s own threads and distributing load between them</li>
-<li>We don&#039;t support automatic stack size adjustments. Each fiber has a stack of fixed size.</li>
-</ul>
-
-<h2 id="why-would-i-not-want-to">Why would I not want to use fibers ? <a href="#why-would-i-not-want-to" class="headerLink">#</a></h2>
-
-<p>The only real downside to using fibers is the need to keep a pre-allocated stack for every fiber being run. That either makes you application use a lot of memory (if you have many concurrent tasks and each of them uses large stacks) or creates a risk of stack overflow bugs (if you try to reduce the stack size).</p>
-
-<p>We believe these problems can be addressed (and we provide some tooling for that), as fibers library is used in many critical applications at Facebook (mcrouter, TAO, Service Router). However, it&#039;s important to be aware of the risks and be ready to deal with stack issues if you decide to use fibers library in your application.</p>
-
-<h2 id="what-are-the-alternative">What are the alternatives ? <a href="#what-are-the-alternative" class="headerLink">#</a></h2>
-
-<ul>
-<li><a href="https://github.com/facebook/folly/blob/master/folly/futures/" target="_blank">Futures</a> library works great for asynchronous high-level application code. Yet code written using fibers library is generally much simpler and much more efficient (you are not paying the penalty of heap allocation).</li>
-<li>You can always keep writing your asynchronous code using traditional callback approach. Such code quickly becomes hard to manage and is error-prone. Even though callback approach seems to allow you writing the most efficient code, inefficiency still comes from heap allocations (<tt>std::function</tt>s used for callbacks, context objects to be passed between callbacks etc.)</li>
-</ul></section><section class="dex_document"><h1>Quick guide</h1><p class="dex_introduction"></p><p>Let&#039;s take a look at this basic example:</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="o">...</span>
-<span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="EventBase">EventBase</span> <span class="no">evb</span><span class="o">;</span>
-<span class="no">auto</span><span class="o">&amp;</span> <span class="no">fiberManager</span> <span class="o">=</span> <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="getFiberManager">getFiberManager</span><span class="o">(</span><span class="no">evb</span><span class="o">);</span>
-<span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Baton">Baton</span> <span class="no">baton</span><span class="o">;</span>
-
-<span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
-  <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="cout">cout</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;Task: start&quot;</span> <span class="o">&lt;&lt;</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="endl">endl</span><span class="o">;</span>
-  <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="wait">wait</span><span class="o">();</span>
-  <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="cout">cout</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;Task: after baton.wait()&quot;</span> <span class="o">&lt;&lt;</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="endl">endl</span><span class="o">;</span> 
-<span class="o">&#125;);</span>
-
-<span class="no">evb</span><span class="o">.</span><span class="nf" data-symbol-name="loop">loop</span><span class="o">();</span>
-
-<span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="post">post</span><span class="o">();</span>
-<span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="cout">cout</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;Baton posted&quot;</span> <span class="o">&lt;&lt;</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="endl">endl</span><span class="o">;</span>
-
-<span class="no">evb</span><span class="o">.</span><span class="nf" data-symbol-name="loop">loop</span><span class="o">();</span>
-
-<span class="o">...</span></pre></div>
-
-<p>This would print:</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">Task</span><span class="o">:</span> <span class="no">start</span>
-<span class="no">Baton</span> <span class="no">posted</span>
-<span class="no">Task</span><span class="o">:</span> <span class="no">after</span> <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="wait">wait</span><span class="o">()</span></pre></div>
-
-<p>What makes fiber-task different from any other task run on e.g. an <tt>folly::EventBase</tt> is the ability to suspend such task, without blocking the system thread. So how do you suspend a fiber-task ?</p>
-
-<h3 id="fibers-baton">fibers::Baton <a href="#fibers-baton" class="headerLink">#</a></h3>
-
-<p><tt>fibers::Baton</tt> is the core synchronization primitive which is used to suspend a fiber-task and notify when the task may be resumed. <tt>fibers::Baton</tt> supports two basic operations: <tt>wait()</tt> and <tt>post()</tt>. Calling <tt>wait()</tt> on a Baton will suspend current fiber-task until <tt>post()</tt> is called on the same Baton.</p>
-
-<p>Please refer to <a href="https://github.com/facebook/folly/blob/master/folly/experimental/fibers/Baton.h" target="_blank">Baton</a> for more detailed documentation.</p>
-
-<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> <tt>fibers::Baton</tt> is the only native synchronization primitive of folly::fibers library. All other synchronization primitives provided by folly::fibers are built on top of <tt>fibers::Baton</tt>.</div>
-
-<h3 id="integrating-with-other-a">Integrating with other asynchronous APIs (callbacks) <a href="#integrating-with-other-a" class="headerLink">#</a></h3>
-
-<p>Let&#039;s say we have some existing library which provides a classic callback-style asynchronous API.</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">void</span> <span class="nf" data-symbol-name="asyncCall">asyncCall</span><span class="o">(</span><span class="no">Request</span> <span class="no">request</span><span class="o">,</span> <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="Function">Function</span><span class="o">&lt;</span><span class="nf" data-symbol-name="void">void</span><span class="o">(</span><span class="no">Response</span><span class="o">)&gt;</span> <span class="no">cb</span><span class="o">);</span></pre></div>
-
-<p>If we use folly::fibers we can just make an async call from a fiber-task and wait until callback is run:</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
-  <span class="o">...</span>
-  <span class="no">Response</span> <span class="no">response</span><span class="o">;</span>
-  <span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-context="fibers" data-symbol-name="Baton">Baton</span> <span class="no">baton</span><span class="o">;</span>
-  
-  <span class="nf" data-symbol-name="asyncCall">asyncCall</span><span class="o">(</span><span class="no">request</span><span class="o">,</span> <span class="o">[&amp;](</span><span class="no">Response</span> <span class="no">r</span><span class="o">)</span> <span class="no">mutable</span> <span class="o">&#123;</span>
-     <span class="no">response</span> <span class="o">=</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="move">move</span><span class="o">(</span><span class="no">r</span><span class="o">);</span>
-     <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="post">post</span><span class="o">();</span>
-  <span class="o">&#125;);</span>
-  <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="wait">wait</span><span class="o">();</span>
-
-  <span class="c">// Now response holds response returned by the async call</span>
-  <span class="o">...</span>
-<span class="o">&#125;</span></pre></div>
-
-<p>Using <tt>fibers::Baton</tt> directly is generally error-prone. To make the task above simpler, folly::fibers provide <tt>fibers::await</tt> function.</p>
-
-<p>With <tt>fibers::await</tt>, the code above transforms into:</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
-  <span class="o">...</span>
-  <span class="no">auto</span> <span class="no">response</span> <span class="o">=</span> <span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="nf" data-symbol-context="fibers" data-symbol-name="await">await</span><span class="o">([&amp;](</span><span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-context="fibers" data-symbol-name="Promise">Promise</span><span class="o">&lt;</span><span class="no">Response</span><span class="o">&gt;</span> <span class="no">promise</span><span class="o">)</span> <span class="o">&#123;</span>
-    <span class="nf" data-symbol-name="asyncCall">asyncCall</span><span class="o">(</span><span class="no">request</span><span class="o">,</span> <span class="o">[</span><span class="no">promise</span> <span class="o">=</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="move">move</span><span class="o">(</span><span class="no">promise</span><span class="o">)](</span><span class="no">Response</span> <span class="no">r</span><span class="o">)</span> <span class="no">mutable</span> <span class="o">&#123;</span>
-      <span class="no">promise</span><span class="o">.</span><span class="nf" data-symbol-name="setValue">setValue</span><span class="o">(</span><span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="move">move</span><span class="o">(</span><span class="no">r</span><span class="o">));</span>
-    <span class="o">&#125;);</span>
-  <span class="o">&#125;);</span>
-
-  <span class="c">// Now response holds response returned by the async call</span>
-  <span class="o">...</span>
-<span class="o">&#125;</span></pre></div>
-
-<p>Callback passed to <tt>fibers::await</tt> is executed immediately and then fiber-task is suspended until <tt>fibers::Promise</tt> is fulfilled. When <tt>fibers::Promise</tt> is fulfilled with a value or exception, fiber-task will be resumed and &#039;fibers::await&#039; returns that value (or throws an exception, if exception was used to fulfill the <tt>Promise</tt>).</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
-  <span class="o">...</span>
-  <span class="k">try</span> <span class="o">&#123;</span>
-    <span class="no">auto</span> <span class="no">response</span> <span class="o">=</span> <span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="nf" data-symbol-context="fibers" data-symbol-name="await">await</span><span class="o">([&amp;](</span><span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-context="fibers" data-symbol-name="Promise">Promise</span><span class="o">&lt;</span><span class="no">Response</span><span class="o">&gt;</span> <span class="no">promise</span><span class="o">)</span> <span class="o">&#123;</span>
-      <span class="nf" data-symbol-name="asyncCall">asyncCall</span><span class="o">(</span><span class="no">request</span><span class="o">,</span> <span class="o">[</span><span class="no">promise</span> <span class="o">=</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="move">move</span><span class="o">(</span><span class="no">promise</span><span class="o">)](</span><span class="no">Response</span> <span class="no">r</span><span class="o">)</span> <span class="no">mutable</span> <span class="o">&#123;</span>
-        <span class="no">promise</span><span class="o">.</span><span class="nf" data-symbol-name="setException">setException</span><span class="o">(</span><span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="runtime_error">runtime_error</span><span class="o">(</span><span class="s2">&quot;Await will re-throw me&quot;</span><span class="o">));</span>
-      <span class="o">&#125;);</span>
-    <span class="o">&#125;);</span>
-    <span class="nf" data-symbol-name="assert">assert</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span> <span class="c">// We should never get here</span>
-  <span class="o">&#125;</span> <span class="k">catch</span> <span class="o">(</span><span class="nc" data-symbol-name="const">const</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-name="exception">exception</span><span class="o">&amp;</span> <span class="no">e</span><span class="o">)</span> <span class="o">&#123;</span>
-    <span class="nf" data-symbol-name="assert">assert</span><span class="o">(</span><span class="no">e</span><span class="o">.</span><span class="nf" data-symbol-name="what">what</span><span class="o">()</span> <span class="o">==</span> <span class="s2">&quot;Await will re-throw me&quot;</span><span class="o">);</span>
-  <span class="o">&#125;</span>
-  <span class="o">...</span>
-<span class="o">&#125;</span></pre></div>
-
-<p>If <tt>fibers::Promise</tt> is not fulfilled, <tt>fibers::await</tt> will throw a <tt>std::logic_error</tt>.</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
-  <span class="o">...</span>
-  <span class="k">try</span> <span class="o">&#123;</span>
-    <span class="no">auto</span> <span class="no">response</span> <span class="o">=</span> <span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="nf" data-symbol-context="fibers" data-symbol-name="await">await</span><span class="o">([&amp;](</span><span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-context="fibers" data-symbol-name="Promise">Promise</span><span class="o">&lt;</span><span class="no">Response</span><span class="o">&gt;</span> <span class="no">promise</span><span class="o">)</span> <span class="o">&#123;</span>
-      <span class="c">// We forget about the promise</span>
-    <span class="o">&#125;);</span>
-    <span class="nf" data-symbol-name="assert">assert</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span> <span class="c">// We should never get here</span>
-  <span class="o">&#125;</span> <span class="k">catch</span> <span class="o">(</span><span class="nc" data-symbol-name="const">const</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-name="logic_error">logic_error</span><span class="o">&amp;</span> <span class="no">e</span><span class="o">)</span> <span class="o">&#123;</span>
-    <span class="o">...</span>
-  <span class="o">&#125;</span>
-  <span class="o">...</span>
-<span class="o">&#125;</span></pre></div>
-
-<p>Please refer to <a href="https://github.com/facebook/folly/blob/master/folly/experimental/fibers/Promise.h" target="_blank">await</a> for more detailed documentation.</p>
-
-<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> most of your code written with folly::fibers, won&#039;t be using <tt>fibers::Baton</tt> or <tt>fibers::await</tt>. These primitives should only be used to integrate with other asynchronous API which are not fibers-compatible.</div>
-
-<h3 id="integrating-with-other-a-1">Integrating with other asynchronous APIs (folly::Future) <a href="#integrating-with-other-a-1" class="headerLink">#</a></h3>
-
-<p>Let&#039;s say we have some existing library which provides a Future-based asynchronous API.</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="Future">Future</span><span class="o">&lt;</span><span class="no">Response</span><span class="o">&gt;</span> <span class="nf" data-symbol-name="asyncCallFuture">asyncCallFuture</span><span class="o">(</span><span class="no">Request</span> <span class="no">request</span><span class="o">);</span></pre></div>
-
-<p>The good news are, <tt>folly::Future</tt> is already fibers-compatible. You can simply write:</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
-  <span class="o">...</span>
-  <span class="no">auto</span> <span class="no">response</span> <span class="o">=</span> <span class="nf" data-symbol-name="asyncCallFuture">asyncCallFuture</span><span class="o">(</span><span class="no">request</span><span class="o">).</span><span class="nf" data-symbol-name="get">get</span><span class="o">();</span>
-  
-  <span class="c">// Now response holds response returned by the async call</span>
-  <span class="o">...</span>
-<span class="o">&#125;</span></pre></div>
-
-<p>Calling <tt>get()</tt> on a <tt>folly::Future</tt> object will only suspend the calling fiber-task. It won&#039;t block the system thread, letting it process other tasks.</p>
-
-<h2 id="writing-code-with-folly">Writing code with folly::fibers <a href="#writing-code-with-folly" class="headerLink">#</a></h2>
-
-<h3 id="building-fibers-compatib">Building fibers-compatible API <a href="#building-fibers-compatib" class="headerLink">#</a></h3>
-
-<p>Following the explanations above we may wrap an existing asynchronous API in a function:</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">Response</span> <span class="nf" data-symbol-name="fiberCall">fiberCall</span><span class="o">(</span><span class="no">Request</span> <span class="no">request</span><span class="o">)</span> <span class="o">&#123;</span>
-  <span class="k">return</span> <span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="nf" data-symbol-context="fibers" data-symbol-name="await">await</span><span class="o">([&amp;](</span><span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-context="fibers" data-symbol-name="Promise">Promise</span><span class="o">&lt;</span><span class="no">Response</span><span class="o">&gt;</span> <span class="no">promise</span><span class="o">)</span> <span class="o">&#123;</span>
-    <span class="nf" data-symbol-name="asyncCall">asyncCall</span><span class="o">(</span><span class="no">request</span><span class="o">,</span> <span class="o">[</span><span class="no">promise</span> <span class="o">=</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="move">move</span><span class="o">(</span><span class="no">promise</span><span class="o">)](</span><span class="no">Response</span> <span class="no">r</span><span class="o">)</span> <span class="no">mutable</span> <span class="o">&#123;</span>
-      <span class="no">promise</span><span class="o">.</span><span class="nf" data-symbol-name="setValue">setValue</span><span class="o">(</span><span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="move">move</span><span class="o">(</span><span class="no">r</span><span class="o">));</span>
-    <span class="o">&#125;);</span>
-  <span class="o">&#125;);</span>
-<span class="o">&#125;</span></pre></div>
-
-<p>We can then call it from a fiber-task:</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
-  <span class="o">...</span>
-  <span class="no">auto</span> <span class="no">response</span> <span class="o">=</span> <span class="nf" data-symbol-name="fiberCall">fiberCall</span><span class="o">(</span><span class="no">request</span><span class="o">);</span>
-  <span class="o">...</span>
-<span class="o">&#125;);</span></pre></div>
-
-<p>But what happens if we just call <tt>fiberCall</tt> not from within a fiber-task, but directly from a system thread ? Here another important feature of <tt>fibers::Baton</tt> (and thus all other folly::fibers synchronization primitives built on top of it) comes into play. Calling <tt>wait()</tt> on a <tt>fibers::Baton</tt> within a system thread context just blocks the thread until <tt>post()</tt> is called on the same <tt>folly::Baton</tt>.</p>
-
-<p>What this means is that you no longer need to write separate code for synchronous and asynchronous APIs. If you use only folly::fibers synchronization primitives for all blocking calls inside of your synchronous function, it automatically becomes asynchronous when run inside a fiber-task.</p>
-
-<h3 id="passing-by-reference">Passing by reference <a href="#passing-by-reference" class="headerLink">#</a></h3>
-
-<p>Classic asynchronous APIs (same applies to folly::Future-based APIs) generally rely on copying/moving-in input arguments and often require you to copy/move in some context variables into the callback. E.g.:</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="o">...</span>
-<span class="no">Context</span> <span class="no">context</span><span class="o">;</span>
-<span class="nf" data-symbol-name="asyncCall">asyncCall</span><span class="o">(</span><span class="no">request</span><span class="o">,</span> <span class="o">[</span><span class="no">request</span><span class="o">,</span> <span class="no">context</span><span class="o">](</span><span class="no">Response</span> <span class="no">response</span><span class="o">)</span> <span class="no">mutable</span> <span class="o">&#123;</span>
-  <span class="nf" data-symbol-name="doSomething">doSomething</span><span class="o">(</span><span class="no">request</span><span class="o">,</span> <span class="no">response</span><span class="o">,</span> <span class="no">context</span><span class="o">);</span>
-<span class="o">&#125;);</span>
-<span class="o">...</span></pre></div>
-
-<p>Fibers-compatible APIs look more like synchronous APIs, so you can actually pass input arguments by reference and you don&#039;t have to think about passing context at all. E.g.</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
-  <span class="o">...</span>
-  <span class="no">Context</span> <span class="no">context</span><span class="o">;</span>
-
-  <span class="no">auto</span> <span class="no">response</span> <span class="o">=</span> <span class="nf" data-symbol-name="fiberCall">fiberCall</span><span class="o">(</span><span class="no">request</span><span class="o">);</span>
-  <span class="nf" data-symbol-name="doSomething">doSomething</span><span class="o">(</span><span class="no">request</span><span class="o">,</span> <span class="no">response</span><span class="o">,</span> <span class="no">context</span><span class="o">);</span>
-  <span class="o">...</span>
-<span class="o">&#125;);</span></pre></div>
-
-<p>Same logic applies to <tt>fibers::await</tt>. Since <tt>fibers::await</tt> call blocks until promise is fulfilled, it&#039;s safe to pass everything by reference.</p>
-
-<h3 id="limited-stack-space">Limited stack space <a href="#limited-stack-space" class="headerLink">#</a></h3>
-
-<p>So should you just run all the code inside a fiber-task ? No exactly.</p>
-
-<p>Similarly to system threads, every fiber-task has some stack space assigned to it. Stack usage goes up with the number of nested function calls and objects allocated on the stack. folly::fibers implementation only supports fiber-tasks with fixed stack size. If you want to have many fiber-tasks running concurrently - you need to reduce the amount of stack assigned to each fiber-task, otherwise you may run out of memory.</p>
-
-<div class="remarkup-important"><span class="remarkup-note-word">IMPORTANT:</span> If a fiber-task runs out of stack space (e.g. calls a function which does a lot of stack allocations) you program will fail.</div>
-
-<p>However if you know that some function never suspends a fiber-task, you can use <tt>fibers::runInMainContext</tt> to safely call it from a fiber-task, without any risk of running out of stack space of the fiber-task.</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">Result</span> <span class="nf" data-symbol-name="useALotOfStack">useALotOfStack</span><span class="o">()</span> <span class="o">&#123;</span>
-  <span class="no">char</span> <span class="no">buffer</span><span class="o">[</span><span class="mi">1024</span><span class="o">*</span><span class="mi">1024</span><span class="o">];</span>
-  <span class="o">...</span>
-<span class="o">&#125;</span>
-
-<span class="o">...</span>
-<span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
-  <span class="o">...</span>
-  <span class="no">auto</span> <span class="no">result</span> <span class="o">=</span> <span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="nf" data-symbol-context="fibers" data-symbol-name="runInMainContext">runInMainContext</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
-    <span class="k">return</span> <span class="nf" data-symbol-name="useALotOfStack">useALotOfStack</span><span class="o">();</span>
-  <span class="o">&#125;);</span>
-  <span class="o">...</span>
-<span class="o">&#125;);</span>
-<span class="o">...</span></pre></div>
-
-<p><tt>fibers::runInMainContext</tt> will switch to the stack of the system thread (main context), run the functor passed to it and then switch back to the fiber-task stack.</p>
-
-<div class="remarkup-important"><span class="remarkup-note-word">IMPORTANT:</span> Make sure you don&#039;t do any blocking calls on main context though. It will suspend the whole system thread, not just the fiber-task which was running.</div>
-
-<p>Remember that it&#039;s fine to use <tt>fibers::runInMainContext</tt> in general purpose functions (those which may be called both from fiber-task and non from fiber-task). When called in non-fiber-task context <tt>fibers::runInMainContext</tt> would simply execute passed functor right away.</p>
-
-<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> Besides <tt>fibers::runInMainContext</tt> some other functions in folly::fibers are also executing some of the passed functors on the main context. E.g. functor passes to <tt>fibers::await</tt> is executed on main context, finally-functor passed to <tt>FiberManager::addTaskFinally</tt> is also executed on main context etc. Relying on this can help you avoid extra <tt>fibers::runInMainContext</tt> calls (and avoid extra context switches).</div>
-
-<h3 id="using-locks">Using locks <a href="#using-locks" class="headerLink">#</a></h3>
-
-<p>Consider the following example:</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="o">...</span>
-<span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="EventBase">EventBase</span> <span class="no">evb</span><span class="o">;</span>
-<span class="no">auto</span><span class="o">&amp;</span> <span class="no">fiberManager</span> <span class="o">=</span> <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="getFiberManager">getFiberManager</span><span class="o">(</span><span class="no">evb</span><span class="o">);</span>
-<span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="mutex">mutex</span> <span class="no">lock</span><span class="o">;</span>
-<span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Baton">Baton</span> <span class="no">baton</span><span class="o">;</span>
-
-<span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
-  <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="lock_guard">lock_guard</span><span class="o">&lt;</span><span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="mutex">mutex</span><span class="o">&gt;</span> <span class="nf" data-symbol-name="lg">lg</span><span class="o">(</span><span class="no">lock</span><span class="o">);</span>
-  <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="wait">wait</span><span class="o">();</span>
-<span class="o">&#125;);</span>
-
-<span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
-  <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="lock_guard">lock_guard</span><span class="o">&lt;</span><span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="mutex">mutex</span><span class="o">&gt;</span> <span class="nf" data-symbol-name="lg">lg</span><span class="o">(</span><span class="no">lock</span><span class="o">);</span>
-<span class="o">&#125;);</span>
-
-<span class="no">evb</span><span class="o">.</span><span class="nf" data-symbol-name="loop">loop</span><span class="o">();</span>
-<span class="c">// We won&#039;t get here :(</span>
-<span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="post">post</span><span class="o">();</span>
-<span class="o">...</span></pre></div>
-
-<p>First fiber-task will grab a lock and then suspend waiting on a <tt>fibers::Baton</tt>. Then second fiber-task will be run and it will try to grab a lock. Unlike system threads, fiber-task can be only suspended explicitly, so the whole system thread will be blocked waiting on the lock, and we end up with a dead-lock.</p>
-
-<p>There&#039;re generally two ways we can solve this problem. Ideally we would re-design the program to never not hold any locks when fiber-task is suspended. However if we are absolutely sure we need that lock - folly::fibers library provides some fiber-task-aware lock implementations (e.g. 
-<a href="https://github.com/facebook/folly/blob/master/folly/experimental/fibers/TimedMutex.h" target="_blank">TimedMutex</a>).</p></section><section class="dex_document"><h1>APIs</h1><p class="dex_introduction"></p><h2 id="fibers-baton">fibers::Baton <a href="#fibers-baton" class="headerLink">#</a></h2>
-
-<p>All of the features of folly::fibers library are actually built on top a single synchronization primitive called Baton. <tt>fibers::Baton</tt> is a fiber-specific version of <tt>folly::Baton</tt>. It only  supports two basic operations: <tt>wait()</tt> and <tt>post()</tt>. Whenever <tt>wait()</tt> is called on the Baton, the current thread or fiber-task is suspended, until <tt>post()</tt> is called on the same Baton. <tt>wait()</tt> does not suspend the thread or fiber-task if <tt>post()</tt> was already called on the Baton. Please refer to <a href="https://github.com/facebook/folly/blob/master/folly/experimental/fibers/Baton.h" target="_blank">Baton</a> for more detailed documentation.</p>
-
-<p>Baton is thread-safe, so <tt>wait()</tt> and <tt>post()</tt> can be (and should be :) ) called from different threads or fiber-tasks.</p>
-
-<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> Because Baton transparently works both on threads and fiber-tasks, any synchronization primitive built using it would have the same property. This means that any library with a synchronous API, which uses only <tt>fibers::Baton</tt> for synchronization, becomes asynchronous when used in fiber context.</div>
-
-<h3 id="timed-wait">timed_wait() <a href="#timed-wait" class="headerLink">#</a></h3>
-
-<p><tt>fibers::Baton</tt> also supports wait with timeout.</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([=]()</span> <span class="o">&#123;</span>
-  <span class="no">auto</span> <span class="no">baton</span> <span class="o">=</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="make_shared">make_shared</span><span class="o">&lt;</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Baton">Baton</span><span class="o">&gt;();</span>
-  <span class="no">auto</span> <span class="no">result</span> <span class="o">=</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="make_shared">make_shared</span><span class="o">&lt;</span><span class="no">Result</span><span class="o">&gt;();</span>
-
-  <span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([=]()</span> <span class="o">&#123;</span>
-    <span class="o">*</span><span class="no">result</span> <span class="o">=</span> <span class="nf" data-symbol-name="sendRequest">sendRequest</span><span class="o">(...);</span>
-    <span class="no">baton</span><span class="o">-&gt;</span><span class="na" data-symbol-name="post">post</span><span class="o">();</span>
-  <span class="o">&#125;);</span>
-
-  <span class="no">bool</span> <span class="no">success</span> <span class="o">=</span> <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="timed_wait">timed_wait</span><span class="o">(</span><span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="chrono">chrono</span><span class="o">::</span><span class="na" data-symbol-name="milliseconds">milliseconds</span><span class="o">&#123;</span><span class="mi">10</span><span class="o">&#125;);</span>
-  <span class="k">if</span> <span class="o">(</span><span class="no">success</span><span class="o">)</span> <span class="o">&#123;</span>
-    <span class="c">// request successful</span>
-    <span class="o">...</span>
-  <span class="o">&#125;</span> <span class="k">else</span> <span class="o">&#123;</span>
-    <span class="c">// handle timeout</span>
-    <span class="o">...</span>
-  <span class="o">&#125;</span>
-<span class="o">&#125;);</span></pre></div>
-
-<div class="remarkup-important"><span class="remarkup-note-word">IMPORTANT:</span> unlike <tt>wait()</tt> when using <tt>timed_wait()</tt> API it&#039;s generally not safe to pass <tt>fibers::Baton</tt> by reference. You have to make sure that task, which fulfills the Baton is either cancelled in case of timeout, or have shared ownership for the Baton.</div>
-
-<h2 id="task-creation">Task creation <a href="#task-creation" class="headerLink">#</a></h2>
-
-<h3 id="addtask-addtaskremote">addTask() / addTaskRemote() <a href="#addtask-addtaskremote" class="headerLink">#</a></h3>
-
-<p>As you could see from previous examples, the easiest way to create a new fiber-task is to call <tt>addTask()</tt>:</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
-  <span class="o">...</span>
-<span class="o">&#125;);</span></pre></div>
-
-<p>It is important to remember that <tt>addTask()</tt> is not thread-safe. I.e. it can only be safely called from the the thread, which is running the <tt>folly::FiberManager</tt> loop.</p>
-
-<p>If you need to create a fiber-task from a different thread, you have to use <tt>addTaskRemote()</tt>:</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="EventBase">EventBase</span> <span class="no">evb</span><span class="o">;</span>
-<span class="no">auto</span><span class="o">&amp;</span> <span class="no">fiberManager</span> <span class="o">=</span> <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="getFiberManager">getFiberManager</span><span class="o">(</span><span class="no">evb</span><span class="o">);</span>
-
-<span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="thread">thread</span> <span class="nf" data-symbol-name="t">t</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
-  <span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTaskRemote">addTaskRemote</span><span class="o">([]()</span> <span class="o">&#123;</span>
-    <span class="o">...</span>
-  <span class="o">&#125;);</span>
-<span class="o">&#125;);</span>
-
-<span class="no">evb</span><span class="o">.</span><span class="nf" data-symbol-name="loopForever">loopForever</span><span class="o">();</span></pre></div>
-
-<h3 id="addtaskfinally">addTaskFinally() <a href="#addtaskfinally" class="headerLink">#</a></h3>
-
-<p><tt>addTaskFinally()</tt> is useful when you need to run some code on the main context in the end of a fiber-task.</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTaskFinally">addTaskFinally</span><span class="o">(</span>
-  <span class="o">[=]()</span> <span class="o">&#123;</span>
-    <span class="o">...</span>
-    <span class="k">return</span> <span class="no">result</span><span class="o">;</span>
-  <span class="o">&#125;,</span>
-  <span class="o">[=](</span><span class="no">Result</span><span class="o">&amp;&amp;</span> <span class="no">result</span><span class="o">)</span> <span class="o">&#123;</span>
-    <span class="nf" data-symbol-name="callUserCallbacks">callUserCallbacks</span><span class="o">(</span><span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="move">move</span><span class="o">(</span><span class="no">result</span><span class="o">),</span> <span class="o">...)</span>
-  <span class="o">&#125;</span>
-<span class="o">);</span></pre></div>
-
-<p>Of course you could achieve the same by calling <tt>fibers::runInMainContext()</tt>, but <tt>addTaskFinally()</tt> reduces the number of fiber context switches:</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([=]()</span> <span class="o">&#123;</span>
-  <span class="o">...</span>
-  <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="runInMainContext">runInMainContext</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
-    <span class="c">// Switched to main context</span>
-    <span class="nf" data-symbol-name="callUserCallbacks">callUserCallbacks</span><span class="o">(</span><span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="move">move</span><span class="o">(</span><span class="no">result</span><span class="o">),</span> <span class="o">...)</span>
-  <span class="o">&#125;</span>
-  <span class="c">// Switched back to fiber context</span>
-
-  <span class="c">// On fiber context we realize there&#039;s no more work to be done.</span>
-  <span class="c">// Fiber-task is complete, switching back to main context.</span>
-<span class="o">&#125;);</span></pre></div>
-
-<p></p>
-
-<h3 id="addtaskfuture-addtaskrem">addTaskFuture() / addTaskRemoteFuture() <a href="#addtaskfuture-addtaskrem" class="headerLink">#</a></h3>
-
-<p><tt>addTask()</tt> and <tt>addTaskRemote()</tt> are creating detached fiber-tasks. If you need to know when fiber-task is complete and/or have some return value for it -  <tt>addTaskFuture()</tt> / <tt>addTaskRemoteFuture()</tt> can be used.</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="EventBase">EventBase</span> <span class="no">evb</span><span class="o">;</span>
-<span class="no">auto</span><span class="o">&amp;</span> <span class="no">fiberManager</span> <span class="o">=</span> <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="getFiberManager">getFiberManager</span><span class="o">(</span><span class="no">evb</span><span class="o">);</span>
-
-<span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="thread">thread</span> <span class="nf" data-symbol-name="t">t</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
-  <span class="no">auto</span> <span class="no">future1</span> <span class="o">=</span> <span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTaskRemoteFuture">addTaskRemoteFuture</span><span class="o">([]()</span> <span class="o">&#123;</span>
-    <span class="o">...</span>
-  <span class="o">&#125;);</span>
-  <span class="no">auto</span> <span class="no">future2</span> <span class="o">=</span> <span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTaskRemoteFuture">addTaskRemoteFuture</span><span class="o">([]()</span> <span class="o">&#123;</span>
-    <span class="o">...</span>
-  <span class="o">&#125;);</span> 
-
-  <span class="no">auto</span> <span class="no">result1</span> <span class="o">=</span> <span class="no">future1</span><span class="o">.</span><span class="nf" data-symbol-name="get">get</span><span class="o">();</span>
-  <span class="no">auto</span> <span class="no">result2</span> <span class="o">=</span> <span class="no">future2</span><span class="o">.</span><span class="nf" data-symbol-name="get">get</span><span class="o">();</span>
-  <span class="o">...</span>
-<span class="o">&#125;);</span>
-
-<span class="no">evb</span><span class="o">.</span><span class="nf" data-symbol-name="loopForever">loopForever</span><span class="o">();</span></pre></div>
-
-<h2 id="other-synchronization-pr">Other synchronization primitives <a href="#other-synchronization-pr" class="headerLink">#</a></h2>
-
-<p>All the listed synchronization primitives are built using <tt>fiber::Baton</tt>. Please check their source code for detailed documentation.</p>
-
-<p><a href="https://github.com/facebook/folly/blob/master/folly/experimental/fibers/Promise.h" target="_blank">await</a></p>
-
-<p><a href="https://github.com/facebook/folly/blob/master/folly/experimental/fibers/WhenN.h" target="_blank">collectN</a></p>
-
-<p><a href="https://github.com/facebook/folly/blob/master/folly/experimental/fibers/WhenN.h" target="_blank">collectAny</a></p>
-
-<p><a href="https://github.com/facebook/folly/blob/master/folly/experimental/fibers/WhenN.h" target="_blank">collectN</a></p>
-
-<p><a href="https://github.com/facebook/folly/blob/master/folly/experimental/fibers/ForEach.h" target="_blank">forEach</a></p>
-
-<p><a href="https://github.com/facebook/folly/blob/master/folly/experimental/fibers/AddTasks.h" target="_blank">addTasks</a></p>
-
-<p><a href="https://github.com/facebook/folly/blob/master/folly/experimental/fibers/TimedMutex.h" target="_blank">TimedMutex</a></p>
-
-<p><a href="https://github.com/facebook/folly/blob/master/folly/experimental/fibers/TimedMutex.h" target="_blank">TimedRWMutex</a></p></section><section class="dex_document"><h1>Fiber stacks</h1><p class="dex_introduction"></p><p>Similarly to system threads, every fiber-task has some stack space assigned to it. Stack usage goes up with the number of nested function calls and objects allocated on the stack. folly::fibers implementation only supports fiber-tasks with fixed stack size. If you want to have many fiber-tasks running concurrently - you need to reduce the amount of stack assigned to each fiber-task, otherwise you may run out of memory.</p>
-
-<h3 id="selecting-stack-size">Selecting stack size <a href="#selecting-stack-size" class="headerLink">#</a></h3>
-
-<p>Stack size used for every fiber-task is part of FiberManager configuration. But how do you pick the right stack size ?</p>
-
-<p>First of all you need to figure out the maximum number of concurrent fiber-tasks your application may have. E.g. if you are writing a Thrift-service you will probably have a single fiber-task for every request in-fly (but remember that e.g. <tt>fibers::collectAll</tt> and some other synchronization primitives may create extra fiber-tasks). It&#039;s very important to get that number first, because if you will at most need 100 concurrent fiber-tasks, even 1MB stacks will result in at most 100MB used for fiber stacks. On the other hand if you need to have 100,000 concurrent fiber-tasks, even 16KB stacks will result in 1.6GB peak memory usage just for fiber stacks.</p>
-
-<p>folly::fibers also supports recording stack usage (it can be enabled via <tt>recordStackEvery</tt> option of <tt>FiberManager</tt>). When enabled, the stack of each fiber-task will be filled with magic values. Later linear search can be performed to find the boundary of unused stack space.</p>
-
-<h3 id="stack-overflow-detection">Stack overflow detection <a href="#stack-overflow-detection" class="headerLink">#</a></h3>
-
-<p>By default every fiber-task stack is allocated with a special guard page next to it (this can be controlled via <tt>useGuardPages</tt> option of <tt>FiberManager</tt>). If a stack overflow happens - this guard page will be accessed, which will result in immediate segmentation fault.</p>
-
-<div class="remarkup-important"><span class="remarkup-note-word">IMPORTANT:</span> disabling guard page protection may result in unnoticed stack overflows. Those will inevitably cause memory corruptions, which are usually very hard to debug.</div></section><section class="dex_document"><h1>Event Loops</h1><p class="dex_introduction"></p><p>folly::fibers library doesn&#039;t implement it&#039;s own event system. Instead it allows <tt>fibers::FiberManager</tt> to work with any other event system by implementing <tt>fibers::LoopController</tt> interface.</p>
-
-<h2 id="folly-eventbase-integrat">folly::EventBase integration <a href="#folly-eventbase-integrat" class="headerLink">#</a></h2>
-
-<p>The easiest way to create a <tt>fibers::FiberManager</tt> attached to a <tt>folly::EventBase</tt> is by using <tt>fibers::getFiberManager</tt> function:</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="EventBase">EventBase</span> <span class="no">evb</span><span class="o">;</span>
-<span class="no">auto</span><span class="o">&amp;</span> <span class="no">fiberManager</span> <span class="o">=</span> <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="getFiberManager">getFiberManager</span><span class="o">(</span><span class="no">evb</span><span class="o">);</span>
-
-<span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
-  <span class="o">...</span>
-<span class="o">&#125;);</span>
-
-<span class="no">evb</span><span class="o">.</span><span class="nf" data-symbol-name="loop">loop</span><span class="o">();</span></pre></div>
-
-<p>Such <tt>fibers::FiberManager</tt> will be automatically destroyed, when <tt>folly::EventBase</tt> is destroyed.</p>
-
-<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> folly::fibers doesn&#039;t support killing fiber-tasks in-flight (for similar reasons you can&#039;t kill a thread). If <tt>fibers::FiberManager</tt> has any outstanding fiber-tasks, when <tt>folly::EventBase</tt> is being destroyed, it will keep running the event loop until all those tasks are finished.</div></section><section class="dex_document"><h1>GDB integration</h1><p class="dex_introduction"></p><p>folly::fibers provide some GDB extensions which can be very useful for debugging. To load them simply the following in dbg console:</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">source</span> <span class="s1">&#039;folly/experimental/fibers/scripts/utils.gdb&#039;</span></pre></div>
-
-<h3 id="show-all-fibermanagers">Show all FiberManagers <a href="#show-all-fibermanagers" class="headerLink">#</a></h3>
-
-<p>You can use <tt>print_folly_fiber_manager_map</tt> to list all <tt>fibers::FiberManager</tt>s and <tt>folly::EventBase</tt>s they are attached to.</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="o">(</span><span class="no">gdb</span><span class="o">)</span> <span class="no">print_folly_fiber_manager_map</span>
-  <span class="k">Global</span> <span class="no">FiberManager</span> <span class="no">map</span> <span class="no">has</span> <span class="mi">2</span> <span class="no">entries</span><span class="o">.</span>
-    <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="EventBase">EventBase</span><span class="o">*)</span><span class="mh">0x7fffffffdb60</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="FiberManager">FiberManager</span><span class="o">*)</span><span class="mh">0x7ffff5b58480</span>
-    <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="EventBase">EventBase</span><span class="o">*)</span><span class="mh">0x7fffffffd930</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="FiberManager">FiberManager</span><span class="o">*)</span><span class="mh">0x7ffff5b58300</span></pre></div>
-
-<p>This will only list <tt>fibers::FiberManager</tt>s created using <tt>fibers::getFiberManager()</tt> function.</p>
-
-<h3 id="print-fibermanager-state">Print FiberManager state <a href="#print-fibermanager-state" class="headerLink">#</a></h3>
-
-<p>You can use <tt>print_folly_fiber_manager</tt> (and passing a pointer to valid <tt>fibers::FiberManager</tt> object) to print the state of given <tt>fibers::FiberManager</tt>.</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="o">(</span><span class="no">gdb</span><span class="o">)</span> <span class="no">print_folly_fiber_manager</span> <span class="o">&amp;</span><span class="nf" data-symbol-name="manager">manager</span>
-  <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="FiberManager">FiberManager</span><span class="o">*)</span><span class="mh">0x7fffffffdbe0</span>
-
-  <span class="no">Fibers</span> <span class="no">active</span><span class="o">:</span> <span class="mi">3</span>
-  <span class="no">Fibers</span> <span class="no">allocated</span><span class="o">:</span> <span class="mi">3</span>
-  <span class="no">Fibers</span> <span class="no">pool</span> <span class="no">size</span><span class="o">:</span> <span class="mi">0</span>
-  <span class="no">Active</span> <span class="no">fiber</span><span class="o">:</span> <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Fiber">Fiber</span><span class="o">*)(</span><span class="no">nil</span><span class="o">)</span>
-  <span class="no">Current</span> <span class="no">fiber</span><span class="o">:</span> <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Fiber">Fiber</span><span class="o">*)(</span><span class="no">nil</span><span class="o">)</span>
-
-  <span class="no">Active</span> <span class="no">fibers</span><span class="o">:</span>
-    <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Fiber">Fiber</span><span class="o">*)</span><span class="mh">0x7ffff5b5b000</span>   <span class="no">State</span><span class="o">:</span> <span class="nf" data-symbol-name="Awaiting">Awaiting</span>
-    <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Fiber">Fiber</span><span class="o">*)</span><span class="mh">0x7ffff5b5b300</span>   <span class="no">State</span><span class="o">:</span> <span class="nf" data-symbol-name="Awaiting">Awaiting</span>
-    <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Fiber">Fiber</span><span class="o">*)</span><span class="mh">0x7ffff5b5b600</span>   <span class="no">State</span><span class="o">:</span> <span class="no">Awaiting</span></pre></div>
-
-<p>It will list all active <tt>fibers::Fiber</tt> objects, which are running fiber-tasks and their states.</p>
-
-<h3 id="print-fiber-task">Print fiber-task <a href="#print-fiber-task" class="headerLink">#</a></h3>
-
-<p>If you have a <tt>fibers::Fiber</tt>, which is running some fiber-task, you can print its state using <tt>print_folly_fiber</tt> command.</p>
-
-<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="o">(</span><span class="no">gdb</span><span class="o">)</span> <span class="no">print_folly_fiber</span> <span class="mh">0x7ffff5b5b600</span>
-  <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Fiber">Fiber</span><span class="o">*)</span><span class="mh">0x7ffff5b5b600</span>
-
-  <span class="no">State</span><span class="o">:</span> <span class="no">Awaiting</span>
-  <span class="no">Backtrace</span><span class="o">:</span>
-    <span class="c">#0 at 0x5a22a8 in folly::fibers::FiberManager::deactivateFiber(folly::fibers::Fiber*) + 194 in section .text of /mnt/fio0/andrii/fbsource/fbcode/buck-out</span>
-<span class="o">/</span><span class="no">gen</span><span class="o">/</span><span class="no">folly</span><span class="o">/</span><span class="no">experimental</span><span class="o">/</span><span class="no">fibers</span><span class="o">/</span><span class="no">fibers</span><span class="o">-</span><span class="no">test</span>
-    <span class="c">#1 at 0x5a1606 in folly::fibers::Fiber::preempt(folly::fibers::Fiber::State)::&#123;lambda()#1&#125;::operator()() + 598 in section .text of /mnt/fio0/andrii/fbsou</span>
-<span class="no">rce</span><span class="o">/</span><span class="no">fbcode</span><span class="o">/</span><span class="no">buck</span><span class="o">-</span><span class="no">out</span><span class="o">/</span><span class="no">gen</span><span class="o">/</span><span class="no">folly</span><span class="o">/</span><span class="no">experimental</span><span class="o">/</span><span class="no">fibers</span><span class="o">/</span><span class="no">fibers</span><span class="o">-</span><span class="no">test</span>
-    <span class="c">#2 at 0x5a17f8 in folly::fibers::Fiber::preempt(folly::fibers::Fiber::State) + 176 in section .text of /mnt/fio0/andrii/fbsource/fbcode/buck-out/gen/foll</span>
-<span class="no">y</span><span class="o">/</span><span class="no">experimental</span><span class="o">/</span><span class="no">fibers</span><span class="o">/</span><span class="no">fibers</span><span class="o">-</span><span class="no">test</span>
-    <span class="c">#3 at 0x43a76e in void folly::fibers::Baton::waitFiber&lt;folly::fibers::FirstArgOf&lt;FiberManager_collectAll_Test::TestBody()::&#123;lambda()#1&#125;::operator()() con</span>
-<span class="nc" data-symbol-name="st">st</span><span class="o">::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda(folly::fibers::Promise&lt;int&gt;)#1&#125;, void&gt;::type::value_type folly::fibers::await</span>
-<span class="o">&lt;</span><span class="nc" data-symbol-name="FiberManager_collectAll_Test">FiberManager_collectAll_Test</span><span class="o">::</span><span class="nf" data-symbol-context="FiberManager_collectAll_Test" data-symbol-name="TestBody">TestBody</span><span class="o">()::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda(foll</span>
-<span class="nc" data-symbol-name="y">y</span><span class="o">::</span><span class="na" data-symbol-context="y" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Promise">Promise</span><span class="o">&lt;</span><span class="no">int</span><span class="o">&gt;)</span><span class="c">#1&#125;&gt;(folly::fibers::Promise&lt;int&gt;&amp;&amp;)::&#123;lambda()#1&#125;&gt;(folly::fibers::FiberManager&amp;, folly::fibers::FirstArgOf&lt;FiberManager_collectAll_Te</span>
-<span class="nc" data-symbol-name="st">st</span><span class="o">::</span><span class="nf" data-symbol-context="st" data-symbol-name="TestBody">TestBody</span><span class="o">()::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda(folly::fibers::Promise&lt;int&gt;)#1&#125;</span>
-<span class="o">,</span> <span class="no">void</span><span class="o">&gt;::</span><span class="nc" data-symbol-name="type">type</span><span class="o">::</span><span class="na" data-symbol-context="type" data-symbol-name="value_type">value_type</span> <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="await">await</span><span class="o">&lt;</span><span class="nc" data-symbol-name="FiberManager_collectAll_Test">FiberManager_collectAll_Test</span><span class="o">::</span><span class="nf" data-symbol-context="FiberManager_collectAll_Test" data-symbol-name="TestBody">TestBody</span><span class="o">()::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::</span>
-<span class="o">&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda(folly::fibers::Promise&lt;int&gt;)#1&#125;&gt;(folly::fibers::Promise&lt;int&gt;&amp;&amp;)::&#123;lambda()#1&#125;) + 110 in section .text of /mnt/fio0/</span>
-<span class="no">andrii</span><span class="o">/</span><span class="no">fbsource</span><span class="o">/</span><span class="no">fbcode</span><span class="o">/</span><span class="no">buck</span><span class="o">-</span><span class="no">out</span><span class="o">/</span><span class="no">gen</span><span class="o">/</span><span class="no">folly</span><span class="o">/</span><span class="no">experimental</span><span class="o">/</span><span class="no">fibers</span><span class="o">/</span><span class="no">fibers</span><span class="o">-</span><span class="no">test</span>
-    <span class="c">#4 at 0x42fa89 in void folly::fibers::Baton::wait&lt;folly::fibers::FirstArgOf&lt;FiberManager_collectAll_Test::TestBody()::&#123;lambda()#1&#125;::operator()() const::&#123;</span>
-<span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda(folly::fibers::Promise&lt;int&gt;)#1&#125;, void&gt;::type::value_type folly::fibers::await&lt;Fibe</span>
-<span class="nc" data-symbol-name="rManager_collectAll_Test">rManager_collectAll_Test</span><span class="o">::</span><span class="nf" data-symbol-context="rManager_collectAll_Test" data-symbol-name="TestBody">TestBody</span><span class="o">()::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda(folly::fi</span>
-<span class="nc" data-symbol-name="bers">bers</span><span class="o">::</span><span class="na" data-symbol-context="bers" data-symbol-name="Promise">Promise</span><span class="o">&lt;</span><span class="no">int</span><span class="o">&gt;)</span><span class="c">#1&#125;&gt;(folly::fibers::Promise&lt;int&gt;&amp;&amp;)::&#123;lambda()#1&#125;&gt;(folly::fibers::FirstArgOf&lt;FiberManager_collectAll_Test::TestBody()::&#123;lambda()#1&#125;::opera</span>
-<span class="nf" data-symbol-name="tor">tor</span><span class="o">()()</span> <span class="k">const</span><span class="o">::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda(folly::fibers::Promise&lt;int&gt;)#1&#125;, void&gt;::type::value_type folly::fi</span>
-<span class="nc" data-symbol-name="bers">bers</span><span class="o">::</span><span class="na" data-symbol-context="bers" data-symbol-name="await">await</span><span class="o">&lt;</span><span class="nc" data-symbol-name="FiberManager_collectAll_Test">FiberManager_collectAll_Test</span><span class="o">::</span><span class="nf" data-symbol-context="FiberManager_collectAll_Test" data-symbol-name="TestBody">TestBody</span><span class="o">()::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;</span>
-<span class="nf" data-symbol-name="lambda">lambda</span><span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Promise">Promise</span><span class="o">&lt;</span><span class="no">int</span><span class="o">&gt;)</span><span class="c">#1&#125;&gt;(folly::fibers::Promise&lt;int&gt;&amp;&amp;)::&#123;lambda()#1&#125;) + 105 in section .text of /mnt/fio0/andrii/fbsource/fbcode/buck-out/gen</span>
-<span class="o">/</span><span class="no">folly</span><span class="o">/</span><span class="no">experimental</span><span class="o">/</span><span class="no">fibers</span><span class="o">/</span><span class="no">fibers</span><span class="o">-</span><span class="no">test</span>
-    <span class="c">#5 at 0x425921 in folly::fibers::FirstArgOf&lt;FiberManager_collectAll_Test::TestBody()::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const:</span>
-<span class="o">:&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda(folly::fibers::Promise&lt;int&gt;)#1&#125;, void&gt;::type::value_type folly::fibers::await&lt;FiberManager_collectAll_Test::TestBo</span>
-<span class="nf" data-symbol-name="dy">dy</span><span class="o">()::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda(folly::fibers::Promise&lt;int&gt;)#1&#125;&gt;(folly::f</span>
-<span class="nc" data-symbol-name="ibers">ibers</span><span class="o">::</span><span class="na" data-symbol-context="ibers" data-symbol-name="Promise">Promise</span><span class="o">&lt;</span><span class="no">int</span><span class="o">&gt;&amp;&amp;)</span> <span class="o">+</span> <span class="mi">80</span> <span class="no">in</span> <span class="no">section</span> <span class="o">.</span><span class="no">text</span> <span class="no">of</span> <span class="o">/</span><span class="no">mnt</span><span class="o">/</span><span class="no">fio0</span><span class="o">/</span><span class="no">andrii</span><span class="o">/</span><span class="no">fbsource</span><span class="o">/</span><span class="no">fbcode</span><span class="o">/</span><span class="no">buck</span><span class="o">-</span><span class="no">out</span><span class="o">/</span><span class="no">gen</span><span class="o">/</span><span class="no">folly</span><span class="o">/</span><span class="no">experimental</span><span class="o">/</span><span class="no">fibers</span><span class="o">/</span><span class="no">fibers</span><span class="o">-</span><span class="no">test</span>
-    <span class="c">#6 at 0x415e9a in FiberManager_collectAll_Test::TestBody()::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()(</span>
-<span class="o">)</span> <span class="k">const</span> <span class="o">+</span> <span class="mi">36</span> <span class="no">in</span> <span class="no">section</span> <span class="o">.</span><span class="no">text</span> <span class="no">of</span> <span class="o">/</span><span class="no">mnt</span><span class="o">/</span><span class="no">fio0</span><span class="o">/</span><span class="no">andrii</span><span class="o">/</span><span class="no">fbsource</span><span class="o">/</span><span class="no">fbcode</span><span class="o">/</span><span class="no">buck</span><span class="o">-</span><span class="no">out</span><span class="o">/</span><span class="no">gen</span><span class="o">/</span><span class="no">folly</span><span class="o">/</span><span class="no">experimental</span><span class="o">/</span><span class="no">fibers</span><span class="o">/</span><span class="no">fibers</span><span class="o">-</span><span class="no">test</span>
-    <span class="c">#7 at 0x42faf9 in std::_Function_handler&lt;int (), FiberManager_collectAll_Test::TestBody()::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() c</span>
-<span class="nc" data-symbol-name="onst">onst</span><span class="o">::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;&gt;::_M_invoke(std::_Any_data const&amp;) + 32 in section .text of /mnt/fio0/andrii/fbsource/fbcode/buck-out/gen/folly/experimental/fibers/fibers</span>
-<span class="o">-</span><span class="no">test</span>
-    <span class="c">#8 at 0x479d5c in std::function&lt;int ()&gt;::operator()() const + 50 in section .text of /mnt/fio0/andrii/fbsource/fbcode/buck-out/gen/folly/experimental/fib</span>
-<span class="no">ers</span><span class="o">/</span><span class="no">fibers</span><span class="o">-</span><span class="no">test</span>
-    <span class="o">...</span></pre></div>
-
-<p>It will print the state of the fiber-task and if fiber-task is currently awaiting - also prints its stack-trace.</p>
-
-<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> For running (i.e. not suspended) fiber-task, you can simply switch to the system thread which owns it and use regular GDB commands for debugging.</div></section></section>
\ No newline at end of file
diff --git a/folly/experimental/fibers/SimpleLoopController.h b/folly/experimental/fibers/SimpleLoopController.h
deleted file mode 100644 (file)
index 94ae9a7..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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 <folly/Likely.h>
-
-#include <folly/experimental/fibers/LoopController.h>
-
-namespace folly {
-namespace fibers {
-
-class FiberManager;
-
-class SimpleLoopController : public LoopController {
- public:
-  SimpleLoopController() : fm_(nullptr), stopRequested_(false) {}
-
-  /**
-   * Run FiberManager loop; if no ready task are present,
-   * run provided function. Stops after both stop() has been called
-   * and no waiting tasks remain.
-   */
-  template <typename F>
-  void loop(F&& func) {
-    bool waiting = false;
-    stopRequested_ = false;
-
-    while (LIKELY(waiting || !stopRequested_)) {
-      func();
-
-      auto time = Clock::now();
-
-      for (size_t i = 0; i < scheduledFuncs_.size(); ++i) {
-        if (scheduledFuncs_[i].first <= time) {
-          scheduledFuncs_[i].second();
-          swap(scheduledFuncs_[i], scheduledFuncs_.back());
-          scheduledFuncs_.pop_back();
-          --i;
-        }
-      }
-
-      if (scheduled_) {
-        scheduled_ = false;
-        waiting = fm_->loopUntilNoReady();
-      }
-    }
-  }
-
-  /**
-   * Requests exit from loop() as soon as all waiting tasks complete.
-   */
-  void stop() {
-    stopRequested_ = true;
-  }
-
-  int remoteScheduleCalled() const {
-    return remoteScheduleCalled_;
-  }
-
-  void schedule() override {
-    scheduled_ = true;
-  }
-
-  void timedSchedule(std::function<void()> func, TimePoint time) override {
-    scheduledFuncs_.emplace_back(time, std::move(func));
-  }
-
- private:
-  FiberManager* fm_;
-  std::atomic<bool> scheduled_{false};
-  bool stopRequested_;
-  std::atomic<int> remoteScheduleCalled_{0};
-  std::vector<std::pair<TimePoint, std::function<void()>>> scheduledFuncs_;
-
-  /* LoopController interface */
-
-  void setFiberManager(FiberManager* fm) override {
-    fm_ = fm;
-  }
-
-  void cancel() override {
-    scheduled_ = false;
-  }
-
-  void scheduleThreadSafe(std::function<bool()> func) override {
-    if (func()) {
-      ++remoteScheduleCalled_;
-      scheduled_ = true;
-    }
-  }
-
-  friend class FiberManager;
-};
-}
-} // folly::fibers
diff --git a/folly/experimental/fibers/TimedMutex-inl.h b/folly/experimental/fibers/TimedMutex-inl.h
deleted file mode 100644 (file)
index 96ee606..0000000
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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
-
-namespace folly {
-namespace fibers {
-
-//
-// TimedMutex implementation
-//
-
-template <typename BatonType>
-void TimedMutex<BatonType>::lock() {
-  pthread_spin_lock(&lock_);
-  if (!locked_) {
-    locked_ = true;
-    pthread_spin_unlock(&lock_);
-    return;
-  }
-
-  // Delay constructing the waiter until it is actually required.
-  // This makes a huge difference, at least in the benchmarks,
-  // when the mutex isn't locked.
-  MutexWaiter waiter;
-  waiters_.push_back(waiter);
-  pthread_spin_unlock(&lock_);
-  waiter.baton.wait();
-}
-
-template <typename BatonType>
-template <typename Rep, typename Period>
-bool TimedMutex<BatonType>::timed_lock(
-    const std::chrono::duration<Rep, Period>& duration) {
-  pthread_spin_lock(&lock_);
-  if (!locked_) {
-    locked_ = true;
-    pthread_spin_unlock(&lock_);
-    return true;
-  }
-
-  MutexWaiter waiter;
-  waiters_.push_back(waiter);
-  pthread_spin_unlock(&lock_);
-
-  if (!waiter.baton.timed_wait(duration)) {
-    // We timed out. Two cases:
-    // 1. We're still in the waiter list and we truly timed out
-    // 2. We're not in the waiter list anymore. This could happen if the baton
-    //    times out but the mutex is unlocked before we reach this code. In this
-    //    case we'll pretend we got the lock on time.
-    pthread_spin_lock(&lock_);
-    if (waiter.hook.is_linked()) {
-      waiters_.erase(waiters_.iterator_to(waiter));
-      pthread_spin_unlock(&lock_);
-      return false;
-    }
-    pthread_spin_unlock(&lock_);
-  }
-  return true;
-}
-
-template <typename BatonType>
-bool TimedMutex<BatonType>::try_lock() {
-  pthread_spin_lock(&lock_);
-  if (locked_) {
-    pthread_spin_unlock(&lock_);
-    return false;
-  }
-  locked_ = true;
-  pthread_spin_unlock(&lock_);
-  return true;
-}
-
-template <typename BatonType>
-void TimedMutex<BatonType>::unlock() {
-  pthread_spin_lock(&lock_);
-  if (waiters_.empty()) {
-    locked_ = false;
-    pthread_spin_unlock(&lock_);
-    return;
-  }
-  MutexWaiter& to_wake = waiters_.front();
-  waiters_.pop_front();
-  to_wake.baton.post();
-  pthread_spin_unlock(&lock_);
-}
-
-//
-// TimedRWMutex implementation
-//
-
-template <typename BatonType>
-void TimedRWMutex<BatonType>::read_lock() {
-  pthread_spin_lock(&lock_);
-  if (state_ == State::WRITE_LOCKED) {
-    MutexWaiter waiter;
-    read_waiters_.push_back(waiter);
-    pthread_spin_unlock(&lock_);
-    waiter.baton.wait();
-    assert(state_ == State::READ_LOCKED);
-    return;
-  }
-  assert(
-      (state_ == State::UNLOCKED && readers_ == 0) ||
-      (state_ == State::READ_LOCKED && readers_ > 0));
-  assert(read_waiters_.empty());
-  state_ = State::READ_LOCKED;
-  readers_ += 1;
-  pthread_spin_unlock(&lock_);
-}
-
-template <typename BatonType>
-template <typename Rep, typename Period>
-bool TimedRWMutex<BatonType>::timed_read_lock(
-    const std::chrono::duration<Rep, Period>& duration) {
-  pthread_spin_lock(&lock_);
-  if (state_ == State::WRITE_LOCKED) {
-    MutexWaiter waiter;
-    read_waiters_.push_back(waiter);
-    pthread_spin_unlock(&lock_);
-
-    if (!waiter.baton.timed_wait(duration)) {
-      // We timed out. Two cases:
-      // 1. We're still in the waiter list and we truly timed out
-      // 2. We're not in the waiter list anymore. This could happen if the baton
-      //    times out but the mutex is unlocked before we reach this code. In
-      //    this case we'll pretend we got the lock on time.
-      pthread_spin_lock(&lock_);
-      if (waiter.hook.is_linked()) {
-        read_waiters_.erase(read_waiters_.iterator_to(waiter));
-        pthread_spin_unlock(&lock_);
-        return false;
-      }
-      pthread_spin_unlock(&lock_);
-    }
-    return true;
-  }
-  assert(
-      (state_ == State::UNLOCKED && readers_ == 0) ||
-      (state_ == State::READ_LOCKED && readers_ > 0));
-  assert(read_waiters_.empty());
-  state_ = State::READ_LOCKED;
-  readers_ += 1;
-  pthread_spin_unlock(&lock_);
-  return true;
-}
-
-template <typename BatonType>
-bool TimedRWMutex<BatonType>::try_read_lock() {
-  pthread_spin_lock(&lock_);
-  if (state_ != State::WRITE_LOCKED) {
-    assert(
-        (state_ == State::UNLOCKED && readers_ == 0) ||
-        (state_ == State::READ_LOCKED && readers_ > 0));
-    assert(read_waiters_.empty());
-    state_ = State::READ_LOCKED;
-    readers_ += 1;
-    pthread_spin_unlock(&lock_);
-    return true;
-  }
-  pthread_spin_unlock(&lock_);
-  return false;
-}
-
-template <typename BatonType>
-void TimedRWMutex<BatonType>::write_lock() {
-  pthread_spin_lock(&lock_);
-  if (state_ == State::UNLOCKED) {
-    verify_unlocked_properties();
-    state_ = State::WRITE_LOCKED;
-    pthread_spin_unlock(&lock_);
-    return;
-  }
-  MutexWaiter waiter;
-  write_waiters_.push_back(waiter);
-  pthread_spin_unlock(&lock_);
-  waiter.baton.wait();
-}
-
-template <typename BatonType>
-template <typename Rep, typename Period>
-bool TimedRWMutex<BatonType>::timed_write_lock(
-    const std::chrono::duration<Rep, Period>& duration) {
-  pthread_spin_lock(&lock_);
-  if (state_ == State::UNLOCKED) {
-    verify_unlocked_properties();
-    state_ = State::WRITE_LOCKED;
-    pthread_spin_unlock(&lock_);
-    return true;
-  }
-  MutexWaiter waiter;
-  write_waiters_.push_back(waiter);
-  pthread_spin_unlock(&lock_);
-
-  if (!waiter.baton.timed_wait(duration)) {
-    // We timed out. Two cases:
-    // 1. We're still in the waiter list and we truly timed out
-    // 2. We're not in the waiter list anymore. This could happen if the baton
-    //    times out but the mutex is unlocked before we reach this code. In
-    //    this case we'll pretend we got the lock on time.
-    pthread_spin_lock(&lock_);
-    if (waiter.hook.is_linked()) {
-      write_waiters_.erase(write_waiters_.iterator_to(waiter));
-      pthread_spin_unlock(&lock_);
-      return false;
-    }
-    pthread_spin_unlock(&lock_);
-  }
-  assert(state_ == State::WRITE_LOCKED);
-  return true;
-}
-
-template <typename BatonType>
-bool TimedRWMutex<BatonType>::try_write_lock() {
-  pthread_spin_lock(&lock_);
-  if (state_ == State::UNLOCKED) {
-    verify_unlocked_properties();
-    state_ = State::WRITE_LOCKED;
-    pthread_spin_unlock(&lock_);
-    return true;
-  }
-  pthread_spin_unlock(&lock_);
-  return false;
-}
-
-template <typename BatonType>
-void TimedRWMutex<BatonType>::unlock() {
-  pthread_spin_lock(&lock_);
-  assert(state_ != State::UNLOCKED);
-  assert(
-      (state_ == State::READ_LOCKED && readers_ > 0) ||
-      (state_ == State::WRITE_LOCKED && readers_ == 0));
-  if (state_ == State::READ_LOCKED) {
-    readers_ -= 1;
-  }
-
-  if (!read_waiters_.empty()) {
-    assert(
-        state_ == State::WRITE_LOCKED && readers_ == 0 &&
-        "read waiters can only accumulate while write locked");
-    state_ = State::READ_LOCKED;
-    readers_ = read_waiters_.size();
-
-    while (!read_waiters_.empty()) {
-      MutexWaiter& to_wake = read_waiters_.front();
-      read_waiters_.pop_front();
-      to_wake.baton.post();
-    }
-  } else if (readers_ == 0) {
-    if (!write_waiters_.empty()) {
-      assert(read_waiters_.empty());
-      state_ = State::WRITE_LOCKED;
-
-      // Wake a single writer (after releasing the spin lock)
-      MutexWaiter& to_wake = write_waiters_.front();
-      write_waiters_.pop_front();
-      to_wake.baton.post();
-    } else {
-      verify_unlocked_properties();
-      state_ = State::UNLOCKED;
-    }
-  } else {
-    assert(state_ == State::READ_LOCKED);
-  }
-  pthread_spin_unlock(&lock_);
-}
-
-template <typename BatonType>
-void TimedRWMutex<BatonType>::downgrade() {
-  pthread_spin_lock(&lock_);
-  assert(state_ == State::WRITE_LOCKED && readers_ == 0);
-  state_ = State::READ_LOCKED;
-  readers_ += 1;
-
-  if (!read_waiters_.empty()) {
-    readers_ += read_waiters_.size();
-
-    while (!read_waiters_.empty()) {
-      MutexWaiter& to_wake = read_waiters_.front();
-      read_waiters_.pop_front();
-      to_wake.baton.post();
-    }
-  }
-  pthread_spin_unlock(&lock_);
-}
-}
-}
diff --git a/folly/experimental/fibers/TimedMutex.h b/folly/experimental/fibers/TimedMutex.h
deleted file mode 100644 (file)
index a697b2f..0000000
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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 <pthread.h>
-
-#include <folly/experimental/fibers/GenericBaton.h>
-
-namespace folly {
-namespace fibers {
-
-/**
- * @class TimedMutex
- *
- * Like mutex but allows timed_lock in addition to lock and try_lock.
- **/
-template <typename BatonType>
-class TimedMutex {
- public:
-  TimedMutex() {
-    pthread_spin_init(&lock_, PTHREAD_PROCESS_PRIVATE);
-  }
-
-  ~TimedMutex() {
-    pthread_spin_destroy(&lock_);
-  }
-
-  TimedMutex(const TimedMutex& rhs) = delete;
-  TimedMutex& operator=(const TimedMutex& rhs) = delete;
-  TimedMutex(TimedMutex&& rhs) = delete;
-  TimedMutex& operator=(TimedMutex&& rhs) = delete;
-
-  // Lock the mutex. The thread / fiber is blocked until the mutex is free
-  void lock();
-
-  // Lock the mutex. The thread / fiber will be blocked for a time duration.
-  //
-  // @return        true if the mutex was locked, false otherwise
-  template <typename Rep, typename Period>
-  bool timed_lock(const std::chrono::duration<Rep, Period>& duration);
-
-  // Try to obtain lock without blocking the thread or fiber
-  bool try_lock();
-
-  // Unlock the mutex and wake up a waiter if there is one
-  void unlock();
-
- private:
-  typedef boost::intrusive::list_member_hook<> MutexWaiterHookType;
-
-  // represents a waiter waiting for the lock. The waiter waits on the
-  // baton until it is woken up by a post or timeout expires.
-  struct MutexWaiter {
-    BatonType baton;
-    MutexWaiterHookType hook;
-  };
-
-  typedef boost::intrusive::
-      member_hook<MutexWaiter, MutexWaiterHookType, &MutexWaiter::hook>
-          MutexWaiterHook;
-
-  typedef boost::intrusive::list<
-      MutexWaiter,
-      MutexWaiterHook,
-      boost::intrusive::constant_time_size<true>>
-      MutexWaiterList;
-
-  pthread_spinlock_t lock_; //< lock to protect waiter list
-  bool locked_ = false; //< is this locked by some thread?
-  MutexWaiterList waiters_; //< list of waiters
-};
-
-/**
- * @class TimedRWMutex
- *
- * A readers-writer lock which allows multiple readers to hold the
- * lock simultaneously or only one writer.
- *
- * NOTE: This is a reader-preferred RWLock i.e. readers are give priority
- * when there are both readers and writers waiting to get the lock.
- **/
-template <typename BatonType>
-class TimedRWMutex {
- public:
-  TimedRWMutex() {
-    pthread_spin_init(&lock_, PTHREAD_PROCESS_PRIVATE);
-  }
-
-  ~TimedRWMutex() {
-    pthread_spin_destroy(&lock_);
-  }
-
-  TimedRWMutex(const TimedRWMutex& rhs) = delete;
-  TimedRWMutex& operator=(const TimedRWMutex& rhs) = delete;
-  TimedRWMutex(TimedRWMutex&& rhs) = delete;
-  TimedRWMutex& operator=(TimedRWMutex&& rhs) = delete;
-
-  // Lock for shared access. The thread / fiber is blocked until the lock
-  // can be acquired.
-  void read_lock();
-
-  // Like read_lock except the thread /fiber is blocked for a time duration
-  // @return        true if locked successfully, false otherwise.
-  template <typename Rep, typename Period>
-  bool timed_read_lock(const std::chrono::duration<Rep, Period>& duration);
-
-  // Like read_lock but doesn't block the thread / fiber if the lock can't
-  // be acquired.
-  // @return        true if lock was acquired, false otherwise.
-  bool try_read_lock();
-
-  // Obtain an exclusive lock. The thread / fiber is blocked until the lock
-  // is available.
-  void write_lock();
-
-  // Like write_lock except the thread / fiber is blocked for a time duration
-  // @return        true if locked successfully, false otherwise.
-  template <typename Rep, typename Period>
-  bool timed_write_lock(const std::chrono::duration<Rep, Period>& duration);
-
-  // Like write_lock but doesn't block the thread / fiber if the lock cant be
-  // obtained.
-  // @return        true if lock was acquired, false otherwise.
-  bool try_write_lock();
-
-  // Wrapper for write_lock() for compatibility with Mutex
-  void lock() {
-    write_lock();
-  }
-
-  // Realease the lock. The thread / fiber will wake up all readers if there are
-  // any. If there are waiting writers then only one of them will be woken up.
-  // NOTE: readers are given priority over writers (see above comment)
-  void unlock();
-
-  // Downgrade the lock. The thread / fiber will wake up all readers if there
-  // are any.
-  void downgrade();
-
-  class ReadHolder {
-   public:
-    explicit ReadHolder(TimedRWMutex& lock) : lock_(&lock) {
-      lock_->read_lock();
-    }
-
-    ~ReadHolder() {
-      if (lock_) {
-        lock_->unlock();
-      }
-    }
-
-    ReadHolder(const ReadHolder& rhs) = delete;
-    ReadHolder& operator=(const ReadHolder& rhs) = delete;
-    ReadHolder(ReadHolder&& rhs) = delete;
-    ReadHolder& operator=(ReadHolder&& rhs) = delete;
-
-   private:
-    TimedRWMutex* lock_;
-  };
-
-  class WriteHolder {
-   public:
-    explicit WriteHolder(TimedRWMutex& lock) : lock_(&lock) {
-      lock_->write_lock();
-    }
-
-    ~WriteHolder() {
-      if (lock_) {
-        lock_->unlock();
-      }
-    }
-
-    WriteHolder(const WriteHolder& rhs) = delete;
-    WriteHolder& operator=(const WriteHolder& rhs) = delete;
-    WriteHolder(WriteHolder&& rhs) = delete;
-    WriteHolder& operator=(WriteHolder&& rhs) = delete;
-
-   private:
-    TimedRWMutex* lock_;
-  };
-
- private:
-  // invariants that must hold when the lock is not held by anyone
-  void verify_unlocked_properties() {
-    assert(readers_ == 0);
-    assert(read_waiters_.empty());
-    assert(write_waiters_.empty());
-  }
-
-  // Different states the lock can be in
-  enum class State {
-    UNLOCKED,
-    READ_LOCKED,
-    WRITE_LOCKED,
-  };
-
-  typedef boost::intrusive::list_member_hook<> MutexWaiterHookType;
-
-  // represents a waiter waiting for the lock.
-  struct MutexWaiter {
-    BatonType baton;
-    MutexWaiterHookType hook;
-  };
-
-  typedef boost::intrusive::
-      member_hook<MutexWaiter, MutexWaiterHookType, &MutexWaiter::hook>
-          MutexWaiterHook;
-
-  typedef boost::intrusive::list<
-      MutexWaiter,
-      MutexWaiterHook,
-      boost::intrusive::constant_time_size<true>>
-      MutexWaiterList;
-
-  pthread_spinlock_t lock_; //< lock protecting the internal state
-  // (state_, read_waiters_, etc.)
-  State state_ = State::UNLOCKED;
-
-  uint32_t readers_ = 0; //< Number of readers who have the lock
-
-  MutexWaiterList write_waiters_; //< List of thread / fibers waiting for
-  //  exclusive access
-
-  MutexWaiterList read_waiters_; //< List of thread / fibers waiting for
-  //  shared access
-};
-}
-}
-
-#include "TimedMutex-inl.h"
diff --git a/folly/experimental/fibers/TimeoutController.cpp b/folly/experimental/fibers/TimeoutController.cpp
deleted file mode 100644 (file)
index 60360e6..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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.
- */
-#include "TimeoutController.h"
-#include <folly/Memory.h>
-
-namespace folly {
-namespace fibers {
-
-TimeoutController::TimeoutController(LoopController& loopController)
-    : nextTimeout_(TimePoint::max()), loopController_(loopController) {}
-
-intptr_t TimeoutController::registerTimeout(
-    std::function<void()> f,
-    Duration duration) {
-  auto& list = [&]() -> TimeoutHandleList& {
-    for (auto& bucket : timeoutHandleBuckets_) {
-      if (bucket.first == duration) {
-        return *bucket.second;
-      }
-    }
-
-    timeoutHandleBuckets_.emplace_back(
-        duration, folly::make_unique<TimeoutHandleList>());
-    return *timeoutHandleBuckets_.back().second;
-  }();
-
-  auto timeout = Clock::now() + duration;
-  list.emplace(std::move(f), timeout, list);
-
-  if (timeout < nextTimeout_) {
-    nextTimeout_ = timeout;
-    scheduleRun();
-  }
-
-  return reinterpret_cast<intptr_t>(&list.back());
-}
-
-void TimeoutController::runTimeouts(TimePoint time) {
-  auto now = Clock::now();
-  // Make sure we don't skip some events if function was run before actual time.
-  if (time < now) {
-    time = now;
-  }
-  if (nextTimeout_ > time) {
-    return;
-  }
-
-  nextTimeout_ = TimePoint::max();
-
-  for (auto& bucket : timeoutHandleBuckets_) {
-    auto& list = *bucket.second;
-
-    while (!list.empty()) {
-      if (!list.front().canceled) {
-        if (list.front().timeout > time) {
-          nextTimeout_ = std::min(nextTimeout_, list.front().timeout);
-          break;
-        }
-
-        list.front().func();
-      }
-      list.pop();
-    }
-  }
-
-  if (nextTimeout_ != TimePoint::max()) {
-    scheduleRun();
-  }
-}
-
-void TimeoutController::scheduleRun() {
-  auto time = nextTimeout_;
-  std::weak_ptr<TimeoutController> timeoutControllerWeak = shared_from_this();
-
-  loopController_.timedSchedule(
-      [timeoutControllerWeak, time]() {
-        if (auto timeoutController = timeoutControllerWeak.lock()) {
-          timeoutController->runTimeouts(time);
-        }
-      },
-      time);
-}
-
-void TimeoutController::cancel(intptr_t p) {
-  auto handle = reinterpret_cast<TimeoutHandle*>(p);
-  handle->canceled = true;
-
-  auto& list = handle->list;
-
-  while (!list.empty() && list.front().canceled) {
-    list.pop();
-  }
-}
-}
-}
diff --git a/folly/experimental/fibers/TimeoutController.h b/folly/experimental/fibers/TimeoutController.h
deleted file mode 100644 (file)
index 0f2dced..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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 <chrono>
-#include <functional>
-#include <memory>
-#include <queue>
-
-#include <boost/intrusive/list.hpp>
-
-#include <folly/Likely.h>
-
-#include <folly/experimental/fibers/LoopController.h>
-
-namespace folly {
-namespace fibers {
-
-class TimeoutController
-    : public std::enable_shared_from_this<TimeoutController> {
- public:
-  typedef std::chrono::steady_clock Clock;
-  typedef std::chrono::time_point<Clock> TimePoint;
-  typedef Clock::duration Duration;
-
-  explicit TimeoutController(LoopController& loopController);
-
-  intptr_t registerTimeout(std::function<void()> f, Duration duration);
-  void cancel(intptr_t id);
-
-  void runTimeouts(TimePoint time);
-
- private:
-  void scheduleRun();
-
-  struct TimeoutHandle;
-  typedef std::queue<TimeoutHandle> TimeoutHandleList;
-  typedef std::unique_ptr<TimeoutHandleList> TimeoutHandleListPtr;
-
-  struct TimeoutHandle {
-    TimeoutHandle(
-        std::function<void()> func_,
-        TimePoint timeout_,
-        TimeoutHandleList& list_)
-        : func(std::move(func_)), timeout(timeout_), list(list_) {}
-
-    std::function<void()> func;
-    bool canceled{false};
-    TimePoint timeout;
-    TimeoutHandleList& list;
-  };
-
-  std::vector<std::pair<Duration, TimeoutHandleListPtr>> timeoutHandleBuckets_;
-  TimePoint nextTimeout_;
-  LoopController& loopController_;
-};
-}
-}
diff --git a/folly/experimental/fibers/WhenN-inl.h b/folly/experimental/fibers/WhenN-inl.h
deleted file mode 100644 (file)
index 6bb2804..0000000
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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.
- */
-#include <folly/Optional.h>
-
-#include <folly/experimental/fibers/FiberManager.h>
-#include <folly/experimental/fibers/ForEach.h>
-
-namespace folly {
-namespace fibers {
-
-template <class InputIterator>
-typename std::vector<typename std::enable_if<
-    !std::is_same<
-        typename std::result_of<
-            typename std::iterator_traits<InputIterator>::value_type()>::type,
-        void>::value,
-    typename std::pair<
-        size_t,
-        typename std::result_of<typename std::iterator_traits<
-            InputIterator>::value_type()>::type>>::type>
-collectN(InputIterator first, InputIterator last, size_t n) {
-  typedef typename std::result_of<
-      typename std::iterator_traits<InputIterator>::value_type()>::type Result;
-  assert(n > 0);
-  assert(std::distance(first, last) >= 0);
-  assert(n <= static_cast<size_t>(std::distance(first, last)));
-
-  struct Context {
-    std::vector<std::pair<size_t, Result>> results;
-    size_t tasksTodo;
-    std::exception_ptr e;
-    folly::Optional<Promise<void>> promise;
-
-    Context(size_t tasksTodo_) : tasksTodo(tasksTodo_) {
-      this->results.reserve(tasksTodo_);
-    }
-  };
-  auto context = std::make_shared<Context>(n);
-
-  await([first, last, context](Promise<void> promise) mutable {
-    context->promise = std::move(promise);
-    for (size_t i = 0; first != last; ++i, ++first) {
-#ifdef __clang__
-#pragma clang diagnostic push // ignore generalized lambda capture warning
-#pragma clang diagnostic ignored "-Wc++1y-extensions"
-#endif
-      addTask([ i, context, f = std::move(*first) ]() {
-        try {
-          auto result = f();
-          if (context->tasksTodo == 0) {
-            return;
-          }
-          context->results.emplace_back(i, std::move(result));
-        } catch (...) {
-          if (context->tasksTodo == 0) {
-            return;
-          }
-          context->e = std::current_exception();
-        }
-        if (--context->tasksTodo == 0) {
-          context->promise->setValue();
-        }
-      });
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-    }
-  });
-
-  if (context->e != std::exception_ptr()) {
-    std::rethrow_exception(context->e);
-  }
-
-  return std::move(context->results);
-}
-
-template <class InputIterator>
-typename std::enable_if<
-    std::is_same<
-        typename std::result_of<
-            typename std::iterator_traits<InputIterator>::value_type()>::type,
-        void>::value,
-    std::vector<size_t>>::type
-collectN(InputIterator first, InputIterator last, size_t n) {
-  assert(n > 0);
-  assert(std::distance(first, last) >= 0);
-  assert(n <= static_cast<size_t>(std::distance(first, last)));
-
-  struct Context {
-    std::vector<size_t> taskIndices;
-    std::exception_ptr e;
-    size_t tasksTodo;
-    folly::Optional<Promise<void>> promise;
-
-    Context(size_t tasksTodo_) : tasksTodo(tasksTodo_) {
-      this->taskIndices.reserve(tasksTodo_);
-    }
-  };
-  auto context = std::make_shared<Context>(n);
-
-  await([first, last, context](Promise<void> promise) mutable {
-    context->promise = std::move(promise);
-    for (size_t i = 0; first != last; ++i, ++first) {
-#ifdef __clang__
-#pragma clang diagnostic push // ignore generalized lambda capture warning
-#pragma clang diagnostic ignored "-Wc++1y-extensions"
-#endif
-      addTask([ i, context, f = std::move(*first) ]() {
-        try {
-          f();
-          if (context->tasksTodo == 0) {
-            return;
-          }
-          context->taskIndices.push_back(i);
-        } catch (...) {
-          if (context->tasksTodo == 0) {
-            return;
-          }
-          context->e = std::current_exception();
-        }
-        if (--context->tasksTodo == 0) {
-          context->promise->setValue();
-        }
-      });
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-    }
-  });
-
-  if (context->e != std::exception_ptr()) {
-    std::rethrow_exception(context->e);
-  }
-
-  return context->taskIndices;
-}
-
-template <class InputIterator>
-typename std::vector<
-    typename std::enable_if<
-        !std::is_same<
-            typename std::result_of<typename std::iterator_traits<
-                InputIterator>::value_type()>::type,
-            void>::value,
-        typename std::result_of<
-            typename std::iterator_traits<InputIterator>::value_type()>::type>::
-        type> inline collectAll(InputIterator first, InputIterator last) {
-  typedef typename std::result_of<
-      typename std::iterator_traits<InputIterator>::value_type()>::type Result;
-  size_t n = std::distance(first, last);
-  std::vector<Result> results;
-  std::vector<size_t> order(n);
-  results.reserve(n);
-
-  forEach(first, last, [&results, &order](size_t id, Result result) {
-    order[id] = results.size();
-    results.emplace_back(std::move(result));
-  });
-  assert(results.size() == n);
-
-  std::vector<Result> orderedResults;
-  orderedResults.reserve(n);
-
-  for (size_t i = 0; i < n; ++i) {
-    orderedResults.emplace_back(std::move(results[order[i]]));
-  }
-
-  return orderedResults;
-}
-
-template <class InputIterator>
-typename std::enable_if<
-    std::is_same<
-        typename std::result_of<
-            typename std::iterator_traits<InputIterator>::value_type()>::type,
-        void>::value,
-    void>::type inline collectAll(InputIterator first, InputIterator last) {
-  forEach(first, last, [](size_t /* id */) {});
-}
-
-template <class InputIterator>
-typename std::enable_if<
-    !std::is_same<
-        typename std::result_of<
-            typename std::iterator_traits<InputIterator>::value_type()>::type,
-        void>::value,
-    typename std::pair<
-        size_t,
-        typename std::result_of<typename std::iterator_traits<
-            InputIterator>::value_type()>::type>>::
-    type inline collectAny(InputIterator first, InputIterator last) {
-  auto result = collectN(first, last, 1);
-  assert(result.size() == 1);
-  return std::move(result[0]);
-}
-
-template <class InputIterator>
-typename std::enable_if<
-    std::is_same<
-        typename std::result_of<
-            typename std::iterator_traits<InputIterator>::value_type()>::type,
-        void>::value,
-    size_t>::type inline collectAny(InputIterator first, InputIterator last) {
-  auto result = collectN(first, last, 1);
-  assert(result.size() == 1);
-  return std::move(result[0]);
-}
-}
-}
diff --git a/folly/experimental/fibers/WhenN.h b/folly/experimental/fibers/WhenN.h
deleted file mode 100644 (file)
index a12e12f..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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
-
-namespace folly {
-namespace fibers {
-
-/**
- * Schedules several tasks and blocks until n of these tasks are completed.
- * If any of these n tasks throws an exception, this exception will be
- * re-thrown, but only when n tasks are complete. If several tasks throw
- * exceptions one of them will be re-thrown.
- *
- * @param first Range of tasks to be scheduled
- * @param last
- * @param n Number of tasks to wait for
- *
- * @return vector of pairs (task index, return value of task)
- */
-template <class InputIterator>
-typename std::vector<
-    typename std::enable_if<
-        !std::is_same<
-            typename std::result_of<typename std::iterator_traits<
-                InputIterator>::value_type()>::type,
-            void>::value,
-        typename std::pair<
-            size_t,
-            typename std::result_of<typename std::iterator_traits<
-                InputIterator>::value_type()>::type>>::
-        type> inline collectN(InputIterator first, InputIterator last, size_t n);
-
-/**
- * collectN specialization for functions returning void
- *
- * @param first Range of tasks to be scheduled
- * @param last
- * @param n Number of tasks to wait for
- *
- * @return vector of completed task indices
- */
-template <class InputIterator>
-typename std::enable_if<
-    std::is_same<
-        typename std::result_of<
-            typename std::iterator_traits<InputIterator>::value_type()>::type,
-        void>::value,
-    std::vector<size_t>>::
-    type inline collectN(InputIterator first, InputIterator last, size_t n);
-
-/**
- * Schedules several tasks and blocks until all of these tasks are completed.
- * If any of the tasks throws an exception, this exception will be re-thrown,
- * but only when all the tasks are complete. If several tasks throw exceptions
- * one of them will be re-thrown.
- *
- * @param first Range of tasks to be scheduled
- * @param last
- *
- * @return vector of values returned by tasks
- */
-template <class InputIterator>
-typename std::vector<typename std::enable_if<
-    !std::is_same<
-        typename std::result_of<
-            typename std::iterator_traits<InputIterator>::value_type()>::type,
-        void>::value,
-    typename std::result_of<
-        typename std::iterator_traits<InputIterator>::value_type()>::
-        type>::type> inline collectAll(InputIterator first, InputIterator last);
-
-/**
- * collectAll specialization for functions returning void
- *
- * @param first Range of tasks to be scheduled
- * @param last
- */
-template <class InputIterator>
-typename std::enable_if<
-    std::is_same<
-        typename std::result_of<
-            typename std::iterator_traits<InputIterator>::value_type()>::type,
-        void>::value,
-    void>::type inline collectAll(InputIterator first, InputIterator last);
-
-/**
- * Schedules several tasks and blocks until one of them is completed.
- * If this task throws an exception, this exception will be re-thrown.
- * Exceptions thrown by all other tasks will be ignored.
- *
- * @param first Range of tasks to be scheduled
- * @param last
- *
- * @return pair of index of the first completed task and its return value
- */
-template <class InputIterator>
-typename std::enable_if<
-    !std::is_same<
-        typename std::result_of<
-            typename std::iterator_traits<InputIterator>::value_type()>::type,
-        void>::value,
-    typename std::pair<
-        size_t,
-        typename std::result_of<typename std::iterator_traits<
-            InputIterator>::value_type()>::type>>::
-    type inline collectAny(InputIterator first, InputIterator last);
-
-/**
- * WhenAny specialization for functions returning void.
- *
- * @param first Range of tasks to be scheduled
- * @param last
- *
- * @return index of the first completed task
- */
-template <class InputIterator>
-typename std::enable_if<
-    std::is_same<
-        typename std::result_of<
-            typename std::iterator_traits<InputIterator>::value_type()>::type,
-        void>::value,
-    size_t>::type inline collectAny(InputIterator first, InputIterator last);
-}
-}
-
-#include <folly/experimental/fibers/WhenN-inl.h>
diff --git a/folly/experimental/fibers/scripts/utils.gdb b/folly/experimental/fibers/scripts/utils.gdb
deleted file mode 100644 (file)
index ada27fe..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-# Print given Fiber state
-# arg0 folly::fibers::Fiber*
-define print_folly_fiber_state
-  set $fiber = (folly::fibers::Fiber*)$arg0
-  if $fiber->state_ == folly::fibers::Fiber::INVALID
-    printf "Invalid"
-  end
-  if $fiber->state_ == folly::fibers::Fiber::NOT_STARTED
-    printf "Not started"
-  end
-  if $fiber->state_ == folly::fibers::Fiber::READY_TO_RUN
-    printf "Ready to run"
-  end
-  if $fiber->state_ == folly::fibers::Fiber::RUNNING
-    printf "Running"
-  end
-  if $fiber->state_ == folly::fibers::Fiber::AWAITING
-    printf "Awaiting"
-  end
-  if $fiber->state_ == folly::fibers::Fiber::AWAITING_IMMEDIATE
-    printf "Awaiting immediate"
-  end
-  if $fiber->state_ == folly::fibers::Fiber::YIELDED
-    printf "Yielded"
-  end
-end
-
-# Print given Fiber
-# arg0 folly::fibers::Fiber*
-define print_folly_fiber
-  set $fiber = (folly::fibers::Fiber*)$arg0
-  printf "  (folly::fibers::Fiber*)%p\n\n", $fiber
-
-  printf "  State: "
-  print_folly_fiber_state $fiber
-  printf "\n"
-
-  if $fiber->state_ != folly::fibers::Fiber::INVALID && \
-     $fiber->state_ != folly::fibers::Fiber::NOT_STARTED && \
-     $fiber->state_ != folly::fibers::Fiber::RUNNING
-    printf "  Backtrace:\n"
-    set $frameptr = ((uint64_t*)$fiber->fcontext_.context_)[6]
-    set $k = 0
-    while $frameptr != 0
-      printf "    #%d at %p in ", $k, *((void**)($frameptr+8))
-      set $k = $k + 1
-      info symbol *((void**)($frameptr+8))
-      set $frameptr = *((void**)($frameptr))
-    end
-  end
-end
-
-# Print given FiberManager
-# arg0 folly::fibers::FiberManager*
-define print_folly_fiber_manager
-  set $fiberManager = (folly::fibers::FiberManager*)$arg0
-
-  printf "  (folly::fibers::FiberManager*)%p\n\n", $fiberManager
-  printf "  Fibers active: %d\n", $fiberManager->fibersActive_
-  printf "  Fibers allocated: %d\n", $fiberManager->fibersAllocated_
-  printf "  Fibers pool size: %d\n", $fiberManager->fibersPoolSize_
-  printf "  Active fiber: (folly::fibers::Fiber*)%p\n", \
-         $fiberManager->activeFiber_
-  printf "  Current fiber: (folly::fibers::Fiber*)%p\n", \
-         $fiberManager->currentFiber_
-
-  set $all_fibers = &($fiberManager->allFibers_.data_.root_plus_size_.m_header)
-  set $fiber_hook = $all_fibers->next_
-  printf "\n  Active fibers:\n"
-  while $fiber_hook != $all_fibers
-    set $fiber = (folly::fibers::Fiber*) \
-        ((int64_t)$fiber_hook - \
-         (int64_t)&folly::fibers::Fiber::globalListHook_)
-    if $fiber->state_ != folly::fibers::Fiber::INVALID
-      printf "    (folly::fibers::Fiber*)%p   State: ", $fiber
-      print_folly_fiber_state $fiber
-      printf "\n"
-    end
-    set $fiber_hook = $fiber_hook->next_
-  end
-end
-
-# Print global FiberManager map
-define print_folly_fiber_manager_map
-  set $global_cache=*(('folly::fibers::(anonymous namespace)::GlobalCache'**) \
-      &'folly::fibers::(anonymous namespace)::GlobalCache::instance()::ret')
-  printf "  Global FiberManager map has %d entries.\n", \
-         $global_cache->map_->_M_h->_M_element_count
-
-  set $item = $global_cache->map_->_M_h->_M_before_begin._M_nxt
-  while $item != 0
-    set $evb = ((folly::EventBase**)$item)[1]
-    set $fiberManager = ((folly::fibers::FiberManager**)$item)[2]
-    printf "    (folly::EventBase*)%p -> (folly::fibers::FiberManager*)%p\n", \
-           $evb, $fiberManager
-
-    set $item = $item->_M_nxt
-  end
-end
diff --git a/folly/experimental/fibers/test/FibersTest.cpp b/folly/experimental/fibers/test/FibersTest.cpp
deleted file mode 100644 (file)
index f4db1f6..0000000
+++ /dev/null
@@ -1,1637 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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.
- */
-#include <atomic>
-#include <thread>
-#include <vector>
-
-#include <gtest/gtest.h>
-
-#include <folly/Benchmark.h>
-#include <folly/Memory.h>
-#include <folly/futures/Future.h>
-
-#include <folly/experimental/fibers/AddTasks.h>
-#include <folly/experimental/fibers/EventBaseLoopController.h>
-#include <folly/experimental/fibers/FiberManager.h>
-#include <folly/experimental/fibers/FiberManagerMap.h>
-#include <folly/experimental/fibers/GenericBaton.h>
-#include <folly/experimental/fibers/SimpleLoopController.h>
-#include <folly/experimental/fibers/WhenN.h>
-
-using namespace folly::fibers;
-
-using folly::Try;
-
-TEST(FiberManager, batonTimedWaitTimeout) {
-  bool taskAdded = false;
-  size_t iterations = 0;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  auto loopFunc = [&]() {
-    if (!taskAdded) {
-      manager.addTask([&]() {
-        Baton baton;
-
-        auto res = baton.timed_wait(std::chrono::milliseconds(230));
-
-        EXPECT_FALSE(res);
-        EXPECT_EQ(5, iterations);
-
-        loopController.stop();
-      });
-      manager.addTask([&]() {
-        Baton baton;
-
-        auto res = baton.timed_wait(std::chrono::milliseconds(130));
-
-        EXPECT_FALSE(res);
-        EXPECT_EQ(3, iterations);
-
-        loopController.stop();
-      });
-      taskAdded = true;
-    } else {
-      std::this_thread::sleep_for(std::chrono::milliseconds(50));
-      iterations++;
-    }
-  };
-
-  loopController.loop(std::move(loopFunc));
-}
-
-TEST(FiberManager, batonTimedWaitPost) {
-  bool taskAdded = false;
-  size_t iterations = 0;
-  Baton* baton_ptr;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  auto loopFunc = [&]() {
-    if (!taskAdded) {
-      manager.addTask([&]() {
-        Baton baton;
-        baton_ptr = &baton;
-
-        auto res = baton.timed_wait(std::chrono::milliseconds(130));
-
-        EXPECT_TRUE(res);
-        EXPECT_EQ(2, iterations);
-
-        loopController.stop();
-      });
-      taskAdded = true;
-    } else {
-      std::this_thread::sleep_for(std::chrono::milliseconds(50));
-      iterations++;
-      if (iterations == 2) {
-        baton_ptr->post();
-      }
-    }
-  };
-
-  loopController.loop(std::move(loopFunc));
-}
-
-TEST(FiberManager, batonTimedWaitTimeoutEvb) {
-  size_t tasksComplete = 0;
-
-  folly::EventBase evb;
-
-  FiberManager manager(folly::make_unique<EventBaseLoopController>());
-  dynamic_cast<EventBaseLoopController&>(manager.loopController())
-      .attachEventBase(evb);
-
-  auto task = [&](size_t timeout_ms) {
-    Baton baton;
-
-    auto start = EventBaseLoopController::Clock::now();
-    auto res = baton.timed_wait(std::chrono::milliseconds(timeout_ms));
-    auto finish = EventBaseLoopController::Clock::now();
-
-    EXPECT_FALSE(res);
-
-    auto duration_ms =
-        std::chrono::duration_cast<std::chrono::milliseconds>(finish - start);
-
-    EXPECT_GT(duration_ms.count(), timeout_ms - 50);
-    EXPECT_LT(duration_ms.count(), timeout_ms + 50);
-
-    if (++tasksComplete == 2) {
-      evb.terminateLoopSoon();
-    }
-  };
-
-  evb.runInEventBaseThread([&]() {
-    manager.addTask([&]() { task(500); });
-    manager.addTask([&]() { task(250); });
-  });
-
-  evb.loopForever();
-
-  EXPECT_EQ(2, tasksComplete);
-}
-
-TEST(FiberManager, batonTimedWaitPostEvb) {
-  size_t tasksComplete = 0;
-
-  folly::EventBase evb;
-
-  FiberManager manager(folly::make_unique<EventBaseLoopController>());
-  dynamic_cast<EventBaseLoopController&>(manager.loopController())
-      .attachEventBase(evb);
-
-  evb.runInEventBaseThread([&]() {
-    manager.addTask([&]() {
-      Baton baton;
-
-      evb.tryRunAfterDelay([&]() { baton.post(); }, 100);
-
-      auto start = EventBaseLoopController::Clock::now();
-      auto res = baton.timed_wait(std::chrono::milliseconds(130));
-      auto finish = EventBaseLoopController::Clock::now();
-
-      EXPECT_TRUE(res);
-
-      auto duration_ms =
-          std::chrono::duration_cast<std::chrono::milliseconds>(finish - start);
-
-      EXPECT_TRUE(duration_ms.count() > 95 && duration_ms.count() < 110);
-
-      if (++tasksComplete == 1) {
-        evb.terminateLoopSoon();
-      }
-    });
-  });
-
-  evb.loopForever();
-
-  EXPECT_EQ(1, tasksComplete);
-}
-
-TEST(FiberManager, batonTryWait) {
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-
-  // Check if try_wait and post work as expected
-  Baton b;
-
-  manager.addTask([&]() {
-    while (!b.try_wait()) {
-    }
-  });
-  auto thr = std::thread([&]() {
-    std::this_thread::sleep_for(std::chrono::milliseconds(300));
-    b.post();
-  });
-
-  manager.loopUntilNoReady();
-  thr.join();
-
-  Baton c;
-
-  // Check try_wait without post
-  manager.addTask([&]() {
-    int cnt = 100;
-    while (cnt && !c.try_wait()) {
-      cnt--;
-    }
-    EXPECT_TRUE(!c.try_wait()); // must still hold
-    EXPECT_EQ(cnt, 0);
-  });
-
-  manager.loopUntilNoReady();
-}
-
-TEST(FiberManager, genericBatonFiberWait) {
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-
-  GenericBaton b;
-  bool fiberRunning = false;
-
-  manager.addTask([&]() {
-    EXPECT_EQ(manager.hasActiveFiber(), true);
-    fiberRunning = true;
-    b.wait();
-    fiberRunning = false;
-  });
-
-  EXPECT_FALSE(fiberRunning);
-  manager.loopUntilNoReady();
-  EXPECT_TRUE(fiberRunning); // ensure fiber still active
-
-  auto thr = std::thread([&]() {
-    std::this_thread::sleep_for(std::chrono::milliseconds(300));
-    b.post();
-  });
-
-  while (fiberRunning) {
-    manager.loopUntilNoReady();
-  }
-
-  thr.join();
-}
-
-TEST(FiberManager, genericBatonThreadWait) {
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  GenericBaton b;
-  std::atomic<bool> threadWaiting(false);
-
-  auto thr = std::thread([&]() {
-    threadWaiting = true;
-    b.wait();
-    threadWaiting = false;
-  });
-
-  while (!threadWaiting) {
-  }
-  std::this_thread::sleep_for(std::chrono::milliseconds(300));
-
-  manager.addTask([&]() {
-    EXPECT_EQ(manager.hasActiveFiber(), true);
-    EXPECT_TRUE(threadWaiting);
-    b.post();
-    while (threadWaiting) {
-    }
-  });
-
-  manager.loopUntilNoReady();
-  thr.join();
-}
-
-TEST(FiberManager, addTasksNoncopyable) {
-  std::vector<Promise<int>> pendingFibers;
-  bool taskAdded = false;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  auto loopFunc = [&]() {
-    if (!taskAdded) {
-      manager.addTask([&]() {
-        std::vector<std::function<std::unique_ptr<int>()>> funcs;
-        for (size_t i = 0; i < 3; ++i) {
-          funcs.push_back([i, &pendingFibers]() {
-            await([&pendingFibers](Promise<int> promise) {
-              pendingFibers.push_back(std::move(promise));
-            });
-            return folly::make_unique<int>(i * 2 + 1);
-          });
-        }
-
-        auto iter = addTasks(funcs.begin(), funcs.end());
-
-        size_t n = 0;
-        while (iter.hasNext()) {
-          auto result = iter.awaitNext();
-          EXPECT_EQ(2 * iter.getTaskID() + 1, *result);
-          EXPECT_GE(2 - n, pendingFibers.size());
-          ++n;
-        }
-        EXPECT_EQ(3, n);
-      });
-      taskAdded = true;
-    } else if (pendingFibers.size()) {
-      pendingFibers.back().setValue(0);
-      pendingFibers.pop_back();
-    } else {
-      loopController.stop();
-    }
-  };
-
-  loopController.loop(std::move(loopFunc));
-}
-
-TEST(FiberManager, awaitThrow) {
-  folly::EventBase evb;
-  struct ExpectedException {};
-  getFiberManager(evb)
-      .addTaskFuture([&] {
-        EXPECT_THROW(
-          await([](Promise<int> p) {
-              p.setValue(42);
-              throw ExpectedException();
-            }),
-          ExpectedException
-        );
-
-        EXPECT_THROW(
-          await([&](Promise<int> p) {
-              evb.runInEventBaseThread([p = std::move(p)]() mutable {
-                  p.setValue(42);
-                });
-              throw ExpectedException();
-            }),
-          ExpectedException);
-      })
-      .waitVia(&evb);
-}
-
-TEST(FiberManager, addTasksThrow) {
-  std::vector<Promise<int>> pendingFibers;
-  bool taskAdded = false;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  auto loopFunc = [&]() {
-    if (!taskAdded) {
-      manager.addTask([&]() {
-        std::vector<std::function<int()>> funcs;
-        for (size_t i = 0; i < 3; ++i) {
-          funcs.push_back([i, &pendingFibers]() {
-            await([&pendingFibers](Promise<int> promise) {
-              pendingFibers.push_back(std::move(promise));
-            });
-            if (i % 2 == 0) {
-              throw std::runtime_error("Runtime");
-            }
-            return i * 2 + 1;
-          });
-        }
-
-        auto iter = addTasks(funcs.begin(), funcs.end());
-
-        size_t n = 0;
-        while (iter.hasNext()) {
-          try {
-            int result = iter.awaitNext();
-            EXPECT_EQ(1, iter.getTaskID() % 2);
-            EXPECT_EQ(2 * iter.getTaskID() + 1, result);
-          } catch (...) {
-            EXPECT_EQ(0, iter.getTaskID() % 2);
-          }
-          EXPECT_GE(2 - n, pendingFibers.size());
-          ++n;
-        }
-        EXPECT_EQ(3, n);
-      });
-      taskAdded = true;
-    } else if (pendingFibers.size()) {
-      pendingFibers.back().setValue(0);
-      pendingFibers.pop_back();
-    } else {
-      loopController.stop();
-    }
-  };
-
-  loopController.loop(std::move(loopFunc));
-}
-
-TEST(FiberManager, addTasksVoid) {
-  std::vector<Promise<int>> pendingFibers;
-  bool taskAdded = false;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  auto loopFunc = [&]() {
-    if (!taskAdded) {
-      manager.addTask([&]() {
-        std::vector<std::function<void()>> funcs;
-        for (size_t i = 0; i < 3; ++i) {
-          funcs.push_back([i, &pendingFibers]() {
-            await([&pendingFibers](Promise<int> promise) {
-              pendingFibers.push_back(std::move(promise));
-            });
-          });
-        }
-
-        auto iter = addTasks(funcs.begin(), funcs.end());
-
-        size_t n = 0;
-        while (iter.hasNext()) {
-          iter.awaitNext();
-          EXPECT_GE(2 - n, pendingFibers.size());
-          ++n;
-        }
-        EXPECT_EQ(3, n);
-      });
-      taskAdded = true;
-    } else if (pendingFibers.size()) {
-      pendingFibers.back().setValue(0);
-      pendingFibers.pop_back();
-    } else {
-      loopController.stop();
-    }
-  };
-
-  loopController.loop(std::move(loopFunc));
-}
-
-TEST(FiberManager, addTasksVoidThrow) {
-  std::vector<Promise<int>> pendingFibers;
-  bool taskAdded = false;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  auto loopFunc = [&]() {
-    if (!taskAdded) {
-      manager.addTask([&]() {
-        std::vector<std::function<void()>> funcs;
-        for (size_t i = 0; i < 3; ++i) {
-          funcs.push_back([i, &pendingFibers]() {
-            await([&pendingFibers](Promise<int> promise) {
-              pendingFibers.push_back(std::move(promise));
-            });
-            if (i % 2 == 0) {
-              throw std::runtime_error("");
-            }
-          });
-        }
-
-        auto iter = addTasks(funcs.begin(), funcs.end());
-
-        size_t n = 0;
-        while (iter.hasNext()) {
-          try {
-            iter.awaitNext();
-            EXPECT_EQ(1, iter.getTaskID() % 2);
-          } catch (...) {
-            EXPECT_EQ(0, iter.getTaskID() % 2);
-          }
-          EXPECT_GE(2 - n, pendingFibers.size());
-          ++n;
-        }
-        EXPECT_EQ(3, n);
-      });
-      taskAdded = true;
-    } else if (pendingFibers.size()) {
-      pendingFibers.back().setValue(0);
-      pendingFibers.pop_back();
-    } else {
-      loopController.stop();
-    }
-  };
-
-  loopController.loop(std::move(loopFunc));
-}
-
-TEST(FiberManager, addTasksReserve) {
-  std::vector<Promise<int>> pendingFibers;
-  bool taskAdded = false;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  auto loopFunc = [&]() {
-    if (!taskAdded) {
-      manager.addTask([&]() {
-        std::vector<std::function<void()>> funcs;
-        for (size_t i = 0; i < 3; ++i) {
-          funcs.push_back([&pendingFibers]() {
-            await([&pendingFibers](Promise<int> promise) {
-              pendingFibers.push_back(std::move(promise));
-            });
-          });
-        }
-
-        auto iter = addTasks(funcs.begin(), funcs.end());
-
-        iter.reserve(2);
-        EXPECT_TRUE(iter.hasCompleted());
-        EXPECT_TRUE(iter.hasPending());
-        EXPECT_TRUE(iter.hasNext());
-
-        iter.awaitNext();
-        EXPECT_TRUE(iter.hasCompleted());
-        EXPECT_TRUE(iter.hasPending());
-        EXPECT_TRUE(iter.hasNext());
-
-        iter.awaitNext();
-        EXPECT_FALSE(iter.hasCompleted());
-        EXPECT_TRUE(iter.hasPending());
-        EXPECT_TRUE(iter.hasNext());
-
-        iter.awaitNext();
-        EXPECT_FALSE(iter.hasCompleted());
-        EXPECT_FALSE(iter.hasPending());
-        EXPECT_FALSE(iter.hasNext());
-      });
-      taskAdded = true;
-    } else if (pendingFibers.size()) {
-      pendingFibers.back().setValue(0);
-      pendingFibers.pop_back();
-    } else {
-      loopController.stop();
-    }
-  };
-
-  loopController.loop(std::move(loopFunc));
-}
-
-TEST(FiberManager, addTaskDynamic) {
-  folly::EventBase evb;
-
-  Baton batons[3];
-
-  auto makeTask = [&](size_t taskId) {
-    return [&, taskId]() -> size_t {
-      batons[taskId].wait();
-      return taskId;
-    };
-  };
-
-  getFiberManager(evb)
-      .addTaskFuture([&]() {
-        TaskIterator<size_t> iterator;
-
-        iterator.addTask(makeTask(0));
-        iterator.addTask(makeTask(1));
-
-        batons[1].post();
-
-        EXPECT_EQ(1, iterator.awaitNext());
-
-        iterator.addTask(makeTask(2));
-
-        batons[2].post();
-
-        EXPECT_EQ(2, iterator.awaitNext());
-
-        batons[0].post();
-
-        EXPECT_EQ(0, iterator.awaitNext());
-      })
-      .waitVia(&evb);
-}
-
-TEST(FiberManager, forEach) {
-  std::vector<Promise<int>> pendingFibers;
-  bool taskAdded = false;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  auto loopFunc = [&]() {
-    if (!taskAdded) {
-      manager.addTask([&]() {
-        std::vector<std::function<int()>> funcs;
-        for (size_t i = 0; i < 3; ++i) {
-          funcs.push_back([i, &pendingFibers]() {
-            await([&pendingFibers](Promise<int> promise) {
-              pendingFibers.push_back(std::move(promise));
-            });
-            return i * 2 + 1;
-          });
-        }
-
-        std::vector<std::pair<size_t, int>> results;
-        forEach(funcs.begin(), funcs.end(), [&results](size_t id, int result) {
-          results.emplace_back(id, result);
-        });
-        EXPECT_EQ(3, results.size());
-        EXPECT_TRUE(pendingFibers.empty());
-        for (size_t i = 0; i < 3; ++i) {
-          EXPECT_EQ(results[i].first * 2 + 1, results[i].second);
-        }
-      });
-      taskAdded = true;
-    } else if (pendingFibers.size()) {
-      pendingFibers.back().setValue(0);
-      pendingFibers.pop_back();
-    } else {
-      loopController.stop();
-    }
-  };
-
-  loopController.loop(std::move(loopFunc));
-}
-
-TEST(FiberManager, collectN) {
-  std::vector<Promise<int>> pendingFibers;
-  bool taskAdded = false;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  auto loopFunc = [&]() {
-    if (!taskAdded) {
-      manager.addTask([&]() {
-        std::vector<std::function<int()>> funcs;
-        for (size_t i = 0; i < 3; ++i) {
-          funcs.push_back([i, &pendingFibers]() {
-            await([&pendingFibers](Promise<int> promise) {
-              pendingFibers.push_back(std::move(promise));
-            });
-            return i * 2 + 1;
-          });
-        }
-
-        auto results = collectN(funcs.begin(), funcs.end(), 2);
-        EXPECT_EQ(2, results.size());
-        EXPECT_EQ(1, pendingFibers.size());
-        for (size_t i = 0; i < 2; ++i) {
-          EXPECT_EQ(results[i].first * 2 + 1, results[i].second);
-        }
-      });
-      taskAdded = true;
-    } else if (pendingFibers.size()) {
-      pendingFibers.back().setValue(0);
-      pendingFibers.pop_back();
-    } else {
-      loopController.stop();
-    }
-  };
-
-  loopController.loop(std::move(loopFunc));
-}
-
-TEST(FiberManager, collectNThrow) {
-  std::vector<Promise<int>> pendingFibers;
-  bool taskAdded = false;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  auto loopFunc = [&]() {
-    if (!taskAdded) {
-      manager.addTask([&]() {
-        std::vector<std::function<int()>> funcs;
-        for (size_t i = 0; i < 3; ++i) {
-          funcs.push_back([i, &pendingFibers]() {
-            await([&pendingFibers](Promise<int> promise) {
-              pendingFibers.push_back(std::move(promise));
-            });
-            throw std::runtime_error("Runtime");
-            return i * 2 + 1;
-          });
-        }
-
-        try {
-          collectN(funcs.begin(), funcs.end(), 2);
-        } catch (...) {
-          EXPECT_EQ(1, pendingFibers.size());
-        }
-      });
-      taskAdded = true;
-    } else if (pendingFibers.size()) {
-      pendingFibers.back().setValue(0);
-      pendingFibers.pop_back();
-    } else {
-      loopController.stop();
-    }
-  };
-
-  loopController.loop(std::move(loopFunc));
-}
-
-TEST(FiberManager, collectNVoid) {
-  std::vector<Promise<int>> pendingFibers;
-  bool taskAdded = false;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  auto loopFunc = [&]() {
-    if (!taskAdded) {
-      manager.addTask([&]() {
-        std::vector<std::function<void()>> funcs;
-        for (size_t i = 0; i < 3; ++i) {
-          funcs.push_back([i, &pendingFibers]() {
-            await([&pendingFibers](Promise<int> promise) {
-              pendingFibers.push_back(std::move(promise));
-            });
-          });
-        }
-
-        auto results = collectN(funcs.begin(), funcs.end(), 2);
-        EXPECT_EQ(2, results.size());
-        EXPECT_EQ(1, pendingFibers.size());
-      });
-      taskAdded = true;
-    } else if (pendingFibers.size()) {
-      pendingFibers.back().setValue(0);
-      pendingFibers.pop_back();
-    } else {
-      loopController.stop();
-    }
-  };
-
-  loopController.loop(std::move(loopFunc));
-}
-
-TEST(FiberManager, collectNVoidThrow) {
-  std::vector<Promise<int>> pendingFibers;
-  bool taskAdded = false;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  auto loopFunc = [&]() {
-    if (!taskAdded) {
-      manager.addTask([&]() {
-        std::vector<std::function<void()>> funcs;
-        for (size_t i = 0; i < 3; ++i) {
-          funcs.push_back([i, &pendingFibers]() {
-            await([&pendingFibers](Promise<int> promise) {
-              pendingFibers.push_back(std::move(promise));
-            });
-            throw std::runtime_error("Runtime");
-          });
-        }
-
-        try {
-          collectN(funcs.begin(), funcs.end(), 2);
-        } catch (...) {
-          EXPECT_EQ(1, pendingFibers.size());
-        }
-      });
-      taskAdded = true;
-    } else if (pendingFibers.size()) {
-      pendingFibers.back().setValue(0);
-      pendingFibers.pop_back();
-    } else {
-      loopController.stop();
-    }
-  };
-
-  loopController.loop(std::move(loopFunc));
-}
-
-TEST(FiberManager, collectAll) {
-  std::vector<Promise<int>> pendingFibers;
-  bool taskAdded = false;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  auto loopFunc = [&]() {
-    if (!taskAdded) {
-      manager.addTask([&]() {
-        std::vector<std::function<int()>> funcs;
-        for (size_t i = 0; i < 3; ++i) {
-          funcs.push_back([i, &pendingFibers]() {
-            await([&pendingFibers](Promise<int> promise) {
-              pendingFibers.push_back(std::move(promise));
-            });
-            return i * 2 + 1;
-          });
-        }
-
-        auto results = collectAll(funcs.begin(), funcs.end());
-        EXPECT_TRUE(pendingFibers.empty());
-        for (size_t i = 0; i < 3; ++i) {
-          EXPECT_EQ(i * 2 + 1, results[i]);
-        }
-      });
-      taskAdded = true;
-    } else if (pendingFibers.size()) {
-      pendingFibers.back().setValue(0);
-      pendingFibers.pop_back();
-    } else {
-      loopController.stop();
-    }
-  };
-
-  loopController.loop(std::move(loopFunc));
-}
-
-TEST(FiberManager, collectAllVoid) {
-  std::vector<Promise<int>> pendingFibers;
-  bool taskAdded = false;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  auto loopFunc = [&]() {
-    if (!taskAdded) {
-      manager.addTask([&]() {
-        std::vector<std::function<void()>> funcs;
-        for (size_t i = 0; i < 3; ++i) {
-          funcs.push_back([i, &pendingFibers]() {
-            await([&pendingFibers](Promise<int> promise) {
-              pendingFibers.push_back(std::move(promise));
-            });
-          });
-        }
-
-        collectAll(funcs.begin(), funcs.end());
-        EXPECT_TRUE(pendingFibers.empty());
-      });
-      taskAdded = true;
-    } else if (pendingFibers.size()) {
-      pendingFibers.back().setValue(0);
-      pendingFibers.pop_back();
-    } else {
-      loopController.stop();
-    }
-  };
-
-  loopController.loop(std::move(loopFunc));
-}
-
-TEST(FiberManager, collectAny) {
-  std::vector<Promise<int>> pendingFibers;
-  bool taskAdded = false;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  auto loopFunc = [&]() {
-    if (!taskAdded) {
-      manager.addTask([&]() {
-        std::vector<std::function<int()>> funcs;
-        for (size_t i = 0; i < 3; ++i) {
-          funcs.push_back([i, &pendingFibers]() {
-            await([&pendingFibers](Promise<int> promise) {
-              pendingFibers.push_back(std::move(promise));
-            });
-            if (i == 1) {
-              throw std::runtime_error("This exception will be ignored");
-            }
-            return i * 2 + 1;
-          });
-        }
-
-        auto result = collectAny(funcs.begin(), funcs.end());
-        EXPECT_EQ(2, pendingFibers.size());
-        EXPECT_EQ(2, result.first);
-        EXPECT_EQ(2 * 2 + 1, result.second);
-      });
-      taskAdded = true;
-    } else if (pendingFibers.size()) {
-      pendingFibers.back().setValue(0);
-      pendingFibers.pop_back();
-    } else {
-      loopController.stop();
-    }
-  };
-
-  loopController.loop(std::move(loopFunc));
-}
-
-namespace {
-/* Checks that this function was run from a main context,
-   by comparing an address on a stack to a known main stack address
-   and a known related fiber stack address.  The assumption
-   is that fiber stack and main stack will be far enough apart,
-   while any two values on the same stack will be close. */
-void expectMainContext(bool& ran, int* mainLocation, int* fiberLocation) {
-  int here;
-  /* 2 pages is a good guess */
-  constexpr ssize_t DISTANCE = 0x2000 / sizeof(int);
-  if (fiberLocation) {
-    EXPECT_TRUE(std::abs(&here - fiberLocation) > DISTANCE);
-  }
-  if (mainLocation) {
-    EXPECT_TRUE(std::abs(&here - mainLocation) < DISTANCE);
-  }
-
-  EXPECT_FALSE(ran);
-  ran = true;
-}
-}
-
-TEST(FiberManager, runInMainContext) {
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  bool checkRan = false;
-
-  int mainLocation;
-  manager.runInMainContext(
-      [&]() { expectMainContext(checkRan, &mainLocation, nullptr); });
-  EXPECT_TRUE(checkRan);
-
-  checkRan = false;
-
-  manager.addTask([&]() {
-    struct A {
-      explicit A(int value_) : value(value_) {}
-      A(const A&) = delete;
-      A(A&&) = default;
-
-      int value;
-    };
-    int stackLocation;
-    auto ret = runInMainContext([&]() {
-      expectMainContext(checkRan, &mainLocation, &stackLocation);
-      return A(42);
-    });
-    EXPECT_TRUE(checkRan);
-    EXPECT_EQ(42, ret.value);
-  });
-
-  loopController.loop([&]() { loopController.stop(); });
-
-  EXPECT_TRUE(checkRan);
-}
-
-TEST(FiberManager, addTaskFinally) {
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  bool checkRan = false;
-
-  int mainLocation;
-
-  manager.addTaskFinally(
-      [&]() { return 1234; },
-      [&](Try<int>&& result) {
-        EXPECT_EQ(result.value(), 1234);
-
-        expectMainContext(checkRan, &mainLocation, nullptr);
-      });
-
-  EXPECT_FALSE(checkRan);
-
-  loopController.loop([&]() { loopController.stop(); });
-
-  EXPECT_TRUE(checkRan);
-}
-
-TEST(FiberManager, fibersPoolWithinLimit) {
-  FiberManager::Options opts;
-  opts.maxFibersPoolSize = 5;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>(), opts);
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  size_t fibersRun = 0;
-
-  for (size_t i = 0; i < 5; ++i) {
-    manager.addTask([&]() { ++fibersRun; });
-  }
-  loopController.loop([&]() { loopController.stop(); });
-
-  EXPECT_EQ(5, fibersRun);
-  EXPECT_EQ(5, manager.fibersAllocated());
-  EXPECT_EQ(5, manager.fibersPoolSize());
-
-  for (size_t i = 0; i < 5; ++i) {
-    manager.addTask([&]() { ++fibersRun; });
-  }
-  loopController.loop([&]() { loopController.stop(); });
-
-  EXPECT_EQ(10, fibersRun);
-  EXPECT_EQ(5, manager.fibersAllocated());
-  EXPECT_EQ(5, manager.fibersPoolSize());
-}
-
-TEST(FiberManager, fibersPoolOverLimit) {
-  FiberManager::Options opts;
-  opts.maxFibersPoolSize = 5;
-
-  FiberManager manager(folly::make_unique<SimpleLoopController>(), opts);
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  size_t fibersRun = 0;
-
-  for (size_t i = 0; i < 10; ++i) {
-    manager.addTask([&]() { ++fibersRun; });
-  }
-
-  EXPECT_EQ(0, fibersRun);
-  EXPECT_EQ(10, manager.fibersAllocated());
-  EXPECT_EQ(0, manager.fibersPoolSize());
-
-  loopController.loop([&]() { loopController.stop(); });
-
-  EXPECT_EQ(10, fibersRun);
-  EXPECT_EQ(5, manager.fibersAllocated());
-  EXPECT_EQ(5, manager.fibersPoolSize());
-}
-
-TEST(FiberManager, remoteFiberBasic) {
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  int result[2];
-  result[0] = result[1] = 0;
-  folly::Optional<Promise<int>> savedPromise[2];
-  manager.addTask([&]() {
-    result[0] = await(
-        [&](Promise<int> promise) { savedPromise[0] = std::move(promise); });
-  });
-  manager.addTask([&]() {
-    result[1] = await(
-        [&](Promise<int> promise) { savedPromise[1] = std::move(promise); });
-  });
-
-  manager.loopUntilNoReady();
-
-  EXPECT_TRUE(savedPromise[0].hasValue());
-  EXPECT_TRUE(savedPromise[1].hasValue());
-  EXPECT_EQ(0, result[0]);
-  EXPECT_EQ(0, result[1]);
-
-  std::thread remoteThread0{[&]() { savedPromise[0]->setValue(42); }};
-  std::thread remoteThread1{[&]() { savedPromise[1]->setValue(43); }};
-  remoteThread0.join();
-  remoteThread1.join();
-  EXPECT_EQ(0, result[0]);
-  EXPECT_EQ(0, result[1]);
-  /* Should only have scheduled once */
-  EXPECT_EQ(1, loopController.remoteScheduleCalled());
-
-  manager.loopUntilNoReady();
-  EXPECT_EQ(42, result[0]);
-  EXPECT_EQ(43, result[1]);
-}
-
-TEST(FiberManager, addTaskRemoteBasic) {
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-
-  int result[2];
-  result[0] = result[1] = 0;
-  folly::Optional<Promise<int>> savedPromise[2];
-
-  std::thread remoteThread0{[&]() {
-    manager.addTaskRemote([&]() {
-      result[0] = await(
-          [&](Promise<int> promise) { savedPromise[0] = std::move(promise); });
-    });
-  }};
-  std::thread remoteThread1{[&]() {
-    manager.addTaskRemote([&]() {
-      result[1] = await(
-          [&](Promise<int> promise) { savedPromise[1] = std::move(promise); });
-    });
-  }};
-  remoteThread0.join();
-  remoteThread1.join();
-
-  manager.loopUntilNoReady();
-
-  EXPECT_TRUE(savedPromise[0].hasValue());
-  EXPECT_TRUE(savedPromise[1].hasValue());
-  EXPECT_EQ(0, result[0]);
-  EXPECT_EQ(0, result[1]);
-
-  savedPromise[0]->setValue(42);
-  savedPromise[1]->setValue(43);
-
-  EXPECT_EQ(0, result[0]);
-  EXPECT_EQ(0, result[1]);
-
-  manager.loopUntilNoReady();
-  EXPECT_EQ(42, result[0]);
-  EXPECT_EQ(43, result[1]);
-}
-
-TEST(FiberManager, remoteHasTasks) {
-  size_t counter = 0;
-  FiberManager fm(folly::make_unique<SimpleLoopController>());
-  std::thread remote([&]() { fm.addTaskRemote([&]() { ++counter; }); });
-
-  remote.join();
-
-  while (fm.hasTasks()) {
-    fm.loopUntilNoReady();
-  }
-
-  EXPECT_FALSE(fm.hasTasks());
-  EXPECT_EQ(counter, 1);
-}
-
-TEST(FiberManager, remoteHasReadyTasks) {
-  int result = 0;
-  folly::Optional<Promise<int>> savedPromise;
-  FiberManager fm(folly::make_unique<SimpleLoopController>());
-  std::thread remote([&]() {
-    fm.addTaskRemote([&]() {
-      result = await(
-          [&](Promise<int> promise) { savedPromise = std::move(promise); });
-      EXPECT_TRUE(fm.hasTasks());
-    });
-  });
-
-  remote.join();
-  EXPECT_TRUE(fm.hasTasks());
-
-  fm.loopUntilNoReady();
-  EXPECT_TRUE(fm.hasTasks());
-
-  std::thread remote2([&]() { savedPromise->setValue(47); });
-  remote2.join();
-  EXPECT_TRUE(fm.hasTasks());
-
-  fm.loopUntilNoReady();
-  EXPECT_FALSE(fm.hasTasks());
-
-  EXPECT_EQ(result, 47);
-}
-
-template <typename Data>
-void testFiberLocal() {
-  FiberManager fm(
-      LocalType<Data>(), folly::make_unique<SimpleLoopController>());
-
-  fm.addTask([]() {
-    EXPECT_EQ(42, local<Data>().value);
-
-    local<Data>().value = 43;
-
-    addTask([]() {
-      EXPECT_EQ(43, local<Data>().value);
-
-      local<Data>().value = 44;
-
-      addTask([]() { EXPECT_EQ(44, local<Data>().value); });
-    });
-  });
-
-  fm.addTask([&]() {
-    EXPECT_EQ(42, local<Data>().value);
-
-    local<Data>().value = 43;
-
-    fm.addTaskRemote([]() { EXPECT_EQ(43, local<Data>().value); });
-  });
-
-  fm.addTask([]() {
-    EXPECT_EQ(42, local<Data>().value);
-    local<Data>().value = 43;
-
-    auto task = []() {
-      EXPECT_EQ(43, local<Data>().value);
-      local<Data>().value = 44;
-    };
-    std::vector<std::function<void()>> tasks{task};
-    collectAny(tasks.begin(), tasks.end());
-
-    EXPECT_EQ(43, local<Data>().value);
-  });
-
-  fm.loopUntilNoReady();
-  EXPECT_FALSE(fm.hasTasks());
-}
-
-TEST(FiberManager, fiberLocal) {
-  struct SimpleData {
-    int value{42};
-  };
-
-  testFiberLocal<SimpleData>();
-}
-
-TEST(FiberManager, fiberLocalHeap) {
-  struct LargeData {
-    char _[1024 * 1024];
-    int value{42};
-  };
-
-  testFiberLocal<LargeData>();
-}
-
-TEST(FiberManager, fiberLocalDestructor) {
-  struct CrazyData {
-    size_t data{42};
-
-    ~CrazyData() {
-      if (data == 41) {
-        addTask([]() {
-          EXPECT_EQ(42, local<CrazyData>().data);
-          // Make sure we don't have infinite loop
-          local<CrazyData>().data = 0;
-        });
-      }
-    }
-  };
-
-  FiberManager fm(
-      LocalType<CrazyData>(), folly::make_unique<SimpleLoopController>());
-
-  fm.addTask([]() { local<CrazyData>().data = 41; });
-
-  fm.loopUntilNoReady();
-  EXPECT_FALSE(fm.hasTasks());
-}
-
-TEST(FiberManager, yieldTest) {
-  FiberManager manager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(manager.loopController());
-
-  bool checkRan = false;
-
-  manager.addTask([&]() {
-    manager.yield();
-    checkRan = true;
-  });
-
-  loopController.loop([&]() {
-    if (checkRan) {
-      loopController.stop();
-    }
-  });
-
-  EXPECT_TRUE(checkRan);
-}
-
-TEST(FiberManager, RequestContext) {
-  FiberManager fm(folly::make_unique<SimpleLoopController>());
-
-  bool checkRun1 = false;
-  bool checkRun2 = false;
-  bool checkRun3 = false;
-  bool checkRun4 = false;
-  folly::fibers::Baton baton1;
-  folly::fibers::Baton baton2;
-  folly::fibers::Baton baton3;
-  folly::fibers::Baton baton4;
-
-  folly::RequestContext::create();
-  auto rcontext1 = folly::RequestContext::get();
-  fm.addTask([&]() {
-    EXPECT_EQ(rcontext1, folly::RequestContext::get());
-    baton1.wait([&]() { EXPECT_EQ(rcontext1, folly::RequestContext::get()); });
-    EXPECT_EQ(rcontext1, folly::RequestContext::get());
-    runInMainContext(
-        [&]() { EXPECT_EQ(rcontext1, folly::RequestContext::get()); });
-    checkRun1 = true;
-  });
-
-  folly::RequestContext::create();
-  auto rcontext2 = folly::RequestContext::get();
-  fm.addTaskRemote([&]() {
-    EXPECT_EQ(rcontext2, folly::RequestContext::get());
-    baton2.wait();
-    EXPECT_EQ(rcontext2, folly::RequestContext::get());
-    checkRun2 = true;
-  });
-
-  folly::RequestContext::create();
-  auto rcontext3 = folly::RequestContext::get();
-  fm.addTaskFinally(
-      [&]() {
-        EXPECT_EQ(rcontext3, folly::RequestContext::get());
-        baton3.wait();
-        EXPECT_EQ(rcontext3, folly::RequestContext::get());
-
-        return folly::Unit();
-      },
-      [&](Try<folly::Unit>&& /* t */) {
-        EXPECT_EQ(rcontext3, folly::RequestContext::get());
-        checkRun3 = true;
-      });
-
-  folly::RequestContext::setContext(nullptr);
-  fm.addTask([&]() {
-    folly::RequestContext::create();
-    auto rcontext4 = folly::RequestContext::get();
-    baton4.wait();
-    EXPECT_EQ(rcontext4, folly::RequestContext::get());
-    checkRun4 = true;
-  });
-
-  folly::RequestContext::create();
-  auto rcontext = folly::RequestContext::get();
-
-  fm.loopUntilNoReady();
-  EXPECT_EQ(rcontext, folly::RequestContext::get());
-
-  baton1.post();
-  EXPECT_EQ(rcontext, folly::RequestContext::get());
-  fm.loopUntilNoReady();
-  EXPECT_TRUE(checkRun1);
-  EXPECT_EQ(rcontext, folly::RequestContext::get());
-
-  baton2.post();
-  EXPECT_EQ(rcontext, folly::RequestContext::get());
-  fm.loopUntilNoReady();
-  EXPECT_TRUE(checkRun2);
-  EXPECT_EQ(rcontext, folly::RequestContext::get());
-
-  baton3.post();
-  EXPECT_EQ(rcontext, folly::RequestContext::get());
-  fm.loopUntilNoReady();
-  EXPECT_TRUE(checkRun3);
-  EXPECT_EQ(rcontext, folly::RequestContext::get());
-
-  baton4.post();
-  EXPECT_EQ(rcontext, folly::RequestContext::get());
-  fm.loopUntilNoReady();
-  EXPECT_TRUE(checkRun4);
-  EXPECT_EQ(rcontext, folly::RequestContext::get());
-}
-
-TEST(FiberManager, resizePeriodically) {
-  FiberManager::Options opts;
-  opts.fibersPoolResizePeriodMs = 300;
-  opts.maxFibersPoolSize = 5;
-
-  FiberManager manager(folly::make_unique<EventBaseLoopController>(), opts);
-
-  folly::EventBase evb;
-  dynamic_cast<EventBaseLoopController&>(manager.loopController())
-      .attachEventBase(evb);
-
-  std::vector<Baton> batons(10);
-
-  size_t tasksRun = 0;
-  for (size_t i = 0; i < 30; ++i) {
-    manager.addTask([i, &batons, &tasksRun]() {
-      ++tasksRun;
-      // Keep some fibers active indefinitely
-      if (i < batons.size()) {
-        batons[i].wait();
-      }
-    });
-  }
-
-  EXPECT_EQ(0, tasksRun);
-  EXPECT_EQ(30, manager.fibersAllocated());
-  EXPECT_EQ(0, manager.fibersPoolSize());
-
-  evb.loopOnce();
-  EXPECT_EQ(30, tasksRun);
-  EXPECT_EQ(30, manager.fibersAllocated());
-  // Can go over maxFibersPoolSize, 10 of 30 fibers still active
-  EXPECT_EQ(20, manager.fibersPoolSize());
-
-  std::this_thread::sleep_for(std::chrono::milliseconds(400));
-  evb.loopOnce(); // no fibers active in this period
-  EXPECT_EQ(30, manager.fibersAllocated());
-  EXPECT_EQ(20, manager.fibersPoolSize());
-
-  std::this_thread::sleep_for(std::chrono::milliseconds(400));
-  evb.loopOnce(); // should shrink fibers pool to maxFibersPoolSize
-  EXPECT_EQ(15, manager.fibersAllocated());
-  EXPECT_EQ(5, manager.fibersPoolSize());
-
-  for (size_t i = 0; i < batons.size(); ++i) {
-    batons[i].post();
-  }
-  evb.loopOnce();
-  EXPECT_EQ(15, manager.fibersAllocated());
-  EXPECT_EQ(15, manager.fibersPoolSize());
-
-  std::this_thread::sleep_for(std::chrono::milliseconds(400));
-  evb.loopOnce(); // 10 fibers active in last period
-  EXPECT_EQ(10, manager.fibersAllocated());
-  EXPECT_EQ(10, manager.fibersPoolSize());
-
-  std::this_thread::sleep_for(std::chrono::milliseconds(400));
-  evb.loopOnce();
-  EXPECT_EQ(5, manager.fibersAllocated());
-  EXPECT_EQ(5, manager.fibersPoolSize());
-}
-
-TEST(FiberManager, batonWaitTimeoutHandler) {
-  FiberManager manager(folly::make_unique<EventBaseLoopController>());
-
-  folly::EventBase evb;
-  dynamic_cast<EventBaseLoopController&>(manager.loopController())
-      .attachEventBase(evb);
-
-  size_t fibersRun = 0;
-  Baton baton;
-  Baton::TimeoutHandler timeoutHandler;
-
-  manager.addTask([&]() {
-    baton.wait(timeoutHandler);
-    ++fibersRun;
-  });
-  manager.loopUntilNoReady();
-
-  EXPECT_FALSE(baton.try_wait());
-  EXPECT_EQ(0, fibersRun);
-
-  timeoutHandler.scheduleTimeout(std::chrono::milliseconds(250));
-  std::this_thread::sleep_for(std::chrono::milliseconds(500));
-
-  EXPECT_FALSE(baton.try_wait());
-  EXPECT_EQ(0, fibersRun);
-
-  evb.loopOnce();
-  manager.loopUntilNoReady();
-
-  EXPECT_EQ(1, fibersRun);
-}
-
-TEST(FiberManager, batonWaitTimeoutMany) {
-  FiberManager manager(folly::make_unique<EventBaseLoopController>());
-
-  folly::EventBase evb;
-  dynamic_cast<EventBaseLoopController&>(manager.loopController())
-      .attachEventBase(evb);
-
-  constexpr size_t kNumTimeoutTasks = 10000;
-  size_t tasksCount = kNumTimeoutTasks;
-
-  // We add many tasks to hit timeout queue deallocation logic.
-  for (size_t i = 0; i < kNumTimeoutTasks; ++i) {
-    manager.addTask([&]() {
-      Baton baton;
-      Baton::TimeoutHandler timeoutHandler;
-
-      folly::fibers::addTask([&] {
-        timeoutHandler.scheduleTimeout(std::chrono::milliseconds(1000));
-      });
-
-      baton.wait(timeoutHandler);
-      if (--tasksCount == 0) {
-        evb.terminateLoopSoon();
-      }
-    });
-  }
-
-  evb.loopForever();
-}
-
-TEST(FiberManager, remoteFutureTest) {
-  FiberManager fiberManager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(fiberManager.loopController());
-
-  int testValue1 = 5;
-  int testValue2 = 7;
-  auto f1 = fiberManager.addTaskFuture([&]() { return testValue1; });
-  auto f2 = fiberManager.addTaskRemoteFuture([&]() { return testValue2; });
-  loopController.loop([&]() { loopController.stop(); });
-  auto v1 = f1.get();
-  auto v2 = f2.get();
-
-  EXPECT_EQ(v1, testValue1);
-  EXPECT_EQ(v2, testValue2);
-}
-
-// Test that a void function produes a Future<Unit>.
-TEST(FiberManager, remoteFutureVoidUnitTest) {
-  FiberManager fiberManager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(fiberManager.loopController());
-
-  bool ranLocal = false;
-  folly::Future<folly::Unit> futureLocal =
-      fiberManager.addTaskFuture([&]() { ranLocal = true; });
-
-  bool ranRemote = false;
-  folly::Future<folly::Unit> futureRemote =
-      fiberManager.addTaskRemoteFuture([&]() { ranRemote = true; });
-
-  loopController.loop([&]() { loopController.stop(); });
-
-  futureLocal.wait();
-  ASSERT_TRUE(ranLocal);
-
-  futureRemote.wait();
-  ASSERT_TRUE(ranRemote);
-}
-
-TEST(FiberManager, nestedFiberManagers) {
-  folly::EventBase outerEvb;
-  folly::EventBase innerEvb;
-
-  getFiberManager(outerEvb).addTask([&]() {
-    EXPECT_EQ(
-        &getFiberManager(outerEvb), FiberManager::getFiberManagerUnsafe());
-
-    runInMainContext([&]() {
-      getFiberManager(innerEvb).addTask([&]() {
-        EXPECT_EQ(
-            &getFiberManager(innerEvb), FiberManager::getFiberManagerUnsafe());
-
-        innerEvb.terminateLoopSoon();
-      });
-
-      innerEvb.loopForever();
-    });
-
-    EXPECT_EQ(
-        &getFiberManager(outerEvb), FiberManager::getFiberManagerUnsafe());
-
-    outerEvb.terminateLoopSoon();
-  });
-
-  outerEvb.loopForever();
-}
-
-static size_t sNumAwaits;
-
-void runBenchmark(size_t numAwaits, size_t toSend) {
-  sNumAwaits = numAwaits;
-
-  FiberManager fiberManager(folly::make_unique<SimpleLoopController>());
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(fiberManager.loopController());
-
-  std::queue<Promise<int>> pendingRequests;
-  static const size_t maxOutstanding = 5;
-
-  auto loop = [&fiberManager, &loopController, &pendingRequests, &toSend]() {
-    if (pendingRequests.size() == maxOutstanding || toSend == 0) {
-      if (pendingRequests.empty()) {
-        return;
-      }
-      pendingRequests.front().setValue(0);
-      pendingRequests.pop();
-    } else {
-      fiberManager.addTask([&pendingRequests]() {
-        for (size_t i = 0; i < sNumAwaits; ++i) {
-          auto result = await([&pendingRequests](Promise<int> promise) {
-            pendingRequests.push(std::move(promise));
-          });
-          DCHECK_EQ(result, 0);
-        }
-      });
-
-      if (--toSend == 0) {
-        loopController.stop();
-      }
-    }
-  };
-
-  loopController.loop(std::move(loop));
-}
-
-BENCHMARK(FiberManagerBasicOneAwait, iters) {
-  runBenchmark(1, iters);
-}
-
-BENCHMARK(FiberManagerBasicFiveAwaits, iters) {
-  runBenchmark(5, iters);
-}
-
-BENCHMARK(FiberManagerCreateDestroy, iters) {
-  for (size_t i = 0; i < iters; ++i) {
-    folly::EventBase evb;
-    auto& fm = folly::fibers::getFiberManager(evb);
-    fm.addTask([]() {});
-    evb.loop();
-  }
-}
-
-BENCHMARK(FiberManagerAllocateDeallocatePattern, iters) {
-  static const size_t kNumAllocations = 10000;
-
-  FiberManager::Options opts;
-  opts.maxFibersPoolSize = 0;
-
-  FiberManager fiberManager(folly::make_unique<SimpleLoopController>(), opts);
-
-  for (size_t iter = 0; iter < iters; ++iter) {
-    EXPECT_EQ(0, fiberManager.fibersPoolSize());
-
-    size_t fibersRun = 0;
-
-    for (size_t i = 0; i < kNumAllocations; ++i) {
-      fiberManager.addTask([&fibersRun] { ++fibersRun; });
-      fiberManager.loopUntilNoReady();
-    }
-
-    EXPECT_EQ(10000, fibersRun);
-    EXPECT_EQ(0, fiberManager.fibersPoolSize());
-  }
-}
-
-BENCHMARK(FiberManagerAllocateLargeChunk, iters) {
-  static const size_t kNumAllocations = 10000;
-
-  FiberManager::Options opts;
-  opts.maxFibersPoolSize = 0;
-
-  FiberManager fiberManager(folly::make_unique<SimpleLoopController>(), opts);
-
-  for (size_t iter = 0; iter < iters; ++iter) {
-    EXPECT_EQ(0, fiberManager.fibersPoolSize());
-
-    size_t fibersRun = 0;
-
-    for (size_t i = 0; i < kNumAllocations; ++i) {
-      fiberManager.addTask([&fibersRun] { ++fibersRun; });
-    }
-
-    fiberManager.loopUntilNoReady();
-
-    EXPECT_EQ(10000, fibersRun);
-    EXPECT_EQ(0, fiberManager.fibersPoolSize());
-  }
-}
diff --git a/folly/experimental/fibers/test/FibersTestApp.cpp b/folly/experimental/fibers/test/FibersTestApp.cpp
deleted file mode 100644 (file)
index 44617ac..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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.
- */
-#include <iostream>
-#include <queue>
-
-#include <folly/Memory.h>
-
-#include <folly/experimental/fibers/FiberManager.h>
-#include <folly/experimental/fibers/SimpleLoopController.h>
-
-using namespace folly::fibers;
-
-struct Application {
- public:
-  Application()
-      : fiberManager(folly::make_unique<SimpleLoopController>()),
-        toSend(20),
-        maxOutstanding(5) {}
-
-  void loop() {
-    if (pendingRequests.size() == maxOutstanding || toSend == 0) {
-      if (pendingRequests.empty()) {
-        return;
-      }
-      intptr_t value = rand() % 1000;
-      std::cout << "Completing request with data = " << value << std::endl;
-
-      pendingRequests.front().setValue(value);
-      pendingRequests.pop();
-    } else {
-      static size_t id_counter = 1;
-      size_t id = id_counter++;
-      std::cout << "Adding new request with id = " << id << std::endl;
-
-      fiberManager.addTask([this, id]() {
-        std::cout << "Executing fiber with id = " << id << std::endl;
-
-        auto result1 = await([this](Promise<int> fiber) {
-          pendingRequests.push(std::move(fiber));
-        });
-
-        std::cout << "Fiber id = " << id << " got result1 = " << result1
-                  << std::endl;
-
-        auto result2 = await([this](Promise<int> fiber) {
-          pendingRequests.push(std::move(fiber));
-        });
-        std::cout << "Fiber id = " << id << " got result2 = " << result2
-                  << std::endl;
-      });
-
-      if (--toSend == 0) {
-        auto& loopController =
-            dynamic_cast<SimpleLoopController&>(fiberManager.loopController());
-        loopController.stop();
-      }
-    }
-  }
-
-  FiberManager fiberManager;
-
-  std::queue<Promise<int>> pendingRequests;
-  size_t toSend;
-  size_t maxOutstanding;
-};
-
-int main() {
-  Application app;
-
-  auto loop = [&app]() { app.loop(); };
-
-  auto& loopController =
-      dynamic_cast<SimpleLoopController&>(app.fiberManager.loopController());
-
-  loopController.loop(std::move(loop));
-
-  return 0;
-}
diff --git a/folly/experimental/fibers/test/StackOverflow.cpp b/folly/experimental/fibers/test/StackOverflow.cpp
deleted file mode 100644 (file)
index d8462e1..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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.
- */
-#include <folly/experimental/fibers/FiberManagerMap.h>
-#include <folly/experimental/symbolizer/SignalHandler.h>
-#include <folly/init/Init.h>
-
-void f(int* p) {
-  // Make sure recursion is not optimized out
-  int a[100];
-  for (size_t i = 0; i < 100; ++i) {
-    a[i] = i;
-    ++(a[i]);
-    if (p) {
-      a[i] += p[i];
-    }
-  }
-  f(a);
-}
-
-int main(int argc, char* argv[]) {
-  folly::init(&argc, &argv);
-
-  folly::EventBase evb;
-  folly::fibers::getFiberManager(evb).addTask([&]() { f(nullptr); });
-  evb.loop();
-}
diff --git a/folly/experimental/fibers/test/main.cpp b/folly/experimental/fibers/test/main.cpp
deleted file mode 100644 (file)
index ac94a9e..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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.
- */
-#include <gtest/gtest.h>
-
-#include <folly/Benchmark.h>
-
-// for backward compatibility with gflags
-namespace gflags {}
-namespace google {
-using namespace gflags;
-}
-
-int main(int argc, char** argv) {
-  testing::InitGoogleTest(&argc, argv);
-  gflags::ParseCommandLineFlags(&argc, &argv, true);
-
-  auto rc = RUN_ALL_TESTS();
-  folly::runBenchmarksOnFlag();
-  return rc;
-}
diff --git a/folly/experimental/fibers/traits.h b/folly/experimental/fibers/traits.h
deleted file mode 100644 (file)
index 3dc0e48..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2016 Facebook, Inc.
- *
- * 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 <boost/type_traits.hpp>
-
-namespace folly {
-namespace fibers {
-
-/**
- * For any functor F taking >= 1 argument,
- * FirstArgOf<F>::type is the type of F's first parameter.
- *
- * Rationale: we want to declare a function func(F), where F has the
- * signature `void(X)` and func should return T<X> (T and X are some types).
- * Solution:
- *
- * template <typename F>
- * T<typename FirstArgOf<F>::type>
- * func(F&& f);
- */
-
-namespace detail {
-
-/**
- * If F is a pointer-to-member, will contain a typedef type
- * with the type of F's first parameter
- */
-template <typename>
-struct ExtractFirstMemfn;
-
-template <typename Ret, typename T, typename First, typename... Args>
-struct ExtractFirstMemfn<Ret (T::*)(First, Args...)> {
-  typedef First type;
-};
-
-template <typename Ret, typename T, typename First, typename... Args>
-struct ExtractFirstMemfn<Ret (T::*)(First, Args...) const> {
-  typedef First type;
-};
-
-} // detail
-
-/** Default - use boost */
-template <typename F, typename Enable = void>
-struct FirstArgOf {
-  typedef typename boost::function_traits<
-      typename std::remove_pointer<F>::type>::arg1_type type;
-};
-
-/** Specialization for function objects */
-template <typename F>
-struct FirstArgOf<F, typename std::enable_if<std::is_class<F>::value>::type> {
-  typedef
-      typename detail::ExtractFirstMemfn<decltype(&F::operator())>::type type;
-};
-}
-} // folly::fibers
diff --git a/folly/fibers/AddTasks-inl.h b/folly/fibers/AddTasks-inl.h
new file mode 100644 (file)
index 0000000..f0a712e
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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.
+ */
+#include <memory>
+#include <vector>
+
+namespace folly {
+namespace fibers {
+
+template <typename T>
+TaskIterator<T>::TaskIterator(TaskIterator&& other) noexcept
+    : context_(std::move(other.context_)), id_(other.id_), fm_(other.fm_) {}
+
+template <typename T>
+inline bool TaskIterator<T>::hasCompleted() const {
+  return context_->tasksConsumed < context_->results.size();
+}
+
+template <typename T>
+inline bool TaskIterator<T>::hasPending() const {
+  return !context_.unique();
+}
+
+template <typename T>
+inline bool TaskIterator<T>::hasNext() const {
+  return hasPending() || hasCompleted();
+}
+
+template <typename T>
+folly::Try<T> TaskIterator<T>::awaitNextResult() {
+  assert(hasCompleted() || hasPending());
+  reserve(1);
+
+  size_t i = context_->tasksConsumed++;
+  id_ = context_->results[i].first;
+  return std::move(context_->results[i].second);
+}
+
+template <typename T>
+inline T TaskIterator<T>::awaitNext() {
+  return std::move(awaitNextResult().value());
+}
+
+template <>
+inline void TaskIterator<void>::awaitNext() {
+  awaitNextResult().value();
+}
+
+template <typename T>
+inline void TaskIterator<T>::reserve(size_t n) {
+  size_t tasksReady = context_->results.size() - context_->tasksConsumed;
+
+  // we don't need to do anything if there are already n or more tasks complete
+  // or if we have no tasks left to execute.
+  if (!hasPending() || tasksReady >= n) {
+    return;
+  }
+
+  n -= tasksReady;
+  size_t tasksLeft = context_->totalTasks - context_->results.size();
+  n = std::min(n, tasksLeft);
+
+  await([this, n](Promise<void> promise) {
+    context_->tasksToFulfillPromise = n;
+    context_->promise.assign(std::move(promise));
+  });
+}
+
+template <typename T>
+inline size_t TaskIterator<T>::getTaskID() const {
+  assert(id_ != static_cast<size_t>(-1));
+  return id_;
+}
+
+template <typename T>
+template <typename F>
+void TaskIterator<T>::addTask(F&& func) {
+  static_assert(
+      std::is_convertible<typename std::result_of<F()>::type, T>::value,
+      "TaskIterator<T>: T must be convertible from func()'s return type");
+
+  auto taskId = context_->totalTasks++;
+
+  fm_.addTask(
+      [ taskId, context = context_, func = std::forward<F>(func) ]() mutable {
+        context->results.emplace_back(
+            taskId, folly::makeTryWith(std::move(func)));
+
+        // Check for awaiting iterator.
+        if (context->promise.hasValue()) {
+          if (--context->tasksToFulfillPromise == 0) {
+            context->promise->setValue();
+            context->promise.clear();
+          }
+        }
+      });
+}
+
+template <class InputIterator>
+TaskIterator<typename std::result_of<
+    typename std::iterator_traits<InputIterator>::value_type()>::type>
+addTasks(InputIterator first, InputIterator last) {
+  typedef typename std::result_of<
+      typename std::iterator_traits<InputIterator>::value_type()>::type
+      ResultType;
+  typedef TaskIterator<ResultType> IteratorType;
+
+  IteratorType iterator;
+
+  for (; first != last; ++first) {
+    iterator.addTask(std::move(*first));
+  }
+
+  iterator.context_->results.reserve(iterator.context_->totalTasks);
+
+  return std::move(iterator);
+}
+}
+}
diff --git a/folly/fibers/AddTasks.h b/folly/fibers/AddTasks.h
new file mode 100644 (file)
index 0000000..9e65fcc
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <vector>
+
+#include <folly/Optional.h>
+#include <folly/fibers/FiberManager.h>
+#include <folly/fibers/Promise.h>
+#include <folly/futures/Try.h>
+
+namespace folly {
+namespace fibers {
+
+template <typename T>
+class TaskIterator;
+
+/**
+ * Schedules several tasks and immediately returns an iterator, that
+ * allow one to traverse tasks in the order of their completion. All results
+ * and exceptions thrown are stored alongside with the task id and are
+ * accessible via iterator.
+ *
+ * @param first Range of tasks to be scheduled
+ * @param last
+ *
+ * @return movable, non-copyable iterator
+ */
+template <class InputIterator>
+TaskIterator<typename std::result_of<
+    typename std::iterator_traits<InputIterator>::value_type()>::
+                 type> inline addTasks(InputIterator first, InputIterator last);
+
+template <typename T>
+class TaskIterator {
+ public:
+  typedef T value_type;
+
+  TaskIterator() : fm_(FiberManager::getFiberManager()) {}
+
+  // not copyable
+  TaskIterator(const TaskIterator& other) = delete;
+  TaskIterator& operator=(const TaskIterator& other) = delete;
+
+  // movable
+  TaskIterator(TaskIterator&& other) noexcept;
+  TaskIterator& operator=(TaskIterator&& other) = delete;
+
+  /**
+   * Add one more task to the TaskIterator.
+   *
+   * @param func task to be added, will be scheduled on current FiberManager
+   */
+  template <typename F>
+  void addTask(F&& func);
+
+  /**
+   * @return True if there are tasks immediately available to be consumed (no
+   *         need to await on them).
+   */
+  bool hasCompleted() const;
+
+  /**
+   * @return True if there are tasks pending execution (need to awaited on).
+   */
+  bool hasPending() const;
+
+  /**
+   * @return True if there are any tasks (hasCompleted() || hasPending()).
+   */
+  bool hasNext() const;
+
+  /**
+   * Await for another task to complete. Will not await if the result is
+   * already available.
+   *
+   * @return result of the task completed.
+   * @throw exception thrown by the task.
+   */
+  T awaitNext();
+
+  /**
+   * Await until the specified number of tasks completes or there are no
+   * tasks left to await for.
+   * Note: Will not await if there are already the specified number of tasks
+   * available.
+   *
+   * @param n   Number of tasks to await for completition.
+   */
+  void reserve(size_t n);
+
+  /**
+   * @return id of the last task that was processed by awaitNext().
+   */
+  size_t getTaskID() const;
+
+ private:
+  template <class InputIterator>
+  friend TaskIterator<typename std::result_of<
+      typename std::iterator_traits<InputIterator>::value_type()>::type>
+  addTasks(InputIterator first, InputIterator last);
+
+  struct Context {
+    std::vector<std::pair<size_t, folly::Try<T>>> results;
+    folly::Optional<Promise<void>> promise;
+    size_t totalTasks{0};
+    size_t tasksConsumed{0};
+    size_t tasksToFulfillPromise{0};
+  };
+
+  std::shared_ptr<Context> context_{std::make_shared<Context>()};
+  size_t id_{std::numeric_limits<size_t>::max()};
+  FiberManager& fm_;
+
+  folly::Try<T> awaitNextResult();
+};
+}
+}
+
+#include <folly/fibers/AddTasks-inl.h>
diff --git a/folly/fibers/Baton-inl.h b/folly/fibers/Baton-inl.h
new file mode 100644 (file)
index 0000000..6ca2de9
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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.
+ */
+#include <folly/fibers/Fiber.h>
+#include <folly/fibers/FiberManager.h>
+
+namespace folly {
+namespace fibers {
+
+inline Baton::Baton() : Baton(NO_WAITER) {
+  assert(Baton(NO_WAITER).futex_.futex == static_cast<uint32_t>(NO_WAITER));
+  assert(Baton(POSTED).futex_.futex == static_cast<uint32_t>(POSTED));
+  assert(Baton(TIMEOUT).futex_.futex == static_cast<uint32_t>(TIMEOUT));
+  assert(
+      Baton(THREAD_WAITING).futex_.futex ==
+      static_cast<uint32_t>(THREAD_WAITING));
+
+  assert(futex_.futex.is_lock_free());
+  assert(waitingFiber_.is_lock_free());
+}
+
+template <typename F>
+void Baton::wait(F&& mainContextFunc) {
+  auto fm = FiberManager::getFiberManagerUnsafe();
+  if (!fm || !fm->activeFiber_) {
+    mainContextFunc();
+    return waitThread();
+  }
+
+  return waitFiber(*fm, std::forward<F>(mainContextFunc));
+}
+
+template <typename F>
+void Baton::waitFiber(FiberManager& fm, F&& mainContextFunc) {
+  auto& waitingFiber = waitingFiber_;
+  auto f = [&mainContextFunc, &waitingFiber](Fiber& fiber) mutable {
+    auto baton_fiber = waitingFiber.load();
+    do {
+      if (LIKELY(baton_fiber == NO_WAITER)) {
+        continue;
+      } else if (baton_fiber == POSTED || baton_fiber == TIMEOUT) {
+        fiber.setData(0);
+        break;
+      } else {
+        throw std::logic_error("Some Fiber is already waiting on this Baton.");
+      }
+    } while (!waitingFiber.compare_exchange_weak(
+        baton_fiber, reinterpret_cast<intptr_t>(&fiber)));
+
+    mainContextFunc();
+  };
+
+  fm.awaitFunc_ = std::ref(f);
+  fm.activeFiber_->preempt(Fiber::AWAITING);
+}
+
+template <typename F>
+bool Baton::timed_wait(
+    TimeoutController::Duration timeout,
+    F&& mainContextFunc) {
+  auto fm = FiberManager::getFiberManagerUnsafe();
+
+  if (!fm || !fm->activeFiber_) {
+    mainContextFunc();
+    return timedWaitThread(timeout);
+  }
+
+  auto& baton = *this;
+  bool canceled = false;
+  auto timeoutFunc = [&baton, &canceled]() mutable {
+    baton.postHelper(TIMEOUT);
+    canceled = true;
+  };
+
+  auto id =
+      fm->timeoutManager_->registerTimeout(std::ref(timeoutFunc), timeout);
+
+  waitFiber(*fm, std::move(mainContextFunc));
+
+  auto posted = waitingFiber_ == POSTED;
+
+  if (!canceled) {
+    fm->timeoutManager_->cancel(id);
+  }
+
+  return posted;
+}
+
+template <typename C, typename D>
+bool Baton::timed_wait(const std::chrono::time_point<C, D>& timeout) {
+  auto now = C::now();
+
+  if (LIKELY(now <= timeout)) {
+    return timed_wait(
+        std::chrono::duration_cast<std::chrono::milliseconds>(timeout - now));
+  } else {
+    return timed_wait(TimeoutController::Duration(0));
+  }
+}
+}
+}
diff --git a/folly/fibers/Baton.cpp b/folly/fibers/Baton.cpp
new file mode 100644 (file)
index 0000000..7161f9f
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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.
+ */
+#include "Baton.h"
+
+#include <chrono>
+
+#include <folly/detail/MemoryIdler.h>
+#include <folly/fibers/FiberManager.h>
+#include <folly/portability/Asm.h>
+
+namespace folly {
+namespace fibers {
+
+void Baton::wait() {
+  wait([]() {});
+}
+
+void Baton::wait(TimeoutHandler& timeoutHandler) {
+  auto timeoutFunc = [this, &timeoutHandler] {
+    if (!try_wait()) {
+      postHelper(TIMEOUT);
+    }
+    timeoutHandler.timeoutPtr_ = 0;
+  };
+  timeoutHandler.timeoutFunc_ = std::ref(timeoutFunc);
+  timeoutHandler.fiberManager_ = FiberManager::getFiberManagerUnsafe();
+  wait();
+  timeoutHandler.cancelTimeout();
+}
+
+bool Baton::timed_wait(TimeoutController::Duration timeout) {
+  return timed_wait(timeout, []() {});
+}
+
+void Baton::waitThread() {
+  if (spinWaitForEarlyPost()) {
+    assert(waitingFiber_.load(std::memory_order_acquire) == POSTED);
+    return;
+  }
+
+  auto fiber = waitingFiber_.load();
+
+  if (LIKELY(
+          fiber == NO_WAITER &&
+          waitingFiber_.compare_exchange_strong(fiber, THREAD_WAITING))) {
+    do {
+      folly::detail::MemoryIdler::futexWait(futex_.futex, THREAD_WAITING);
+      fiber = waitingFiber_.load(std::memory_order_relaxed);
+    } while (fiber == THREAD_WAITING);
+  }
+
+  if (LIKELY(fiber == POSTED)) {
+    return;
+  }
+
+  // Handle errors
+  if (fiber == TIMEOUT) {
+    throw std::logic_error("Thread baton can't have timeout status");
+  }
+  if (fiber == THREAD_WAITING) {
+    throw std::logic_error("Other thread is already waiting on this baton");
+  }
+  throw std::logic_error("Other fiber is already waiting on this baton");
+}
+
+bool Baton::spinWaitForEarlyPost() {
+  static_assert(
+      PreBlockAttempts > 0,
+      "isn't this assert clearer than an uninitialized variable warning?");
+  for (int i = 0; i < PreBlockAttempts; ++i) {
+    if (try_wait()) {
+      // hooray!
+      return true;
+    }
+    // The pause instruction is the polite way to spin, but it doesn't
+    // actually affect correctness to omit it if we don't have it.
+    // Pausing donates the full capabilities of the current core to
+    // its other hyperthreads for a dozen cycles or so
+    asm_volatile_pause();
+  }
+
+  return false;
+}
+
+bool Baton::timedWaitThread(TimeoutController::Duration timeout) {
+  if (spinWaitForEarlyPost()) {
+    assert(waitingFiber_.load(std::memory_order_acquire) == POSTED);
+    return true;
+  }
+
+  auto fiber = waitingFiber_.load();
+
+  if (LIKELY(
+          fiber == NO_WAITER &&
+          waitingFiber_.compare_exchange_strong(fiber, THREAD_WAITING))) {
+    auto deadline = TimeoutController::Clock::now() + timeout;
+    do {
+      const auto wait_rv =
+          futex_.futex.futexWaitUntil(THREAD_WAITING, deadline);
+      if (wait_rv == folly::detail::FutexResult::TIMEDOUT) {
+        return false;
+      }
+      fiber = waitingFiber_.load(std::memory_order_relaxed);
+    } while (fiber == THREAD_WAITING);
+  }
+
+  if (LIKELY(fiber == POSTED)) {
+    return true;
+  }
+
+  // Handle errors
+  if (fiber == TIMEOUT) {
+    throw std::logic_error("Thread baton can't have timeout status");
+  }
+  if (fiber == THREAD_WAITING) {
+    throw std::logic_error("Other thread is already waiting on this baton");
+  }
+  throw std::logic_error("Other fiber is already waiting on this baton");
+}
+
+void Baton::post() {
+  postHelper(POSTED);
+}
+
+void Baton::postHelper(intptr_t new_value) {
+  auto fiber = waitingFiber_.load();
+
+  do {
+    if (fiber == THREAD_WAITING) {
+      assert(new_value == POSTED);
+
+      return postThread();
+    }
+
+    if (fiber == POSTED || fiber == TIMEOUT) {
+      return;
+    }
+  } while (!waitingFiber_.compare_exchange_weak(fiber, new_value));
+
+  if (fiber != NO_WAITER) {
+    reinterpret_cast<Fiber*>(fiber)->setData(0);
+  }
+}
+
+bool Baton::try_wait() {
+  auto state = waitingFiber_.load();
+  return state == POSTED;
+}
+
+void Baton::postThread() {
+  auto expected = THREAD_WAITING;
+
+  if (!waitingFiber_.compare_exchange_strong(expected, POSTED)) {
+    return;
+  }
+
+  futex_.futex.futexWake(1);
+}
+
+void Baton::reset() {
+  waitingFiber_.store(NO_WAITER, std::memory_order_relaxed);
+  ;
+}
+
+void Baton::TimeoutHandler::scheduleTimeout(
+    TimeoutController::Duration timeout) {
+  assert(fiberManager_ != nullptr);
+  assert(timeoutFunc_ != nullptr);
+  assert(timeoutPtr_ == 0);
+
+  if (timeout.count() > 0) {
+    timeoutPtr_ =
+        fiberManager_->timeoutManager_->registerTimeout(timeoutFunc_, timeout);
+  }
+}
+
+void Baton::TimeoutHandler::cancelTimeout() {
+  if (timeoutPtr_) {
+    fiberManager_->timeoutManager_->cancel(timeoutPtr_);
+  }
+}
+}
+}
diff --git a/folly/fibers/Baton.h b/folly/fibers/Baton.h
new file mode 100644 (file)
index 0000000..e5b92d1
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <atomic>
+
+#include <folly/detail/Futex.h>
+#include <folly/fibers/TimeoutController.h>
+
+namespace folly {
+namespace fibers {
+
+class Fiber;
+class FiberManager;
+
+/**
+ * @class Baton
+ *
+ * Primitive which allows one to put current Fiber to sleep and wake it from
+ * another Fiber/thread.
+ */
+class Baton {
+ public:
+  class TimeoutHandler;
+
+  Baton();
+
+  ~Baton() {}
+
+  /**
+   * Puts active fiber to sleep. Returns when post is called.
+   */
+  void wait();
+
+  /**
+   * Put active fiber to sleep indefinitely. However, timeoutHandler may
+   * be used elsewhere on the same thread in order to schedule a wakeup
+   * for the active fiber.  Users of timeoutHandler must be on the same thread
+   * as the active fiber and may only schedule one timeout, which must occur
+   * after the active fiber calls wait.
+   */
+  void wait(TimeoutHandler& timeoutHandler);
+
+  /**
+   * Puts active fiber to sleep. Returns when post is called.
+   *
+   * @param mainContextFunc this function is immediately executed on the main
+   *        context.
+   */
+  template <typename F>
+  void wait(F&& mainContextFunc);
+
+  /**
+   * This is here only not break tao/locks. Please don't use it, because it is
+   * inefficient when used on Fibers.
+   */
+  template <typename C, typename D = typename C::duration>
+  bool timed_wait(const std::chrono::time_point<C, D>& timeout);
+
+  /**
+   * Puts active fiber to sleep. Returns when post is called.
+   *
+   * @param timeout Baton will be automatically awaken if timeout is hit
+   *
+   * @return true if was posted, false if timeout expired
+   */
+  bool timed_wait(TimeoutController::Duration timeout);
+
+  /**
+   * Puts active fiber to sleep. Returns when post is called.
+   *
+   * @param timeout Baton will be automatically awaken if timeout is hit
+   * @param mainContextFunc this function is immediately executed on the main
+   *        context.
+   *
+   * @return true if was posted, false if timeout expired
+   */
+  template <typename F>
+  bool timed_wait(TimeoutController::Duration timeout, F&& mainContextFunc);
+
+  /**
+   * Checks if the baton has been posted without blocking.
+   * @return    true iff the baton has been posted.
+   */
+  bool try_wait();
+
+  /**
+   * Wakes up Fiber which was waiting on this Baton (or if no Fiber is waiting,
+   * next wait() call will return immediately).
+   */
+  void post();
+
+  /**
+   * Reset's the baton (equivalent to destroying the object and constructing
+   * another one in place).
+   * Caller is responsible for making sure no one is waiting on/posting the
+   * baton when reset() is called.
+   */
+  void reset();
+
+  /**
+   * Provides a way to schedule a wakeup for a wait()ing fiber.
+   * A TimeoutHandler must be passed to Baton::wait(TimeoutHandler&)
+   * before a timeout is scheduled. It is only safe to use the
+   * TimeoutHandler on the same thread as the wait()ing fiber.
+   * scheduleTimeout() may only be called once prior to the end of the
+   * associated Baton's life.
+   */
+  class TimeoutHandler {
+   public:
+    void scheduleTimeout(TimeoutController::Duration timeoutMs);
+
+   private:
+    friend class Baton;
+
+    void cancelTimeout();
+
+    std::function<void()> timeoutFunc_{nullptr};
+    FiberManager* fiberManager_{nullptr};
+
+    intptr_t timeoutPtr_{0};
+  };
+
+ private:
+  enum {
+    /**
+     * Must be positive.  If multiple threads are actively using a
+     * higher-level data structure that uses batons internally, it is
+     * likely that the post() and wait() calls happen almost at the same
+     * time.  In this state, we lose big 50% of the time if the wait goes
+     * to sleep immediately.  On circa-2013 devbox hardware it costs about
+     * 7 usec to FUTEX_WAIT and then be awoken (half the t/iter as the
+     * posix_sem_pingpong test in BatonTests).  We can improve our chances
+     * of early post by spinning for a bit, although we have to balance
+     * this against the loss if we end up sleeping any way.  Spins on this
+     * hw take about 7 nanos (all but 0.5 nanos is the pause instruction).
+     * We give ourself 300 spins, which is about 2 usec of waiting.  As a
+     * partial consolation, since we are using the pause instruction we
+     * are giving a speed boost to the colocated hyperthread.
+     */
+    PreBlockAttempts = 300,
+  };
+
+  explicit Baton(intptr_t state) : waitingFiber_(state){};
+
+  void postHelper(intptr_t new_value);
+  void postThread();
+  void waitThread();
+
+  template <typename F>
+  inline void waitFiber(FiberManager& fm, F&& mainContextFunc);
+  /**
+   * Spin for "some time" (see discussion on PreBlockAttempts) waiting
+   * for a post.
+   * @return true if we received a post the spin wait, false otherwise. If the
+   *         function returns true then Baton state is guaranteed to be POSTED
+   */
+  bool spinWaitForEarlyPost();
+
+  bool timedWaitThread(TimeoutController::Duration timeout);
+
+  static constexpr intptr_t NO_WAITER = 0;
+  static constexpr intptr_t POSTED = -1;
+  static constexpr intptr_t TIMEOUT = -2;
+  static constexpr intptr_t THREAD_WAITING = -3;
+
+  union {
+    std::atomic<intptr_t> waitingFiber_;
+    struct {
+      folly::detail::Futex<> futex;
+      int32_t _unused_packing;
+    } futex_;
+  };
+};
+}
+}
+
+#include <folly/fibers/Baton-inl.h>
diff --git a/folly/fibers/BoostContextCompatibility.h b/folly/fibers/BoostContextCompatibility.h
new file mode 100644 (file)
index 0000000..e243767
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <boost/context/all.hpp>
+#include <boost/version.hpp>
+
+/**
+ * Wrappers for different versions of boost::context library
+ * API reference for different versions
+ * Boost 1.51:
+ * http://www.boost.org/doc/libs/1_51_0/libs/context/doc/html/context/context/boost_fcontext.html
+ * Boost 1.52:
+ * http://www.boost.org/doc/libs/1_52_0/libs/context/doc/html/context/context/boost_fcontext.html
+ * Boost 1.56:
+ * http://www.boost.org/doc/libs/1_56_0/libs/context/doc/html/context/context/boost_fcontext.html
+ */
+
+namespace folly {
+namespace fibers {
+
+struct FContext {
+ public:
+#if BOOST_VERSION >= 105200
+  using ContextStruct = boost::context::fcontext_t;
+#else
+  using ContextStruct = boost::ctx::fcontext_t;
+#endif
+
+  void* stackLimit() const {
+    return stackLimit_;
+  }
+
+  void* stackBase() const {
+    return stackBase_;
+  }
+
+ private:
+  void* stackLimit_;
+  void* stackBase_;
+
+#if BOOST_VERSION >= 105600
+  ContextStruct context_;
+#elif BOOST_VERSION >= 105200
+  ContextStruct* context_;
+#else
+  ContextStruct context_;
+#endif
+
+  friend intptr_t
+  jumpContext(FContext* oldC, FContext::ContextStruct* newC, intptr_t p);
+  friend intptr_t
+  jumpContext(FContext::ContextStruct* oldC, FContext* newC, intptr_t p);
+  friend FContext
+  makeContext(void* stackLimit, size_t stackSize, void (*fn)(intptr_t));
+};
+
+inline intptr_t
+jumpContext(FContext* oldC, FContext::ContextStruct* newC, intptr_t p) {
+#if BOOST_VERSION >= 105600
+  return boost::context::jump_fcontext(&oldC->context_, *newC, p);
+#elif BOOST_VERSION >= 105200
+  return boost::context::jump_fcontext(oldC->context_, newC, p);
+#else
+  return jump_fcontext(&oldC->context_, newC, p);
+#endif
+}
+
+inline intptr_t
+jumpContext(FContext::ContextStruct* oldC, FContext* newC, intptr_t p) {
+#if BOOST_VERSION >= 105200
+  return boost::context::jump_fcontext(oldC, newC->context_, p);
+#else
+  return jump_fcontext(oldC, &newC->context_, p);
+#endif
+}
+
+inline FContext
+makeContext(void* stackLimit, size_t stackSize, void (*fn)(intptr_t)) {
+  FContext res;
+  res.stackLimit_ = stackLimit;
+  res.stackBase_ = static_cast<unsigned char*>(stackLimit) + stackSize;
+
+#if BOOST_VERSION >= 105200
+  res.context_ = boost::context::make_fcontext(res.stackBase_, stackSize, fn);
+#else
+  res.context_.fc_stack.limit = stackLimit;
+  res.context_.fc_stack.base = res.stackBase_;
+  make_fcontext(&res.context_, fn);
+#endif
+
+  return res;
+}
+}
+} // folly::fibers
diff --git a/folly/fibers/EventBaseLoopController-inl.h b/folly/fibers/EventBaseLoopController-inl.h
new file mode 100644 (file)
index 0000000..c9109c9
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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.
+ */
+#include <folly/Memory.h>
+#include <folly/fibers/EventBaseLoopController.h>
+#include <folly/fibers/FiberManager.h>
+
+namespace folly {
+namespace fibers {
+
+inline EventBaseLoopController::EventBaseLoopController()
+    : callback_(*this), aliveWeak_(destructionCallback_.getWeak()) {}
+
+inline EventBaseLoopController::~EventBaseLoopController() {
+  callback_.cancelLoopCallback();
+}
+
+inline void EventBaseLoopController::attachEventBase(
+    folly::EventBase& eventBase) {
+  if (eventBase_ != nullptr) {
+    LOG(ERROR) << "Attempt to reattach EventBase to LoopController";
+  }
+
+  eventBase_ = &eventBase;
+  eventBase_->runOnDestruction(&destructionCallback_);
+
+  eventBaseAttached_ = true;
+
+  if (awaitingScheduling_) {
+    schedule();
+  }
+}
+
+inline void EventBaseLoopController::setFiberManager(FiberManager* fm) {
+  fm_ = fm;
+}
+
+inline void EventBaseLoopController::schedule() {
+  if (eventBase_ == nullptr) {
+    // In this case we need to postpone scheduling.
+    awaitingScheduling_ = true;
+  } else {
+    // Schedule it to run in current iteration.
+    eventBase_->runInLoop(&callback_, true);
+    awaitingScheduling_ = false;
+  }
+}
+
+inline void EventBaseLoopController::cancel() {
+  callback_.cancelLoopCallback();
+}
+
+inline void EventBaseLoopController::runLoop() {
+  fm_->loopUntilNoReady();
+}
+
+inline void EventBaseLoopController::scheduleThreadSafe(
+    std::function<bool()> func) {
+  /* The only way we could end up here is if
+     1) Fiber thread creates a fiber that awaits (which means we must
+        have already attached, fiber thread wouldn't be running).
+     2) We move the promise to another thread (this move is a memory fence)
+     3) We fulfill the promise from the other thread. */
+  assert(eventBaseAttached_);
+
+  auto alive = aliveWeak_.lock();
+
+  if (func() && alive) {
+    auto aliveWeak = aliveWeak_;
+    eventBase_->runInEventBaseThread([this, aliveWeak]() {
+      if (!aliveWeak.expired()) {
+        runLoop();
+      }
+    });
+  }
+}
+
+inline void EventBaseLoopController::timedSchedule(
+    std::function<void()> func,
+    TimePoint time) {
+  assert(eventBaseAttached_);
+
+  // We want upper bound for the cast, thus we just add 1
+  auto delay_ms =
+      std::chrono::duration_cast<std::chrono::milliseconds>(time - Clock::now())
+          .count() +
+      1;
+  // If clock is not monotonic
+  delay_ms = std::max<decltype(delay_ms)>(delay_ms, 0L);
+  eventBase_->tryRunAfterDelay(func, delay_ms);
+}
+}
+} // folly::fibers
diff --git a/folly/fibers/EventBaseLoopController.h b/folly/fibers/EventBaseLoopController.h
new file mode 100644 (file)
index 0000000..4d4ca99
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <folly/fibers/LoopController.h>
+#include <folly/io/async/EventBase.h>
+#include <atomic>
+#include <memory>
+
+namespace folly {
+class EventBase;
+}
+
+namespace folly {
+namespace fibers {
+
+class FiberManager;
+
+class EventBaseLoopController : public LoopController {
+ public:
+  explicit EventBaseLoopController();
+  ~EventBaseLoopController();
+
+  /**
+   * Attach EventBase after LoopController was created.
+   */
+  void attachEventBase(folly::EventBase& eventBase);
+
+  folly::EventBase* getEventBase() {
+    return eventBase_;
+  }
+
+ private:
+  class ControllerCallback : public folly::EventBase::LoopCallback {
+   public:
+    explicit ControllerCallback(EventBaseLoopController& controller)
+        : controller_(controller) {}
+
+    void runLoopCallback() noexcept override {
+      controller_.runLoop();
+    }
+
+   private:
+    EventBaseLoopController& controller_;
+  };
+
+  class DestructionCallback : public folly::EventBase::LoopCallback {
+   public:
+    DestructionCallback() : alive_(new int(42)) {}
+    ~DestructionCallback() {
+      reset();
+    }
+
+    void runLoopCallback() noexcept override {
+      reset();
+    }
+
+    std::weak_ptr<void> getWeak() {
+      return {alive_};
+    }
+
+   private:
+    void reset() {
+      std::weak_ptr<void> aliveWeak(alive_);
+      alive_.reset();
+
+      while (!aliveWeak.expired()) {
+        // Spin until all operations requiring EventBaseLoopController to be
+        // alive are complete.
+      }
+    }
+
+    std::shared_ptr<void> alive_;
+  };
+
+  bool awaitingScheduling_{false};
+  folly::EventBase* eventBase_{nullptr};
+  ControllerCallback callback_;
+  DestructionCallback destructionCallback_;
+  FiberManager* fm_{nullptr};
+  std::atomic<bool> eventBaseAttached_{false};
+  std::weak_ptr<void> aliveWeak_;
+
+  /* LoopController interface */
+
+  void setFiberManager(FiberManager* fm) override;
+  void schedule() override;
+  void cancel() override;
+  void runLoop();
+  void scheduleThreadSafe(std::function<bool()> func) override;
+  void timedSchedule(std::function<void()> func, TimePoint time) override;
+
+  friend class FiberManager;
+};
+}
+} // folly::fibers
+
+#include "EventBaseLoopController-inl.h"
diff --git a/folly/fibers/Fiber-inl.h b/folly/fibers/Fiber-inl.h
new file mode 100644 (file)
index 0000000..9c87169
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <cassert>
+
+namespace folly {
+namespace fibers {
+
+template <typename F>
+void Fiber::setFunction(F&& func) {
+  assert(state_ == INVALID);
+  func_ = std::forward<F>(func);
+  state_ = NOT_STARTED;
+}
+
+template <typename F, typename G>
+void Fiber::setFunctionFinally(F&& resultFunc, G&& finallyFunc) {
+  assert(state_ == INVALID);
+  resultFunc_ = std::forward<F>(resultFunc);
+  finallyFunc_ = std::forward<G>(finallyFunc);
+  state_ = NOT_STARTED;
+}
+
+inline void* Fiber::getUserBuffer() {
+  return &userBuffer_;
+}
+
+template <typename T>
+T& Fiber::LocalData::getSlow() {
+  dataSize_ = sizeof(T);
+  dataType_ = &typeid(T);
+  if (sizeof(T) <= kBufferSize) {
+    dataDestructor_ = dataBufferDestructor<T>;
+    data_ = &buffer_;
+  } else {
+    dataDestructor_ = dataHeapDestructor<T>;
+    data_ = allocateHeapBuffer(dataSize_);
+  }
+  dataCopyConstructor_ = dataCopyConstructor<T>;
+
+  new (reinterpret_cast<T*>(data_)) T();
+
+  return *reinterpret_cast<T*>(data_);
+}
+
+template <typename T>
+void Fiber::LocalData::dataCopyConstructor(void* ptr, const void* other) {
+  new (reinterpret_cast<T*>(ptr)) T(*reinterpret_cast<const T*>(other));
+}
+
+template <typename T>
+void Fiber::LocalData::dataBufferDestructor(void* ptr) {
+  reinterpret_cast<T*>(ptr)->~T();
+}
+
+template <typename T>
+void Fiber::LocalData::dataHeapDestructor(void* ptr) {
+  reinterpret_cast<T*>(ptr)->~T();
+  freeHeapBuffer(ptr);
+}
+}
+} // folly::fibers
diff --git a/folly/fibers/Fiber.cpp b/folly/fibers/Fiber.cpp
new file mode 100644 (file)
index 0000000..bbbc5b8
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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.
+ */
+#include "Fiber.h"
+
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <glog/logging.h>
+#include <algorithm>
+#include <cstring>
+#include <stdexcept>
+
+#include <folly/Likely.h>
+#include <folly/Portability.h>
+#include <folly/fibers/BoostContextCompatibility.h>
+#include <folly/fibers/FiberManager.h>
+#include <folly/portability/SysSyscall.h>
+
+namespace folly {
+namespace fibers {
+
+namespace {
+static const uint64_t kMagic8Bytes = 0xfaceb00cfaceb00c;
+
+std::thread::id localThreadId() {
+  return std::this_thread::get_id();
+}
+
+/* Size of the region from p + nBytes down to the last non-magic value */
+static size_t nonMagicInBytes(const FContext& context) {
+  uint64_t* begin = static_cast<uint64_t*>(context.stackLimit());
+  uint64_t* end = static_cast<uint64_t*>(context.stackBase());
+
+  auto firstNonMagic = std::find_if(
+      begin, end, [](uint64_t val) { return val != kMagic8Bytes; });
+
+  return (end - firstNonMagic) * sizeof(uint64_t);
+}
+
+} // anonymous namespace
+
+void Fiber::setData(intptr_t data) {
+  DCHECK_EQ(state_, AWAITING);
+  data_ = data;
+  state_ = READY_TO_RUN;
+
+  if (fiberManager_.observer_) {
+    fiberManager_.observer_->runnable(reinterpret_cast<uintptr_t>(this));
+  }
+
+  if (LIKELY(threadId_ == localThreadId())) {
+    fiberManager_.readyFibers_.push_back(*this);
+    fiberManager_.ensureLoopScheduled();
+  } else {
+    fiberManager_.remoteReadyInsert(this);
+  }
+}
+
+Fiber::Fiber(FiberManager& fiberManager) : fiberManager_(fiberManager) {
+  auto size = fiberManager_.options_.stackSize;
+  auto limit = fiberManager_.stackAllocator_.allocate(size);
+
+  fcontext_ = makeContext(limit, size, &Fiber::fiberFuncHelper);
+
+  fiberManager_.allFibers_.push_back(*this);
+}
+
+void Fiber::init(bool recordStackUsed) {
+// It is necessary to disable the logic for ASAN because we change
+// the fiber's stack.
+#ifndef FOLLY_SANITIZE_ADDRESS
+  recordStackUsed_ = recordStackUsed;
+  if (UNLIKELY(recordStackUsed_ && !stackFilledWithMagic_)) {
+    auto limit = fcontext_.stackLimit();
+    auto base = fcontext_.stackBase();
+
+    std::fill(
+        static_cast<uint64_t*>(limit),
+        static_cast<uint64_t*>(base),
+        kMagic8Bytes);
+
+    // newer versions of boost allocate context on fiber stack,
+    // need to create a new one
+    auto size = fiberManager_.options_.stackSize;
+    fcontext_ = makeContext(limit, size, &Fiber::fiberFuncHelper);
+
+    stackFilledWithMagic_ = true;
+  }
+#else
+  (void)recordStackUsed;
+#endif
+}
+
+Fiber::~Fiber() {
+#ifdef FOLLY_SANITIZE_ADDRESS
+  fiberManager_.unpoisonFiberStack(this);
+#endif
+  fiberManager_.stackAllocator_.deallocate(
+      static_cast<unsigned char*>(fcontext_.stackLimit()),
+      fiberManager_.options_.stackSize);
+}
+
+void Fiber::recordStackPosition() {
+  int stackDummy;
+  auto currentPosition = static_cast<size_t>(
+      static_cast<unsigned char*>(fcontext_.stackBase()) -
+      static_cast<unsigned char*>(static_cast<void*>(&stackDummy)));
+  fiberManager_.stackHighWatermark_ =
+      std::max(fiberManager_.stackHighWatermark_, currentPosition);
+  VLOG(4) << "Stack usage: " << currentPosition;
+}
+
+void Fiber::fiberFuncHelper(intptr_t fiber) {
+  reinterpret_cast<Fiber*>(fiber)->fiberFunc();
+}
+
+void Fiber::fiberFunc() {
+  while (true) {
+    DCHECK_EQ(state_, NOT_STARTED);
+
+    threadId_ = localThreadId();
+    state_ = RUNNING;
+
+    try {
+      if (resultFunc_) {
+        DCHECK(finallyFunc_);
+        DCHECK(!func_);
+
+        resultFunc_();
+      } else {
+        DCHECK(func_);
+        func_();
+      }
+    } catch (...) {
+      fiberManager_.exceptionCallback_(
+          std::current_exception(), "running Fiber func_/resultFunc_");
+    }
+
+    if (UNLIKELY(recordStackUsed_)) {
+      fiberManager_.stackHighWatermark_ = std::max(
+          fiberManager_.stackHighWatermark_, nonMagicInBytes(fcontext_));
+      VLOG(3) << "Max stack usage: " << fiberManager_.stackHighWatermark_;
+      CHECK(
+          fiberManager_.stackHighWatermark_ <
+          fiberManager_.options_.stackSize - 64)
+          << "Fiber stack overflow";
+    }
+
+    state_ = INVALID;
+
+    auto context = fiberManager_.deactivateFiber(this);
+
+    DCHECK_EQ(reinterpret_cast<Fiber*>(context), this);
+  }
+}
+
+intptr_t Fiber::preempt(State state) {
+  intptr_t ret;
+
+  auto preemptImpl = [&]() mutable {
+    DCHECK_EQ(fiberManager_.activeFiber_, this);
+    DCHECK_EQ(state_, RUNNING);
+    DCHECK_NE(state, RUNNING);
+
+    state_ = state;
+
+    recordStackPosition();
+
+    ret = fiberManager_.deactivateFiber(this);
+
+    DCHECK_EQ(fiberManager_.activeFiber_, this);
+    DCHECK_EQ(state_, READY_TO_RUN);
+    state_ = RUNNING;
+  };
+
+  if (fiberManager_.preemptRunner_) {
+    fiberManager_.preemptRunner_->run(std::ref(preemptImpl));
+  } else {
+    preemptImpl();
+  }
+
+  return ret;
+}
+
+Fiber::LocalData::LocalData(const LocalData& other) : data_(nullptr) {
+  *this = other;
+}
+
+Fiber::LocalData& Fiber::LocalData::operator=(const LocalData& other) {
+  reset();
+  if (!other.data_) {
+    return *this;
+  }
+
+  dataSize_ = other.dataSize_;
+  dataType_ = other.dataType_;
+  dataDestructor_ = other.dataDestructor_;
+  dataCopyConstructor_ = other.dataCopyConstructor_;
+
+  if (dataSize_ <= kBufferSize) {
+    data_ = &buffer_;
+  } else {
+    data_ = allocateHeapBuffer(dataSize_);
+  }
+
+  dataCopyConstructor_(data_, other.data_);
+
+  return *this;
+}
+
+void Fiber::LocalData::reset() {
+  if (!data_) {
+    return;
+  }
+
+  dataDestructor_(data_);
+  data_ = nullptr;
+}
+
+void* Fiber::LocalData::allocateHeapBuffer(size_t size) {
+  return new char[size];
+}
+
+void Fiber::LocalData::freeHeapBuffer(void* buffer) {
+  delete[] reinterpret_cast<char*>(buffer);
+}
+}
+}
diff --git a/folly/fibers/Fiber.h b/folly/fibers/Fiber.h
new file mode 100644 (file)
index 0000000..543deef
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <typeinfo>
+
+#include <boost/context/all.hpp>
+#include <boost/version.hpp>
+#include <folly/AtomicIntrusiveLinkedList.h>
+#include <folly/CPortability.h>
+#include <folly/Function.h>
+#include <folly/IntrusiveList.h>
+#include <folly/Portability.h>
+#include <folly/fibers/BoostContextCompatibility.h>
+#include <folly/io/async/Request.h>
+
+namespace folly {
+namespace fibers {
+
+class Baton;
+class FiberManager;
+
+/**
+ * @class Fiber
+ * @brief Fiber object used by FiberManager to execute tasks.
+ *
+ * Each Fiber object can be executing at most one task at a time. In active
+ * phase it is running the task function and keeps its context.
+ * Fiber is also used to pass data to blocked task and thus unblock it.
+ * Each Fiber may be associated with a single FiberManager.
+ */
+class Fiber {
+ public:
+  /**
+   * Sets data for the blocked task
+   *
+   * @param data this data will be returned by await() when task is resumed.
+   */
+  void setData(intptr_t data);
+
+  Fiber(const Fiber&) = delete;
+  Fiber& operator=(const Fiber&) = delete;
+
+  ~Fiber();
+
+  /**
+   * Retrieve this fiber's base stack and stack size.
+   *
+   * @return This fiber's stack pointer and stack size.
+   */
+  std::pair<void*, size_t> getStack() const {
+    void* const stack =
+        std::min<void*>(fcontext_.stackLimit(), fcontext_.stackBase());
+    const size_t size = std::abs<intptr_t>(
+        reinterpret_cast<intptr_t>(fcontext_.stackBase()) -
+        reinterpret_cast<intptr_t>(fcontext_.stackLimit()));
+    return {stack, size};
+  }
+
+ private:
+  enum State {
+    INVALID, /**< Does't have task function */
+    NOT_STARTED, /**< Has task function, not started */
+    READY_TO_RUN, /**< Was started, blocked, then unblocked */
+    RUNNING, /**< Is running right now */
+    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 */
+
+  friend class Baton;
+  friend class FiberManager;
+
+  explicit Fiber(FiberManager& fiberManager);
+
+  void init(bool recordStackUsed);
+
+  template <typename F>
+  void setFunction(F&& func);
+
+  template <typename F, typename G>
+  void setFunctionFinally(F&& func, G&& finally);
+
+  static void fiberFuncHelper(intptr_t fiber);
+  void fiberFunc();
+
+  /**
+   * Switch out of fiber context into the main context,
+   * performing necessary housekeeping for the new state.
+   *
+   * @param state New state, must not be RUNNING.
+   *
+   * @return The value passed back from the main context.
+   */
+  intptr_t preempt(State state);
+
+  /**
+   * Examines how much of the stack we used at this moment and
+   * registers with the FiberManager (for monitoring).
+   */
+  void recordStackPosition();
+
+  FiberManager& fiberManager_; /**< Associated FiberManager */
+  FContext fcontext_; /**< current task execution context */
+  intptr_t data_; /**< Used to keep some data with the Fiber */
+  std::shared_ptr<RequestContext> rcontext_; /**< current RequestContext */
+  folly::Function<void()> func_; /**< task function */
+  bool recordStackUsed_{false};
+  bool stackFilledWithMagic_{false};
+
+  /**
+   * Points to next fiber in remote ready list
+   */
+  folly::AtomicIntrusiveLinkedListHook<Fiber> nextRemoteReady_;
+
+  static constexpr size_t kUserBufferSize = 256;
+  std::aligned_storage<kUserBufferSize>::type userBuffer_;
+
+  void* getUserBuffer();
+
+  folly::Function<void()> resultFunc_;
+  folly::Function<void()> finallyFunc_;
+
+  class LocalData {
+   public:
+    LocalData() {}
+    LocalData(const LocalData& other);
+    LocalData& operator=(const LocalData& other);
+
+    template <typename T>
+    T& get() {
+      if (data_) {
+        assert(*dataType_ == typeid(T));
+        return *reinterpret_cast<T*>(data_);
+      }
+      return getSlow<T>();
+    }
+
+    void reset();
+
+    // private:
+    template <typename T>
+    FOLLY_NOINLINE T& getSlow();
+
+    static void* allocateHeapBuffer(size_t size);
+    static void freeHeapBuffer(void* buffer);
+
+    template <typename T>
+    static void dataCopyConstructor(void*, const void*);
+    template <typename T>
+    static void dataBufferDestructor(void*);
+    template <typename T>
+    static void dataHeapDestructor(void*);
+
+    static constexpr size_t kBufferSize = 128;
+    std::aligned_storage<kBufferSize>::type buffer_;
+    size_t dataSize_;
+
+    const std::type_info* dataType_;
+    void (*dataDestructor_)(void*);
+    void (*dataCopyConstructor_)(void*, const void*);
+    void* data_{nullptr};
+  };
+
+  LocalData localData_;
+
+  folly::IntrusiveListHook listHook_; /**< list hook for different FiberManager
+                                           queues */
+  folly::IntrusiveListHook globalListHook_; /**< list hook for global list */
+  std::thread::id threadId_{};
+};
+}
+}
+
+#include <folly/fibers/Fiber-inl.h>
diff --git a/folly/fibers/FiberManager-inl.h b/folly/fibers/FiberManager-inl.h
new file mode 100644 (file)
index 0000000..6b4e94a
--- /dev/null
@@ -0,0 +1,552 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <cassert>
+
+#include <folly/CPortability.h>
+#include <folly/Memory.h>
+#include <folly/Optional.h>
+#include <folly/Portability.h>
+#include <folly/ScopeGuard.h>
+#ifdef __APPLE__
+#include <folly/ThreadLocal.h>
+#endif
+#include <folly/fibers/Baton.h>
+#include <folly/fibers/Fiber.h>
+#include <folly/fibers/LoopController.h>
+#include <folly/fibers/Promise.h>
+#include <folly/futures/Promise.h>
+#include <folly/futures/Try.h>
+
+namespace folly {
+namespace fibers {
+
+namespace {
+
+inline FiberManager::Options preprocessOptions(FiberManager::Options opts) {
+#ifdef FOLLY_SANITIZE_ADDRESS
+  /* ASAN needs a lot of extra stack space.
+     16x is a conservative estimate, 8x also worked with tests
+     where it mattered.  Note that overallocating here does not necessarily
+     increase RSS, since unused memory is pretty much free. */
+  opts.stackSize *= 16;
+#endif
+  return opts;
+}
+
+} // anonymous
+
+inline void FiberManager::ensureLoopScheduled() {
+  if (isLoopScheduled_) {
+    return;
+  }
+
+  isLoopScheduled_ = true;
+  loopController_->schedule();
+}
+
+inline intptr_t FiberManager::activateFiber(Fiber* fiber) {
+  DCHECK_EQ(activeFiber_, (Fiber*)nullptr);
+
+#ifdef FOLLY_SANITIZE_ADDRESS
+  registerFiberActivationWithAsan(fiber);
+#endif
+
+  activeFiber_ = fiber;
+  return jumpContext(&mainContext_, &fiber->fcontext_, fiber->data_);
+}
+
+inline intptr_t FiberManager::deactivateFiber(Fiber* fiber) {
+  DCHECK_EQ(activeFiber_, fiber);
+
+#ifdef FOLLY_SANITIZE_ADDRESS
+  registerFiberDeactivationWithAsan(fiber);
+#endif
+
+  activeFiber_ = nullptr;
+  return jumpContext(&fiber->fcontext_, &mainContext_, 0);
+}
+
+inline void FiberManager::runReadyFiber(Fiber* fiber) {
+  SCOPE_EXIT {
+    assert(currentFiber_ == nullptr);
+    assert(activeFiber_ == nullptr);
+  };
+
+  assert(
+      fiber->state_ == Fiber::NOT_STARTED ||
+      fiber->state_ == Fiber::READY_TO_RUN);
+  currentFiber_ = fiber;
+  fiber->rcontext_ = RequestContext::setContext(std::move(fiber->rcontext_));
+  if (observer_) {
+    observer_->starting(reinterpret_cast<uintptr_t>(fiber));
+  }
+
+  while (fiber->state_ == Fiber::NOT_STARTED ||
+         fiber->state_ == Fiber::READY_TO_RUN) {
+    activateFiber(fiber);
+    if (fiber->state_ == Fiber::AWAITING_IMMEDIATE) {
+      try {
+        immediateFunc_();
+      } catch (...) {
+        exceptionCallback_(std::current_exception(), "running immediateFunc_");
+      }
+      immediateFunc_ = nullptr;
+      fiber->state_ = Fiber::READY_TO_RUN;
+    }
+  }
+
+  if (fiber->state_ == Fiber::AWAITING) {
+    awaitFunc_(*fiber);
+    awaitFunc_ = nullptr;
+    if (observer_) {
+      observer_->stopped(reinterpret_cast<uintptr_t>(fiber));
+    }
+    currentFiber_ = nullptr;
+    fiber->rcontext_ = RequestContext::setContext(std::move(fiber->rcontext_));
+  } else if (fiber->state_ == Fiber::INVALID) {
+    assert(fibersActive_ > 0);
+    --fibersActive_;
+    // Making sure that task functor is deleted once task is complete.
+    // NOTE: we must do it on main context, as the fiber is not
+    // running at this point.
+    fiber->func_ = nullptr;
+    fiber->resultFunc_ = nullptr;
+    if (fiber->finallyFunc_) {
+      try {
+        fiber->finallyFunc_();
+      } catch (...) {
+        exceptionCallback_(std::current_exception(), "running finallyFunc_");
+      }
+      fiber->finallyFunc_ = nullptr;
+    }
+    // Make sure LocalData is not accessible from its destructor
+    if (observer_) {
+      observer_->stopped(reinterpret_cast<uintptr_t>(fiber));
+    }
+    currentFiber_ = nullptr;
+    fiber->rcontext_ = RequestContext::setContext(std::move(fiber->rcontext_));
+    fiber->localData_.reset();
+    fiber->rcontext_.reset();
+
+    if (fibersPoolSize_ < options_.maxFibersPoolSize ||
+        options_.fibersPoolResizePeriodMs > 0) {
+      fibersPool_.push_front(*fiber);
+      ++fibersPoolSize_;
+    } else {
+      delete fiber;
+      assert(fibersAllocated_ > 0);
+      --fibersAllocated_;
+    }
+  } else if (fiber->state_ == Fiber::YIELDED) {
+    if (observer_) {
+      observer_->stopped(reinterpret_cast<uintptr_t>(fiber));
+    }
+    currentFiber_ = nullptr;
+    fiber->rcontext_ = RequestContext::setContext(std::move(fiber->rcontext_));
+    fiber->state_ = Fiber::READY_TO_RUN;
+    yieldedFibers_.push_back(*fiber);
+  }
+}
+
+inline bool FiberManager::loopUntilNoReady() {
+  if (UNLIKELY(!alternateSignalStackRegistered_)) {
+    registerAlternateSignalStack();
+  }
+
+  // Support nested FiberManagers
+  auto originalFiberManager = this;
+  std::swap(currentFiberManager_, originalFiberManager);
+
+  SCOPE_EXIT {
+    isLoopScheduled_ = false;
+    if (!readyFibers_.empty()) {
+      ensureLoopScheduled();
+    }
+    std::swap(currentFiberManager_, originalFiberManager);
+    CHECK_EQ(this, originalFiberManager);
+  };
+
+  bool hadRemoteFiber = true;
+  while (hadRemoteFiber) {
+    hadRemoteFiber = false;
+
+    while (!readyFibers_.empty()) {
+      auto& fiber = readyFibers_.front();
+      readyFibers_.pop_front();
+      runReadyFiber(&fiber);
+    }
+
+    remoteReadyQueue_.sweep([this, &hadRemoteFiber](Fiber* fiber) {
+      runReadyFiber(fiber);
+      hadRemoteFiber = true;
+    });
+
+    remoteTaskQueue_.sweep([this, &hadRemoteFiber](RemoteTask* taskPtr) {
+      std::unique_ptr<RemoteTask> task(taskPtr);
+      auto fiber = getFiber();
+      if (task->localData) {
+        fiber->localData_ = *task->localData;
+      }
+      fiber->rcontext_ = std::move(task->rcontext);
+
+      fiber->setFunction(std::move(task->func));
+      fiber->data_ = reinterpret_cast<intptr_t>(fiber);
+      if (observer_) {
+        observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
+      }
+      runReadyFiber(fiber);
+      hadRemoteFiber = true;
+    });
+  }
+
+  if (observer_) {
+    for (auto& yielded : yieldedFibers_) {
+      observer_->runnable(reinterpret_cast<uintptr_t>(&yielded));
+    }
+  }
+  readyFibers_.splice(readyFibers_.end(), yieldedFibers_);
+
+  return fibersActive_ > 0;
+}
+
+// We need this to be in a struct, not inlined in addTask, because clang crashes
+// otherwise.
+template <typename F>
+struct FiberManager::AddTaskHelper {
+  class Func;
+
+  static constexpr bool allocateInBuffer =
+      sizeof(Func) <= Fiber::kUserBufferSize;
+
+  class Func {
+   public:
+    Func(F&& func, FiberManager& fm) : func_(std::forward<F>(func)), fm_(fm) {}
+
+    void operator()() {
+      try {
+        func_();
+      } catch (...) {
+        fm_.exceptionCallback_(
+            std::current_exception(), "running Func functor");
+      }
+      if (allocateInBuffer) {
+        this->~Func();
+      } else {
+        delete this;
+      }
+    }
+
+   private:
+    F func_;
+    FiberManager& fm_;
+  };
+};
+
+template <typename F>
+void FiberManager::addTask(F&& func) {
+  typedef AddTaskHelper<F> Helper;
+
+  auto fiber = getFiber();
+  initLocalData(*fiber);
+
+  if (Helper::allocateInBuffer) {
+    auto funcLoc = static_cast<typename Helper::Func*>(fiber->getUserBuffer());
+    new (funcLoc) typename Helper::Func(std::forward<F>(func), *this);
+
+    fiber->setFunction(std::ref(*funcLoc));
+  } else {
+    auto funcLoc = new typename Helper::Func(std::forward<F>(func), *this);
+
+    fiber->setFunction(std::ref(*funcLoc));
+  }
+
+  fiber->data_ = reinterpret_cast<intptr_t>(fiber);
+  readyFibers_.push_back(*fiber);
+  if (observer_) {
+    observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
+  }
+
+  ensureLoopScheduled();
+}
+
+template <typename F>
+auto FiberManager::addTaskFuture(F&& func) -> folly::Future<
+    typename folly::Unit::Lift<typename std::result_of<F()>::type>::type> {
+  using T = typename std::result_of<F()>::type;
+  using FutureT = typename folly::Unit::Lift<T>::type;
+
+  folly::Promise<FutureT> p;
+  auto f = p.getFuture();
+  addTaskFinally(
+      [func = std::forward<F>(func)]() mutable { return func(); },
+      [p = std::move(p)](folly::Try<T> && t) mutable {
+        p.setTry(std::move(t));
+      });
+  return f;
+}
+
+template <typename F>
+void FiberManager::addTaskRemote(F&& func) {
+  auto task = [&]() {
+    auto currentFm = getFiberManagerUnsafe();
+    if (currentFm && currentFm->currentFiber_ &&
+        currentFm->localType_ == localType_) {
+      return folly::make_unique<RemoteTask>(
+          std::forward<F>(func), currentFm->currentFiber_->localData_);
+    }
+    return folly::make_unique<RemoteTask>(std::forward<F>(func));
+  }();
+  auto insertHead = [&]() {
+    return remoteTaskQueue_.insertHead(task.release());
+  };
+  loopController_->scheduleThreadSafe(std::ref(insertHead));
+}
+
+template <typename F>
+auto FiberManager::addTaskRemoteFuture(F&& func) -> folly::Future<
+    typename folly::Unit::Lift<typename std::result_of<F()>::type>::type> {
+  folly::Promise<
+      typename folly::Unit::Lift<typename std::result_of<F()>::type>::type>
+      p;
+  auto f = p.getFuture();
+  addTaskRemote(
+      [ p = std::move(p), func = std::forward<F>(func), this ]() mutable {
+        auto t = folly::makeTryWith(std::forward<F>(func));
+        runInMainContext([&]() { p.setTry(std::move(t)); });
+      });
+  return f;
+}
+
+template <typename X>
+struct IsRvalueRefTry {
+  static const bool value = false;
+};
+template <typename T>
+struct IsRvalueRefTry<folly::Try<T>&&> {
+  static const bool value = true;
+};
+
+// We need this to be in a struct, not inlined in addTaskFinally, because clang
+// crashes otherwise.
+template <typename F, typename G>
+struct FiberManager::AddTaskFinallyHelper {
+  class Func;
+
+  typedef typename std::result_of<F()>::type Result;
+
+  class Finally {
+   public:
+    Finally(G finally, FiberManager& fm)
+        : finally_(std::move(finally)), fm_(fm) {}
+
+    void operator()() {
+      try {
+        finally_(std::move(*result_));
+      } catch (...) {
+        fm_.exceptionCallback_(
+            std::current_exception(), "running Finally functor");
+      }
+
+      if (allocateInBuffer) {
+        this->~Finally();
+      } else {
+        delete this;
+      }
+    }
+
+   private:
+    friend class Func;
+
+    G finally_;
+    folly::Optional<folly::Try<Result>> result_;
+    FiberManager& fm_;
+  };
+
+  class Func {
+   public:
+    Func(F func, Finally& finally)
+        : func_(std::move(func)), result_(finally.result_) {}
+
+    void operator()() {
+      result_ = folly::makeTryWith(std::move(func_));
+
+      if (allocateInBuffer) {
+        this->~Func();
+      } else {
+        delete this;
+      }
+    }
+
+   private:
+    F func_;
+    folly::Optional<folly::Try<Result>>& result_;
+  };
+
+  static constexpr bool allocateInBuffer =
+      sizeof(Func) + sizeof(Finally) <= Fiber::kUserBufferSize;
+};
+
+template <typename F, typename G>
+void FiberManager::addTaskFinally(F&& func, G&& finally) {
+  typedef typename std::result_of<F()>::type Result;
+
+  static_assert(
+      IsRvalueRefTry<typename FirstArgOf<G>::type>::value,
+      "finally(arg): arg must be Try<T>&&");
+  static_assert(
+      std::is_convertible<
+          Result,
+          typename std::remove_reference<
+              typename FirstArgOf<G>::type>::type::element_type>::value,
+      "finally(Try<T>&&): T must be convertible from func()'s return type");
+
+  auto fiber = getFiber();
+  initLocalData(*fiber);
+
+  typedef AddTaskFinallyHelper<
+      typename std::decay<F>::type,
+      typename std::decay<G>::type>
+      Helper;
+
+  if (Helper::allocateInBuffer) {
+    auto funcLoc = static_cast<typename Helper::Func*>(fiber->getUserBuffer());
+    auto finallyLoc =
+        static_cast<typename Helper::Finally*>(static_cast<void*>(funcLoc + 1));
+
+    new (finallyLoc) typename Helper::Finally(std::forward<G>(finally), *this);
+    new (funcLoc) typename Helper::Func(std::forward<F>(func), *finallyLoc);
+
+    fiber->setFunctionFinally(std::ref(*funcLoc), std::ref(*finallyLoc));
+  } else {
+    auto finallyLoc =
+        new typename Helper::Finally(std::forward<G>(finally), *this);
+    auto funcLoc =
+        new typename Helper::Func(std::forward<F>(func), *finallyLoc);
+
+    fiber->setFunctionFinally(std::ref(*funcLoc), std::ref(*finallyLoc));
+  }
+
+  fiber->data_ = reinterpret_cast<intptr_t>(fiber);
+  readyFibers_.push_back(*fiber);
+  if (observer_) {
+    observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
+  }
+
+  ensureLoopScheduled();
+}
+
+template <typename F>
+typename std::result_of<F()>::type FiberManager::runInMainContext(F&& func) {
+  if (UNLIKELY(activeFiber_ == nullptr)) {
+    return func();
+  }
+
+  typedef typename std::result_of<F()>::type Result;
+
+  folly::Try<Result> result;
+  auto f = [&func, &result]() mutable {
+    result = folly::makeTryWith(std::forward<F>(func));
+  };
+
+  immediateFunc_ = std::ref(f);
+  activeFiber_->preempt(Fiber::AWAITING_IMMEDIATE);
+
+  return std::move(result).value();
+}
+
+inline FiberManager& FiberManager::getFiberManager() {
+  assert(currentFiberManager_ != nullptr);
+  return *currentFiberManager_;
+}
+
+inline FiberManager* FiberManager::getFiberManagerUnsafe() {
+  return currentFiberManager_;
+}
+
+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_) {
+    return currentFiber_->localData_.get<T>();
+  }
+  return localThread<T>();
+}
+
+template <typename T>
+T& FiberManager::localThread() {
+#ifndef __APPLE__
+  static thread_local T t;
+  return t;
+#else // osx doesn't support thread_local
+  static ThreadLocal<T> t;
+  return *t;
+#endif
+}
+
+inline void FiberManager::initLocalData(Fiber& fiber) {
+  auto fm = getFiberManagerUnsafe();
+  if (fm && fm->currentFiber_ && fm->localType_ == localType_) {
+    fiber.localData_ = fm->currentFiber_->localData_;
+  }
+  fiber.rcontext_ = RequestContext::saveContext();
+}
+
+template <typename LocalT>
+FiberManager::FiberManager(
+    LocalType<LocalT>,
+    std::unique_ptr<LoopController> loopController__,
+    Options options)
+    : loopController_(std::move(loopController__)),
+      stackAllocator_(options.useGuardPages),
+      options_(preprocessOptions(std::move(options))),
+      exceptionCallback_([](std::exception_ptr eptr, std::string context) {
+        try {
+          std::rethrow_exception(eptr);
+        } catch (const std::exception& e) {
+          LOG(DFATAL) << "Exception " << typeid(e).name() << " with message '"
+                      << e.what() << "' was thrown in "
+                      << "FiberManager with context '" << context << "'";
+        } catch (...) {
+          LOG(DFATAL) << "Unknown exception was thrown in FiberManager with "
+                      << "context '" << context << "'";
+        }
+      }),
+      timeoutManager_(std::make_shared<TimeoutController>(*loopController_)),
+      fibersPoolResizer_(*this),
+      localType_(typeid(LocalT)) {
+  loopController_->setFiberManager(this);
+}
+
+template <typename F>
+typename FirstArgOf<F>::type::value_type inline await(F&& func) {
+  typedef typename FirstArgOf<F>::type::value_type Result;
+
+  return Promise<Result>::await(std::forward<F>(func));
+}
+}
+}
diff --git a/folly/fibers/FiberManager.cpp b/folly/fibers/FiberManager.cpp
new file mode 100644 (file)
index 0000000..727b7d7
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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.
+ */
+#include "FiberManager.h"
+
+#include <signal.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include <cassert>
+#include <stdexcept>
+
+#include <glog/logging.h>
+
+#include <folly/fibers/Fiber.h>
+#include <folly/fibers/LoopController.h>
+
+#include <folly/SingletonThreadLocal.h>
+
+#ifdef FOLLY_SANITIZE_ADDRESS
+
+#include <dlfcn.h>
+
+static void __asan_enter_fiber_weak(
+    void const* fiber_stack_base,
+    size_t fiber_stack_extent)
+    __attribute__((__weakref__("__asan_enter_fiber")));
+static void __asan_exit_fiber_weak()
+    __attribute__((__weakref__("__asan_exit_fiber")));
+static void __asan_unpoison_memory_region_weak(
+    void const /* nolint */ volatile* addr,
+    size_t size) __attribute__((__weakref__("__asan_unpoison_memory_region")));
+
+typedef void (*AsanEnterFiberFuncPtr)(void const*, size_t);
+typedef void (*AsanExitFiberFuncPtr)();
+typedef void (*AsanUnpoisonMemoryRegionFuncPtr)(
+    void const /* nolint */ volatile*,
+    size_t);
+
+namespace folly {
+namespace fibers {
+
+static AsanEnterFiberFuncPtr getEnterFiberFunc();
+static AsanExitFiberFuncPtr getExitFiberFunc();
+static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc();
+}
+}
+
+#endif
+
+namespace folly {
+namespace fibers {
+
+FOLLY_TLS FiberManager* FiberManager::currentFiberManager_ = nullptr;
+
+FiberManager::FiberManager(
+    std::unique_ptr<LoopController> loopController,
+    Options options)
+    : FiberManager(
+          LocalType<void>(),
+          std::move(loopController),
+          std::move(options)) {}
+
+FiberManager::~FiberManager() {
+  if (isLoopScheduled_) {
+    loopController_->cancel();
+  }
+
+  while (!fibersPool_.empty()) {
+    fibersPool_.pop_front_and_dispose([](Fiber* fiber) { delete fiber; });
+  }
+  assert(readyFibers_.empty());
+  assert(fibersActive_ == 0);
+}
+
+LoopController& FiberManager::loopController() {
+  return *loopController_;
+}
+
+const LoopController& FiberManager::loopController() const {
+  return *loopController_;
+}
+
+bool FiberManager::hasTasks() const {
+  return fibersActive_ > 0 || !remoteReadyQueue_.empty() ||
+      !remoteTaskQueue_.empty();
+}
+
+Fiber* FiberManager::getFiber() {
+  Fiber* fiber = nullptr;
+
+  if (options_.fibersPoolResizePeriodMs > 0 && !fibersPoolResizerScheduled_) {
+    fibersPoolResizer_();
+    fibersPoolResizerScheduled_ = true;
+  }
+
+  if (fibersPool_.empty()) {
+    fiber = new Fiber(*this);
+    ++fibersAllocated_;
+  } else {
+    fiber = &fibersPool_.front();
+    fibersPool_.pop_front();
+    assert(fibersPoolSize_ > 0);
+    --fibersPoolSize_;
+  }
+  assert(fiber);
+  if (++fibersActive_ > maxFibersActiveLastPeriod_) {
+    maxFibersActiveLastPeriod_ = fibersActive_;
+  }
+  ++fiberId_;
+  bool recordStack = (options_.recordStackEvery != 0) &&
+      (fiberId_ % options_.recordStackEvery == 0);
+  return fiber;
+}
+
+void FiberManager::setExceptionCallback(FiberManager::ExceptionCallback ec) {
+  assert(ec);
+  exceptionCallback_ = std::move(ec);
+}
+
+size_t FiberManager::fibersAllocated() const {
+  return fibersAllocated_;
+}
+
+size_t FiberManager::fibersPoolSize() const {
+  return fibersPoolSize_;
+}
+
+size_t FiberManager::stackHighWatermark() const {
+  return stackHighWatermark_;
+}
+
+void FiberManager::remoteReadyInsert(Fiber* fiber) {
+  if (observer_) {
+    observer_->runnable(reinterpret_cast<uintptr_t>(fiber));
+  }
+  auto insertHead = [&]() { return remoteReadyQueue_.insertHead(fiber); };
+  loopController_->scheduleThreadSafe(std::ref(insertHead));
+}
+
+void FiberManager::setObserver(ExecutionObserver* observer) {
+  observer_ = observer;
+}
+
+void FiberManager::setPreemptRunner(InlineFunctionRunner* preemptRunner) {
+  preemptRunner_ = preemptRunner;
+}
+
+void FiberManager::doFibersPoolResizing() {
+  while (fibersAllocated_ > maxFibersActiveLastPeriod_ &&
+         fibersPoolSize_ > options_.maxFibersPoolSize) {
+    auto fiber = &fibersPool_.front();
+    assert(fiber != nullptr);
+    fibersPool_.pop_front();
+    delete fiber;
+    --fibersPoolSize_;
+    --fibersAllocated_;
+  }
+
+  maxFibersActiveLastPeriod_ = fibersActive_;
+}
+
+void FiberManager::FibersPoolResizer::operator()() {
+  fiberManager_.doFibersPoolResizing();
+  fiberManager_.timeoutManager_->registerTimeout(
+      *this,
+      std::chrono::milliseconds(
+          fiberManager_.options_.fibersPoolResizePeriodMs));
+}
+
+#ifdef FOLLY_SANITIZE_ADDRESS
+
+void FiberManager::registerFiberActivationWithAsan(Fiber* fiber) {
+  auto context = &fiber->fcontext_;
+  void* top = context->stackBase();
+  void* bottom = context->stackLimit();
+  size_t extent = static_cast<char*>(top) - static_cast<char*>(bottom);
+
+  // Check if we can find a fiber enter function and call it if we find one
+  static AsanEnterFiberFuncPtr fn = getEnterFiberFunc();
+  if (fn == nullptr) {
+    LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
+  } else {
+    fn(bottom, extent);
+  }
+}
+
+void FiberManager::registerFiberDeactivationWithAsan(Fiber* fiber) {
+  (void)fiber; // currently unused
+
+  // Check if we can find a fiber exit function and call it if we find one
+  static AsanExitFiberFuncPtr fn = getExitFiberFunc();
+  if (fn == nullptr) {
+    LOG(FATAL) << "The version of ASAN in use doesn't support fibers";
+  } else {
+    fn();
+  }
+}
+
+void FiberManager::unpoisonFiberStack(const Fiber* fiber) {
+  auto stack = fiber->getStack();
+
+  // Check if we can find a fiber enter function and call it if we find one
+  static AsanUnpoisonMemoryRegionFuncPtr fn = getUnpoisonMemoryRegionFunc();
+  if (fn == nullptr) {
+    LOG(FATAL) << "This version of ASAN doesn't support memory unpoisoning";
+  } else {
+    fn(stack.first, stack.second);
+  }
+}
+
+static AsanEnterFiberFuncPtr getEnterFiberFunc() {
+  AsanEnterFiberFuncPtr fn{nullptr};
+
+  // Check whether weak reference points to statically linked enter function
+  if (nullptr != (fn = &::__asan_enter_fiber_weak)) {
+    return fn;
+  }
+
+  // Check whether we can find a dynamically linked enter function
+  if (nullptr !=
+      (fn = (AsanEnterFiberFuncPtr)dlsym(RTLD_DEFAULT, "__asan_enter_fiber"))) {
+    return fn;
+  }
+
+  // Couldn't find the function at all
+  return nullptr;
+}
+
+static AsanExitFiberFuncPtr getExitFiberFunc() {
+  AsanExitFiberFuncPtr fn{nullptr};
+
+  // Check whether weak reference points to statically linked exit function
+  if (nullptr != (fn = &::__asan_exit_fiber_weak)) {
+    return fn;
+  }
+
+  // Check whether we can find a dynamically linked exit function
+  if (nullptr !=
+      (fn = (AsanExitFiberFuncPtr)dlsym(RTLD_DEFAULT, "__asan_exit_fiber"))) {
+    return fn;
+  }
+
+  // Couldn't find the function at all
+  return nullptr;
+}
+
+static AsanUnpoisonMemoryRegionFuncPtr getUnpoisonMemoryRegionFunc() {
+  AsanUnpoisonMemoryRegionFuncPtr fn{nullptr};
+
+  // Check whether weak reference points to statically linked unpoison function
+  if (nullptr != (fn = &::__asan_unpoison_memory_region_weak)) {
+    return fn;
+  }
+
+  // Check whether we can find a dynamically linked unpoison function
+  if (nullptr != (fn = (AsanUnpoisonMemoryRegionFuncPtr)dlsym(
+                      RTLD_DEFAULT, "__asan_unpoison_memory_region"))) {
+    return fn;
+  }
+
+  // Couldn't find the function at all
+  return nullptr;
+}
+
+#endif // FOLLY_SANITIZE_ADDRESS
+
+namespace {
+
+// SIGSTKSZ (8 kB on our architectures) isn't always enough for
+// folly::symbolizer, so allocate 32 kB.
+constexpr size_t kAltStackSize = folly::constexpr_max(SIGSTKSZ, 32 * 1024);
+
+bool hasAlternateStack() {
+  stack_t ss;
+  sigaltstack(nullptr, &ss);
+  return !(ss.ss_flags & SS_DISABLE);
+}
+
+int setAlternateStack(char* sp, size_t size) {
+  CHECK(sp);
+  stack_t ss{};
+  ss.ss_sp = sp;
+  ss.ss_size = size;
+  return sigaltstack(&ss, nullptr);
+}
+
+int unsetAlternateStack() {
+  stack_t ss{};
+  ss.ss_flags = SS_DISABLE;
+  return sigaltstack(&ss, nullptr);
+}
+
+class ScopedAlternateSignalStack {
+ public:
+  ScopedAlternateSignalStack() {
+    if (hasAlternateStack()) {
+      return;
+    }
+
+    stack_ = folly::make_unique<AltStackBuffer>();
+
+    setAlternateStack(stack_->data(), stack_->size());
+  }
+
+  ~ScopedAlternateSignalStack() {
+    if (stack_) {
+      unsetAlternateStack();
+    }
+  }
+
+ private:
+  using AltStackBuffer = std::array<char, kAltStackSize>;
+  std::unique_ptr<AltStackBuffer> stack_;
+};
+}
+
+void FiberManager::registerAlternateSignalStack() {
+  static folly::SingletonThreadLocal<ScopedAlternateSignalStack> singleton;
+  singleton.get();
+
+  alternateSignalStackRegistered_ = true;
+}
+}
+}
diff --git a/folly/fibers/FiberManager.h b/folly/fibers/FiberManager.h
new file mode 100644 (file)
index 0000000..a617dd6
--- /dev/null
@@ -0,0 +1,565 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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/Executor.h>
+#include <folly/IntrusiveList.h>
+#include <folly/Likely.h>
+#include <folly/futures/Try.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/TimeoutController.h>
+#include <folly/fibers/traits.h>
+
+namespace folly {
+
+template <class T>
+class Future;
+
+namespace fibers {
+
+class Baton;
+class Fiber;
+class LoopController;
+class TimeoutController;
+
+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};
+
+    /**
+     * 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 limited amount of fiber stacks with guard pages.
+     */
+    bool useGuardPages{true};
+
+    /**
+     * 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() {}
+  };
+
+  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
+   * @param options FiberManager options
+   */
+  explicit FiberManager(
+      std::unique_ptr<LoopController> loopController,
+      Options options = Options());
+
+  /**
+   * Initializes, but doesn't start FiberManager loop
+   *
+   * @param loopController
+   * @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();
+
+  /**
+   * Controller access.
+   */
+  LoopController& loopController();
+  const LoopController& loopController() const;
+
+  /**
+   * Keeps running ready tasks until the list of ready tasks is empty.
+   *
+   * @return True if there are any waiting tasks remaining.
+   */
+  bool loopUntilNoReady();
+
+  /**
+   * @return true if there are outstanding tasks.
+   */
+  bool hasTasks() const;
+
+  /**
+   * Sets exception callback which will be called if any of the tasks throws an
+   * exception.
+   *
+   * @param ec
+   */
+  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.
+   */
+  template <typename F>
+  void addTask(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.
+   *
+   * @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<
+      typename folly::Unit::Lift<typename std::result_of<F()>::type>::type>;
+  /**
+   * 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<
+      typename folly::Unit::Lift<typename std::result_of<F()>::type>::type>;
+
+  // 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);
+
+  /**
+   * 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>
+  typename std::result_of<F()>::type 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>
+  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 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);
+
+  /**
+   * 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_.size();
+  }
+
+  static FiberManager& getFiberManager();
+  static FiberManager* getFiberManagerUnsafe();
+
+ 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(folly::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;
+  };
+
+  intptr_t activateFiber(Fiber* fiber);
+  intptr_t deactivateFiber(Fiber* fiber);
+
+  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_; /**< queue of fibers which have yielded
+                                      execution */
+  FiberTailQueue fibersPool_; /**< pool of unitialized Fiber objects */
+
+  GlobalFiberTailQueue allFibers_; /**< list of all Fiber objects owned */
+
+  size_t fibersAllocated_{0}; /**< total number of fibers allocated */
+  size_t fibersPoolSize_{0}; /**< total number of fibers in the free pool */
+  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};
+
+  FContext::ContextStruct mainContext_; /**< stores loop function context */
+
+  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 FOLLY_TLS FiberManager* currentFiberManager_;
+
+  /**
+   * 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.
+   */
+  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_;
+
+  std::shared_ptr<TimeoutController> timeoutManager_;
+
+  struct FibersPoolResizer {
+    explicit FibersPoolResizer(FiberManager& fm) : fiberManager_(fm) {}
+    void operator()();
+
+   private:
+    FiberManager& fiberManager_;
+  };
+
+  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 registerFiberActivationWithAsan(Fiber* fiber);
+  void registerFiberDeactivationWithAsan(Fiber* fiber);
+  void unpoisonFiberStack(const Fiber* fiber);
+
+#endif // FOLLY_SANITIZE_ADDRESS
+
+  bool alternateSignalStackRegistered_{false};
+
+  void registerAlternateSignalStack();
+};
+
+/**
+ * @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));
+}
+
+/**
+ * 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));
+}
+
+/**
+ * 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(F&& func);
+
+/**
+ * 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>
+typename std::result_of<F()>::type inline runInMainContext(F&& func) {
+  auto fm = FiberManager::getFiberManagerUnsafe();
+  if (UNLIKELY(fm == nullptr)) {
+    return func();
+  }
+  return fm->runInMainContext(std::forward<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();
+  }
+}
+}
+}
+
+#include "FiberManager-inl.h"
diff --git a/folly/fibers/FiberManagerMap.cpp b/folly/fibers/FiberManagerMap.cpp
new file mode 100644 (file)
index 0000000..4548137
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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.
+ */
+#include "FiberManagerMap.h"
+
+#include <memory>
+#include <unordered_map>
+
+#include <folly/Synchronized.h>
+#include <folly/ThreadLocal.h>
+
+namespace folly {
+namespace fibers {
+
+namespace {
+
+class EventBaseOnDestructionCallback : public EventBase::LoopCallback {
+ public:
+  explicit EventBaseOnDestructionCallback(EventBase& evb) : evb_(evb) {}
+  void runLoopCallback() noexcept override;
+
+ private:
+  EventBase& evb_;
+};
+
+class GlobalCache {
+ public:
+  static FiberManager& get(EventBase& evb, const FiberManager::Options& opts) {
+    return instance().getImpl(evb, opts);
+  }
+
+  static std::unique_ptr<FiberManager> erase(EventBase& evb) {
+    return instance().eraseImpl(evb);
+  }
+
+ private:
+  GlobalCache() {}
+
+  // Leak this intentionally. During shutdown, we may call getFiberManager,
+  // and want access to the fiber managers during that time.
+  static GlobalCache& instance() {
+    static auto ret = new GlobalCache();
+    return *ret;
+  }
+
+  FiberManager& getImpl(EventBase& evb, const FiberManager::Options& opts) {
+    std::lock_guard<std::mutex> lg(mutex_);
+
+    auto& fmPtrRef = map_[&evb];
+
+    if (!fmPtrRef) {
+      auto loopController = make_unique<EventBaseLoopController>();
+      loopController->attachEventBase(evb);
+      evb.runOnDestruction(new EventBaseOnDestructionCallback(evb));
+
+      fmPtrRef = make_unique<FiberManager>(std::move(loopController), opts);
+    }
+
+    return *fmPtrRef;
+  }
+
+  std::unique_ptr<FiberManager> eraseImpl(EventBase& evb) {
+    std::lock_guard<std::mutex> lg(mutex_);
+
+    DCHECK_EQ(1, map_.count(&evb));
+
+    auto ret = std::move(map_[&evb]);
+    map_.erase(&evb);
+    return ret;
+  }
+
+  std::mutex mutex_;
+  std::unordered_map<EventBase*, std::unique_ptr<FiberManager>> map_;
+};
+
+constexpr size_t kEraseListMaxSize = 64;
+
+class ThreadLocalCache {
+ public:
+  static FiberManager& get(EventBase& evb, const FiberManager::Options& opts) {
+    return instance()->getImpl(evb, opts);
+  }
+
+  static void erase(EventBase& evb) {
+    for (auto& localInstance : instance().accessAllThreads()) {
+      SYNCHRONIZED(info, localInstance.eraseInfo_) {
+        if (info.eraseList.size() >= kEraseListMaxSize) {
+          info.eraseAll = true;
+        } else {
+          info.eraseList.push_back(&evb);
+        }
+        localInstance.eraseRequested_ = true;
+      }
+    }
+  }
+
+ private:
+  ThreadLocalCache() {}
+
+  struct ThreadLocalCacheTag {};
+  using ThreadThreadLocalCache =
+      ThreadLocal<ThreadLocalCache, ThreadLocalCacheTag>;
+
+  // Leak this intentionally. During shutdown, we may call getFiberManager,
+  // and want access to the fiber managers during that time.
+  static ThreadThreadLocalCache& instance() {
+    static auto ret =
+        new ThreadThreadLocalCache([]() { return new ThreadLocalCache(); });
+    return *ret;
+  }
+
+  FiberManager& getImpl(EventBase& evb, const FiberManager::Options& opts) {
+    eraseImpl();
+
+    auto& fmPtrRef = map_[&evb];
+    if (!fmPtrRef) {
+      fmPtrRef = &GlobalCache::get(evb, opts);
+    }
+
+    DCHECK(fmPtrRef != nullptr);
+
+    return *fmPtrRef;
+  }
+
+  void eraseImpl() {
+    if (!eraseRequested_.load()) {
+      return;
+    }
+
+    SYNCHRONIZED(info, eraseInfo_) {
+      if (info.eraseAll) {
+        map_.clear();
+      } else {
+        for (auto evbPtr : info.eraseList) {
+          map_.erase(evbPtr);
+        }
+      }
+
+      info.eraseList.clear();
+      info.eraseAll = false;
+      eraseRequested_ = false;
+    }
+  }
+
+  std::unordered_map<EventBase*, FiberManager*> map_;
+  std::atomic<bool> eraseRequested_{false};
+
+  struct EraseInfo {
+    bool eraseAll{false};
+    std::vector<EventBase*> eraseList;
+  };
+
+  folly::Synchronized<EraseInfo> eraseInfo_;
+};
+
+void EventBaseOnDestructionCallback::runLoopCallback() noexcept {
+  auto fm = GlobalCache::erase(evb_);
+  DCHECK(fm.get() != nullptr);
+  ThreadLocalCache::erase(evb_);
+
+  while (fm->hasTasks()) {
+    fm->loopUntilNoReady();
+    evb_.loopOnce();
+  }
+
+  delete this;
+}
+
+} // namespace
+
+FiberManager& getFiberManager(
+    EventBase& evb,
+    const FiberManager::Options& opts) {
+  return ThreadLocalCache::get(evb, opts);
+}
+}
+}
diff --git a/folly/fibers/FiberManagerMap.h b/folly/fibers/FiberManagerMap.h
new file mode 100644 (file)
index 0000000..b210b73
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <folly/fibers/EventBaseLoopController.h>
+#include <folly/fibers/FiberManager.h>
+
+namespace folly {
+namespace fibers {
+
+FiberManager& getFiberManager(
+    folly::EventBase& evb,
+    const FiberManager::Options& opts = FiberManager::Options());
+}
+}
diff --git a/folly/fibers/ForEach-inl.h b/folly/fibers/ForEach-inl.h
new file mode 100644 (file)
index 0000000..4c6df91
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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.
+ */
+#include <folly/fibers/FiberManager.h>
+
+namespace folly {
+namespace fibers {
+
+namespace {
+
+template <class F, class G>
+typename std::enable_if<
+    !std::is_same<typename std::result_of<F()>::type, void>::value,
+    void>::type inline callFuncs(F&& f, G&& g, size_t id) {
+  g(id, f());
+}
+
+template <class F, class G>
+typename std::enable_if<
+    std::is_same<typename std::result_of<F()>::type, void>::value,
+    void>::type inline callFuncs(F&& f, G&& g, size_t id) {
+  f();
+  g(id);
+}
+
+} // anonymous namespace
+
+template <class InputIterator, class F>
+inline void forEach(InputIterator first, InputIterator last, F&& f) {
+  if (first == last) {
+    return;
+  }
+
+  typedef typename std::iterator_traits<InputIterator>::value_type FuncType;
+
+  size_t tasksTodo = 1;
+  std::exception_ptr e;
+  Baton baton;
+
+#ifdef __clang__
+#pragma clang diagnostic push // ignore generalized lambda capture warning
+#pragma clang diagnostic ignored "-Wc++1y-extensions"
+#endif
+  auto taskFunc = [&tasksTodo, &e, &f, &baton](size_t id, FuncType&& func) {
+    return [
+      id,
+      &tasksTodo,
+      &e,
+      &f,
+      &baton,
+      func_ = std::forward<FuncType>(func)
+    ]() mutable {
+      try {
+        callFuncs(std::forward<FuncType>(func_), f, id);
+      } catch (...) {
+        e = std::current_exception();
+      }
+      if (--tasksTodo == 0) {
+        baton.post();
+      }
+    };
+  };
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+  auto firstTask = first;
+  ++first;
+
+  for (size_t i = 1; first != last; ++i, ++first, ++tasksTodo) {
+    addTask(taskFunc(i, std::move(*first)));
+  }
+
+  taskFunc(0, std::move(*firstTask))();
+  baton.wait();
+
+  if (e != std::exception_ptr()) {
+    std::rethrow_exception(e);
+  }
+}
+}
+} // folly::fibers
diff --git a/folly/fibers/ForEach.h b/folly/fibers/ForEach.h
new file mode 100644 (file)
index 0000000..f3e1c29
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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
+
+namespace folly {
+namespace fibers {
+
+/**
+ * Schedules several tasks and blocks until all of them are completed.
+ * In the process of their successfull completion given callback would be called
+ * for each of them with the index of the task and the result it returned (if
+ * not void).
+ * If any of these n tasks throws an exception, this exception will be
+ * re-thrown, but only when all tasks are complete. If several tasks throw
+ * exceptions one of them will be re-thrown. Callback won't be called for
+ * tasks that throw exception.
+ *
+ * @param first  Range of tasks to be scheduled
+ * @param last
+ * @param F      callback to call for each result.
+ *               In case of each task returning void it should be callable
+ *                  F(size_t id)
+ *               otherwise should be callable
+ *                  F(size_t id, Result)
+ */
+template <class InputIterator, class F>
+inline void forEach(InputIterator first, InputIterator last, F&& f);
+}
+} // folly::fibers
+
+#include <folly/fibers/ForEach-inl.h>
diff --git a/folly/fibers/GenericBaton.h b/folly/fibers/GenericBaton.h
new file mode 100644 (file)
index 0000000..cf61ab6
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <folly/Baton.h>
+
+#include <folly/fibers/Baton.h>
+
+namespace folly {
+namespace fibers {
+
+typedef Baton GenericBaton;
+}
+}
diff --git a/folly/fibers/GuardPageAllocator.cpp b/folly/fibers/GuardPageAllocator.cpp
new file mode 100644 (file)
index 0000000..b424e6f
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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.
+ */
+#include "GuardPageAllocator.h"
+
+#include <unistd.h>
+
+#include <mutex>
+
+#include <folly/Singleton.h>
+#include <folly/SpinLock.h>
+#include <folly/portability/SysMman.h>
+
+#include <glog/logging.h>
+
+namespace folly {
+namespace fibers {
+
+/**
+ * Each stack with a guard page creates two memory mappings.
+ * Since this is a limited resource, we don't want to create too many of these.
+ *
+ * The upper bound on total number of mappings created
+ * is kNumGuarded * kMaxInUse.
+ */
+
+/**
+ * Number of guarded stacks per allocator instance
+ */
+constexpr size_t kNumGuarded = 100;
+
+/**
+ * Maximum number of allocator instances with guarded stacks enabled
+ */
+constexpr size_t kMaxInUse = 100;
+
+/**
+ * A cache for kNumGuarded stacks of a given size
+ *
+ * Thread safe.
+ */
+class StackCache {
+ public:
+  explicit StackCache(size_t stackSize) : allocSize_(allocSize(stackSize)) {
+    auto p = ::mmap(
+        nullptr,
+        allocSize_ * kNumGuarded,
+        PROT_READ | PROT_WRITE,
+        MAP_PRIVATE | MAP_ANONYMOUS,
+        -1,
+        0);
+    PCHECK(p != (void*)(-1));
+    storage_ = reinterpret_cast<unsigned char*>(p);
+
+    /* Protect the bottommost page of every stack allocation */
+    for (size_t i = 0; i < kNumGuarded; ++i) {
+      auto allocBegin = storage_ + allocSize_ * i;
+      freeList_.emplace_back(allocBegin, /* protected= */ false);
+    }
+  }
+
+  unsigned char* borrow(size_t size) {
+    std::lock_guard<folly::SpinLock> lg(lock_);
+
+    assert(storage_);
+
+    auto as = allocSize(size);
+    if (as != allocSize_ || freeList_.empty()) {
+      return nullptr;
+    }
+
+    auto p = freeList_.back().first;
+    if (!freeList_.back().second) {
+      PCHECK(0 == ::mprotect(p, pagesize(), PROT_NONE));
+    }
+    freeList_.pop_back();
+
+    /* We allocate minimum number of pages required, plus a guard page.
+       Since we use this for stack storage, requested allocation is aligned
+       at the top of the allocated pages, while the guard page is at the bottom.
+
+               -- increasing addresses -->
+             Guard page     Normal pages
+            |xxxxxxxxxx|..........|..........|
+            <- allocSize_ ------------------->
+         p -^                <- size -------->
+                      limit -^
+    */
+    auto limit = p + allocSize_ - size;
+    assert(limit >= p + pagesize());
+    return limit;
+  }
+
+  bool giveBack(unsigned char* limit, size_t size) {
+    std::lock_guard<folly::SpinLock> lg(lock_);
+
+    assert(storage_);
+
+    auto as = allocSize(size);
+    auto p = limit + size - as;
+    if (p < storage_ || p >= storage_ + allocSize_ * kNumGuarded) {
+      /* not mine */
+      return false;
+    }
+
+    assert(as == allocSize_);
+    assert((p - storage_) % allocSize_ == 0);
+    freeList_.emplace_back(p, /* protected= */ true);
+    return true;
+  }
+
+  ~StackCache() {
+    assert(storage_);
+    PCHECK(0 == ::munmap(storage_, allocSize_ * kNumGuarded));
+  }
+
+ private:
+  folly::SpinLock lock_;
+  unsigned char* storage_{nullptr};
+  size_t allocSize_{0};
+
+  /**
+   * LIFO free list. Each pair contains stack pointer and protected flag.
+   */
+  std::vector<std::pair<unsigned char*, bool>> freeList_;
+
+  static size_t pagesize() {
+    static const size_t pagesize = sysconf(_SC_PAGESIZE);
+    return pagesize;
+  }
+
+  /* Returns a multiple of pagesize() enough to store size + one guard page */
+  static size_t allocSize(size_t size) {
+    return pagesize() * ((size + pagesize() - 1) / pagesize() + 1);
+  }
+};
+
+class CacheManager {
+ public:
+  static CacheManager& instance() {
+    static auto inst = new CacheManager();
+    return *inst;
+  }
+
+  std::unique_ptr<StackCacheEntry> getStackCache(size_t stackSize) {
+    std::lock_guard<folly::SpinLock> lg(lock_);
+    if (inUse_ < kMaxInUse) {
+      ++inUse_;
+      return folly::make_unique<StackCacheEntry>(stackSize);
+    }
+
+    return nullptr;
+  }
+
+ private:
+  folly::SpinLock lock_;
+  size_t inUse_{0};
+
+  friend class StackCacheEntry;
+
+  void giveBack(std::unique_ptr<StackCache> /* stackCache_ */) {
+    assert(inUse_ > 0);
+    --inUse_;
+    /* Note: we can add a free list for each size bucket
+       if stack re-use is important.
+       In this case this needs to be a folly::Singleton
+       to make sure the free list is cleaned up on fork.
+
+       TODO(t7351705): fix Singleton destruction order
+    */
+  }
+};
+
+/*
+ * RAII Wrapper around a StackCache that calls
+ * CacheManager::giveBack() on destruction.
+ */
+class StackCacheEntry {
+ public:
+  explicit StackCacheEntry(size_t stackSize)
+      : stackCache_(folly::make_unique<StackCache>(stackSize)) {}
+
+  StackCache& cache() const noexcept {
+    return *stackCache_;
+  }
+
+  ~StackCacheEntry() {
+    CacheManager::instance().giveBack(std::move(stackCache_));
+  }
+
+ private:
+  std::unique_ptr<StackCache> stackCache_;
+};
+
+GuardPageAllocator::GuardPageAllocator(bool useGuardPages)
+    : useGuardPages_(useGuardPages) {}
+
+GuardPageAllocator::~GuardPageAllocator() = default;
+
+unsigned char* GuardPageAllocator::allocate(size_t size) {
+  if (useGuardPages_ && !stackCache_) {
+    stackCache_ = CacheManager::instance().getStackCache(size);
+  }
+
+  if (stackCache_) {
+    auto p = stackCache_->cache().borrow(size);
+    if (p != nullptr) {
+      return p;
+    }
+  }
+  return fallbackAllocator_.allocate(size);
+}
+
+void GuardPageAllocator::deallocate(unsigned char* limit, size_t size) {
+  if (!(stackCache_ && stackCache_->cache().giveBack(limit, size))) {
+    fallbackAllocator_.deallocate(limit, size);
+  }
+}
+}
+} // folly::fibers
diff --git a/folly/fibers/GuardPageAllocator.h b/folly/fibers/GuardPageAllocator.h
new file mode 100644 (file)
index 0000000..21d684b
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <memory>
+
+namespace folly {
+namespace fibers {
+
+class StackCacheEntry;
+
+/**
+ * Stack allocator that protects an extra memory page after
+ * the end of the stack.
+ * Will only add extra memory pages up to a certain number of allocations
+ * to avoid creating too many memory maps for the process.
+ */
+class GuardPageAllocator {
+ public:
+  /**
+   * @param useGuardPages if true, protect limited amount of stacks with guard
+   *                      pages, otherwise acts as std::allocator.
+   */
+  explicit GuardPageAllocator(bool useGuardPages);
+  ~GuardPageAllocator();
+
+  /**
+   * @return pointer to the bottom of the allocated stack of `size' bytes.
+   */
+  unsigned char* allocate(size_t size);
+
+  /**
+   * Deallocates the previous result of an `allocate(size)' call.
+   */
+  void deallocate(unsigned char* limit, size_t size);
+
+ private:
+  std::unique_ptr<StackCacheEntry> stackCache_;
+  std::allocator<unsigned char> fallbackAllocator_;
+  bool useGuardPages_{true};
+};
+}
+} // folly::fibers
diff --git a/folly/fibers/LoopController.h b/folly/fibers/LoopController.h
new file mode 100644 (file)
index 0000000..917a655
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <chrono>
+#include <functional>
+
+namespace folly {
+namespace fibers {
+
+class FiberManager;
+
+class LoopController {
+ public:
+  typedef std::chrono::steady_clock Clock;
+  typedef std::chrono::time_point<Clock> TimePoint;
+
+  virtual ~LoopController() {}
+
+  /**
+   * Called by FiberManager to associate itself with the LoopController.
+   */
+  virtual void setFiberManager(FiberManager*) = 0;
+
+  /**
+   * Called by FiberManager to schedule the loop function run
+   * at some point in the future.
+   */
+  virtual void schedule() = 0;
+
+  /**
+   * Same as schedule(), but safe to call from any thread.
+   * Runs func and only schedules if func returned true.
+   */
+  virtual void scheduleThreadSafe(std::function<bool()> func) = 0;
+
+  /**
+   * Called by FiberManager to cancel a previously scheduled
+   * loop function run.
+   */
+  virtual void cancel() = 0;
+
+  /**
+   * Called by FiberManager to schedule some function to be run at some time.
+   */
+  virtual void timedSchedule(std::function<void()> func, TimePoint time) = 0;
+};
+}
+} // folly::fibers
diff --git a/folly/fibers/Promise-inl.h b/folly/fibers/Promise-inl.h
new file mode 100644 (file)
index 0000000..2adaf78
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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.
+ */
+#include <folly/fibers/Baton.h>
+
+namespace folly {
+namespace fibers {
+
+template <class T>
+Promise<T>::Promise(folly::Try<T>& value, Baton& baton)
+    : value_(&value), baton_(&baton) {}
+
+template <class T>
+Promise<T>::Promise(Promise&& other) noexcept
+    : value_(other.value_), baton_(other.baton_) {
+  other.value_ = nullptr;
+  other.baton_ = nullptr;
+}
+
+template <class T>
+Promise<T>& Promise<T>::operator=(Promise&& other) {
+  std::swap(value_, other.value_);
+  std::swap(baton_, other.baton_);
+  return *this;
+}
+
+template <class T>
+void Promise<T>::throwIfFulfilled() const {
+  if (!value_) {
+    throw std::logic_error("promise already fulfilled");
+  }
+}
+
+template <class T>
+Promise<T>::~Promise() {
+  if (value_) {
+    setException(folly::make_exception_wrapper<std::logic_error>(
+        "promise not fulfilled"));
+  }
+}
+
+template <class T>
+void Promise<T>::setException(folly::exception_wrapper e) {
+  setTry(folly::Try<T>(e));
+}
+
+template <class T>
+void Promise<T>::setTry(folly::Try<T>&& t) {
+  throwIfFulfilled();
+
+  *value_ = std::move(t);
+  value_ = nullptr;
+
+  // Baton::post has to be the last step here, since if Promise is not owned by
+  // the posting thread, it may be destroyed right after Baton::post is called.
+  baton_->post();
+}
+
+template <class T>
+template <class M>
+void Promise<T>::setValue(M&& v) {
+  static_assert(!std::is_same<T, void>::value, "Use setValue() instead");
+
+  setTry(folly::Try<T>(std::forward<M>(v)));
+}
+
+template <class T>
+void Promise<T>::setValue() {
+  static_assert(std::is_same<T, void>::value, "Use setValue(value) instead");
+
+  setTry(folly::Try<void>());
+}
+
+template <class T>
+template <class F>
+void Promise<T>::setWith(F&& func) {
+  setTry(makeTryWith(std::forward<F>(func)));
+}
+
+template <class T>
+template <class F>
+typename Promise<T>::value_type Promise<T>::await(F&& func) {
+  folly::Try<value_type> result;
+  std::exception_ptr funcException;
+
+  Baton baton;
+  baton.wait([&func, &result, &baton, &funcException]() mutable {
+    try {
+      func(Promise<value_type>(result, baton));
+    } catch (...) {
+      // Save the exception, but still wait for baton to be posted by user code
+      // or promise destructor.
+      funcException = std::current_exception();
+    }
+  });
+
+  if (UNLIKELY(funcException != nullptr)) {
+    std::rethrow_exception(funcException);
+  }
+
+  return folly::moveFromTry(result);
+}
+}
+}
diff --git a/folly/fibers/Promise.h b/folly/fibers/Promise.h
new file mode 100644 (file)
index 0000000..807898e
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <folly/fibers/traits.h>
+#include <folly/futures/Try.h>
+
+namespace folly {
+namespace fibers {
+
+class Baton;
+
+template <typename T>
+class Promise {
+ public:
+  typedef T value_type;
+
+  ~Promise();
+
+  // not copyable
+  Promise(const Promise&) = delete;
+  Promise& operator=(const Promise&) = delete;
+
+  // movable
+  Promise(Promise&&) noexcept;
+  Promise& operator=(Promise&&);
+
+  /** Fulfill this promise (only for Promise<void>) */
+  void setValue();
+
+  /** Set the value (use perfect forwarding for both move and copy) */
+  template <class M>
+  void setValue(M&& value);
+
+  /**
+   * Fulfill the promise with a given try
+   *
+   * @param t
+   */
+  void setTry(folly::Try<T>&& t);
+
+  /** Fulfill this promise with the result of a function that takes no
+    arguments and returns something implicitly convertible to T.
+    Captures exceptions. e.g.
+
+    p.setWith([] { do something that may throw; return a T; });
+  */
+  template <class F>
+  void setWith(F&& func);
+
+  /** Fulfill the Promise with an exception_wrapper, e.g.
+    auto ew = folly::try_and_catch<std::exception>([]{ ... });
+    if (ew) {
+      p.setException(std::move(ew));
+    }
+    */
+  void setException(folly::exception_wrapper);
+
+  /**
+   * 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 <class F>
+  static value_type await(F&& func);
+
+ private:
+  Promise(folly::Try<T>& value, Baton& baton);
+  folly::Try<T>* value_;
+  Baton* baton_;
+
+  void throwIfFulfilled() const;
+
+  template <class F>
+  typename std::enable_if<
+      std::is_convertible<typename std::result_of<F()>::type, T>::value &&
+      !std::is_same<T, void>::value>::type
+  fulfilHelper(F&& func);
+
+  template <class F>
+  typename std::enable_if<
+      std::is_same<typename std::result_of<F()>::type, void>::value &&
+      std::is_same<T, void>::value>::type
+  fulfilHelper(F&& func);
+};
+}
+}
+
+#include <folly/fibers/Promise-inl.h>
diff --git a/folly/fibers/README.md b/folly/fibers/README.md
new file mode 100644 (file)
index 0000000..3dfdef4
--- /dev/null
@@ -0,0 +1,552 @@
+<!-- This file is generated from internal wiki guide by folly/facebook/fibers-update-readme.sh. -->
+<section class="dex_guide"><h1 class="dex_title">folly::fibers</h1><section class="dex_document"><h1></h1><p class="dex_introduction">folly::fibers is an async C++ framework, which uses fibers for parallelism.</p><h2 id="overview">Overview <a href="#overview" class="headerLink">#</a></h2>
+
+<p>Fibers (or coroutines) are lightweight application threads. Multiple fibers can be running on top of a single system thread. Unlike system threads, all the context switching between fibers is happening explicitly. Because of this every such context switch is very fast (~200 million of fiber context switches can be made per second on a single CPU core).</p>
+
+<p>folly::fibers implements a task manager (FiberManager), which executes scheduled tasks on fibers. It also provides some fiber-compatible synchronization primitives.</p>
+
+<h2 id="basic-example">Basic example <a href="#basic-example" class="headerLink">#</a></h2>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="o">...</span>
+<span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="EventBase">EventBase</span> <span class="no">evb</span><span class="o">;</span>
+<span class="no">auto</span><span class="o">&amp;</span> <span class="no">fiberManager</span> <span class="o">=</span> <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="getFiberManager">getFiberManager</span><span class="o">(</span><span class="no">evb</span><span class="o">);</span>
+<span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Baton">Baton</span> <span class="no">baton</span><span class="o">;</span>
+
+<span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
+  <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="cout">cout</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;Task 1: start&quot;</span> <span class="o">&lt;&lt;</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="endl">endl</span><span class="o">;</span>
+  <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="wait">wait</span><span class="o">();</span>
+  <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="cout">cout</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;Task 1: after baton.wait()&quot;</span> <span class="o">&lt;&lt;</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="endl">endl</span><span class="o">;</span>
+<span class="o">&#125;);</span>
+
+<span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
+  <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="cout">cout</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;Task 2: start&quot;</span> <span class="o">&lt;&lt;</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="endl">endl</span><span class="o">;</span>
+  <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="post">post</span><span class="o">();</span>
+  <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="cout">cout</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;Task 2: after baton.post()&quot;</span> <span class="o">&lt;&lt;</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="endl">endl</span><span class="o">;</span>
+<span class="o">&#125;);</span>
+
+<span class="no">evb</span><span class="o">.</span><span class="nf" data-symbol-name="loop">loop</span><span class="o">();</span>
+<span class="o">...</span></pre></div>
+
+<p>This would print:</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">Task</span> <span class="mi">1</span><span class="o">:</span> <span class="no">start</span>
+<span class="no">Task</span> <span class="mi">2</span><span class="o">:</span> <span class="no">start</span>
+<span class="no">Task</span> <span class="mi">2</span><span class="o">:</span> <span class="no">after</span> <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="post">post</span><span class="o">()</span>
+<span class="no">Task</span> <span class="mi">1</span><span class="o">:</span> <span class="no">after</span> <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="wait">wait</span><span class="o">()</span></pre></div>
+
+<p>It&#039;s very important to note that both tasks in this example were executed on the same system thread. Task 1 was suspended by <tt>baton.wait()</tt> call. Task 2 then started and called <tt>baton.post()</tt>, resuming Task 1.</p>
+
+<h2 id="features">Features <a href="#features" class="headerLink">#</a></h2>
+
+<ul>
+<li>Fibers creation and scheduling is performed by FiberManager</li>
+<li>Integration with any event-management system (e.g. EventBase)</li>
+<li>Low-level synchronization primitives (Baton) as well as higher-level primitives built on top of them (await, collectN, mutexes, ... )</li>
+<li>Synchronization primitives have timeout support</li>
+<li>Built-in mechanisms for fiber stack-overflow detection</li>
+<li>Optional fiber-local data (i.e. equivalent of thread locals)</li>
+</ul>
+
+<h2 id="non-features">Non-features <a href="#non-features" class="headerLink">#</a></h2>
+
+<ul>
+<li>Individual fibers scheduling can&#039;t be directly controlled by application</li>
+<li>FiberManager is not thread-safe (we recommend to keep one FiberManager per thread). Application is responsible for managing it&#039;s own threads and distributing load between them</li>
+<li>We don&#039;t support automatic stack size adjustments. Each fiber has a stack of fixed size.</li>
+</ul>
+
+<h2 id="why-would-i-not-want-to">Why would I not want to use fibers ? <a href="#why-would-i-not-want-to" class="headerLink">#</a></h2>
+
+<p>The only real downside to using fibers is the need to keep a pre-allocated stack for every fiber being run. That either makes you application use a lot of memory (if you have many concurrent tasks and each of them uses large stacks) or creates a risk of stack overflow bugs (if you try to reduce the stack size).</p>
+
+<p>We believe these problems can be addressed (and we provide some tooling for that), as fibers library is used in many critical applications at Facebook (mcrouter, TAO, Service Router). However, it&#039;s important to be aware of the risks and be ready to deal with stack issues if you decide to use fibers library in your application.</p>
+
+<h2 id="what-are-the-alternative">What are the alternatives ? <a href="#what-are-the-alternative" class="headerLink">#</a></h2>
+
+<ul>
+<li><a href="https://github.com/facebook/folly/blob/master/folly/futures/" target="_blank">Futures</a> library works great for asynchronous high-level application code. Yet code written using fibers library is generally much simpler and much more efficient (you are not paying the penalty of heap allocation).</li>
+<li>You can always keep writing your asynchronous code using traditional callback approach. Such code quickly becomes hard to manage and is error-prone. Even though callback approach seems to allow you writing the most efficient code, inefficiency still comes from heap allocations (<tt>std::function</tt>s used for callbacks, context objects to be passed between callbacks etc.)</li>
+</ul></section><section class="dex_document"><h1>Quick guide</h1><p class="dex_introduction"></p><p>Let&#039;s take a look at this basic example:</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="o">...</span>
+<span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="EventBase">EventBase</span> <span class="no">evb</span><span class="o">;</span>
+<span class="no">auto</span><span class="o">&amp;</span> <span class="no">fiberManager</span> <span class="o">=</span> <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="getFiberManager">getFiberManager</span><span class="o">(</span><span class="no">evb</span><span class="o">);</span>
+<span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Baton">Baton</span> <span class="no">baton</span><span class="o">;</span>
+
+<span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
+  <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="cout">cout</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;Task: start&quot;</span> <span class="o">&lt;&lt;</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="endl">endl</span><span class="o">;</span>
+  <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="wait">wait</span><span class="o">();</span>
+  <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="cout">cout</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;Task: after baton.wait()&quot;</span> <span class="o">&lt;&lt;</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="endl">endl</span><span class="o">;</span>
+<span class="o">&#125;);</span>
+
+<span class="no">evb</span><span class="o">.</span><span class="nf" data-symbol-name="loop">loop</span><span class="o">();</span>
+
+<span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="post">post</span><span class="o">();</span>
+<span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="cout">cout</span> <span class="o">&lt;&lt;</span> <span class="s2">&quot;Baton posted&quot;</span> <span class="o">&lt;&lt;</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="endl">endl</span><span class="o">;</span>
+
+<span class="no">evb</span><span class="o">.</span><span class="nf" data-symbol-name="loop">loop</span><span class="o">();</span>
+
+<span class="o">...</span></pre></div>
+
+<p>This would print:</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">Task</span><span class="o">:</span> <span class="no">start</span>
+<span class="no">Baton</span> <span class="no">posted</span>
+<span class="no">Task</span><span class="o">:</span> <span class="no">after</span> <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="wait">wait</span><span class="o">()</span></pre></div>
+
+<p>What makes fiber-task different from any other task run on e.g. an <tt>folly::EventBase</tt> is the ability to suspend such task, without blocking the system thread. So how do you suspend a fiber-task ?</p>
+
+<h3 id="fibers-baton">fibers::Baton <a href="#fibers-baton" class="headerLink">#</a></h3>
+
+<p><tt>fibers::Baton</tt> is the core synchronization primitive which is used to suspend a fiber-task and notify when the task may be resumed. <tt>fibers::Baton</tt> supports two basic operations: <tt>wait()</tt> and <tt>post()</tt>. Calling <tt>wait()</tt> on a Baton will suspend current fiber-task until <tt>post()</tt> is called on the same Baton.</p>
+
+<p>Please refer to <a href="https://github.com/facebook/folly/blob/master/folly/fibers/Baton.h" target="_blank">Baton</a> for more detailed documentation.</p>
+
+<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> <tt>fibers::Baton</tt> is the only native synchronization primitive of folly::fibers library. All other synchronization primitives provided by folly::fibers are built on top of <tt>fibers::Baton</tt>.</div>
+
+<h3 id="integrating-with-other-a">Integrating with other asynchronous APIs (callbacks) <a href="#integrating-with-other-a" class="headerLink">#</a></h3>
+
+<p>Let&#039;s say we have some existing library which provides a classic callback-style asynchronous API.</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">void</span> <span class="nf" data-symbol-name="asyncCall">asyncCall</span><span class="o">(</span><span class="no">Request</span> <span class="no">request</span><span class="o">,</span> <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="Function">Function</span><span class="o">&lt;</span><span class="nf" data-symbol-name="void">void</span><span class="o">(</span><span class="no">Response</span><span class="o">)&gt;</span> <span class="no">cb</span><span class="o">);</span></pre></div>
+
+<p>If we use folly::fibers we can just make an async call from a fiber-task and wait until callback is run:</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
+  <span class="o">...</span>
+  <span class="no">Response</span> <span class="no">response</span><span class="o">;</span>
+  <span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-context="fibers" data-symbol-name="Baton">Baton</span> <span class="no">baton</span><span class="o">;</span>
+
+  <span class="nf" data-symbol-name="asyncCall">asyncCall</span><span class="o">(</span><span class="no">request</span><span class="o">,</span> <span class="o">[&amp;](</span><span class="no">Response</span> <span class="no">r</span><span class="o">)</span> <span class="no">mutable</span> <span class="o">&#123;</span>
+     <span class="no">response</span> <span class="o">=</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="move">move</span><span class="o">(</span><span class="no">r</span><span class="o">);</span>
+     <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="post">post</span><span class="o">();</span>
+  <span class="o">&#125;);</span>
+  <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="wait">wait</span><span class="o">();</span>
+
+  <span class="c">// Now response holds response returned by the async call</span>
+  <span class="o">...</span>
+<span class="o">&#125;</span></pre></div>
+
+<p>Using <tt>fibers::Baton</tt> directly is generally error-prone. To make the task above simpler, folly::fibers provide <tt>fibers::await</tt> function.</p>
+
+<p>With <tt>fibers::await</tt>, the code above transforms into:</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
+  <span class="o">...</span>
+  <span class="no">auto</span> <span class="no">response</span> <span class="o">=</span> <span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="nf" data-symbol-context="fibers" data-symbol-name="await">await</span><span class="o">([&amp;](</span><span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-context="fibers" data-symbol-name="Promise">Promise</span><span class="o">&lt;</span><span class="no">Response</span><span class="o">&gt;</span> <span class="no">promise</span><span class="o">)</span> <span class="o">&#123;</span>
+    <span class="nf" data-symbol-name="asyncCall">asyncCall</span><span class="o">(</span><span class="no">request</span><span class="o">,</span> <span class="o">[</span><span class="no">promise</span> <span class="o">=</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="move">move</span><span class="o">(</span><span class="no">promise</span><span class="o">)](</span><span class="no">Response</span> <span class="no">r</span><span class="o">)</span> <span class="no">mutable</span> <span class="o">&#123;</span>
+      <span class="no">promise</span><span class="o">.</span><span class="nf" data-symbol-name="setValue">setValue</span><span class="o">(</span><span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="move">move</span><span class="o">(</span><span class="no">r</span><span class="o">));</span>
+    <span class="o">&#125;);</span>
+  <span class="o">&#125;);</span>
+
+  <span class="c">// Now response holds response returned by the async call</span>
+  <span class="o">...</span>
+<span class="o">&#125;</span></pre></div>
+
+<p>Callback passed to <tt>fibers::await</tt> is executed immediately and then fiber-task is suspended until <tt>fibers::Promise</tt> is fulfilled. When <tt>fibers::Promise</tt> is fulfilled with a value or exception, fiber-task will be resumed and &#039;fibers::await&#039; returns that value (or throws an exception, if exception was used to fulfill the <tt>Promise</tt>).</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
+  <span class="o">...</span>
+  <span class="k">try</span> <span class="o">&#123;</span>
+    <span class="no">auto</span> <span class="no">response</span> <span class="o">=</span> <span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="nf" data-symbol-context="fibers" data-symbol-name="await">await</span><span class="o">([&amp;](</span><span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-context="fibers" data-symbol-name="Promise">Promise</span><span class="o">&lt;</span><span class="no">Response</span><span class="o">&gt;</span> <span class="no">promise</span><span class="o">)</span> <span class="o">&#123;</span>
+      <span class="nf" data-symbol-name="asyncCall">asyncCall</span><span class="o">(</span><span class="no">request</span><span class="o">,</span> <span class="o">[</span><span class="no">promise</span> <span class="o">=</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="move">move</span><span class="o">(</span><span class="no">promise</span><span class="o">)](</span><span class="no">Response</span> <span class="no">r</span><span class="o">)</span> <span class="no">mutable</span> <span class="o">&#123;</span>
+        <span class="no">promise</span><span class="o">.</span><span class="nf" data-symbol-name="setException">setException</span><span class="o">(</span><span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="runtime_error">runtime_error</span><span class="o">(</span><span class="s2">&quot;Await will re-throw me&quot;</span><span class="o">));</span>
+      <span class="o">&#125;);</span>
+    <span class="o">&#125;);</span>
+    <span class="nf" data-symbol-name="assert">assert</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span> <span class="c">// We should never get here</span>
+  <span class="o">&#125;</span> <span class="k">catch</span> <span class="o">(</span><span class="nc" data-symbol-name="const">const</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-name="exception">exception</span><span class="o">&amp;</span> <span class="no">e</span><span class="o">)</span> <span class="o">&#123;</span>
+    <span class="nf" data-symbol-name="assert">assert</span><span class="o">(</span><span class="no">e</span><span class="o">.</span><span class="nf" data-symbol-name="what">what</span><span class="o">()</span> <span class="o">==</span> <span class="s2">&quot;Await will re-throw me&quot;</span><span class="o">);</span>
+  <span class="o">&#125;</span>
+  <span class="o">...</span>
+<span class="o">&#125;</span></pre></div>
+
+<p>If <tt>fibers::Promise</tt> is not fulfilled, <tt>fibers::await</tt> will throw a <tt>std::logic_error</tt>.</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
+  <span class="o">...</span>
+  <span class="k">try</span> <span class="o">&#123;</span>
+    <span class="no">auto</span> <span class="no">response</span> <span class="o">=</span> <span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="nf" data-symbol-context="fibers" data-symbol-name="await">await</span><span class="o">([&amp;](</span><span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-context="fibers" data-symbol-name="Promise">Promise</span><span class="o">&lt;</span><span class="no">Response</span><span class="o">&gt;</span> <span class="no">promise</span><span class="o">)</span> <span class="o">&#123;</span>
+      <span class="c">// We forget about the promise</span>
+    <span class="o">&#125;);</span>
+    <span class="nf" data-symbol-name="assert">assert</span><span class="o">(</span><span class="kc">false</span><span class="o">);</span> <span class="c">// We should never get here</span>
+  <span class="o">&#125;</span> <span class="k">catch</span> <span class="o">(</span><span class="nc" data-symbol-name="const">const</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-name="logic_error">logic_error</span><span class="o">&amp;</span> <span class="no">e</span><span class="o">)</span> <span class="o">&#123;</span>
+    <span class="o">...</span>
+  <span class="o">&#125;</span>
+  <span class="o">...</span>
+<span class="o">&#125;</span></pre></div>
+
+<p>Please refer to <a href="https://github.com/facebook/folly/blob/master/folly/fibers/Promise.h" target="_blank">await</a> for more detailed documentation.</p>
+
+<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> most of your code written with folly::fibers, won&#039;t be using <tt>fibers::Baton</tt> or <tt>fibers::await</tt>. These primitives should only be used to integrate with other asynchronous API which are not fibers-compatible.</div>
+
+<h3 id="integrating-with-other-a-1">Integrating with other asynchronous APIs (folly::Future) <a href="#integrating-with-other-a-1" class="headerLink">#</a></h3>
+
+<p>Let&#039;s say we have some existing library which provides a Future-based asynchronous API.</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="Future">Future</span><span class="o">&lt;</span><span class="no">Response</span><span class="o">&gt;</span> <span class="nf" data-symbol-name="asyncCallFuture">asyncCallFuture</span><span class="o">(</span><span class="no">Request</span> <span class="no">request</span><span class="o">);</span></pre></div>
+
+<p>The good news are, <tt>folly::Future</tt> is already fibers-compatible. You can simply write:</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
+  <span class="o">...</span>
+  <span class="no">auto</span> <span class="no">response</span> <span class="o">=</span> <span class="nf" data-symbol-name="asyncCallFuture">asyncCallFuture</span><span class="o">(</span><span class="no">request</span><span class="o">).</span><span class="nf" data-symbol-name="get">get</span><span class="o">();</span>
+
+  <span class="c">// Now response holds response returned by the async call</span>
+  <span class="o">...</span>
+<span class="o">&#125;</span></pre></div>
+
+<p>Calling <tt>get()</tt> on a <tt>folly::Future</tt> object will only suspend the calling fiber-task. It won&#039;t block the system thread, letting it process other tasks.</p>
+
+<h2 id="writing-code-with-folly">Writing code with folly::fibers <a href="#writing-code-with-folly" class="headerLink">#</a></h2>
+
+<h3 id="building-fibers-compatib">Building fibers-compatible API <a href="#building-fibers-compatib" class="headerLink">#</a></h3>
+
+<p>Following the explanations above we may wrap an existing asynchronous API in a function:</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">Response</span> <span class="nf" data-symbol-name="fiberCall">fiberCall</span><span class="o">(</span><span class="no">Request</span> <span class="no">request</span><span class="o">)</span> <span class="o">&#123;</span>
+  <span class="k">return</span> <span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="nf" data-symbol-context="fibers" data-symbol-name="await">await</span><span class="o">([&amp;](</span><span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-context="fibers" data-symbol-name="Promise">Promise</span><span class="o">&lt;</span><span class="no">Response</span><span class="o">&gt;</span> <span class="no">promise</span><span class="o">)</span> <span class="o">&#123;</span>
+    <span class="nf" data-symbol-name="asyncCall">asyncCall</span><span class="o">(</span><span class="no">request</span><span class="o">,</span> <span class="o">[</span><span class="no">promise</span> <span class="o">=</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="move">move</span><span class="o">(</span><span class="no">promise</span><span class="o">)](</span><span class="no">Response</span> <span class="no">r</span><span class="o">)</span> <span class="no">mutable</span> <span class="o">&#123;</span>
+      <span class="no">promise</span><span class="o">.</span><span class="nf" data-symbol-name="setValue">setValue</span><span class="o">(</span><span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="move">move</span><span class="o">(</span><span class="no">r</span><span class="o">));</span>
+    <span class="o">&#125;);</span>
+  <span class="o">&#125;);</span>
+<span class="o">&#125;</span></pre></div>
+
+<p>We can then call it from a fiber-task:</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
+  <span class="o">...</span>
+  <span class="no">auto</span> <span class="no">response</span> <span class="o">=</span> <span class="nf" data-symbol-name="fiberCall">fiberCall</span><span class="o">(</span><span class="no">request</span><span class="o">);</span>
+  <span class="o">...</span>
+<span class="o">&#125;);</span></pre></div>
+
+<p>But what happens if we just call <tt>fiberCall</tt> not from within a fiber-task, but directly from a system thread ? Here another important feature of <tt>fibers::Baton</tt> (and thus all other folly::fibers synchronization primitives built on top of it) comes into play. Calling <tt>wait()</tt> on a <tt>fibers::Baton</tt> within a system thread context just blocks the thread until <tt>post()</tt> is called on the same <tt>folly::Baton</tt>.</p>
+
+<p>What this means is that you no longer need to write separate code for synchronous and asynchronous APIs. If you use only folly::fibers synchronization primitives for all blocking calls inside of your synchronous function, it automatically becomes asynchronous when run inside a fiber-task.</p>
+
+<h3 id="passing-by-reference">Passing by reference <a href="#passing-by-reference" class="headerLink">#</a></h3>
+
+<p>Classic asynchronous APIs (same applies to folly::Future-based APIs) generally rely on copying/moving-in input arguments and often require you to copy/move in some context variables into the callback. E.g.:</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="o">...</span>
+<span class="no">Context</span> <span class="no">context</span><span class="o">;</span>
+
+<span class="nf" data-symbol-name="asyncCall">asyncCall</span><span class="o">(</span><span class="no">request</span><span class="o">,</span> <span class="o">[</span><span class="no">request</span><span class="o">,</span> <span class="no">context</span><span class="o">](</span><span class="no">Response</span> <span class="no">response</span><span class="o">)</span> <span class="no">mutable</span> <span class="o">&#123;</span>
+  <span class="nf" data-symbol-name="doSomething">doSomething</span><span class="o">(</span><span class="no">request</span><span class="o">,</span> <span class="no">response</span><span class="o">,</span> <span class="no">context</span><span class="o">);</span>
+<span class="o">&#125;);</span>
+<span class="o">...</span></pre></div>
+
+<p>Fibers-compatible APIs look more like synchronous APIs, so you can actually pass input arguments by reference and you don&#039;t have to think about passing context at all. E.g.</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
+  <span class="o">...</span>
+  <span class="no">Context</span> <span class="no">context</span><span class="o">;</span>
+
+  <span class="no">auto</span> <span class="no">response</span> <span class="o">=</span> <span class="nf" data-symbol-name="fiberCall">fiberCall</span><span class="o">(</span><span class="no">request</span><span class="o">);</span>
+
+  <span class="nf" data-symbol-name="doSomething">doSomething</span><span class="o">(</span><span class="no">request</span><span class="o">,</span> <span class="no">response</span><span class="o">,</span> <span class="no">context</span><span class="o">);</span>
+  <span class="o">...</span>
+<span class="o">&#125;);</span></pre></div>
+
+<p>Same logic applies to <tt>fibers::await</tt>. Since <tt>fibers::await</tt> call blocks until promise is fulfilled, it&#039;s safe to pass everything by reference.</p>
+
+<h3 id="limited-stack-space">Limited stack space <a href="#limited-stack-space" class="headerLink">#</a></h3>
+
+<p>So should you just run all the code inside a fiber-task ? No exactly.</p>
+
+<p>Similarly to system threads, every fiber-task has some stack space assigned to it. Stack usage goes up with the number of nested function calls and objects allocated on the stack. folly::fibers implementation only supports fiber-tasks with fixed stack size. If you want to have many fiber-tasks running concurrently - you need to reduce the amount of stack assigned to each fiber-task, otherwise you may run out of memory.</p>
+
+<div class="remarkup-important"><span class="remarkup-note-word">IMPORTANT:</span> If a fiber-task runs out of stack space (e.g. calls a function which does a lot of stack allocations) you program will fail.</div>
+
+<p>However if you know that some function never suspends a fiber-task, you can use <tt>fibers::runInMainContext</tt> to safely call it from a fiber-task, without any risk of running out of stack space of the fiber-task.</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">Result</span> <span class="nf" data-symbol-name="useALotOfStack">useALotOfStack</span><span class="o">()</span> <span class="o">&#123;</span>
+  <span class="no">char</span> <span class="no">buffer</span><span class="o">[</span><span class="mi">1024</span><span class="o">*</span><span class="mi">1024</span><span class="o">];</span>
+  <span class="o">...</span>
+<span class="o">&#125;</span>
+
+<span class="o">...</span>
+<span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
+  <span class="o">...</span>
+  <span class="no">auto</span> <span class="no">result</span> <span class="o">=</span> <span class="nc" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="nf" data-symbol-context="fibers" data-symbol-name="runInMainContext">runInMainContext</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
+    <span class="k">return</span> <span class="nf" data-symbol-name="useALotOfStack">useALotOfStack</span><span class="o">();</span>
+  <span class="o">&#125;);</span>
+  <span class="o">...</span>
+<span class="o">&#125;);</span>
+<span class="o">...</span></pre></div>
+
+<p><tt>fibers::runInMainContext</tt> will switch to the stack of the system thread (main context), run the functor passed to it and then switch back to the fiber-task stack.</p>
+
+<div class="remarkup-important"><span class="remarkup-note-word">IMPORTANT:</span> Make sure you don&#039;t do any blocking calls on main context though. It will suspend the whole system thread, not just the fiber-task which was running.</div>
+
+<p>Remember that it&#039;s fine to use <tt>fibers::runInMainContext</tt> in general purpose functions (those which may be called both from fiber-task and non from fiber-task). When called in non-fiber-task context <tt>fibers::runInMainContext</tt> would simply execute passed functor right away.</p>
+
+<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> Besides <tt>fibers::runInMainContext</tt> some other functions in folly::fibers are also executing some of the passed functors on the main context. E.g. functor passes to <tt>fibers::await</tt> is executed on main context, finally-functor passed to <tt>FiberManager::addTaskFinally</tt> is also executed on main context etc. Relying on this can help you avoid extra <tt>fibers::runInMainContext</tt> calls (and avoid extra context switches).</div>
+
+<h3 id="using-locks">Using locks <a href="#using-locks" class="headerLink">#</a></h3>
+
+<p>Consider the following example:</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="o">...</span>
+<span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="EventBase">EventBase</span> <span class="no">evb</span><span class="o">;</span>
+<span class="no">auto</span><span class="o">&amp;</span> <span class="no">fiberManager</span> <span class="o">=</span> <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="getFiberManager">getFiberManager</span><span class="o">(</span><span class="no">evb</span><span class="o">);</span>
+<span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="mutex">mutex</span> <span class="no">lock</span><span class="o">;</span>
+<span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Baton">Baton</span> <span class="no">baton</span><span class="o">;</span>
+
+<span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
+  <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="lock_guard">lock_guard</span><span class="o">&lt;</span><span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="mutex">mutex</span><span class="o">&gt;</span> <span class="nf" data-symbol-name="lg">lg</span><span class="o">(</span><span class="no">lock</span><span class="o">);</span>
+  <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="wait">wait</span><span class="o">();</span>
+<span class="o">&#125;);</span>
+
+<span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
+  <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="lock_guard">lock_guard</span><span class="o">&lt;</span><span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="mutex">mutex</span><span class="o">&gt;</span> <span class="nf" data-symbol-name="lg">lg</span><span class="o">(</span><span class="no">lock</span><span class="o">);</span>
+<span class="o">&#125;);</span>
+
+<span class="no">evb</span><span class="o">.</span><span class="nf" data-symbol-name="loop">loop</span><span class="o">();</span>
+<span class="c">// We won&#039;t get here :(</span>
+<span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="post">post</span><span class="o">();</span>
+<span class="o">...</span></pre></div>
+
+<p>First fiber-task will grab a lock and then suspend waiting on a <tt>fibers::Baton</tt>. Then second fiber-task will be run and it will try to grab a lock. Unlike system threads, fiber-task can be only suspended explicitly, so the whole system thread will be blocked waiting on the lock, and we end up with a dead-lock.</p>
+
+<p>There&#039;re generally two ways we can solve this problem. Ideally we would re-design the program to never not hold any locks when fiber-task is suspended. However if we are absolutely sure we need that lock - folly::fibers library provides some fiber-task-aware lock implementations (e.g.
+<a href="https://github.com/facebook/folly/blob/master/folly/fibers/TimedMutex.h" target="_blank">TimedMutex</a>).</p></section><section class="dex_document"><h1>APIs</h1><p class="dex_introduction"></p><h2 id="fibers-baton">fibers::Baton <a href="#fibers-baton" class="headerLink">#</a></h2>
+
+<p>All of the features of folly::fibers library are actually built on top a single synchronization primitive called Baton. <tt>fibers::Baton</tt> is a fiber-specific version of <tt>folly::Baton</tt>. It only  supports two basic operations: <tt>wait()</tt> and <tt>post()</tt>. Whenever <tt>wait()</tt> is called on the Baton, the current thread or fiber-task is suspended, until <tt>post()</tt> is called on the same Baton. <tt>wait()</tt> does not suspend the thread or fiber-task if <tt>post()</tt> was already called on the Baton. Please refer to <a href="https://github.com/facebook/folly/blob/master/folly/fibers/Baton.h" target="_blank">Baton</a> for more detailed documentation.</p>
+
+<p>Baton is thread-safe, so <tt>wait()</tt> and <tt>post()</tt> can be (and should be :) ) called from different threads or fiber-tasks.</p>
+
+<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> Because Baton transparently works both on threads and fiber-tasks, any synchronization primitive built using it would have the same property. This means that any library with a synchronous API, which uses only <tt>fibers::Baton</tt> for synchronization, becomes asynchronous when used in fiber context.</div>
+
+<h3 id="timed-wait">timed_wait() <a href="#timed-wait" class="headerLink">#</a></h3>
+
+<p><tt>fibers::Baton</tt> also supports wait with timeout.</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([=]()</span> <span class="o">&#123;</span>
+  <span class="no">auto</span> <span class="no">baton</span> <span class="o">=</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="make_shared">make_shared</span><span class="o">&lt;</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Baton">Baton</span><span class="o">&gt;();</span>
+  <span class="no">auto</span> <span class="no">result</span> <span class="o">=</span> <span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="make_shared">make_shared</span><span class="o">&lt;</span><span class="no">Result</span><span class="o">&gt;();</span>
+
+  <span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([=]()</span> <span class="o">&#123;</span>
+    <span class="o">*</span><span class="no">result</span> <span class="o">=</span> <span class="nf" data-symbol-name="sendRequest">sendRequest</span><span class="o">(...);</span>
+    <span class="no">baton</span><span class="o">-&gt;</span><span class="na" data-symbol-name="post">post</span><span class="o">();</span>
+  <span class="o">&#125;);</span>
+
+  <span class="no">bool</span> <span class="no">success</span> <span class="o">=</span> <span class="no">baton</span><span class="o">.</span><span class="nf" data-symbol-name="timed_wait">timed_wait</span><span class="o">(</span><span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="chrono">chrono</span><span class="o">::</span><span class="na" data-symbol-name="milliseconds">milliseconds</span><span class="o">&#123;</span><span class="mi">10</span><span class="o">&#125;);</span>
+  <span class="k">if</span> <span class="o">(</span><span class="no">success</span><span class="o">)</span> <span class="o">&#123;</span>
+    <span class="c">// request successful</span>
+    <span class="o">...</span>
+  <span class="o">&#125;</span> <span class="k">else</span> <span class="o">&#123;</span>
+    <span class="c">// handle timeout</span>
+    <span class="o">...</span>
+  <span class="o">&#125;</span>
+<span class="o">&#125;);</span></pre></div>
+
+<div class="remarkup-important"><span class="remarkup-note-word">IMPORTANT:</span> unlike <tt>wait()</tt> when using <tt>timed_wait()</tt> API it&#039;s generally not safe to pass <tt>fibers::Baton</tt> by reference. You have to make sure that task, which fulfills the Baton is either cancelled in case of timeout, or have shared ownership for the Baton.</div>
+
+<h2 id="task-creation">Task creation <a href="#task-creation" class="headerLink">#</a></h2>
+
+<h3 id="addtask-addtaskremote">addTask() / addTaskRemote() <a href="#addtask-addtaskremote" class="headerLink">#</a></h3>
+
+<p>As you could see from previous examples, the easiest way to create a new fiber-task is to call <tt>addTask()</tt>:</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
+  <span class="o">...</span>
+<span class="o">&#125;);</span></pre></div>
+
+<p>It is important to remember that <tt>addTask()</tt> is not thread-safe. I.e. it can only be safely called from the the thread, which is running the <tt>folly::FiberManager</tt> loop.</p>
+
+<p>If you need to create a fiber-task from a different thread, you have to use <tt>addTaskRemote()</tt>:</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="EventBase">EventBase</span> <span class="no">evb</span><span class="o">;</span>
+<span class="no">auto</span><span class="o">&amp;</span> <span class="no">fiberManager</span> <span class="o">=</span> <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="getFiberManager">getFiberManager</span><span class="o">(</span><span class="no">evb</span><span class="o">);</span>
+
+<span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="thread">thread</span> <span class="nf" data-symbol-name="t">t</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
+  <span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTaskRemote">addTaskRemote</span><span class="o">([]()</span> <span class="o">&#123;</span>
+    <span class="o">...</span>
+  <span class="o">&#125;);</span>
+<span class="o">&#125;);</span>
+
+<span class="no">evb</span><span class="o">.</span><span class="nf" data-symbol-name="loopForever">loopForever</span><span class="o">();</span></pre></div>
+
+<h3 id="addtaskfinally">addTaskFinally() <a href="#addtaskfinally" class="headerLink">#</a></h3>
+
+<p><tt>addTaskFinally()</tt> is useful when you need to run some code on the main context in the end of a fiber-task.</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTaskFinally">addTaskFinally</span><span class="o">(</span>
+  <span class="o">[=]()</span> <span class="o">&#123;</span>
+    <span class="o">...</span>
+    <span class="k">return</span> <span class="no">result</span><span class="o">;</span>
+  <span class="o">&#125;,</span>
+  <span class="o">[=](</span><span class="no">Result</span><span class="o">&amp;&amp;</span> <span class="no">result</span><span class="o">)</span> <span class="o">&#123;</span>
+    <span class="nf" data-symbol-name="callUserCallbacks">callUserCallbacks</span><span class="o">(</span><span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="move">move</span><span class="o">(</span><span class="no">result</span><span class="o">),</span> <span class="o">...)</span>
+  <span class="o">&#125;</span>
+<span class="o">);</span></pre></div>
+
+<p>Of course you could achieve the same by calling <tt>fibers::runInMainContext()</tt>, but <tt>addTaskFinally()</tt> reduces the number of fiber context switches:</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([=]()</span> <span class="o">&#123;</span>
+  <span class="o">...</span>
+  <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="runInMainContext">runInMainContext</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
+    <span class="c">// Switched to main context</span>
+    <span class="nf" data-symbol-name="callUserCallbacks">callUserCallbacks</span><span class="o">(</span><span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="nf" data-symbol-context="std" data-symbol-name="move">move</span><span class="o">(</span><span class="no">result</span><span class="o">),</span> <span class="o">...)</span>
+  <span class="o">&#125;</span>
+  <span class="c">// Switched back to fiber context</span>
+
+  <span class="c">// On fiber context we realize there&#039;s no more work to be done.</span>
+  <span class="c">// Fiber-task is complete, switching back to main context.</span>
+<span class="o">&#125;);</span></pre></div>
+
+<p></p>
+
+<h3 id="addtaskfuture-addtaskrem">addTaskFuture() / addTaskRemoteFuture() <a href="#addtaskfuture-addtaskrem" class="headerLink">#</a></h3>
+
+<p><tt>addTask()</tt> and <tt>addTaskRemote()</tt> are creating detached fiber-tasks. If you need to know when fiber-task is complete and/or have some return value for it -  <tt>addTaskFuture()</tt> / <tt>addTaskRemoteFuture()</tt> can be used.</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="EventBase">EventBase</span> <span class="no">evb</span><span class="o">;</span>
+<span class="no">auto</span><span class="o">&amp;</span> <span class="no">fiberManager</span> <span class="o">=</span> <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="getFiberManager">getFiberManager</span><span class="o">(</span><span class="no">evb</span><span class="o">);</span>
+
+<span class="nc" data-symbol-name="std">std</span><span class="o">::</span><span class="na" data-symbol-context="std" data-symbol-name="thread">thread</span> <span class="nf" data-symbol-name="t">t</span><span class="o">([&amp;]()</span> <span class="o">&#123;</span>
+  <span class="no">auto</span> <span class="no">future1</span> <span class="o">=</span> <span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTaskRemoteFuture">addTaskRemoteFuture</span><span class="o">([]()</span> <span class="o">&#123;</span>
+    <span class="o">...</span>
+  <span class="o">&#125;);</span>
+  <span class="no">auto</span> <span class="no">future2</span> <span class="o">=</span> <span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTaskRemoteFuture">addTaskRemoteFuture</span><span class="o">([]()</span> <span class="o">&#123;</span>
+    <span class="o">...</span>
+  <span class="o">&#125;);</span>
+
+  <span class="no">auto</span> <span class="no">result1</span> <span class="o">=</span> <span class="no">future1</span><span class="o">.</span><span class="nf" data-symbol-name="get">get</span><span class="o">();</span>
+  <span class="no">auto</span> <span class="no">result2</span> <span class="o">=</span> <span class="no">future2</span><span class="o">.</span><span class="nf" data-symbol-name="get">get</span><span class="o">();</span>
+  <span class="o">...</span>
+<span class="o">&#125;);</span>
+
+<span class="no">evb</span><span class="o">.</span><span class="nf" data-symbol-name="loopForever">loopForever</span><span class="o">();</span></pre></div>
+
+<h2 id="other-synchronization-pr">Other synchronization primitives <a href="#other-synchronization-pr" class="headerLink">#</a></h2>
+
+<p>All the listed synchronization primitives are built using <tt>fiber::Baton</tt>. Please check their source code for detailed documentation.</p>
+
+<p><a href="https://github.com/facebook/folly/blob/master/folly/fibers/Promise.h" target="_blank">await</a></p>
+
+<p><a href="https://github.com/facebook/folly/blob/master/folly/fibers/WhenN.h" target="_blank">collectN</a></p>
+
+<p><a href="https://github.com/facebook/folly/blob/master/folly/fibers/WhenN.h" target="_blank">collectAny</a></p>
+
+<p><a href="https://github.com/facebook/folly/blob/master/folly/fibers/WhenN.h" target="_blank">collectN</a></p>
+
+<p><a href="https://github.com/facebook/folly/blob/master/folly/fibers/ForEach.h" target="_blank">forEach</a></p>
+
+<p><a href="https://github.com/facebook/folly/blob/master/folly/fibers/AddTasks.h" target="_blank">addTasks</a></p>
+
+<p><a href="https://github.com/facebook/folly/blob/master/folly/fibers/TimedMutex.h" target="_blank">TimedMutex</a></p>
+
+<p><a href="https://github.com/facebook/folly/blob/master/folly/fibers/TimedMutex.h" target="_blank">TimedRWMutex</a></p></section><section class="dex_document"><h1>Fiber stacks</h1><p class="dex_introduction"></p><p>Similarly to system threads, every fiber-task has some stack space assigned to it. Stack usage goes up with the number of nested function calls and objects allocated on the stack. folly::fibers implementation only supports fiber-tasks with fixed stack size. If you want to have many fiber-tasks running concurrently - you need to reduce the amount of stack assigned to each fiber-task, otherwise you may run out of memory.</p>
+
+<h3 id="selecting-stack-size">Selecting stack size <a href="#selecting-stack-size" class="headerLink">#</a></h3>
+
+<p>Stack size used for every fiber-task is part of FiberManager configuration. But how do you pick the right stack size ?</p>
+
+<p>First of all you need to figure out the maximum number of concurrent fiber-tasks your application may have. E.g. if you are writing a Thrift-service you will probably have a single fiber-task for every request in-fly (but remember that e.g. <tt>fibers::collectAll</tt> and some other synchronization primitives may create extra fiber-tasks). It&#039;s very important to get that number first, because if you will at most need 100 concurrent fiber-tasks, even 1MB stacks will result in at most 100MB used for fiber stacks. On the other hand if you need to have 100,000 concurrent fiber-tasks, even 16KB stacks will result in 1.6GB peak memory usage just for fiber stacks.</p>
+
+<p>folly::fibers also supports recording stack usage (it can be enabled via <tt>recordStackEvery</tt> option of <tt>FiberManager</tt>). When enabled, the stack of each fiber-task will be filled with magic values. Later linear search can be performed to find the boundary of unused stack space.</p>
+
+<h3 id="stack-overflow-detection">Stack overflow detection <a href="#stack-overflow-detection" class="headerLink">#</a></h3>
+
+<p>By default every fiber-task stack is allocated with a special guard page next to it (this can be controlled via <tt>useGuardPages</tt> option of <tt>FiberManager</tt>). If a stack overflow happens - this guard page will be accessed, which will result in immediate segmentation fault.</p>
+
+<div class="remarkup-important"><span class="remarkup-note-word">IMPORTANT:</span> disabling guard page protection may result in unnoticed stack overflows. Those will inevitably cause memory corruptions, which are usually very hard to debug.</div></section><section class="dex_document"><h1>Event Loops</h1><p class="dex_introduction"></p><p>folly::fibers library doesn&#039;t implement it&#039;s own event system. Instead it allows <tt>fibers::FiberManager</tt> to work with any other event system by implementing <tt>fibers::LoopController</tt> interface.</p>
+
+<h2 id="folly-eventbase-integrat">folly::EventBase integration <a href="#folly-eventbase-integrat" class="headerLink">#</a></h2>
+
+<p>The easiest way to create a <tt>fibers::FiberManager</tt> attached to a <tt>folly::EventBase</tt> is by using <tt>fibers::getFiberManager</tt> function:</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="EventBase">EventBase</span> <span class="no">evb</span><span class="o">;</span>
+<span class="no">auto</span><span class="o">&amp;</span> <span class="no">fiberManager</span> <span class="o">=</span> <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="getFiberManager">getFiberManager</span><span class="o">(</span><span class="no">evb</span><span class="o">);</span>
+
+<span class="no">fiberManager</span><span class="o">.</span><span class="nf" data-symbol-name="addTask">addTask</span><span class="o">([]()</span> <span class="o">&#123;</span>
+  <span class="o">...</span>
+<span class="o">&#125;);</span>
+
+<span class="no">evb</span><span class="o">.</span><span class="nf" data-symbol-name="loop">loop</span><span class="o">();</span></pre></div>
+
+<p>Such <tt>fibers::FiberManager</tt> will be automatically destroyed, when <tt>folly::EventBase</tt> is destroyed.</p>
+
+<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> folly::fibers doesn&#039;t support killing fiber-tasks in-flight (for similar reasons you can&#039;t kill a thread). If <tt>fibers::FiberManager</tt> has any outstanding fiber-tasks, when <tt>folly::EventBase</tt> is being destroyed, it will keep running the event loop until all those tasks are finished.</div></section><section class="dex_document"><h1>GDB integration</h1><p class="dex_introduction"></p><p>folly::fibers provide some GDB extensions which can be very useful for debugging. To load them simply the following in dbg console:</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="no">source</span> <span class="s1">&#039;folly/fibers/scripts/utils.gdb&#039;</span></pre></div>
+
+<h3 id="show-all-fibermanagers">Show all FiberManagers <a href="#show-all-fibermanagers" class="headerLink">#</a></h3>
+
+<p>You can use <tt>print_folly_fiber_manager_map</tt> to list all <tt>fibers::FiberManager</tt>s and <tt>folly::EventBase</tt>s they are attached to.</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="o">(</span><span class="no">gdb</span><span class="o">)</span> <span class="no">print_folly_fiber_manager_map</span>
+  <span class="k">Global</span> <span class="no">FiberManager</span> <span class="no">map</span> <span class="no">has</span> <span class="mi">2</span> <span class="no">entries</span><span class="o">.</span>
+    <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="EventBase">EventBase</span><span class="o">*)</span><span class="mh">0x7fffffffdb60</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="FiberManager">FiberManager</span><span class="o">*)</span><span class="mh">0x7ffff5b58480</span>
+    <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="EventBase">EventBase</span><span class="o">*)</span><span class="mh">0x7fffffffd930</span> <span class="o">-&gt;</span> <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="FiberManager">FiberManager</span><span class="o">*)</span><span class="mh">0x7ffff5b58300</span></pre></div>
+
+<p>This will only list <tt>fibers::FiberManager</tt>s created using <tt>fibers::getFiberManager()</tt> function.</p>
+
+<h3 id="print-fibermanager-state">Print FiberManager state <a href="#print-fibermanager-state" class="headerLink">#</a></h3>
+
+<p>You can use <tt>print_folly_fiber_manager</tt> (and passing a pointer to valid <tt>fibers::FiberManager</tt> object) to print the state of given <tt>fibers::FiberManager</tt>.</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="o">(</span><span class="no">gdb</span><span class="o">)</span> <span class="no">print_folly_fiber_manager</span> <span class="o">&amp;</span><span class="nf" data-symbol-name="manager">manager</span>
+  <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="FiberManager">FiberManager</span><span class="o">*)</span><span class="mh">0x7fffffffdbe0</span>
+
+  <span class="no">Fibers</span> <span class="no">active</span><span class="o">:</span> <span class="mi">3</span>
+  <span class="no">Fibers</span> <span class="no">allocated</span><span class="o">:</span> <span class="mi">3</span>
+  <span class="no">Fibers</span> <span class="no">pool</span> <span class="no">size</span><span class="o">:</span> <span class="mi">0</span>
+  <span class="no">Active</span> <span class="no">fiber</span><span class="o">:</span> <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Fiber">Fiber</span><span class="o">*)(</span><span class="no">nil</span><span class="o">)</span>
+  <span class="no">Current</span> <span class="no">fiber</span><span class="o">:</span> <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Fiber">Fiber</span><span class="o">*)(</span><span class="no">nil</span><span class="o">)</span>
+
+  <span class="no">Active</span> <span class="no">fibers</span><span class="o">:</span>
+    <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Fiber">Fiber</span><span class="o">*)</span><span class="mh">0x7ffff5b5b000</span>   <span class="no">State</span><span class="o">:</span> <span class="nf" data-symbol-name="Awaiting">Awaiting</span>
+    <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Fiber">Fiber</span><span class="o">*)</span><span class="mh">0x7ffff5b5b300</span>   <span class="no">State</span><span class="o">:</span> <span class="nf" data-symbol-name="Awaiting">Awaiting</span>
+    <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Fiber">Fiber</span><span class="o">*)</span><span class="mh">0x7ffff5b5b600</span>   <span class="no">State</span><span class="o">:</span> <span class="no">Awaiting</span></pre></div>
+
+<p>It will list all active <tt>fibers::Fiber</tt> objects, which are running fiber-tasks and their states.</p>
+
+<h3 id="print-fiber-task">Print fiber-task <a href="#print-fiber-task" class="headerLink">#</a></h3>
+
+<p>If you have a <tt>fibers::Fiber</tt>, which is running some fiber-task, you can print its state using <tt>print_folly_fiber</tt> command.</p>
+
+<div class="remarkup-code-block" data-code-lang="php"><pre class="remarkup-code"><span class="o">(</span><span class="no">gdb</span><span class="o">)</span> <span class="no">print_folly_fiber</span> <span class="mh">0x7ffff5b5b600</span>
+  <span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Fiber">Fiber</span><span class="o">*)</span><span class="mh">0x7ffff5b5b600</span>
+
+  <span class="no">State</span><span class="o">:</span> <span class="no">Awaiting</span>
+  <span class="no">Backtrace</span><span class="o">:</span>
+    <span class="c">#0 at 0x5a22a8 in folly::fibers::FiberManager::deactivateFiber(folly::fibers::Fiber*) + 194 in section .text of /mnt/fio0/andrii/fbsource/fbcode/buck-out</span>
+<span class="o">/</span><span class="no">gen</span><span class="o">/</span><span class="no">folly</span><span class="o">/</span><span class="no">experimental</span><span class="o">/</span><span class="no">fibers</span><span class="o">/</span><span class="no">fibers</span><span class="o">-</span><span class="no">test</span>
+    <span class="c">#1 at 0x5a1606 in folly::fibers::Fiber::preempt(folly::fibers::Fiber::State)::&#123;lambda()#1&#125;::operator()() + 598 in section .text of /mnt/fio0/andrii/fbsou</span>
+<span class="no">rce</span><span class="o">/</span><span class="no">fbcode</span><span class="o">/</span><span class="no">buck</span><span class="o">-</span><span class="no">out</span><span class="o">/</span><span class="no">gen</span><span class="o">/</span><span class="no">folly</span><span class="o">/</span><span class="no">experimental</span><span class="o">/</span><span class="no">fibers</span><span class="o">/</span><span class="no">fibers</span><span class="o">-</span><span class="no">test</span>
+    <span class="c">#2 at 0x5a17f8 in folly::fibers::Fiber::preempt(folly::fibers::Fiber::State) + 176 in section .text of /mnt/fio0/andrii/fbsource/fbcode/buck-out/gen/foll</span>
+<span class="no">y</span><span class="o">/</span><span class="no">experimental</span><span class="o">/</span><span class="no">fibers</span><span class="o">/</span><span class="no">fibers</span><span class="o">-</span><span class="no">test</span>
+    <span class="c">#3 at 0x43a76e in void folly::fibers::Baton::waitFiber&lt;folly::fibers::FirstArgOf&lt;FiberManager_collectAll_Test::TestBody()::&#123;lambda()#1&#125;::operator()() con</span>
+<span class="nc" data-symbol-name="st">st</span><span class="o">::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda(folly::fibers::Promise&lt;int&gt;)#1&#125;, void&gt;::type::value_type folly::fibers::await</span>
+<span class="o">&lt;</span><span class="nc" data-symbol-name="FiberManager_collectAll_Test">FiberManager_collectAll_Test</span><span class="o">::</span><span class="nf" data-symbol-context="FiberManager_collectAll_Test" data-symbol-name="TestBody">TestBody</span><span class="o">()::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda(foll</span>
+<span class="nc" data-symbol-name="y">y</span><span class="o">::</span><span class="na" data-symbol-context="y" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Promise">Promise</span><span class="o">&lt;</span><span class="no">int</span><span class="o">&gt;)</span><span class="c">#1&#125;&gt;(folly::fibers::Promise&lt;int&gt;&amp;&amp;)::&#123;lambda()#1&#125;&gt;(folly::fibers::FiberManager&amp;, folly::fibers::FirstArgOf&lt;FiberManager_collectAll_Te</span>
+<span class="nc" data-symbol-name="st">st</span><span class="o">::</span><span class="nf" data-symbol-context="st" data-symbol-name="TestBody">TestBody</span><span class="o">()::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda(folly::fibers::Promise&lt;int&gt;)#1&#125;</span>
+<span class="o">,</span> <span class="no">void</span><span class="o">&gt;::</span><span class="nc" data-symbol-name="type">type</span><span class="o">::</span><span class="na" data-symbol-context="type" data-symbol-name="value_type">value_type</span> <span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="await">await</span><span class="o">&lt;</span><span class="nc" data-symbol-name="FiberManager_collectAll_Test">FiberManager_collectAll_Test</span><span class="o">::</span><span class="nf" data-symbol-context="FiberManager_collectAll_Test" data-symbol-name="TestBody">TestBody</span><span class="o">()::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::</span>
+<span class="o">&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda(folly::fibers::Promise&lt;int&gt;)#1&#125;&gt;(folly::fibers::Promise&lt;int&gt;&amp;&amp;)::&#123;lambda()#1&#125;) + 110 in section .text of /mnt/fio0/</span>
+<span class="no">andrii</span><span class="o">/</span><span class="no">fbsource</span><span class="o">/</span><span class="no">fbcode</span><span class="o">/</span><span class="no">buck</span><span class="o">-</span><span class="no">out</span><span class="o">/</span><span class="no">gen</span><span class="o">/</span><span class="no">folly</span><span class="o">/</span><span class="no">experimental</span><span class="o">/</span><span class="no">fibers</span><span class="o">/</span><span class="no">fibers</span><span class="o">-</span><span class="no">test</span>
+    <span class="c">#4 at 0x42fa89 in void folly::fibers::Baton::wait&lt;folly::fibers::FirstArgOf&lt;FiberManager_collectAll_Test::TestBody()::&#123;lambda()#1&#125;::operator()() const::&#123;</span>
+<span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda(folly::fibers::Promise&lt;int&gt;)#1&#125;, void&gt;::type::value_type folly::fibers::await&lt;Fibe</span>
+<span class="nc" data-symbol-name="rManager_collectAll_Test">rManager_collectAll_Test</span><span class="o">::</span><span class="nf" data-symbol-context="rManager_collectAll_Test" data-symbol-name="TestBody">TestBody</span><span class="o">()::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda(folly::fi</span>
+<span class="nc" data-symbol-name="bers">bers</span><span class="o">::</span><span class="na" data-symbol-context="bers" data-symbol-name="Promise">Promise</span><span class="o">&lt;</span><span class="no">int</span><span class="o">&gt;)</span><span class="c">#1&#125;&gt;(folly::fibers::Promise&lt;int&gt;&amp;&amp;)::&#123;lambda()#1&#125;&gt;(folly::fibers::FirstArgOf&lt;FiberManager_collectAll_Test::TestBody()::&#123;lambda()#1&#125;::opera</span>
+<span class="nf" data-symbol-name="tor">tor</span><span class="o">()()</span> <span class="k">const</span><span class="o">::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda(folly::fibers::Promise&lt;int&gt;)#1&#125;, void&gt;::type::value_type folly::fi</span>
+<span class="nc" data-symbol-name="bers">bers</span><span class="o">::</span><span class="na" data-symbol-context="bers" data-symbol-name="await">await</span><span class="o">&lt;</span><span class="nc" data-symbol-name="FiberManager_collectAll_Test">FiberManager_collectAll_Test</span><span class="o">::</span><span class="nf" data-symbol-context="FiberManager_collectAll_Test" data-symbol-name="TestBody">TestBody</span><span class="o">()::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;</span>
+<span class="nf" data-symbol-name="lambda">lambda</span><span class="o">(</span><span class="nc" data-symbol-name="folly">folly</span><span class="o">::</span><span class="na" data-symbol-context="folly" data-symbol-name="fibers">fibers</span><span class="o">::</span><span class="na" data-symbol-name="Promise">Promise</span><span class="o">&lt;</span><span class="no">int</span><span class="o">&gt;)</span><span class="c">#1&#125;&gt;(folly::fibers::Promise&lt;int&gt;&amp;&amp;)::&#123;lambda()#1&#125;) + 105 in section .text of /mnt/fio0/andrii/fbsource/fbcode/buck-out/gen</span>
+<span class="o">/</span><span class="no">folly</span><span class="o">/</span><span class="no">experimental</span><span class="o">/</span><span class="no">fibers</span><span class="o">/</span><span class="no">fibers</span><span class="o">-</span><span class="no">test</span>
+    <span class="c">#5 at 0x425921 in folly::fibers::FirstArgOf&lt;FiberManager_collectAll_Test::TestBody()::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const:</span>
+<span class="o">:&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda(folly::fibers::Promise&lt;int&gt;)#1&#125;, void&gt;::type::value_type folly::fibers::await&lt;FiberManager_collectAll_Test::TestBo</span>
+<span class="nf" data-symbol-name="dy">dy</span><span class="o">()::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda(folly::fibers::Promise&lt;int&gt;)#1&#125;&gt;(folly::f</span>
+<span class="nc" data-symbol-name="ibers">ibers</span><span class="o">::</span><span class="na" data-symbol-context="ibers" data-symbol-name="Promise">Promise</span><span class="o">&lt;</span><span class="no">int</span><span class="o">&gt;&amp;&amp;)</span> <span class="o">+</span> <span class="mi">80</span> <span class="no">in</span> <span class="no">section</span> <span class="o">.</span><span class="no">text</span> <span class="no">of</span> <span class="o">/</span><span class="no">mnt</span><span class="o">/</span><span class="no">fio0</span><span class="o">/</span><span class="no">andrii</span><span class="o">/</span><span class="no">fbsource</span><span class="o">/</span><span class="no">fbcode</span><span class="o">/</span><span class="no">buck</span><span class="o">-</span><span class="no">out</span><span class="o">/</span><span class="no">gen</span><span class="o">/</span><span class="no">folly</span><span class="o">/</span><span class="no">experimental</span><span class="o">/</span><span class="no">fibers</span><span class="o">/</span><span class="no">fibers</span><span class="o">-</span><span class="no">test</span>
+    <span class="c">#6 at 0x415e9a in FiberManager_collectAll_Test::TestBody()::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()(</span>
+<span class="o">)</span> <span class="k">const</span> <span class="o">+</span> <span class="mi">36</span> <span class="no">in</span> <span class="no">section</span> <span class="o">.</span><span class="no">text</span> <span class="no">of</span> <span class="o">/</span><span class="no">mnt</span><span class="o">/</span><span class="no">fio0</span><span class="o">/</span><span class="no">andrii</span><span class="o">/</span><span class="no">fbsource</span><span class="o">/</span><span class="no">fbcode</span><span class="o">/</span><span class="no">buck</span><span class="o">-</span><span class="no">out</span><span class="o">/</span><span class="no">gen</span><span class="o">/</span><span class="no">folly</span><span class="o">/</span><span class="no">experimental</span><span class="o">/</span><span class="no">fibers</span><span class="o">/</span><span class="no">fibers</span><span class="o">-</span><span class="no">test</span>
+    <span class="c">#7 at 0x42faf9 in std::_Function_handler&lt;int (), FiberManager_collectAll_Test::TestBody()::&#123;lambda()#1&#125;::operator()() const::&#123;lambda()#1&#125;::operator()() c</span>
+<span class="nc" data-symbol-name="onst">onst</span><span class="o">::&#123;</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1&#125;&gt;::_M_invoke(std::_Any_data const&amp;) + 32 in section .text of /mnt/fio0/andrii/fbsource/fbcode/buck-out/gen/folly/fibers/fibers</span>
+<span class="o">-</span><span class="no">test</span>
+    <span class="c">#8 at 0x479d5c in std::function&lt;int ()&gt;::operator()() const + 50 in section .text of /mnt/fio0/andrii/fbsource/fbcode/buck-out/gen/folly/experimental/fib</span>
+<span class="no">ers</span><span class="o">/</span><span class="no">fibers</span><span class="o">-</span><span class="no">test</span>
+    <span class="o">...</span></pre></div>
+
+<p>It will print the state of the fiber-task and if fiber-task is currently awaiting - also prints its stack-trace.</p>
+
+<div class="remarkup-note"><span class="remarkup-note-word">NOTE:</span> For running (i.e. not suspended) fiber-task, you can simply switch to the system thread which owns it and use regular GDB commands for debugging.</div></section></section>
\ No newline at end of file
diff --git a/folly/fibers/SimpleLoopController.h b/folly/fibers/SimpleLoopController.h
new file mode 100644 (file)
index 0000000..8c728bd
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <folly/Likely.h>
+
+#include <folly/fibers/LoopController.h>
+
+namespace folly {
+namespace fibers {
+
+class FiberManager;
+
+class SimpleLoopController : public LoopController {
+ public:
+  SimpleLoopController() : fm_(nullptr), stopRequested_(false) {}
+
+  /**
+   * Run FiberManager loop; if no ready task are present,
+   * run provided function. Stops after both stop() has been called
+   * and no waiting tasks remain.
+   */
+  template <typename F>
+  void loop(F&& func) {
+    bool waiting = false;
+    stopRequested_ = false;
+
+    while (LIKELY(waiting || !stopRequested_)) {
+      func();
+
+      auto time = Clock::now();
+
+      for (size_t i = 0; i < scheduledFuncs_.size(); ++i) {
+        if (scheduledFuncs_[i].first <= time) {
+          scheduledFuncs_[i].second();
+          swap(scheduledFuncs_[i], scheduledFuncs_.back());
+          scheduledFuncs_.pop_back();
+          --i;
+        }
+      }
+
+      if (scheduled_) {
+        scheduled_ = false;
+        waiting = fm_->loopUntilNoReady();
+      }
+    }
+  }
+
+  /**
+   * Requests exit from loop() as soon as all waiting tasks complete.
+   */
+  void stop() {
+    stopRequested_ = true;
+  }
+
+  int remoteScheduleCalled() const {
+    return remoteScheduleCalled_;
+  }
+
+  void schedule() override {
+    scheduled_ = true;
+  }
+
+  void timedSchedule(std::function<void()> func, TimePoint time) override {
+    scheduledFuncs_.emplace_back(time, std::move(func));
+  }
+
+ private:
+  FiberManager* fm_;
+  std::atomic<bool> scheduled_{false};
+  bool stopRequested_;
+  std::atomic<int> remoteScheduleCalled_{0};
+  std::vector<std::pair<TimePoint, std::function<void()>>> scheduledFuncs_;
+
+  /* LoopController interface */
+
+  void setFiberManager(FiberManager* fm) override {
+    fm_ = fm;
+  }
+
+  void cancel() override {
+    scheduled_ = false;
+  }
+
+  void scheduleThreadSafe(std::function<bool()> func) override {
+    if (func()) {
+      ++remoteScheduleCalled_;
+      scheduled_ = true;
+    }
+  }
+
+  friend class FiberManager;
+};
+}
+} // folly::fibers
diff --git a/folly/fibers/TimedMutex-inl.h b/folly/fibers/TimedMutex-inl.h
new file mode 100644 (file)
index 0000000..96ee606
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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
+
+namespace folly {
+namespace fibers {
+
+//
+// TimedMutex implementation
+//
+
+template <typename BatonType>
+void TimedMutex<BatonType>::lock() {
+  pthread_spin_lock(&lock_);
+  if (!locked_) {
+    locked_ = true;
+    pthread_spin_unlock(&lock_);
+    return;
+  }
+
+  // Delay constructing the waiter until it is actually required.
+  // This makes a huge difference, at least in the benchmarks,
+  // when the mutex isn't locked.
+  MutexWaiter waiter;
+  waiters_.push_back(waiter);
+  pthread_spin_unlock(&lock_);
+  waiter.baton.wait();
+}
+
+template <typename BatonType>
+template <typename Rep, typename Period>
+bool TimedMutex<BatonType>::timed_lock(
+    const std::chrono::duration<Rep, Period>& duration) {
+  pthread_spin_lock(&lock_);
+  if (!locked_) {
+    locked_ = true;
+    pthread_spin_unlock(&lock_);
+    return true;
+  }
+
+  MutexWaiter waiter;
+  waiters_.push_back(waiter);
+  pthread_spin_unlock(&lock_);
+
+  if (!waiter.baton.timed_wait(duration)) {
+    // We timed out. Two cases:
+    // 1. We're still in the waiter list and we truly timed out
+    // 2. We're not in the waiter list anymore. This could happen if the baton
+    //    times out but the mutex is unlocked before we reach this code. In this
+    //    case we'll pretend we got the lock on time.
+    pthread_spin_lock(&lock_);
+    if (waiter.hook.is_linked()) {
+      waiters_.erase(waiters_.iterator_to(waiter));
+      pthread_spin_unlock(&lock_);
+      return false;
+    }
+    pthread_spin_unlock(&lock_);
+  }
+  return true;
+}
+
+template <typename BatonType>
+bool TimedMutex<BatonType>::try_lock() {
+  pthread_spin_lock(&lock_);
+  if (locked_) {
+    pthread_spin_unlock(&lock_);
+    return false;
+  }
+  locked_ = true;
+  pthread_spin_unlock(&lock_);
+  return true;
+}
+
+template <typename BatonType>
+void TimedMutex<BatonType>::unlock() {
+  pthread_spin_lock(&lock_);
+  if (waiters_.empty()) {
+    locked_ = false;
+    pthread_spin_unlock(&lock_);
+    return;
+  }
+  MutexWaiter& to_wake = waiters_.front();
+  waiters_.pop_front();
+  to_wake.baton.post();
+  pthread_spin_unlock(&lock_);
+}
+
+//
+// TimedRWMutex implementation
+//
+
+template <typename BatonType>
+void TimedRWMutex<BatonType>::read_lock() {
+  pthread_spin_lock(&lock_);
+  if (state_ == State::WRITE_LOCKED) {
+    MutexWaiter waiter;
+    read_waiters_.push_back(waiter);
+    pthread_spin_unlock(&lock_);
+    waiter.baton.wait();
+    assert(state_ == State::READ_LOCKED);
+    return;
+  }
+  assert(
+      (state_ == State::UNLOCKED && readers_ == 0) ||
+      (state_ == State::READ_LOCKED && readers_ > 0));
+  assert(read_waiters_.empty());
+  state_ = State::READ_LOCKED;
+  readers_ += 1;
+  pthread_spin_unlock(&lock_);
+}
+
+template <typename BatonType>
+template <typename Rep, typename Period>
+bool TimedRWMutex<BatonType>::timed_read_lock(
+    const std::chrono::duration<Rep, Period>& duration) {
+  pthread_spin_lock(&lock_);
+  if (state_ == State::WRITE_LOCKED) {
+    MutexWaiter waiter;
+    read_waiters_.push_back(waiter);
+    pthread_spin_unlock(&lock_);
+
+    if (!waiter.baton.timed_wait(duration)) {
+      // We timed out. Two cases:
+      // 1. We're still in the waiter list and we truly timed out
+      // 2. We're not in the waiter list anymore. This could happen if the baton
+      //    times out but the mutex is unlocked before we reach this code. In
+      //    this case we'll pretend we got the lock on time.
+      pthread_spin_lock(&lock_);
+      if (waiter.hook.is_linked()) {
+        read_waiters_.erase(read_waiters_.iterator_to(waiter));
+        pthread_spin_unlock(&lock_);
+        return false;
+      }
+      pthread_spin_unlock(&lock_);
+    }
+    return true;
+  }
+  assert(
+      (state_ == State::UNLOCKED && readers_ == 0) ||
+      (state_ == State::READ_LOCKED && readers_ > 0));
+  assert(read_waiters_.empty());
+  state_ = State::READ_LOCKED;
+  readers_ += 1;
+  pthread_spin_unlock(&lock_);
+  return true;
+}
+
+template <typename BatonType>
+bool TimedRWMutex<BatonType>::try_read_lock() {
+  pthread_spin_lock(&lock_);
+  if (state_ != State::WRITE_LOCKED) {
+    assert(
+        (state_ == State::UNLOCKED && readers_ == 0) ||
+        (state_ == State::READ_LOCKED && readers_ > 0));
+    assert(read_waiters_.empty());
+    state_ = State::READ_LOCKED;
+    readers_ += 1;
+    pthread_spin_unlock(&lock_);
+    return true;
+  }
+  pthread_spin_unlock(&lock_);
+  return false;
+}
+
+template <typename BatonType>
+void TimedRWMutex<BatonType>::write_lock() {
+  pthread_spin_lock(&lock_);
+  if (state_ == State::UNLOCKED) {
+    verify_unlocked_properties();
+    state_ = State::WRITE_LOCKED;
+    pthread_spin_unlock(&lock_);
+    return;
+  }
+  MutexWaiter waiter;
+  write_waiters_.push_back(waiter);
+  pthread_spin_unlock(&lock_);
+  waiter.baton.wait();
+}
+
+template <typename BatonType>
+template <typename Rep, typename Period>
+bool TimedRWMutex<BatonType>::timed_write_lock(
+    const std::chrono::duration<Rep, Period>& duration) {
+  pthread_spin_lock(&lock_);
+  if (state_ == State::UNLOCKED) {
+    verify_unlocked_properties();
+    state_ = State::WRITE_LOCKED;
+    pthread_spin_unlock(&lock_);
+    return true;
+  }
+  MutexWaiter waiter;
+  write_waiters_.push_back(waiter);
+  pthread_spin_unlock(&lock_);
+
+  if (!waiter.baton.timed_wait(duration)) {
+    // We timed out. Two cases:
+    // 1. We're still in the waiter list and we truly timed out
+    // 2. We're not in the waiter list anymore. This could happen if the baton
+    //    times out but the mutex is unlocked before we reach this code. In
+    //    this case we'll pretend we got the lock on time.
+    pthread_spin_lock(&lock_);
+    if (waiter.hook.is_linked()) {
+      write_waiters_.erase(write_waiters_.iterator_to(waiter));
+      pthread_spin_unlock(&lock_);
+      return false;
+    }
+    pthread_spin_unlock(&lock_);
+  }
+  assert(state_ == State::WRITE_LOCKED);
+  return true;
+}
+
+template <typename BatonType>
+bool TimedRWMutex<BatonType>::try_write_lock() {
+  pthread_spin_lock(&lock_);
+  if (state_ == State::UNLOCKED) {
+    verify_unlocked_properties();
+    state_ = State::WRITE_LOCKED;
+    pthread_spin_unlock(&lock_);
+    return true;
+  }
+  pthread_spin_unlock(&lock_);
+  return false;
+}
+
+template <typename BatonType>
+void TimedRWMutex<BatonType>::unlock() {
+  pthread_spin_lock(&lock_);
+  assert(state_ != State::UNLOCKED);
+  assert(
+      (state_ == State::READ_LOCKED && readers_ > 0) ||
+      (state_ == State::WRITE_LOCKED && readers_ == 0));
+  if (state_ == State::READ_LOCKED) {
+    readers_ -= 1;
+  }
+
+  if (!read_waiters_.empty()) {
+    assert(
+        state_ == State::WRITE_LOCKED && readers_ == 0 &&
+        "read waiters can only accumulate while write locked");
+    state_ = State::READ_LOCKED;
+    readers_ = read_waiters_.size();
+
+    while (!read_waiters_.empty()) {
+      MutexWaiter& to_wake = read_waiters_.front();
+      read_waiters_.pop_front();
+      to_wake.baton.post();
+    }
+  } else if (readers_ == 0) {
+    if (!write_waiters_.empty()) {
+      assert(read_waiters_.empty());
+      state_ = State::WRITE_LOCKED;
+
+      // Wake a single writer (after releasing the spin lock)
+      MutexWaiter& to_wake = write_waiters_.front();
+      write_waiters_.pop_front();
+      to_wake.baton.post();
+    } else {
+      verify_unlocked_properties();
+      state_ = State::UNLOCKED;
+    }
+  } else {
+    assert(state_ == State::READ_LOCKED);
+  }
+  pthread_spin_unlock(&lock_);
+}
+
+template <typename BatonType>
+void TimedRWMutex<BatonType>::downgrade() {
+  pthread_spin_lock(&lock_);
+  assert(state_ == State::WRITE_LOCKED && readers_ == 0);
+  state_ = State::READ_LOCKED;
+  readers_ += 1;
+
+  if (!read_waiters_.empty()) {
+    readers_ += read_waiters_.size();
+
+    while (!read_waiters_.empty()) {
+      MutexWaiter& to_wake = read_waiters_.front();
+      read_waiters_.pop_front();
+      to_wake.baton.post();
+    }
+  }
+  pthread_spin_unlock(&lock_);
+}
+}
+}
diff --git a/folly/fibers/TimedMutex.h b/folly/fibers/TimedMutex.h
new file mode 100644 (file)
index 0000000..e21f246
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <pthread.h>
+
+#include <folly/fibers/GenericBaton.h>
+
+namespace folly {
+namespace fibers {
+
+/**
+ * @class TimedMutex
+ *
+ * Like mutex but allows timed_lock in addition to lock and try_lock.
+ **/
+template <typename BatonType>
+class TimedMutex {
+ public:
+  TimedMutex() {
+    pthread_spin_init(&lock_, PTHREAD_PROCESS_PRIVATE);
+  }
+
+  ~TimedMutex() {
+    pthread_spin_destroy(&lock_);
+  }
+
+  TimedMutex(const TimedMutex& rhs) = delete;
+  TimedMutex& operator=(const TimedMutex& rhs) = delete;
+  TimedMutex(TimedMutex&& rhs) = delete;
+  TimedMutex& operator=(TimedMutex&& rhs) = delete;
+
+  // Lock the mutex. The thread / fiber is blocked until the mutex is free
+  void lock();
+
+  // Lock the mutex. The thread / fiber will be blocked for a time duration.
+  //
+  // @return        true if the mutex was locked, false otherwise
+  template <typename Rep, typename Period>
+  bool timed_lock(const std::chrono::duration<Rep, Period>& duration);
+
+  // Try to obtain lock without blocking the thread or fiber
+  bool try_lock();
+
+  // Unlock the mutex and wake up a waiter if there is one
+  void unlock();
+
+ private:
+  typedef boost::intrusive::list_member_hook<> MutexWaiterHookType;
+
+  // represents a waiter waiting for the lock. The waiter waits on the
+  // baton until it is woken up by a post or timeout expires.
+  struct MutexWaiter {
+    BatonType baton;
+    MutexWaiterHookType hook;
+  };
+
+  typedef boost::intrusive::
+      member_hook<MutexWaiter, MutexWaiterHookType, &MutexWaiter::hook>
+          MutexWaiterHook;
+
+  typedef boost::intrusive::list<
+      MutexWaiter,
+      MutexWaiterHook,
+      boost::intrusive::constant_time_size<true>>
+      MutexWaiterList;
+
+  pthread_spinlock_t lock_; //< lock to protect waiter list
+  bool locked_ = false; //< is this locked by some thread?
+  MutexWaiterList waiters_; //< list of waiters
+};
+
+/**
+ * @class TimedRWMutex
+ *
+ * A readers-writer lock which allows multiple readers to hold the
+ * lock simultaneously or only one writer.
+ *
+ * NOTE: This is a reader-preferred RWLock i.e. readers are give priority
+ * when there are both readers and writers waiting to get the lock.
+ **/
+template <typename BatonType>
+class TimedRWMutex {
+ public:
+  TimedRWMutex() {
+    pthread_spin_init(&lock_, PTHREAD_PROCESS_PRIVATE);
+  }
+
+  ~TimedRWMutex() {
+    pthread_spin_destroy(&lock_);
+  }
+
+  TimedRWMutex(const TimedRWMutex& rhs) = delete;
+  TimedRWMutex& operator=(const TimedRWMutex& rhs) = delete;
+  TimedRWMutex(TimedRWMutex&& rhs) = delete;
+  TimedRWMutex& operator=(TimedRWMutex&& rhs) = delete;
+
+  // Lock for shared access. The thread / fiber is blocked until the lock
+  // can be acquired.
+  void read_lock();
+
+  // Like read_lock except the thread /fiber is blocked for a time duration
+  // @return        true if locked successfully, false otherwise.
+  template <typename Rep, typename Period>
+  bool timed_read_lock(const std::chrono::duration<Rep, Period>& duration);
+
+  // Like read_lock but doesn't block the thread / fiber if the lock can't
+  // be acquired.
+  // @return        true if lock was acquired, false otherwise.
+  bool try_read_lock();
+
+  // Obtain an exclusive lock. The thread / fiber is blocked until the lock
+  // is available.
+  void write_lock();
+
+  // Like write_lock except the thread / fiber is blocked for a time duration
+  // @return        true if locked successfully, false otherwise.
+  template <typename Rep, typename Period>
+  bool timed_write_lock(const std::chrono::duration<Rep, Period>& duration);
+
+  // Like write_lock but doesn't block the thread / fiber if the lock cant be
+  // obtained.
+  // @return        true if lock was acquired, false otherwise.
+  bool try_write_lock();
+
+  // Wrapper for write_lock() for compatibility with Mutex
+  void lock() {
+    write_lock();
+  }
+
+  // Realease the lock. The thread / fiber will wake up all readers if there are
+  // any. If there are waiting writers then only one of them will be woken up.
+  // NOTE: readers are given priority over writers (see above comment)
+  void unlock();
+
+  // Downgrade the lock. The thread / fiber will wake up all readers if there
+  // are any.
+  void downgrade();
+
+  class ReadHolder {
+   public:
+    explicit ReadHolder(TimedRWMutex& lock) : lock_(&lock) {
+      lock_->read_lock();
+    }
+
+    ~ReadHolder() {
+      if (lock_) {
+        lock_->unlock();
+      }
+    }
+
+    ReadHolder(const ReadHolder& rhs) = delete;
+    ReadHolder& operator=(const ReadHolder& rhs) = delete;
+    ReadHolder(ReadHolder&& rhs) = delete;
+    ReadHolder& operator=(ReadHolder&& rhs) = delete;
+
+   private:
+    TimedRWMutex* lock_;
+  };
+
+  class WriteHolder {
+   public:
+    explicit WriteHolder(TimedRWMutex& lock) : lock_(&lock) {
+      lock_->write_lock();
+    }
+
+    ~WriteHolder() {
+      if (lock_) {
+        lock_->unlock();
+      }
+    }
+
+    WriteHolder(const WriteHolder& rhs) = delete;
+    WriteHolder& operator=(const WriteHolder& rhs) = delete;
+    WriteHolder(WriteHolder&& rhs) = delete;
+    WriteHolder& operator=(WriteHolder&& rhs) = delete;
+
+   private:
+    TimedRWMutex* lock_;
+  };
+
+ private:
+  // invariants that must hold when the lock is not held by anyone
+  void verify_unlocked_properties() {
+    assert(readers_ == 0);
+    assert(read_waiters_.empty());
+    assert(write_waiters_.empty());
+  }
+
+  // Different states the lock can be in
+  enum class State {
+    UNLOCKED,
+    READ_LOCKED,
+    WRITE_LOCKED,
+  };
+
+  typedef boost::intrusive::list_member_hook<> MutexWaiterHookType;
+
+  // represents a waiter waiting for the lock.
+  struct MutexWaiter {
+    BatonType baton;
+    MutexWaiterHookType hook;
+  };
+
+  typedef boost::intrusive::
+      member_hook<MutexWaiter, MutexWaiterHookType, &MutexWaiter::hook>
+          MutexWaiterHook;
+
+  typedef boost::intrusive::list<
+      MutexWaiter,
+      MutexWaiterHook,
+      boost::intrusive::constant_time_size<true>>
+      MutexWaiterList;
+
+  pthread_spinlock_t lock_; //< lock protecting the internal state
+  // (state_, read_waiters_, etc.)
+  State state_ = State::UNLOCKED;
+
+  uint32_t readers_ = 0; //< Number of readers who have the lock
+
+  MutexWaiterList write_waiters_; //< List of thread / fibers waiting for
+  //  exclusive access
+
+  MutexWaiterList read_waiters_; //< List of thread / fibers waiting for
+  //  shared access
+};
+}
+}
+
+#include "TimedMutex-inl.h"
diff --git a/folly/fibers/TimeoutController.cpp b/folly/fibers/TimeoutController.cpp
new file mode 100644 (file)
index 0000000..60360e6
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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.
+ */
+#include "TimeoutController.h"
+#include <folly/Memory.h>
+
+namespace folly {
+namespace fibers {
+
+TimeoutController::TimeoutController(LoopController& loopController)
+    : nextTimeout_(TimePoint::max()), loopController_(loopController) {}
+
+intptr_t TimeoutController::registerTimeout(
+    std::function<void()> f,
+    Duration duration) {
+  auto& list = [&]() -> TimeoutHandleList& {
+    for (auto& bucket : timeoutHandleBuckets_) {
+      if (bucket.first == duration) {
+        return *bucket.second;
+      }
+    }
+
+    timeoutHandleBuckets_.emplace_back(
+        duration, folly::make_unique<TimeoutHandleList>());
+    return *timeoutHandleBuckets_.back().second;
+  }();
+
+  auto timeout = Clock::now() + duration;
+  list.emplace(std::move(f), timeout, list);
+
+  if (timeout < nextTimeout_) {
+    nextTimeout_ = timeout;
+    scheduleRun();
+  }
+
+  return reinterpret_cast<intptr_t>(&list.back());
+}
+
+void TimeoutController::runTimeouts(TimePoint time) {
+  auto now = Clock::now();
+  // Make sure we don't skip some events if function was run before actual time.
+  if (time < now) {
+    time = now;
+  }
+  if (nextTimeout_ > time) {
+    return;
+  }
+
+  nextTimeout_ = TimePoint::max();
+
+  for (auto& bucket : timeoutHandleBuckets_) {
+    auto& list = *bucket.second;
+
+    while (!list.empty()) {
+      if (!list.front().canceled) {
+        if (list.front().timeout > time) {
+          nextTimeout_ = std::min(nextTimeout_, list.front().timeout);
+          break;
+        }
+
+        list.front().func();
+      }
+      list.pop();
+    }
+  }
+
+  if (nextTimeout_ != TimePoint::max()) {
+    scheduleRun();
+  }
+}
+
+void TimeoutController::scheduleRun() {
+  auto time = nextTimeout_;
+  std::weak_ptr<TimeoutController> timeoutControllerWeak = shared_from_this();
+
+  loopController_.timedSchedule(
+      [timeoutControllerWeak, time]() {
+        if (auto timeoutController = timeoutControllerWeak.lock()) {
+          timeoutController->runTimeouts(time);
+        }
+      },
+      time);
+}
+
+void TimeoutController::cancel(intptr_t p) {
+  auto handle = reinterpret_cast<TimeoutHandle*>(p);
+  handle->canceled = true;
+
+  auto& list = handle->list;
+
+  while (!list.empty() && list.front().canceled) {
+    list.pop();
+  }
+}
+}
+}
diff --git a/folly/fibers/TimeoutController.h b/folly/fibers/TimeoutController.h
new file mode 100644 (file)
index 0000000..4b06009
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <chrono>
+#include <functional>
+#include <memory>
+#include <queue>
+
+#include <boost/intrusive/list.hpp>
+
+#include <folly/Likely.h>
+
+#include <folly/fibers/LoopController.h>
+
+namespace folly {
+namespace fibers {
+
+class TimeoutController
+    : public std::enable_shared_from_this<TimeoutController> {
+ public:
+  typedef std::chrono::steady_clock Clock;
+  typedef std::chrono::time_point<Clock> TimePoint;
+  typedef Clock::duration Duration;
+
+  explicit TimeoutController(LoopController& loopController);
+
+  intptr_t registerTimeout(std::function<void()> f, Duration duration);
+  void cancel(intptr_t id);
+
+  void runTimeouts(TimePoint time);
+
+ private:
+  void scheduleRun();
+
+  struct TimeoutHandle;
+  typedef std::queue<TimeoutHandle> TimeoutHandleList;
+  typedef std::unique_ptr<TimeoutHandleList> TimeoutHandleListPtr;
+
+  struct TimeoutHandle {
+    TimeoutHandle(
+        std::function<void()> func_,
+        TimePoint timeout_,
+        TimeoutHandleList& list_)
+        : func(std::move(func_)), timeout(timeout_), list(list_) {}
+
+    std::function<void()> func;
+    bool canceled{false};
+    TimePoint timeout;
+    TimeoutHandleList& list;
+  };
+
+  std::vector<std::pair<Duration, TimeoutHandleListPtr>> timeoutHandleBuckets_;
+  TimePoint nextTimeout_;
+  LoopController& loopController_;
+};
+}
+}
diff --git a/folly/fibers/WhenN-inl.h b/folly/fibers/WhenN-inl.h
new file mode 100644 (file)
index 0000000..4c49da8
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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.
+ */
+#include <folly/Optional.h>
+
+#include <folly/fibers/FiberManager.h>
+#include <folly/fibers/ForEach.h>
+
+namespace folly {
+namespace fibers {
+
+template <class InputIterator>
+typename std::vector<typename std::enable_if<
+    !std::is_same<
+        typename std::result_of<
+            typename std::iterator_traits<InputIterator>::value_type()>::type,
+        void>::value,
+    typename std::pair<
+        size_t,
+        typename std::result_of<typename std::iterator_traits<
+            InputIterator>::value_type()>::type>>::type>
+collectN(InputIterator first, InputIterator last, size_t n) {
+  typedef typename std::result_of<
+      typename std::iterator_traits<InputIterator>::value_type()>::type Result;
+  assert(n > 0);
+  assert(std::distance(first, last) >= 0);
+  assert(n <= static_cast<size_t>(std::distance(first, last)));
+
+  struct Context {
+    std::vector<std::pair<size_t, Result>> results;
+    size_t tasksTodo;
+    std::exception_ptr e;
+    folly::Optional<Promise<void>> promise;
+
+    Context(size_t tasksTodo_) : tasksTodo(tasksTodo_) {
+      this->results.reserve(tasksTodo_);
+    }
+  };
+  auto context = std::make_shared<Context>(n);
+
+  await([first, last, context](Promise<void> promise) mutable {
+    context->promise = std::move(promise);
+    for (size_t i = 0; first != last; ++i, ++first) {
+#ifdef __clang__
+#pragma clang diagnostic push // ignore generalized lambda capture warning
+#pragma clang diagnostic ignored "-Wc++1y-extensions"
+#endif
+      addTask([ i, context, f = std::move(*first) ]() {
+        try {
+          auto result = f();
+          if (context->tasksTodo == 0) {
+            return;
+          }
+          context->results.emplace_back(i, std::move(result));
+        } catch (...) {
+          if (context->tasksTodo == 0) {
+            return;
+          }
+          context->e = std::current_exception();
+        }
+        if (--context->tasksTodo == 0) {
+          context->promise->setValue();
+        }
+      });
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+    }
+  });
+
+  if (context->e != std::exception_ptr()) {
+    std::rethrow_exception(context->e);
+  }
+
+  return std::move(context->results);
+}
+
+template <class InputIterator>
+typename std::enable_if<
+    std::is_same<
+        typename std::result_of<
+            typename std::iterator_traits<InputIterator>::value_type()>::type,
+        void>::value,
+    std::vector<size_t>>::type
+collectN(InputIterator first, InputIterator last, size_t n) {
+  assert(n > 0);
+  assert(std::distance(first, last) >= 0);
+  assert(n <= static_cast<size_t>(std::distance(first, last)));
+
+  struct Context {
+    std::vector<size_t> taskIndices;
+    std::exception_ptr e;
+    size_t tasksTodo;
+    folly::Optional<Promise<void>> promise;
+
+    Context(size_t tasksTodo_) : tasksTodo(tasksTodo_) {
+      this->taskIndices.reserve(tasksTodo_);
+    }
+  };
+  auto context = std::make_shared<Context>(n);
+
+  await([first, last, context](Promise<void> promise) mutable {
+    context->promise = std::move(promise);
+    for (size_t i = 0; first != last; ++i, ++first) {
+#ifdef __clang__
+#pragma clang diagnostic push // ignore generalized lambda capture warning
+#pragma clang diagnostic ignored "-Wc++1y-extensions"
+#endif
+      addTask([ i, context, f = std::move(*first) ]() {
+        try {
+          f();
+          if (context->tasksTodo == 0) {
+            return;
+          }
+          context->taskIndices.push_back(i);
+        } catch (...) {
+          if (context->tasksTodo == 0) {
+            return;
+          }
+          context->e = std::current_exception();
+        }
+        if (--context->tasksTodo == 0) {
+          context->promise->setValue();
+        }
+      });
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+    }
+  });
+
+  if (context->e != std::exception_ptr()) {
+    std::rethrow_exception(context->e);
+  }
+
+  return context->taskIndices;
+}
+
+template <class InputIterator>
+typename std::vector<
+    typename std::enable_if<
+        !std::is_same<
+            typename std::result_of<typename std::iterator_traits<
+                InputIterator>::value_type()>::type,
+            void>::value,
+        typename std::result_of<
+            typename std::iterator_traits<InputIterator>::value_type()>::type>::
+        type> inline collectAll(InputIterator first, InputIterator last) {
+  typedef typename std::result_of<
+      typename std::iterator_traits<InputIterator>::value_type()>::type Result;
+  size_t n = std::distance(first, last);
+  std::vector<Result> results;
+  std::vector<size_t> order(n);
+  results.reserve(n);
+
+  forEach(first, last, [&results, &order](size_t id, Result result) {
+    order[id] = results.size();
+    results.emplace_back(std::move(result));
+  });
+  assert(results.size() == n);
+
+  std::vector<Result> orderedResults;
+  orderedResults.reserve(n);
+
+  for (size_t i = 0; i < n; ++i) {
+    orderedResults.emplace_back(std::move(results[order[i]]));
+  }
+
+  return orderedResults;
+}
+
+template <class InputIterator>
+typename std::enable_if<
+    std::is_same<
+        typename std::result_of<
+            typename std::iterator_traits<InputIterator>::value_type()>::type,
+        void>::value,
+    void>::type inline collectAll(InputIterator first, InputIterator last) {
+  forEach(first, last, [](size_t /* id */) {});
+}
+
+template <class InputIterator>
+typename std::enable_if<
+    !std::is_same<
+        typename std::result_of<
+            typename std::iterator_traits<InputIterator>::value_type()>::type,
+        void>::value,
+    typename std::pair<
+        size_t,
+        typename std::result_of<typename std::iterator_traits<
+            InputIterator>::value_type()>::type>>::
+    type inline collectAny(InputIterator first, InputIterator last) {
+  auto result = collectN(first, last, 1);
+  assert(result.size() == 1);
+  return std::move(result[0]);
+}
+
+template <class InputIterator>
+typename std::enable_if<
+    std::is_same<
+        typename std::result_of<
+            typename std::iterator_traits<InputIterator>::value_type()>::type,
+        void>::value,
+    size_t>::type inline collectAny(InputIterator first, InputIterator last) {
+  auto result = collectN(first, last, 1);
+  assert(result.size() == 1);
+  return std::move(result[0]);
+}
+}
+}
diff --git a/folly/fibers/WhenN.h b/folly/fibers/WhenN.h
new file mode 100644 (file)
index 0000000..6f54bab
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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
+
+namespace folly {
+namespace fibers {
+
+/**
+ * Schedules several tasks and blocks until n of these tasks are completed.
+ * If any of these n tasks throws an exception, this exception will be
+ * re-thrown, but only when n tasks are complete. If several tasks throw
+ * exceptions one of them will be re-thrown.
+ *
+ * @param first Range of tasks to be scheduled
+ * @param last
+ * @param n Number of tasks to wait for
+ *
+ * @return vector of pairs (task index, return value of task)
+ */
+template <class InputIterator>
+typename std::vector<
+    typename std::enable_if<
+        !std::is_same<
+            typename std::result_of<typename std::iterator_traits<
+                InputIterator>::value_type()>::type,
+            void>::value,
+        typename std::pair<
+            size_t,
+            typename std::result_of<typename std::iterator_traits<
+                InputIterator>::value_type()>::type>>::
+        type> inline collectN(InputIterator first, InputIterator last, size_t n);
+
+/**
+ * collectN specialization for functions returning void
+ *
+ * @param first Range of tasks to be scheduled
+ * @param last
+ * @param n Number of tasks to wait for
+ *
+ * @return vector of completed task indices
+ */
+template <class InputIterator>
+typename std::enable_if<
+    std::is_same<
+        typename std::result_of<
+            typename std::iterator_traits<InputIterator>::value_type()>::type,
+        void>::value,
+    std::vector<size_t>>::
+    type inline collectN(InputIterator first, InputIterator last, size_t n);
+
+/**
+ * Schedules several tasks and blocks until all of these tasks are completed.
+ * If any of the tasks throws an exception, this exception will be re-thrown,
+ * but only when all the tasks are complete. If several tasks throw exceptions
+ * one of them will be re-thrown.
+ *
+ * @param first Range of tasks to be scheduled
+ * @param last
+ *
+ * @return vector of values returned by tasks
+ */
+template <class InputIterator>
+typename std::vector<typename std::enable_if<
+    !std::is_same<
+        typename std::result_of<
+            typename std::iterator_traits<InputIterator>::value_type()>::type,
+        void>::value,
+    typename std::result_of<
+        typename std::iterator_traits<InputIterator>::value_type()>::
+        type>::type> inline collectAll(InputIterator first, InputIterator last);
+
+/**
+ * collectAll specialization for functions returning void
+ *
+ * @param first Range of tasks to be scheduled
+ * @param last
+ */
+template <class InputIterator>
+typename std::enable_if<
+    std::is_same<
+        typename std::result_of<
+            typename std::iterator_traits<InputIterator>::value_type()>::type,
+        void>::value,
+    void>::type inline collectAll(InputIterator first, InputIterator last);
+
+/**
+ * Schedules several tasks and blocks until one of them is completed.
+ * If this task throws an exception, this exception will be re-thrown.
+ * Exceptions thrown by all other tasks will be ignored.
+ *
+ * @param first Range of tasks to be scheduled
+ * @param last
+ *
+ * @return pair of index of the first completed task and its return value
+ */
+template <class InputIterator>
+typename std::enable_if<
+    !std::is_same<
+        typename std::result_of<
+            typename std::iterator_traits<InputIterator>::value_type()>::type,
+        void>::value,
+    typename std::pair<
+        size_t,
+        typename std::result_of<typename std::iterator_traits<
+            InputIterator>::value_type()>::type>>::
+    type inline collectAny(InputIterator first, InputIterator last);
+
+/**
+ * WhenAny specialization for functions returning void.
+ *
+ * @param first Range of tasks to be scheduled
+ * @param last
+ *
+ * @return index of the first completed task
+ */
+template <class InputIterator>
+typename std::enable_if<
+    std::is_same<
+        typename std::result_of<
+            typename std::iterator_traits<InputIterator>::value_type()>::type,
+        void>::value,
+    size_t>::type inline collectAny(InputIterator first, InputIterator last);
+}
+}
+
+#include <folly/fibers/WhenN-inl.h>
diff --git a/folly/fibers/scripts/utils.gdb b/folly/fibers/scripts/utils.gdb
new file mode 100644 (file)
index 0000000..ada27fe
--- /dev/null
@@ -0,0 +1,99 @@
+# Print given Fiber state
+# arg0 folly::fibers::Fiber*
+define print_folly_fiber_state
+  set $fiber = (folly::fibers::Fiber*)$arg0
+  if $fiber->state_ == folly::fibers::Fiber::INVALID
+    printf "Invalid"
+  end
+  if $fiber->state_ == folly::fibers::Fiber::NOT_STARTED
+    printf "Not started"
+  end
+  if $fiber->state_ == folly::fibers::Fiber::READY_TO_RUN
+    printf "Ready to run"
+  end
+  if $fiber->state_ == folly::fibers::Fiber::RUNNING
+    printf "Running"
+  end
+  if $fiber->state_ == folly::fibers::Fiber::AWAITING
+    printf "Awaiting"
+  end
+  if $fiber->state_ == folly::fibers::Fiber::AWAITING_IMMEDIATE
+    printf "Awaiting immediate"
+  end
+  if $fiber->state_ == folly::fibers::Fiber::YIELDED
+    printf "Yielded"
+  end
+end
+
+# Print given Fiber
+# arg0 folly::fibers::Fiber*
+define print_folly_fiber
+  set $fiber = (folly::fibers::Fiber*)$arg0
+  printf "  (folly::fibers::Fiber*)%p\n\n", $fiber
+
+  printf "  State: "
+  print_folly_fiber_state $fiber
+  printf "\n"
+
+  if $fiber->state_ != folly::fibers::Fiber::INVALID && \
+     $fiber->state_ != folly::fibers::Fiber::NOT_STARTED && \
+     $fiber->state_ != folly::fibers::Fiber::RUNNING
+    printf "  Backtrace:\n"
+    set $frameptr = ((uint64_t*)$fiber->fcontext_.context_)[6]
+    set $k = 0
+    while $frameptr != 0
+      printf "    #%d at %p in ", $k, *((void**)($frameptr+8))
+      set $k = $k + 1
+      info symbol *((void**)($frameptr+8))
+      set $frameptr = *((void**)($frameptr))
+    end
+  end
+end
+
+# Print given FiberManager
+# arg0 folly::fibers::FiberManager*
+define print_folly_fiber_manager
+  set $fiberManager = (folly::fibers::FiberManager*)$arg0
+
+  printf "  (folly::fibers::FiberManager*)%p\n\n", $fiberManager
+  printf "  Fibers active: %d\n", $fiberManager->fibersActive_
+  printf "  Fibers allocated: %d\n", $fiberManager->fibersAllocated_
+  printf "  Fibers pool size: %d\n", $fiberManager->fibersPoolSize_
+  printf "  Active fiber: (folly::fibers::Fiber*)%p\n", \
+         $fiberManager->activeFiber_
+  printf "  Current fiber: (folly::fibers::Fiber*)%p\n", \
+         $fiberManager->currentFiber_
+
+  set $all_fibers = &($fiberManager->allFibers_.data_.root_plus_size_.m_header)
+  set $fiber_hook = $all_fibers->next_
+  printf "\n  Active fibers:\n"
+  while $fiber_hook != $all_fibers
+    set $fiber = (folly::fibers::Fiber*) \
+        ((int64_t)$fiber_hook - \
+         (int64_t)&folly::fibers::Fiber::globalListHook_)
+    if $fiber->state_ != folly::fibers::Fiber::INVALID
+      printf "    (folly::fibers::Fiber*)%p   State: ", $fiber
+      print_folly_fiber_state $fiber
+      printf "\n"
+    end
+    set $fiber_hook = $fiber_hook->next_
+  end
+end
+
+# Print global FiberManager map
+define print_folly_fiber_manager_map
+  set $global_cache=*(('folly::fibers::(anonymous namespace)::GlobalCache'**) \
+      &'folly::fibers::(anonymous namespace)::GlobalCache::instance()::ret')
+  printf "  Global FiberManager map has %d entries.\n", \
+         $global_cache->map_->_M_h->_M_element_count
+
+  set $item = $global_cache->map_->_M_h->_M_before_begin._M_nxt
+  while $item != 0
+    set $evb = ((folly::EventBase**)$item)[1]
+    set $fiberManager = ((folly::fibers::FiberManager**)$item)[2]
+    printf "    (folly::EventBase*)%p -> (folly::fibers::FiberManager*)%p\n", \
+           $evb, $fiberManager
+
+    set $item = $item->_M_nxt
+  end
+end
diff --git a/folly/fibers/test/FibersTest.cpp b/folly/fibers/test/FibersTest.cpp
new file mode 100644 (file)
index 0000000..e5762af
--- /dev/null
@@ -0,0 +1,1637 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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.
+ */
+#include <atomic>
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <folly/Benchmark.h>
+#include <folly/Memory.h>
+#include <folly/futures/Future.h>
+
+#include <folly/fibers/AddTasks.h>
+#include <folly/fibers/EventBaseLoopController.h>
+#include <folly/fibers/FiberManager.h>
+#include <folly/fibers/FiberManagerMap.h>
+#include <folly/fibers/GenericBaton.h>
+#include <folly/fibers/SimpleLoopController.h>
+#include <folly/fibers/WhenN.h>
+
+using namespace folly::fibers;
+
+using folly::Try;
+
+TEST(FiberManager, batonTimedWaitTimeout) {
+  bool taskAdded = false;
+  size_t iterations = 0;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  auto loopFunc = [&]() {
+    if (!taskAdded) {
+      manager.addTask([&]() {
+        Baton baton;
+
+        auto res = baton.timed_wait(std::chrono::milliseconds(230));
+
+        EXPECT_FALSE(res);
+        EXPECT_EQ(5, iterations);
+
+        loopController.stop();
+      });
+      manager.addTask([&]() {
+        Baton baton;
+
+        auto res = baton.timed_wait(std::chrono::milliseconds(130));
+
+        EXPECT_FALSE(res);
+        EXPECT_EQ(3, iterations);
+
+        loopController.stop();
+      });
+      taskAdded = true;
+    } else {
+      std::this_thread::sleep_for(std::chrono::milliseconds(50));
+      iterations++;
+    }
+  };
+
+  loopController.loop(std::move(loopFunc));
+}
+
+TEST(FiberManager, batonTimedWaitPost) {
+  bool taskAdded = false;
+  size_t iterations = 0;
+  Baton* baton_ptr;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  auto loopFunc = [&]() {
+    if (!taskAdded) {
+      manager.addTask([&]() {
+        Baton baton;
+        baton_ptr = &baton;
+
+        auto res = baton.timed_wait(std::chrono::milliseconds(130));
+
+        EXPECT_TRUE(res);
+        EXPECT_EQ(2, iterations);
+
+        loopController.stop();
+      });
+      taskAdded = true;
+    } else {
+      std::this_thread::sleep_for(std::chrono::milliseconds(50));
+      iterations++;
+      if (iterations == 2) {
+        baton_ptr->post();
+      }
+    }
+  };
+
+  loopController.loop(std::move(loopFunc));
+}
+
+TEST(FiberManager, batonTimedWaitTimeoutEvb) {
+  size_t tasksComplete = 0;
+
+  folly::EventBase evb;
+
+  FiberManager manager(folly::make_unique<EventBaseLoopController>());
+  dynamic_cast<EventBaseLoopController&>(manager.loopController())
+      .attachEventBase(evb);
+
+  auto task = [&](size_t timeout_ms) {
+    Baton baton;
+
+    auto start = EventBaseLoopController::Clock::now();
+    auto res = baton.timed_wait(std::chrono::milliseconds(timeout_ms));
+    auto finish = EventBaseLoopController::Clock::now();
+
+    EXPECT_FALSE(res);
+
+    auto duration_ms =
+        std::chrono::duration_cast<std::chrono::milliseconds>(finish - start);
+
+    EXPECT_GT(duration_ms.count(), timeout_ms - 50);
+    EXPECT_LT(duration_ms.count(), timeout_ms + 50);
+
+    if (++tasksComplete == 2) {
+      evb.terminateLoopSoon();
+    }
+  };
+
+  evb.runInEventBaseThread([&]() {
+    manager.addTask([&]() { task(500); });
+    manager.addTask([&]() { task(250); });
+  });
+
+  evb.loopForever();
+
+  EXPECT_EQ(2, tasksComplete);
+}
+
+TEST(FiberManager, batonTimedWaitPostEvb) {
+  size_t tasksComplete = 0;
+
+  folly::EventBase evb;
+
+  FiberManager manager(folly::make_unique<EventBaseLoopController>());
+  dynamic_cast<EventBaseLoopController&>(manager.loopController())
+      .attachEventBase(evb);
+
+  evb.runInEventBaseThread([&]() {
+    manager.addTask([&]() {
+      Baton baton;
+
+      evb.tryRunAfterDelay([&]() { baton.post(); }, 100);
+
+      auto start = EventBaseLoopController::Clock::now();
+      auto res = baton.timed_wait(std::chrono::milliseconds(130));
+      auto finish = EventBaseLoopController::Clock::now();
+
+      EXPECT_TRUE(res);
+
+      auto duration_ms =
+          std::chrono::duration_cast<std::chrono::milliseconds>(finish - start);
+
+      EXPECT_TRUE(duration_ms.count() > 95 && duration_ms.count() < 110);
+
+      if (++tasksComplete == 1) {
+        evb.terminateLoopSoon();
+      }
+    });
+  });
+
+  evb.loopForever();
+
+  EXPECT_EQ(1, tasksComplete);
+}
+
+TEST(FiberManager, batonTryWait) {
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+
+  // Check if try_wait and post work as expected
+  Baton b;
+
+  manager.addTask([&]() {
+    while (!b.try_wait()) {
+    }
+  });
+  auto thr = std::thread([&]() {
+    std::this_thread::sleep_for(std::chrono::milliseconds(300));
+    b.post();
+  });
+
+  manager.loopUntilNoReady();
+  thr.join();
+
+  Baton c;
+
+  // Check try_wait without post
+  manager.addTask([&]() {
+    int cnt = 100;
+    while (cnt && !c.try_wait()) {
+      cnt--;
+    }
+    EXPECT_TRUE(!c.try_wait()); // must still hold
+    EXPECT_EQ(cnt, 0);
+  });
+
+  manager.loopUntilNoReady();
+}
+
+TEST(FiberManager, genericBatonFiberWait) {
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+
+  GenericBaton b;
+  bool fiberRunning = false;
+
+  manager.addTask([&]() {
+    EXPECT_EQ(manager.hasActiveFiber(), true);
+    fiberRunning = true;
+    b.wait();
+    fiberRunning = false;
+  });
+
+  EXPECT_FALSE(fiberRunning);
+  manager.loopUntilNoReady();
+  EXPECT_TRUE(fiberRunning); // ensure fiber still active
+
+  auto thr = std::thread([&]() {
+    std::this_thread::sleep_for(std::chrono::milliseconds(300));
+    b.post();
+  });
+
+  while (fiberRunning) {
+    manager.loopUntilNoReady();
+  }
+
+  thr.join();
+}
+
+TEST(FiberManager, genericBatonThreadWait) {
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  GenericBaton b;
+  std::atomic<bool> threadWaiting(false);
+
+  auto thr = std::thread([&]() {
+    threadWaiting = true;
+    b.wait();
+    threadWaiting = false;
+  });
+
+  while (!threadWaiting) {
+  }
+  std::this_thread::sleep_for(std::chrono::milliseconds(300));
+
+  manager.addTask([&]() {
+    EXPECT_EQ(manager.hasActiveFiber(), true);
+    EXPECT_TRUE(threadWaiting);
+    b.post();
+    while (threadWaiting) {
+    }
+  });
+
+  manager.loopUntilNoReady();
+  thr.join();
+}
+
+TEST(FiberManager, addTasksNoncopyable) {
+  std::vector<Promise<int>> pendingFibers;
+  bool taskAdded = false;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  auto loopFunc = [&]() {
+    if (!taskAdded) {
+      manager.addTask([&]() {
+        std::vector<std::function<std::unique_ptr<int>()>> funcs;
+        for (size_t i = 0; i < 3; ++i) {
+          funcs.push_back([i, &pendingFibers]() {
+            await([&pendingFibers](Promise<int> promise) {
+              pendingFibers.push_back(std::move(promise));
+            });
+            return folly::make_unique<int>(i * 2 + 1);
+          });
+        }
+
+        auto iter = addTasks(funcs.begin(), funcs.end());
+
+        size_t n = 0;
+        while (iter.hasNext()) {
+          auto result = iter.awaitNext();
+          EXPECT_EQ(2 * iter.getTaskID() + 1, *result);
+          EXPECT_GE(2 - n, pendingFibers.size());
+          ++n;
+        }
+        EXPECT_EQ(3, n);
+      });
+      taskAdded = true;
+    } else if (pendingFibers.size()) {
+      pendingFibers.back().setValue(0);
+      pendingFibers.pop_back();
+    } else {
+      loopController.stop();
+    }
+  };
+
+  loopController.loop(std::move(loopFunc));
+}
+
+TEST(FiberManager, awaitThrow) {
+  folly::EventBase evb;
+  struct ExpectedException {};
+  getFiberManager(evb)
+      .addTaskFuture([&] {
+        EXPECT_THROW(
+          await([](Promise<int> p) {
+              p.setValue(42);
+              throw ExpectedException();
+            }),
+          ExpectedException
+        );
+
+        EXPECT_THROW(
+          await([&](Promise<int> p) {
+              evb.runInEventBaseThread([p = std::move(p)]() mutable {
+                  p.setValue(42);
+                });
+              throw ExpectedException();
+            }),
+          ExpectedException);
+      })
+      .waitVia(&evb);
+}
+
+TEST(FiberManager, addTasksThrow) {
+  std::vector<Promise<int>> pendingFibers;
+  bool taskAdded = false;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  auto loopFunc = [&]() {
+    if (!taskAdded) {
+      manager.addTask([&]() {
+        std::vector<std::function<int()>> funcs;
+        for (size_t i = 0; i < 3; ++i) {
+          funcs.push_back([i, &pendingFibers]() {
+            await([&pendingFibers](Promise<int> promise) {
+              pendingFibers.push_back(std::move(promise));
+            });
+            if (i % 2 == 0) {
+              throw std::runtime_error("Runtime");
+            }
+            return i * 2 + 1;
+          });
+        }
+
+        auto iter = addTasks(funcs.begin(), funcs.end());
+
+        size_t n = 0;
+        while (iter.hasNext()) {
+          try {
+            int result = iter.awaitNext();
+            EXPECT_EQ(1, iter.getTaskID() % 2);
+            EXPECT_EQ(2 * iter.getTaskID() + 1, result);
+          } catch (...) {
+            EXPECT_EQ(0, iter.getTaskID() % 2);
+          }
+          EXPECT_GE(2 - n, pendingFibers.size());
+          ++n;
+        }
+        EXPECT_EQ(3, n);
+      });
+      taskAdded = true;
+    } else if (pendingFibers.size()) {
+      pendingFibers.back().setValue(0);
+      pendingFibers.pop_back();
+    } else {
+      loopController.stop();
+    }
+  };
+
+  loopController.loop(std::move(loopFunc));
+}
+
+TEST(FiberManager, addTasksVoid) {
+  std::vector<Promise<int>> pendingFibers;
+  bool taskAdded = false;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  auto loopFunc = [&]() {
+    if (!taskAdded) {
+      manager.addTask([&]() {
+        std::vector<std::function<void()>> funcs;
+        for (size_t i = 0; i < 3; ++i) {
+          funcs.push_back([i, &pendingFibers]() {
+            await([&pendingFibers](Promise<int> promise) {
+              pendingFibers.push_back(std::move(promise));
+            });
+          });
+        }
+
+        auto iter = addTasks(funcs.begin(), funcs.end());
+
+        size_t n = 0;
+        while (iter.hasNext()) {
+          iter.awaitNext();
+          EXPECT_GE(2 - n, pendingFibers.size());
+          ++n;
+        }
+        EXPECT_EQ(3, n);
+      });
+      taskAdded = true;
+    } else if (pendingFibers.size()) {
+      pendingFibers.back().setValue(0);
+      pendingFibers.pop_back();
+    } else {
+      loopController.stop();
+    }
+  };
+
+  loopController.loop(std::move(loopFunc));
+}
+
+TEST(FiberManager, addTasksVoidThrow) {
+  std::vector<Promise<int>> pendingFibers;
+  bool taskAdded = false;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  auto loopFunc = [&]() {
+    if (!taskAdded) {
+      manager.addTask([&]() {
+        std::vector<std::function<void()>> funcs;
+        for (size_t i = 0; i < 3; ++i) {
+          funcs.push_back([i, &pendingFibers]() {
+            await([&pendingFibers](Promise<int> promise) {
+              pendingFibers.push_back(std::move(promise));
+            });
+            if (i % 2 == 0) {
+              throw std::runtime_error("");
+            }
+          });
+        }
+
+        auto iter = addTasks(funcs.begin(), funcs.end());
+
+        size_t n = 0;
+        while (iter.hasNext()) {
+          try {
+            iter.awaitNext();
+            EXPECT_EQ(1, iter.getTaskID() % 2);
+          } catch (...) {
+            EXPECT_EQ(0, iter.getTaskID() % 2);
+          }
+          EXPECT_GE(2 - n, pendingFibers.size());
+          ++n;
+        }
+        EXPECT_EQ(3, n);
+      });
+      taskAdded = true;
+    } else if (pendingFibers.size()) {
+      pendingFibers.back().setValue(0);
+      pendingFibers.pop_back();
+    } else {
+      loopController.stop();
+    }
+  };
+
+  loopController.loop(std::move(loopFunc));
+}
+
+TEST(FiberManager, addTasksReserve) {
+  std::vector<Promise<int>> pendingFibers;
+  bool taskAdded = false;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  auto loopFunc = [&]() {
+    if (!taskAdded) {
+      manager.addTask([&]() {
+        std::vector<std::function<void()>> funcs;
+        for (size_t i = 0; i < 3; ++i) {
+          funcs.push_back([&pendingFibers]() {
+            await([&pendingFibers](Promise<int> promise) {
+              pendingFibers.push_back(std::move(promise));
+            });
+          });
+        }
+
+        auto iter = addTasks(funcs.begin(), funcs.end());
+
+        iter.reserve(2);
+        EXPECT_TRUE(iter.hasCompleted());
+        EXPECT_TRUE(iter.hasPending());
+        EXPECT_TRUE(iter.hasNext());
+
+        iter.awaitNext();
+        EXPECT_TRUE(iter.hasCompleted());
+        EXPECT_TRUE(iter.hasPending());
+        EXPECT_TRUE(iter.hasNext());
+
+        iter.awaitNext();
+        EXPECT_FALSE(iter.hasCompleted());
+        EXPECT_TRUE(iter.hasPending());
+        EXPECT_TRUE(iter.hasNext());
+
+        iter.awaitNext();
+        EXPECT_FALSE(iter.hasCompleted());
+        EXPECT_FALSE(iter.hasPending());
+        EXPECT_FALSE(iter.hasNext());
+      });
+      taskAdded = true;
+    } else if (pendingFibers.size()) {
+      pendingFibers.back().setValue(0);
+      pendingFibers.pop_back();
+    } else {
+      loopController.stop();
+    }
+  };
+
+  loopController.loop(std::move(loopFunc));
+}
+
+TEST(FiberManager, addTaskDynamic) {
+  folly::EventBase evb;
+
+  Baton batons[3];
+
+  auto makeTask = [&](size_t taskId) {
+    return [&, taskId]() -> size_t {
+      batons[taskId].wait();
+      return taskId;
+    };
+  };
+
+  getFiberManager(evb)
+      .addTaskFuture([&]() {
+        TaskIterator<size_t> iterator;
+
+        iterator.addTask(makeTask(0));
+        iterator.addTask(makeTask(1));
+
+        batons[1].post();
+
+        EXPECT_EQ(1, iterator.awaitNext());
+
+        iterator.addTask(makeTask(2));
+
+        batons[2].post();
+
+        EXPECT_EQ(2, iterator.awaitNext());
+
+        batons[0].post();
+
+        EXPECT_EQ(0, iterator.awaitNext());
+      })
+      .waitVia(&evb);
+}
+
+TEST(FiberManager, forEach) {
+  std::vector<Promise<int>> pendingFibers;
+  bool taskAdded = false;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  auto loopFunc = [&]() {
+    if (!taskAdded) {
+      manager.addTask([&]() {
+        std::vector<std::function<int()>> funcs;
+        for (size_t i = 0; i < 3; ++i) {
+          funcs.push_back([i, &pendingFibers]() {
+            await([&pendingFibers](Promise<int> promise) {
+              pendingFibers.push_back(std::move(promise));
+            });
+            return i * 2 + 1;
+          });
+        }
+
+        std::vector<std::pair<size_t, int>> results;
+        forEach(funcs.begin(), funcs.end(), [&results](size_t id, int result) {
+          results.emplace_back(id, result);
+        });
+        EXPECT_EQ(3, results.size());
+        EXPECT_TRUE(pendingFibers.empty());
+        for (size_t i = 0; i < 3; ++i) {
+          EXPECT_EQ(results[i].first * 2 + 1, results[i].second);
+        }
+      });
+      taskAdded = true;
+    } else if (pendingFibers.size()) {
+      pendingFibers.back().setValue(0);
+      pendingFibers.pop_back();
+    } else {
+      loopController.stop();
+    }
+  };
+
+  loopController.loop(std::move(loopFunc));
+}
+
+TEST(FiberManager, collectN) {
+  std::vector<Promise<int>> pendingFibers;
+  bool taskAdded = false;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  auto loopFunc = [&]() {
+    if (!taskAdded) {
+      manager.addTask([&]() {
+        std::vector<std::function<int()>> funcs;
+        for (size_t i = 0; i < 3; ++i) {
+          funcs.push_back([i, &pendingFibers]() {
+            await([&pendingFibers](Promise<int> promise) {
+              pendingFibers.push_back(std::move(promise));
+            });
+            return i * 2 + 1;
+          });
+        }
+
+        auto results = collectN(funcs.begin(), funcs.end(), 2);
+        EXPECT_EQ(2, results.size());
+        EXPECT_EQ(1, pendingFibers.size());
+        for (size_t i = 0; i < 2; ++i) {
+          EXPECT_EQ(results[i].first * 2 + 1, results[i].second);
+        }
+      });
+      taskAdded = true;
+    } else if (pendingFibers.size()) {
+      pendingFibers.back().setValue(0);
+      pendingFibers.pop_back();
+    } else {
+      loopController.stop();
+    }
+  };
+
+  loopController.loop(std::move(loopFunc));
+}
+
+TEST(FiberManager, collectNThrow) {
+  std::vector<Promise<int>> pendingFibers;
+  bool taskAdded = false;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  auto loopFunc = [&]() {
+    if (!taskAdded) {
+      manager.addTask([&]() {
+        std::vector<std::function<int()>> funcs;
+        for (size_t i = 0; i < 3; ++i) {
+          funcs.push_back([i, &pendingFibers]() {
+            await([&pendingFibers](Promise<int> promise) {
+              pendingFibers.push_back(std::move(promise));
+            });
+            throw std::runtime_error("Runtime");
+            return i * 2 + 1;
+          });
+        }
+
+        try {
+          collectN(funcs.begin(), funcs.end(), 2);
+        } catch (...) {
+          EXPECT_EQ(1, pendingFibers.size());
+        }
+      });
+      taskAdded = true;
+    } else if (pendingFibers.size()) {
+      pendingFibers.back().setValue(0);
+      pendingFibers.pop_back();
+    } else {
+      loopController.stop();
+    }
+  };
+
+  loopController.loop(std::move(loopFunc));
+}
+
+TEST(FiberManager, collectNVoid) {
+  std::vector<Promise<int>> pendingFibers;
+  bool taskAdded = false;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  auto loopFunc = [&]() {
+    if (!taskAdded) {
+      manager.addTask([&]() {
+        std::vector<std::function<void()>> funcs;
+        for (size_t i = 0; i < 3; ++i) {
+          funcs.push_back([i, &pendingFibers]() {
+            await([&pendingFibers](Promise<int> promise) {
+              pendingFibers.push_back(std::move(promise));
+            });
+          });
+        }
+
+        auto results = collectN(funcs.begin(), funcs.end(), 2);
+        EXPECT_EQ(2, results.size());
+        EXPECT_EQ(1, pendingFibers.size());
+      });
+      taskAdded = true;
+    } else if (pendingFibers.size()) {
+      pendingFibers.back().setValue(0);
+      pendingFibers.pop_back();
+    } else {
+      loopController.stop();
+    }
+  };
+
+  loopController.loop(std::move(loopFunc));
+}
+
+TEST(FiberManager, collectNVoidThrow) {
+  std::vector<Promise<int>> pendingFibers;
+  bool taskAdded = false;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  auto loopFunc = [&]() {
+    if (!taskAdded) {
+      manager.addTask([&]() {
+        std::vector<std::function<void()>> funcs;
+        for (size_t i = 0; i < 3; ++i) {
+          funcs.push_back([i, &pendingFibers]() {
+            await([&pendingFibers](Promise<int> promise) {
+              pendingFibers.push_back(std::move(promise));
+            });
+            throw std::runtime_error("Runtime");
+          });
+        }
+
+        try {
+          collectN(funcs.begin(), funcs.end(), 2);
+        } catch (...) {
+          EXPECT_EQ(1, pendingFibers.size());
+        }
+      });
+      taskAdded = true;
+    } else if (pendingFibers.size()) {
+      pendingFibers.back().setValue(0);
+      pendingFibers.pop_back();
+    } else {
+      loopController.stop();
+    }
+  };
+
+  loopController.loop(std::move(loopFunc));
+}
+
+TEST(FiberManager, collectAll) {
+  std::vector<Promise<int>> pendingFibers;
+  bool taskAdded = false;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  auto loopFunc = [&]() {
+    if (!taskAdded) {
+      manager.addTask([&]() {
+        std::vector<std::function<int()>> funcs;
+        for (size_t i = 0; i < 3; ++i) {
+          funcs.push_back([i, &pendingFibers]() {
+            await([&pendingFibers](Promise<int> promise) {
+              pendingFibers.push_back(std::move(promise));
+            });
+            return i * 2 + 1;
+          });
+        }
+
+        auto results = collectAll(funcs.begin(), funcs.end());
+        EXPECT_TRUE(pendingFibers.empty());
+        for (size_t i = 0; i < 3; ++i) {
+          EXPECT_EQ(i * 2 + 1, results[i]);
+        }
+      });
+      taskAdded = true;
+    } else if (pendingFibers.size()) {
+      pendingFibers.back().setValue(0);
+      pendingFibers.pop_back();
+    } else {
+      loopController.stop();
+    }
+  };
+
+  loopController.loop(std::move(loopFunc));
+}
+
+TEST(FiberManager, collectAllVoid) {
+  std::vector<Promise<int>> pendingFibers;
+  bool taskAdded = false;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  auto loopFunc = [&]() {
+    if (!taskAdded) {
+      manager.addTask([&]() {
+        std::vector<std::function<void()>> funcs;
+        for (size_t i = 0; i < 3; ++i) {
+          funcs.push_back([i, &pendingFibers]() {
+            await([&pendingFibers](Promise<int> promise) {
+              pendingFibers.push_back(std::move(promise));
+            });
+          });
+        }
+
+        collectAll(funcs.begin(), funcs.end());
+        EXPECT_TRUE(pendingFibers.empty());
+      });
+      taskAdded = true;
+    } else if (pendingFibers.size()) {
+      pendingFibers.back().setValue(0);
+      pendingFibers.pop_back();
+    } else {
+      loopController.stop();
+    }
+  };
+
+  loopController.loop(std::move(loopFunc));
+}
+
+TEST(FiberManager, collectAny) {
+  std::vector<Promise<int>> pendingFibers;
+  bool taskAdded = false;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  auto loopFunc = [&]() {
+    if (!taskAdded) {
+      manager.addTask([&]() {
+        std::vector<std::function<int()>> funcs;
+        for (size_t i = 0; i < 3; ++i) {
+          funcs.push_back([i, &pendingFibers]() {
+            await([&pendingFibers](Promise<int> promise) {
+              pendingFibers.push_back(std::move(promise));
+            });
+            if (i == 1) {
+              throw std::runtime_error("This exception will be ignored");
+            }
+            return i * 2 + 1;
+          });
+        }
+
+        auto result = collectAny(funcs.begin(), funcs.end());
+        EXPECT_EQ(2, pendingFibers.size());
+        EXPECT_EQ(2, result.first);
+        EXPECT_EQ(2 * 2 + 1, result.second);
+      });
+      taskAdded = true;
+    } else if (pendingFibers.size()) {
+      pendingFibers.back().setValue(0);
+      pendingFibers.pop_back();
+    } else {
+      loopController.stop();
+    }
+  };
+
+  loopController.loop(std::move(loopFunc));
+}
+
+namespace {
+/* Checks that this function was run from a main context,
+   by comparing an address on a stack to a known main stack address
+   and a known related fiber stack address.  The assumption
+   is that fiber stack and main stack will be far enough apart,
+   while any two values on the same stack will be close. */
+void expectMainContext(bool& ran, int* mainLocation, int* fiberLocation) {
+  int here;
+  /* 2 pages is a good guess */
+  constexpr ssize_t DISTANCE = 0x2000 / sizeof(int);
+  if (fiberLocation) {
+    EXPECT_TRUE(std::abs(&here - fiberLocation) > DISTANCE);
+  }
+  if (mainLocation) {
+    EXPECT_TRUE(std::abs(&here - mainLocation) < DISTANCE);
+  }
+
+  EXPECT_FALSE(ran);
+  ran = true;
+}
+}
+
+TEST(FiberManager, runInMainContext) {
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  bool checkRan = false;
+
+  int mainLocation;
+  manager.runInMainContext(
+      [&]() { expectMainContext(checkRan, &mainLocation, nullptr); });
+  EXPECT_TRUE(checkRan);
+
+  checkRan = false;
+
+  manager.addTask([&]() {
+    struct A {
+      explicit A(int value_) : value(value_) {}
+      A(const A&) = delete;
+      A(A&&) = default;
+
+      int value;
+    };
+    int stackLocation;
+    auto ret = runInMainContext([&]() {
+      expectMainContext(checkRan, &mainLocation, &stackLocation);
+      return A(42);
+    });
+    EXPECT_TRUE(checkRan);
+    EXPECT_EQ(42, ret.value);
+  });
+
+  loopController.loop([&]() { loopController.stop(); });
+
+  EXPECT_TRUE(checkRan);
+}
+
+TEST(FiberManager, addTaskFinally) {
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  bool checkRan = false;
+
+  int mainLocation;
+
+  manager.addTaskFinally(
+      [&]() { return 1234; },
+      [&](Try<int>&& result) {
+        EXPECT_EQ(result.value(), 1234);
+
+        expectMainContext(checkRan, &mainLocation, nullptr);
+      });
+
+  EXPECT_FALSE(checkRan);
+
+  loopController.loop([&]() { loopController.stop(); });
+
+  EXPECT_TRUE(checkRan);
+}
+
+TEST(FiberManager, fibersPoolWithinLimit) {
+  FiberManager::Options opts;
+  opts.maxFibersPoolSize = 5;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>(), opts);
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  size_t fibersRun = 0;
+
+  for (size_t i = 0; i < 5; ++i) {
+    manager.addTask([&]() { ++fibersRun; });
+  }
+  loopController.loop([&]() { loopController.stop(); });
+
+  EXPECT_EQ(5, fibersRun);
+  EXPECT_EQ(5, manager.fibersAllocated());
+  EXPECT_EQ(5, manager.fibersPoolSize());
+
+  for (size_t i = 0; i < 5; ++i) {
+    manager.addTask([&]() { ++fibersRun; });
+  }
+  loopController.loop([&]() { loopController.stop(); });
+
+  EXPECT_EQ(10, fibersRun);
+  EXPECT_EQ(5, manager.fibersAllocated());
+  EXPECT_EQ(5, manager.fibersPoolSize());
+}
+
+TEST(FiberManager, fibersPoolOverLimit) {
+  FiberManager::Options opts;
+  opts.maxFibersPoolSize = 5;
+
+  FiberManager manager(folly::make_unique<SimpleLoopController>(), opts);
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  size_t fibersRun = 0;
+
+  for (size_t i = 0; i < 10; ++i) {
+    manager.addTask([&]() { ++fibersRun; });
+  }
+
+  EXPECT_EQ(0, fibersRun);
+  EXPECT_EQ(10, manager.fibersAllocated());
+  EXPECT_EQ(0, manager.fibersPoolSize());
+
+  loopController.loop([&]() { loopController.stop(); });
+
+  EXPECT_EQ(10, fibersRun);
+  EXPECT_EQ(5, manager.fibersAllocated());
+  EXPECT_EQ(5, manager.fibersPoolSize());
+}
+
+TEST(FiberManager, remoteFiberBasic) {
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  int result[2];
+  result[0] = result[1] = 0;
+  folly::Optional<Promise<int>> savedPromise[2];
+  manager.addTask([&]() {
+    result[0] = await(
+        [&](Promise<int> promise) { savedPromise[0] = std::move(promise); });
+  });
+  manager.addTask([&]() {
+    result[1] = await(
+        [&](Promise<int> promise) { savedPromise[1] = std::move(promise); });
+  });
+
+  manager.loopUntilNoReady();
+
+  EXPECT_TRUE(savedPromise[0].hasValue());
+  EXPECT_TRUE(savedPromise[1].hasValue());
+  EXPECT_EQ(0, result[0]);
+  EXPECT_EQ(0, result[1]);
+
+  std::thread remoteThread0{[&]() { savedPromise[0]->setValue(42); }};
+  std::thread remoteThread1{[&]() { savedPromise[1]->setValue(43); }};
+  remoteThread0.join();
+  remoteThread1.join();
+  EXPECT_EQ(0, result[0]);
+  EXPECT_EQ(0, result[1]);
+  /* Should only have scheduled once */
+  EXPECT_EQ(1, loopController.remoteScheduleCalled());
+
+  manager.loopUntilNoReady();
+  EXPECT_EQ(42, result[0]);
+  EXPECT_EQ(43, result[1]);
+}
+
+TEST(FiberManager, addTaskRemoteBasic) {
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+
+  int result[2];
+  result[0] = result[1] = 0;
+  folly::Optional<Promise<int>> savedPromise[2];
+
+  std::thread remoteThread0{[&]() {
+    manager.addTaskRemote([&]() {
+      result[0] = await(
+          [&](Promise<int> promise) { savedPromise[0] = std::move(promise); });
+    });
+  }};
+  std::thread remoteThread1{[&]() {
+    manager.addTaskRemote([&]() {
+      result[1] = await(
+          [&](Promise<int> promise) { savedPromise[1] = std::move(promise); });
+    });
+  }};
+  remoteThread0.join();
+  remoteThread1.join();
+
+  manager.loopUntilNoReady();
+
+  EXPECT_TRUE(savedPromise[0].hasValue());
+  EXPECT_TRUE(savedPromise[1].hasValue());
+  EXPECT_EQ(0, result[0]);
+  EXPECT_EQ(0, result[1]);
+
+  savedPromise[0]->setValue(42);
+  savedPromise[1]->setValue(43);
+
+  EXPECT_EQ(0, result[0]);
+  EXPECT_EQ(0, result[1]);
+
+  manager.loopUntilNoReady();
+  EXPECT_EQ(42, result[0]);
+  EXPECT_EQ(43, result[1]);
+}
+
+TEST(FiberManager, remoteHasTasks) {
+  size_t counter = 0;
+  FiberManager fm(folly::make_unique<SimpleLoopController>());
+  std::thread remote([&]() { fm.addTaskRemote([&]() { ++counter; }); });
+
+  remote.join();
+
+  while (fm.hasTasks()) {
+    fm.loopUntilNoReady();
+  }
+
+  EXPECT_FALSE(fm.hasTasks());
+  EXPECT_EQ(counter, 1);
+}
+
+TEST(FiberManager, remoteHasReadyTasks) {
+  int result = 0;
+  folly::Optional<Promise<int>> savedPromise;
+  FiberManager fm(folly::make_unique<SimpleLoopController>());
+  std::thread remote([&]() {
+    fm.addTaskRemote([&]() {
+      result = await(
+          [&](Promise<int> promise) { savedPromise = std::move(promise); });
+      EXPECT_TRUE(fm.hasTasks());
+    });
+  });
+
+  remote.join();
+  EXPECT_TRUE(fm.hasTasks());
+
+  fm.loopUntilNoReady();
+  EXPECT_TRUE(fm.hasTasks());
+
+  std::thread remote2([&]() { savedPromise->setValue(47); });
+  remote2.join();
+  EXPECT_TRUE(fm.hasTasks());
+
+  fm.loopUntilNoReady();
+  EXPECT_FALSE(fm.hasTasks());
+
+  EXPECT_EQ(result, 47);
+}
+
+template <typename Data>
+void testFiberLocal() {
+  FiberManager fm(
+      LocalType<Data>(), folly::make_unique<SimpleLoopController>());
+
+  fm.addTask([]() {
+    EXPECT_EQ(42, local<Data>().value);
+
+    local<Data>().value = 43;
+
+    addTask([]() {
+      EXPECT_EQ(43, local<Data>().value);
+
+      local<Data>().value = 44;
+
+      addTask([]() { EXPECT_EQ(44, local<Data>().value); });
+    });
+  });
+
+  fm.addTask([&]() {
+    EXPECT_EQ(42, local<Data>().value);
+
+    local<Data>().value = 43;
+
+    fm.addTaskRemote([]() { EXPECT_EQ(43, local<Data>().value); });
+  });
+
+  fm.addTask([]() {
+    EXPECT_EQ(42, local<Data>().value);
+    local<Data>().value = 43;
+
+    auto task = []() {
+      EXPECT_EQ(43, local<Data>().value);
+      local<Data>().value = 44;
+    };
+    std::vector<std::function<void()>> tasks{task};
+    collectAny(tasks.begin(), tasks.end());
+
+    EXPECT_EQ(43, local<Data>().value);
+  });
+
+  fm.loopUntilNoReady();
+  EXPECT_FALSE(fm.hasTasks());
+}
+
+TEST(FiberManager, fiberLocal) {
+  struct SimpleData {
+    int value{42};
+  };
+
+  testFiberLocal<SimpleData>();
+}
+
+TEST(FiberManager, fiberLocalHeap) {
+  struct LargeData {
+    char _[1024 * 1024];
+    int value{42};
+  };
+
+  testFiberLocal<LargeData>();
+}
+
+TEST(FiberManager, fiberLocalDestructor) {
+  struct CrazyData {
+    size_t data{42};
+
+    ~CrazyData() {
+      if (data == 41) {
+        addTask([]() {
+          EXPECT_EQ(42, local<CrazyData>().data);
+          // Make sure we don't have infinite loop
+          local<CrazyData>().data = 0;
+        });
+      }
+    }
+  };
+
+  FiberManager fm(
+      LocalType<CrazyData>(), folly::make_unique<SimpleLoopController>());
+
+  fm.addTask([]() { local<CrazyData>().data = 41; });
+
+  fm.loopUntilNoReady();
+  EXPECT_FALSE(fm.hasTasks());
+}
+
+TEST(FiberManager, yieldTest) {
+  FiberManager manager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(manager.loopController());
+
+  bool checkRan = false;
+
+  manager.addTask([&]() {
+    manager.yield();
+    checkRan = true;
+  });
+
+  loopController.loop([&]() {
+    if (checkRan) {
+      loopController.stop();
+    }
+  });
+
+  EXPECT_TRUE(checkRan);
+}
+
+TEST(FiberManager, RequestContext) {
+  FiberManager fm(folly::make_unique<SimpleLoopController>());
+
+  bool checkRun1 = false;
+  bool checkRun2 = false;
+  bool checkRun3 = false;
+  bool checkRun4 = false;
+  folly::fibers::Baton baton1;
+  folly::fibers::Baton baton2;
+  folly::fibers::Baton baton3;
+  folly::fibers::Baton baton4;
+
+  folly::RequestContext::create();
+  auto rcontext1 = folly::RequestContext::get();
+  fm.addTask([&]() {
+    EXPECT_EQ(rcontext1, folly::RequestContext::get());
+    baton1.wait([&]() { EXPECT_EQ(rcontext1, folly::RequestContext::get()); });
+    EXPECT_EQ(rcontext1, folly::RequestContext::get());
+    runInMainContext(
+        [&]() { EXPECT_EQ(rcontext1, folly::RequestContext::get()); });
+    checkRun1 = true;
+  });
+
+  folly::RequestContext::create();
+  auto rcontext2 = folly::RequestContext::get();
+  fm.addTaskRemote([&]() {
+    EXPECT_EQ(rcontext2, folly::RequestContext::get());
+    baton2.wait();
+    EXPECT_EQ(rcontext2, folly::RequestContext::get());
+    checkRun2 = true;
+  });
+
+  folly::RequestContext::create();
+  auto rcontext3 = folly::RequestContext::get();
+  fm.addTaskFinally(
+      [&]() {
+        EXPECT_EQ(rcontext3, folly::RequestContext::get());
+        baton3.wait();
+        EXPECT_EQ(rcontext3, folly::RequestContext::get());
+
+        return folly::Unit();
+      },
+      [&](Try<folly::Unit>&& /* t */) {
+        EXPECT_EQ(rcontext3, folly::RequestContext::get());
+        checkRun3 = true;
+      });
+
+  folly::RequestContext::setContext(nullptr);
+  fm.addTask([&]() {
+    folly::RequestContext::create();
+    auto rcontext4 = folly::RequestContext::get();
+    baton4.wait();
+    EXPECT_EQ(rcontext4, folly::RequestContext::get());
+    checkRun4 = true;
+  });
+
+  folly::RequestContext::create();
+  auto rcontext = folly::RequestContext::get();
+
+  fm.loopUntilNoReady();
+  EXPECT_EQ(rcontext, folly::RequestContext::get());
+
+  baton1.post();
+  EXPECT_EQ(rcontext, folly::RequestContext::get());
+  fm.loopUntilNoReady();
+  EXPECT_TRUE(checkRun1);
+  EXPECT_EQ(rcontext, folly::RequestContext::get());
+
+  baton2.post();
+  EXPECT_EQ(rcontext, folly::RequestContext::get());
+  fm.loopUntilNoReady();
+  EXPECT_TRUE(checkRun2);
+  EXPECT_EQ(rcontext, folly::RequestContext::get());
+
+  baton3.post();
+  EXPECT_EQ(rcontext, folly::RequestContext::get());
+  fm.loopUntilNoReady();
+  EXPECT_TRUE(checkRun3);
+  EXPECT_EQ(rcontext, folly::RequestContext::get());
+
+  baton4.post();
+  EXPECT_EQ(rcontext, folly::RequestContext::get());
+  fm.loopUntilNoReady();
+  EXPECT_TRUE(checkRun4);
+  EXPECT_EQ(rcontext, folly::RequestContext::get());
+}
+
+TEST(FiberManager, resizePeriodically) {
+  FiberManager::Options opts;
+  opts.fibersPoolResizePeriodMs = 300;
+  opts.maxFibersPoolSize = 5;
+
+  FiberManager manager(folly::make_unique<EventBaseLoopController>(), opts);
+
+  folly::EventBase evb;
+  dynamic_cast<EventBaseLoopController&>(manager.loopController())
+      .attachEventBase(evb);
+
+  std::vector<Baton> batons(10);
+
+  size_t tasksRun = 0;
+  for (size_t i = 0; i < 30; ++i) {
+    manager.addTask([i, &batons, &tasksRun]() {
+      ++tasksRun;
+      // Keep some fibers active indefinitely
+      if (i < batons.size()) {
+        batons[i].wait();
+      }
+    });
+  }
+
+  EXPECT_EQ(0, tasksRun);
+  EXPECT_EQ(30, manager.fibersAllocated());
+  EXPECT_EQ(0, manager.fibersPoolSize());
+
+  evb.loopOnce();
+  EXPECT_EQ(30, tasksRun);
+  EXPECT_EQ(30, manager.fibersAllocated());
+  // Can go over maxFibersPoolSize, 10 of 30 fibers still active
+  EXPECT_EQ(20, manager.fibersPoolSize());
+
+  std::this_thread::sleep_for(std::chrono::milliseconds(400));
+  evb.loopOnce(); // no fibers active in this period
+  EXPECT_EQ(30, manager.fibersAllocated());
+  EXPECT_EQ(20, manager.fibersPoolSize());
+
+  std::this_thread::sleep_for(std::chrono::milliseconds(400));
+  evb.loopOnce(); // should shrink fibers pool to maxFibersPoolSize
+  EXPECT_EQ(15, manager.fibersAllocated());
+  EXPECT_EQ(5, manager.fibersPoolSize());
+
+  for (size_t i = 0; i < batons.size(); ++i) {
+    batons[i].post();
+  }
+  evb.loopOnce();
+  EXPECT_EQ(15, manager.fibersAllocated());
+  EXPECT_EQ(15, manager.fibersPoolSize());
+
+  std::this_thread::sleep_for(std::chrono::milliseconds(400));
+  evb.loopOnce(); // 10 fibers active in last period
+  EXPECT_EQ(10, manager.fibersAllocated());
+  EXPECT_EQ(10, manager.fibersPoolSize());
+
+  std::this_thread::sleep_for(std::chrono::milliseconds(400));
+  evb.loopOnce();
+  EXPECT_EQ(5, manager.fibersAllocated());
+  EXPECT_EQ(5, manager.fibersPoolSize());
+}
+
+TEST(FiberManager, batonWaitTimeoutHandler) {
+  FiberManager manager(folly::make_unique<EventBaseLoopController>());
+
+  folly::EventBase evb;
+  dynamic_cast<EventBaseLoopController&>(manager.loopController())
+      .attachEventBase(evb);
+
+  size_t fibersRun = 0;
+  Baton baton;
+  Baton::TimeoutHandler timeoutHandler;
+
+  manager.addTask([&]() {
+    baton.wait(timeoutHandler);
+    ++fibersRun;
+  });
+  manager.loopUntilNoReady();
+
+  EXPECT_FALSE(baton.try_wait());
+  EXPECT_EQ(0, fibersRun);
+
+  timeoutHandler.scheduleTimeout(std::chrono::milliseconds(250));
+  std::this_thread::sleep_for(std::chrono::milliseconds(500));
+
+  EXPECT_FALSE(baton.try_wait());
+  EXPECT_EQ(0, fibersRun);
+
+  evb.loopOnce();
+  manager.loopUntilNoReady();
+
+  EXPECT_EQ(1, fibersRun);
+}
+
+TEST(FiberManager, batonWaitTimeoutMany) {
+  FiberManager manager(folly::make_unique<EventBaseLoopController>());
+
+  folly::EventBase evb;
+  dynamic_cast<EventBaseLoopController&>(manager.loopController())
+      .attachEventBase(evb);
+
+  constexpr size_t kNumTimeoutTasks = 10000;
+  size_t tasksCount = kNumTimeoutTasks;
+
+  // We add many tasks to hit timeout queue deallocation logic.
+  for (size_t i = 0; i < kNumTimeoutTasks; ++i) {
+    manager.addTask([&]() {
+      Baton baton;
+      Baton::TimeoutHandler timeoutHandler;
+
+      folly::fibers::addTask([&] {
+        timeoutHandler.scheduleTimeout(std::chrono::milliseconds(1000));
+      });
+
+      baton.wait(timeoutHandler);
+      if (--tasksCount == 0) {
+        evb.terminateLoopSoon();
+      }
+    });
+  }
+
+  evb.loopForever();
+}
+
+TEST(FiberManager, remoteFutureTest) {
+  FiberManager fiberManager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(fiberManager.loopController());
+
+  int testValue1 = 5;
+  int testValue2 = 7;
+  auto f1 = fiberManager.addTaskFuture([&]() { return testValue1; });
+  auto f2 = fiberManager.addTaskRemoteFuture([&]() { return testValue2; });
+  loopController.loop([&]() { loopController.stop(); });
+  auto v1 = f1.get();
+  auto v2 = f2.get();
+
+  EXPECT_EQ(v1, testValue1);
+  EXPECT_EQ(v2, testValue2);
+}
+
+// Test that a void function produes a Future<Unit>.
+TEST(FiberManager, remoteFutureVoidUnitTest) {
+  FiberManager fiberManager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(fiberManager.loopController());
+
+  bool ranLocal = false;
+  folly::Future<folly::Unit> futureLocal =
+      fiberManager.addTaskFuture([&]() { ranLocal = true; });
+
+  bool ranRemote = false;
+  folly::Future<folly::Unit> futureRemote =
+      fiberManager.addTaskRemoteFuture([&]() { ranRemote = true; });
+
+  loopController.loop([&]() { loopController.stop(); });
+
+  futureLocal.wait();
+  ASSERT_TRUE(ranLocal);
+
+  futureRemote.wait();
+  ASSERT_TRUE(ranRemote);
+}
+
+TEST(FiberManager, nestedFiberManagers) {
+  folly::EventBase outerEvb;
+  folly::EventBase innerEvb;
+
+  getFiberManager(outerEvb).addTask([&]() {
+    EXPECT_EQ(
+        &getFiberManager(outerEvb), FiberManager::getFiberManagerUnsafe());
+
+    runInMainContext([&]() {
+      getFiberManager(innerEvb).addTask([&]() {
+        EXPECT_EQ(
+            &getFiberManager(innerEvb), FiberManager::getFiberManagerUnsafe());
+
+        innerEvb.terminateLoopSoon();
+      });
+
+      innerEvb.loopForever();
+    });
+
+    EXPECT_EQ(
+        &getFiberManager(outerEvb), FiberManager::getFiberManagerUnsafe());
+
+    outerEvb.terminateLoopSoon();
+  });
+
+  outerEvb.loopForever();
+}
+
+static size_t sNumAwaits;
+
+void runBenchmark(size_t numAwaits, size_t toSend) {
+  sNumAwaits = numAwaits;
+
+  FiberManager fiberManager(folly::make_unique<SimpleLoopController>());
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(fiberManager.loopController());
+
+  std::queue<Promise<int>> pendingRequests;
+  static const size_t maxOutstanding = 5;
+
+  auto loop = [&fiberManager, &loopController, &pendingRequests, &toSend]() {
+    if (pendingRequests.size() == maxOutstanding || toSend == 0) {
+      if (pendingRequests.empty()) {
+        return;
+      }
+      pendingRequests.front().setValue(0);
+      pendingRequests.pop();
+    } else {
+      fiberManager.addTask([&pendingRequests]() {
+        for (size_t i = 0; i < sNumAwaits; ++i) {
+          auto result = await([&pendingRequests](Promise<int> promise) {
+            pendingRequests.push(std::move(promise));
+          });
+          DCHECK_EQ(result, 0);
+        }
+      });
+
+      if (--toSend == 0) {
+        loopController.stop();
+      }
+    }
+  };
+
+  loopController.loop(std::move(loop));
+}
+
+BENCHMARK(FiberManagerBasicOneAwait, iters) {
+  runBenchmark(1, iters);
+}
+
+BENCHMARK(FiberManagerBasicFiveAwaits, iters) {
+  runBenchmark(5, iters);
+}
+
+BENCHMARK(FiberManagerCreateDestroy, iters) {
+  for (size_t i = 0; i < iters; ++i) {
+    folly::EventBase evb;
+    auto& fm = folly::fibers::getFiberManager(evb);
+    fm.addTask([]() {});
+    evb.loop();
+  }
+}
+
+BENCHMARK(FiberManagerAllocateDeallocatePattern, iters) {
+  static const size_t kNumAllocations = 10000;
+
+  FiberManager::Options opts;
+  opts.maxFibersPoolSize = 0;
+
+  FiberManager fiberManager(folly::make_unique<SimpleLoopController>(), opts);
+
+  for (size_t iter = 0; iter < iters; ++iter) {
+    EXPECT_EQ(0, fiberManager.fibersPoolSize());
+
+    size_t fibersRun = 0;
+
+    for (size_t i = 0; i < kNumAllocations; ++i) {
+      fiberManager.addTask([&fibersRun] { ++fibersRun; });
+      fiberManager.loopUntilNoReady();
+    }
+
+    EXPECT_EQ(10000, fibersRun);
+    EXPECT_EQ(0, fiberManager.fibersPoolSize());
+  }
+}
+
+BENCHMARK(FiberManagerAllocateLargeChunk, iters) {
+  static const size_t kNumAllocations = 10000;
+
+  FiberManager::Options opts;
+  opts.maxFibersPoolSize = 0;
+
+  FiberManager fiberManager(folly::make_unique<SimpleLoopController>(), opts);
+
+  for (size_t iter = 0; iter < iters; ++iter) {
+    EXPECT_EQ(0, fiberManager.fibersPoolSize());
+
+    size_t fibersRun = 0;
+
+    for (size_t i = 0; i < kNumAllocations; ++i) {
+      fiberManager.addTask([&fibersRun] { ++fibersRun; });
+    }
+
+    fiberManager.loopUntilNoReady();
+
+    EXPECT_EQ(10000, fibersRun);
+    EXPECT_EQ(0, fiberManager.fibersPoolSize());
+  }
+}
diff --git a/folly/fibers/test/FibersTestApp.cpp b/folly/fibers/test/FibersTestApp.cpp
new file mode 100644 (file)
index 0000000..96a7df4
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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.
+ */
+#include <iostream>
+#include <queue>
+
+#include <folly/Memory.h>
+
+#include <folly/fibers/FiberManager.h>
+#include <folly/fibers/SimpleLoopController.h>
+
+using namespace folly::fibers;
+
+struct Application {
+ public:
+  Application()
+      : fiberManager(folly::make_unique<SimpleLoopController>()),
+        toSend(20),
+        maxOutstanding(5) {}
+
+  void loop() {
+    if (pendingRequests.size() == maxOutstanding || toSend == 0) {
+      if (pendingRequests.empty()) {
+        return;
+      }
+      intptr_t value = rand() % 1000;
+      std::cout << "Completing request with data = " << value << std::endl;
+
+      pendingRequests.front().setValue(value);
+      pendingRequests.pop();
+    } else {
+      static size_t id_counter = 1;
+      size_t id = id_counter++;
+      std::cout << "Adding new request with id = " << id << std::endl;
+
+      fiberManager.addTask([this, id]() {
+        std::cout << "Executing fiber with id = " << id << std::endl;
+
+        auto result1 = await([this](Promise<int> fiber) {
+          pendingRequests.push(std::move(fiber));
+        });
+
+        std::cout << "Fiber id = " << id << " got result1 = " << result1
+                  << std::endl;
+
+        auto result2 = await([this](Promise<int> fiber) {
+          pendingRequests.push(std::move(fiber));
+        });
+        std::cout << "Fiber id = " << id << " got result2 = " << result2
+                  << std::endl;
+      });
+
+      if (--toSend == 0) {
+        auto& loopController =
+            dynamic_cast<SimpleLoopController&>(fiberManager.loopController());
+        loopController.stop();
+      }
+    }
+  }
+
+  FiberManager fiberManager;
+
+  std::queue<Promise<int>> pendingRequests;
+  size_t toSend;
+  size_t maxOutstanding;
+};
+
+int main() {
+  Application app;
+
+  auto loop = [&app]() { app.loop(); };
+
+  auto& loopController =
+      dynamic_cast<SimpleLoopController&>(app.fiberManager.loopController());
+
+  loopController.loop(std::move(loop));
+
+  return 0;
+}
diff --git a/folly/fibers/test/StackOverflow.cpp b/folly/fibers/test/StackOverflow.cpp
new file mode 100644 (file)
index 0000000..8cbd4d0
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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.
+ */
+#include <folly/experimental/symbolizer/SignalHandler.h>
+#include <folly/fibers/FiberManagerMap.h>
+#include <folly/init/Init.h>
+
+void f(int* p) {
+  // Make sure recursion is not optimized out
+  int a[100];
+  for (size_t i = 0; i < 100; ++i) {
+    a[i] = i;
+    ++(a[i]);
+    if (p) {
+      a[i] += p[i];
+    }
+  }
+  f(a);
+}
+
+int main(int argc, char* argv[]) {
+  folly::init(&argc, &argv);
+
+  folly::EventBase evb;
+  folly::fibers::getFiberManager(evb).addTask([&]() { f(nullptr); });
+  evb.loop();
+}
diff --git a/folly/fibers/test/main.cpp b/folly/fibers/test/main.cpp
new file mode 100644 (file)
index 0000000..ac94a9e
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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.
+ */
+#include <gtest/gtest.h>
+
+#include <folly/Benchmark.h>
+
+// for backward compatibility with gflags
+namespace gflags {}
+namespace google {
+using namespace gflags;
+}
+
+int main(int argc, char** argv) {
+  testing::InitGoogleTest(&argc, argv);
+  gflags::ParseCommandLineFlags(&argc, &argv, true);
+
+  auto rc = RUN_ALL_TESTS();
+  folly::runBenchmarksOnFlag();
+  return rc;
+}
diff --git a/folly/fibers/traits.h b/folly/fibers/traits.h
new file mode 100644 (file)
index 0000000..3dc0e48
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * 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 <boost/type_traits.hpp>
+
+namespace folly {
+namespace fibers {
+
+/**
+ * For any functor F taking >= 1 argument,
+ * FirstArgOf<F>::type is the type of F's first parameter.
+ *
+ * Rationale: we want to declare a function func(F), where F has the
+ * signature `void(X)` and func should return T<X> (T and X are some types).
+ * Solution:
+ *
+ * template <typename F>
+ * T<typename FirstArgOf<F>::type>
+ * func(F&& f);
+ */
+
+namespace detail {
+
+/**
+ * If F is a pointer-to-member, will contain a typedef type
+ * with the type of F's first parameter
+ */
+template <typename>
+struct ExtractFirstMemfn;
+
+template <typename Ret, typename T, typename First, typename... Args>
+struct ExtractFirstMemfn<Ret (T::*)(First, Args...)> {
+  typedef First type;
+};
+
+template <typename Ret, typename T, typename First, typename... Args>
+struct ExtractFirstMemfn<Ret (T::*)(First, Args...) const> {
+  typedef First type;
+};
+
+} // detail
+
+/** Default - use boost */
+template <typename F, typename Enable = void>
+struct FirstArgOf {
+  typedef typename boost::function_traits<
+      typename std::remove_pointer<F>::type>::arg1_type type;
+};
+
+/** Specialization for function objects */
+template <typename F>
+struct FirstArgOf<F, typename std::enable_if<std::is_class<F>::value>::type> {
+  typedef
+      typename detail::ExtractFirstMemfn<decltype(&F::operator())>::type type;
+};
+}
+} // folly::fibers
index aebe58b652182fbf6380c38ed06071e7be0ebc09..094ccdc0ece17f43e303d908bcbceee274fc3a2e 100644 (file)
@@ -32,7 +32,7 @@
 #define FOLLY_FUTURE_USING_FIBER 0
 #else
 #define FOLLY_FUTURE_USING_FIBER 1
-#include <folly/experimental/fibers/Baton.h>
+#include <folly/fibers/Baton.h>
 #endif
 
 namespace folly {