#pragma once
#include <utility>
#include <folly/Executor.h>
#include <folly/experimental/coro/Coroutine.h>
#include <folly/experimental/coro/ViaIfAsync.h>
#include <folly/experimental/coro/WithAsyncStack.h>
#include <folly/io/async/Request.h>
#if FOLLY_HAS_COROUTINES
namespace folly {
namespace coro {
namespace detail {
struct co_current_executor_ {
enum class secret_ { token_ };
explicit constexpr co_current_executor_(secret_) {}
};
}
using co_current_executor_t = detail::co_current_executor_;
inline constexpr co_current_executor_t co_current_executor{
co_current_executor_t::secret_::token_};
namespace detail {
class co_reschedule_on_current_executor_ {
class AwaiterBase {
public:
explicit AwaiterBase(folly::Executor::KeepAlive<> executor) noexcept
: executor_(std::move(executor)) {}
bool await_ready() noexcept { return false; }
void await_resume() noexcept {}
protected:
folly::Executor::KeepAlive<> executor_;
};
public:
class StackAwareAwaiter : public AwaiterBase {
public:
using AwaiterBase::AwaiterBase;
template <typename Promise>
void await_suspend(coroutine_handle<Promise> coro) noexcept {
await_suspend_impl(coro, coro.promise().getAsyncFrame());
}
private:
FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES void await_suspend_impl(
coroutine_handle<> coro, AsyncStackFrame& frame) {
auto& stackRoot = *frame.getStackRoot();
folly::deactivateAsyncStackFrame(frame);
try {
executor_->add(
[coro, &frame, ctx = RequestContext::saveContext()]() mutable {
RequestContextScopeGuard contextScope{std::move(ctx)};
folly::resumeCoroutineWithNewAsyncStackRoot(coro, frame);
});
} catch (...) {
folly::activateAsyncStackFrame(stackRoot, frame);
throw;
}
}
};
class Awaiter : public AwaiterBase {
public:
using AwaiterBase::AwaiterBase;
FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES void await_suspend(
coroutine_handle<> coro) {
executor_->add([coro, ctx = RequestContext::saveContext()]() mutable {
RequestContextScopeGuard contextScope{std::move(ctx)};
coro.resume();
});
}
friend StackAwareAwaiter tag_invoke(
cpo_t<co_withAsyncStack>, Awaiter awaiter) {
return StackAwareAwaiter{std::move(awaiter.executor_)};
}
};
friend Awaiter co_viaIfAsync(
folly::Executor::KeepAlive<> executor,
co_reschedule_on_current_executor_) {
return Awaiter{std::move(executor)};
}
};
}
using co_reschedule_on_current_executor_t =
detail::co_reschedule_on_current_executor_;
inline constexpr co_reschedule_on_current_executor_t
co_reschedule_on_current_executor;
namespace detail {
struct co_current_cancellation_token_ {
enum class secret_ { token_ };
explicit constexpr co_current_cancellation_token_(secret_) {}
};
}
using co_current_cancellation_token_t = detail::co_current_cancellation_token_;
inline constexpr co_current_cancellation_token_t co_current_cancellation_token{
co_current_cancellation_token_t::secret_::token_};
class co_safe_point_t final {};
inline constexpr co_safe_point_t co_safe_point{};
}
}
#endif