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 \
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 \
+++ /dev/null
-/*
- * 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);
-}
-}
-}
+++ /dev/null
-/*
- * 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>
+++ /dev/null
-/*
- * 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));
- }
-}
-}
-}
+++ /dev/null
-/*
- * 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_);
- }
-}
-}
-}
+++ /dev/null
-/*
- * 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>
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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"
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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);
-}
-}
-}
+++ /dev/null
-/*
- * 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>
+++ /dev/null
-/*
- * 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));
-}
-}
-}
+++ /dev/null
-/*
- * 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;
-}
-}
-}
+++ /dev/null
-/*
- * 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"
+++ /dev/null
-/*
- * 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);
-}
-}
-}
+++ /dev/null
-/*
- * 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());
-}
-}
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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>
+++ /dev/null
-/*
- * 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;
-}
-}
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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);
-}
-}
-}
+++ /dev/null
-/*
- * 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>
+++ /dev/null
-<!-- 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">&</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">([&]()</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"><<</span> <span class="s2">"Task 1: start"</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="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"><<</span> <span class="s2">"Task 1: after baton.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="endl">endl</span><span class="o">;</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">{</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"><<</span> <span class="s2">"Task 2: start"</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="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"><<</span> <span class="s2">"Task 2: after baton.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="endl">endl</span><span class="o">;</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="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'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'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's own threads and distributing load between them</li>
-<li>We don'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'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'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">&</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">([&]()</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"><<</span> <span class="s2">"Task: start"</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="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"><<</span> <span class="s2">"Task: after baton.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="endl">endl</span><span class="o">;</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="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"><<</span> <span class="s2">"Baton posted"</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="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'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"><</span><span class="nf" data-symbol-name="void">void</span><span class="o">(</span><span class="no">Response</span><span class="o">)></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">{</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">[&](</span><span class="no">Response</span> <span class="no">r</span><span class="o">)</span> <span class="no">mutable</span> <span class="o">{</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">});</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">}</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">{</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">([&](</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"><</span><span class="no">Response</span><span class="o">></span> <span class="no">promise</span><span class="o">)</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">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">{</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">});</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">}</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 'fibers::await' 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">{</span>
- <span class="o">...</span>
- <span class="k">try</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">([&](</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"><</span><span class="no">Response</span><span class="o">></span> <span class="no">promise</span><span class="o">)</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">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">{</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">"Await will re-throw me"</span><span class="o">));</span>
- <span class="o">});</span>
- <span class="o">});</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">}</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">&</span> <span class="no">e</span><span class="o">)</span> <span class="o">{</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">"Await will re-throw me"</span><span class="o">);</span>
- <span class="o">}</span>
- <span class="o">...</span>
-<span class="o">}</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">{</span>
- <span class="o">...</span>
- <span class="k">try</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">([&](</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"><</span><span class="no">Response</span><span class="o">></span> <span class="no">promise</span><span class="o">)</span> <span class="o">{</span>
- <span class="c">// We forget about the promise</span>
- <span class="o">});</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">}</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">&</span> <span class="no">e</span><span class="o">)</span> <span class="o">{</span>
- <span class="o">...</span>
- <span class="o">}</span>
- <span class="o">...</span>
-<span class="o">}</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'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'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"><</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="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">{</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">}</span></pre></div>
-
-<p>Calling <tt>get()</tt> on a <tt>folly::Future</tt> object will only suspend the calling fiber-task. It won'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">{</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">([&](</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"><</span><span class="no">Response</span><span class="o">></span> <span class="no">promise</span><span class="o">)</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">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">{</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">});</span>
- <span class="o">});</span>
-<span class="o">}</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">{</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">});</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">{</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">...</span></pre></div>
-
-<p>Fibers-compatible APIs look more like synchronous APIs, so you can actually pass input arguments by reference and you don'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">{</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">});</span></pre></div>
-
-<p>Same logic applies to <tt>fibers::await</tt>. Since <tt>fibers::await</tt> call blocks until promise is fulfilled, it'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">{</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">}</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">{</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">([&]()</span> <span class="o">{</span>
- <span class="k">return</span> <span class="nf" data-symbol-name="useALotOfStack">useALotOfStack</span><span class="o">();</span>
- <span class="o">});</span>
- <span class="o">...</span>
-<span class="o">});</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'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'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">&</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">([&]()</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="lock_guard">lock_guard</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="o">></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">});</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">{</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"><</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">></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">});</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'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'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">{</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"><</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">>();</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"><</span><span class="no">Result</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">{</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">-></span><span class="na" data-symbol-name="post">post</span><span class="o">();</span>
- <span class="o">});</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">{</span><span class="mi">10</span><span class="o">});</span>
- <span class="k">if</span> <span class="o">(</span><span class="no">success</span><span class="o">)</span> <span class="o">{</span>
- <span class="c">// request successful</span>
- <span class="o">...</span>
- <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
- <span class="c">// handle timeout</span>
- <span class="o">...</span>
- <span class="o">}</span>
-<span class="o">});</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'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">{</span>
- <span class="o">...</span>
-<span class="o">});</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">&</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">([&]()</span> <span class="o">{</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">{</span>
- <span class="o">...</span>
- <span class="o">});</span>
-<span class="o">});</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">{</span>
- <span class="o">...</span>
- <span class="k">return</span> <span class="no">result</span><span class="o">;</span>
- <span class="o">},</span>
- <span class="o">[=](</span><span class="no">Result</span><span class="o">&&</span> <span class="no">result</span><span class="o">)</span> <span class="o">{</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">}</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">{</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">([&]()</span> <span class="o">{</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">}</span>
- <span class="c">// Switched back to fiber context</span>
-
- <span class="c">// On fiber context we realize there's no more work to be done.</span>
- <span class="c">// Fiber-task is complete, switching back to main context.</span>
-<span class="o">});</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">&</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">([&]()</span> <span class="o">{</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">{</span>
- <span class="o">...</span>
- <span class="o">});</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">{</span>
- <span class="o">...</span>
- <span class="o">});</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">});</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'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't implement it'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">&</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">{</span>
- <span class="o">...</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></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't support killing fiber-tasks in-flight (for similar reasons you can'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">'folly/experimental/fibers/scripts/utils.gdb'</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">-></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">-></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">&</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)::{lambda()#1}::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<folly::fibers::FirstArgOf<FiberManager_collectAll_Test::TestBody()::{lambda()#1}::operator()() con</span>
-<span class="nc" data-symbol-name="st">st</span><span class="o">::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::{lambda(folly::fibers::Promise<int>)#1}, void>::type::value_type folly::fibers::await</span>
-<span class="o"><</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">()::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const::{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"><</span><span class="no">int</span><span class="o">>)</span><span class="c">#1}>(folly::fibers::Promise<int>&&)::{lambda()#1}>(folly::fibers::FiberManager&, folly::fibers::FirstArgOf<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">()::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const::{lambda(folly::fibers::Promise<int>)#1}</span>
-<span class="o">,</span> <span class="no">void</span><span class="o">>::</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"><</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">()::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::</span>
-<span class="o">{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda(folly::fibers::Promise<int>)#1}>(folly::fibers::Promise<int>&&)::{lambda()#1}) + 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<folly::fibers::FirstArgOf<FiberManager_collectAll_Test::TestBody()::{lambda()#1}::operator()() const::{</span>
-<span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::{lambda(folly::fibers::Promise<int>)#1}, void>::type::value_type folly::fibers::await<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">()::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const::{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"><</span><span class="no">int</span><span class="o">>)</span><span class="c">#1}>(folly::fibers::Promise<int>&&)::{lambda()#1}>(folly::fibers::FirstArgOf<FiberManager_collectAll_Test::TestBody()::{lambda()#1}::opera</span>
-<span class="nf" data-symbol-name="tor">tor</span><span class="o">()()</span> <span class="k">const</span><span class="o">::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::{lambda(folly::fibers::Promise<int>)#1}, void>::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"><</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">()::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const::{</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"><</span><span class="no">int</span><span class="o">>)</span><span class="c">#1}>(folly::fibers::Promise<int>&&)::{lambda()#1}) + 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<FiberManager_collectAll_Test::TestBody()::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const:</span>
-<span class="o">:{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda(folly::fibers::Promise<int>)#1}, void>::type::value_type folly::fibers::await<FiberManager_collectAll_Test::TestBo</span>
-<span class="nf" data-symbol-name="dy">dy</span><span class="o">()::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const::{lambda(folly::fibers::Promise<int>)#1}>(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"><</span><span class="no">int</span><span class="o">>&&)</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()::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const::{lambda()#1}::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<int (), FiberManager_collectAll_Test::TestBody()::{lambda()#1}::operator()() const::{lambda()#1}::operator()() c</span>
-<span class="nc" data-symbol-name="onst">onst</span><span class="o">::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}>::_M_invoke(std::_Any_data const&) + 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<int ()>::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
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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_);
-}
-}
-}
+++ /dev/null
-/*
- * 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"
+++ /dev/null
-/*
- * 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();
- }
-}
-}
-}
+++ /dev/null
-/*
- * 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_;
-};
-}
-}
+++ /dev/null
-/*
- * 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]);
-}
-}
-}
+++ /dev/null
-/*
- * 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>
+++ /dev/null
-# 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
+++ /dev/null
-/*
- * 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());
- }
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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();
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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
--- /dev/null
+/*
+ * 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);
+}
+}
+}
--- /dev/null
+/*
+ * 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>
--- /dev/null
+/*
+ * 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));
+ }
+}
+}
+}
--- /dev/null
+/*
+ * 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_);
+ }
+}
+}
+}
--- /dev/null
+/*
+ * 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>
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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"
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+}
+}
+}
--- /dev/null
+/*
+ * 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>
--- /dev/null
+/*
+ * 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));
+}
+}
+}
--- /dev/null
+/*
+ * 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;
+}
+}
+}
--- /dev/null
+/*
+ * 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"
--- /dev/null
+/*
+ * 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);
+}
+}
+}
--- /dev/null
+/*
+ * 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());
+}
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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>
--- /dev/null
+/*
+ * 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;
+}
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+}
+}
+}
--- /dev/null
+/*
+ * 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>
--- /dev/null
+<!-- 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">&</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">([&]()</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"><<</span> <span class="s2">"Task 1: start"</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="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"><<</span> <span class="s2">"Task 1: after baton.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="endl">endl</span><span class="o">;</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">{</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"><<</span> <span class="s2">"Task 2: start"</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="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"><<</span> <span class="s2">"Task 2: after baton.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="endl">endl</span><span class="o">;</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="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'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'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's own threads and distributing load between them</li>
+<li>We don'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'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'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">&</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">([&]()</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"><<</span> <span class="s2">"Task: start"</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="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"><<</span> <span class="s2">"Task: after baton.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="endl">endl</span><span class="o">;</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="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"><<</span> <span class="s2">"Baton posted"</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="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'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"><</span><span class="nf" data-symbol-name="void">void</span><span class="o">(</span><span class="no">Response</span><span class="o">)></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">{</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">[&](</span><span class="no">Response</span> <span class="no">r</span><span class="o">)</span> <span class="no">mutable</span> <span class="o">{</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">});</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">}</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">{</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">([&](</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"><</span><span class="no">Response</span><span class="o">></span> <span class="no">promise</span><span class="o">)</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">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">{</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">});</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">}</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 'fibers::await' 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">{</span>
+ <span class="o">...</span>
+ <span class="k">try</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">([&](</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"><</span><span class="no">Response</span><span class="o">></span> <span class="no">promise</span><span class="o">)</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">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">{</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">"Await will re-throw me"</span><span class="o">));</span>
+ <span class="o">});</span>
+ <span class="o">});</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">}</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">&</span> <span class="no">e</span><span class="o">)</span> <span class="o">{</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">"Await will re-throw me"</span><span class="o">);</span>
+ <span class="o">}</span>
+ <span class="o">...</span>
+<span class="o">}</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">{</span>
+ <span class="o">...</span>
+ <span class="k">try</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">([&](</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"><</span><span class="no">Response</span><span class="o">></span> <span class="no">promise</span><span class="o">)</span> <span class="o">{</span>
+ <span class="c">// We forget about the promise</span>
+ <span class="o">});</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">}</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">&</span> <span class="no">e</span><span class="o">)</span> <span class="o">{</span>
+ <span class="o">...</span>
+ <span class="o">}</span>
+ <span class="o">...</span>
+<span class="o">}</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'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'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"><</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="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">{</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">}</span></pre></div>
+
+<p>Calling <tt>get()</tt> on a <tt>folly::Future</tt> object will only suspend the calling fiber-task. It won'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">{</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">([&](</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"><</span><span class="no">Response</span><span class="o">></span> <span class="no">promise</span><span class="o">)</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">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">{</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">});</span>
+ <span class="o">});</span>
+<span class="o">}</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">{</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">});</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">{</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">...</span></pre></div>
+
+<p>Fibers-compatible APIs look more like synchronous APIs, so you can actually pass input arguments by reference and you don'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">{</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">});</span></pre></div>
+
+<p>Same logic applies to <tt>fibers::await</tt>. Since <tt>fibers::await</tt> call blocks until promise is fulfilled, it'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">{</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">}</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">{</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">([&]()</span> <span class="o">{</span>
+ <span class="k">return</span> <span class="nf" data-symbol-name="useALotOfStack">useALotOfStack</span><span class="o">();</span>
+ <span class="o">});</span>
+ <span class="o">...</span>
+<span class="o">});</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'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'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">&</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">([&]()</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="lock_guard">lock_guard</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="o">></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">});</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">{</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"><</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">></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">});</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'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'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">{</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"><</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">>();</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"><</span><span class="no">Result</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">{</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">-></span><span class="na" data-symbol-name="post">post</span><span class="o">();</span>
+ <span class="o">});</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">{</span><span class="mi">10</span><span class="o">});</span>
+ <span class="k">if</span> <span class="o">(</span><span class="no">success</span><span class="o">)</span> <span class="o">{</span>
+ <span class="c">// request successful</span>
+ <span class="o">...</span>
+ <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
+ <span class="c">// handle timeout</span>
+ <span class="o">...</span>
+ <span class="o">}</span>
+<span class="o">});</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'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">{</span>
+ <span class="o">...</span>
+<span class="o">});</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">&</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">([&]()</span> <span class="o">{</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">{</span>
+ <span class="o">...</span>
+ <span class="o">});</span>
+<span class="o">});</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">{</span>
+ <span class="o">...</span>
+ <span class="k">return</span> <span class="no">result</span><span class="o">;</span>
+ <span class="o">},</span>
+ <span class="o">[=](</span><span class="no">Result</span><span class="o">&&</span> <span class="no">result</span><span class="o">)</span> <span class="o">{</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">}</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">{</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">([&]()</span> <span class="o">{</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">}</span>
+ <span class="c">// Switched back to fiber context</span>
+
+ <span class="c">// On fiber context we realize there's no more work to be done.</span>
+ <span class="c">// Fiber-task is complete, switching back to main context.</span>
+<span class="o">});</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">&</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">([&]()</span> <span class="o">{</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">{</span>
+ <span class="o">...</span>
+ <span class="o">});</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">{</span>
+ <span class="o">...</span>
+ <span class="o">});</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">});</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'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't implement it'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">&</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">{</span>
+ <span class="o">...</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></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't support killing fiber-tasks in-flight (for similar reasons you can'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">'folly/fibers/scripts/utils.gdb'</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">-></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">-></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">&</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)::{lambda()#1}::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<folly::fibers::FirstArgOf<FiberManager_collectAll_Test::TestBody()::{lambda()#1}::operator()() con</span>
+<span class="nc" data-symbol-name="st">st</span><span class="o">::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::{lambda(folly::fibers::Promise<int>)#1}, void>::type::value_type folly::fibers::await</span>
+<span class="o"><</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">()::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const::{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"><</span><span class="no">int</span><span class="o">>)</span><span class="c">#1}>(folly::fibers::Promise<int>&&)::{lambda()#1}>(folly::fibers::FiberManager&, folly::fibers::FirstArgOf<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">()::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const::{lambda(folly::fibers::Promise<int>)#1}</span>
+<span class="o">,</span> <span class="no">void</span><span class="o">>::</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"><</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">()::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::</span>
+<span class="o">{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda(folly::fibers::Promise<int>)#1}>(folly::fibers::Promise<int>&&)::{lambda()#1}) + 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<folly::fibers::FirstArgOf<FiberManager_collectAll_Test::TestBody()::{lambda()#1}::operator()() const::{</span>
+<span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::{lambda(folly::fibers::Promise<int>)#1}, void>::type::value_type folly::fibers::await<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">()::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const::{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"><</span><span class="no">int</span><span class="o">>)</span><span class="c">#1}>(folly::fibers::Promise<int>&&)::{lambda()#1}>(folly::fibers::FirstArgOf<FiberManager_collectAll_Test::TestBody()::{lambda()#1}::opera</span>
+<span class="nf" data-symbol-name="tor">tor</span><span class="o">()()</span> <span class="k">const</span><span class="o">::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::{lambda(folly::fibers::Promise<int>)#1}, void>::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"><</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">()::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const::{</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"><</span><span class="no">int</span><span class="o">>)</span><span class="c">#1}>(folly::fibers::Promise<int>&&)::{lambda()#1}) + 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<FiberManager_collectAll_Test::TestBody()::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const:</span>
+<span class="o">:{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda(folly::fibers::Promise<int>)#1}, void>::type::value_type folly::fibers::await<FiberManager_collectAll_Test::TestBo</span>
+<span class="nf" data-symbol-name="dy">dy</span><span class="o">()::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}::operator()() const::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const::{lambda(folly::fibers::Promise<int>)#1}>(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"><</span><span class="no">int</span><span class="o">>&&)</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()::{lambda()#1}::operator()() const::{lambda()#1}::operator()() const::{lambda()#1}::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<int (), FiberManager_collectAll_Test::TestBody()::{lambda()#1}::operator()() const::{lambda()#1}::operator()() c</span>
+<span class="nc" data-symbol-name="onst">onst</span><span class="o">::{</span><span class="nf" data-symbol-name="lambda">lambda</span><span class="o">()</span><span class="c">#1}>::_M_invoke(std::_Any_data const&) + 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<int ()>::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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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_);
+}
+}
+}
--- /dev/null
+/*
+ * 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"
--- /dev/null
+/*
+ * 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();
+ }
+}
+}
+}
--- /dev/null
+/*
+ * 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_;
+};
+}
+}
--- /dev/null
+/*
+ * 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]);
+}
+}
+}
--- /dev/null
+/*
+ * 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>
--- /dev/null
+# 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
--- /dev/null
+/*
+ * 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());
+ }
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
#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 {