Commit bb168bd8 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

move ready_awaitable, variant_awaitable to coroutine header

Summary:
Move `ready_awaitable`, `variant_awaitable` types to `folly/experimental/coro/Coroutine.h`, which reexports the foundational vocabulary coroutine helper types and is a suitable place for these vocabulary coroutine helper types.

`ready_awaitable` is an awaitable which is ready and has a value or, if of void, which is just ready. `variant_awaitable` is an awaitable composed of one of several possible backing awaitables.

Reviewed By: aary

Differential Revision: D26270528

fbshipit-source-id: e98f64b3d3ffaf5bbd322397a41bb5fe85a6bb55
parent 4e249e08
......@@ -24,7 +24,6 @@
#include <folly/experimental/coro/CurrentExecutor.h>
#include <folly/experimental/coro/Invoke.h>
#include <folly/experimental/coro/Result.h>
#include <folly/experimental/coro/Utils.h>
#include <folly/experimental/coro/ViaIfAsync.h>
#include <folly/experimental/coro/WithAsyncStack.h>
#include <folly/experimental/coro/WithCancellation.h>
......@@ -146,12 +145,12 @@ class AsyncGeneratorPromise {
}
}
AwaitableVariant<YieldAwaiter, AwaitableReady<void>> await_transform(
variant_awaitable<YieldAwaiter, ready_awaitable<>> await_transform(
co_safe_point_t) noexcept {
if (cancelToken_.isCancellationRequested()) {
return yield_value(co_cancelled);
}
return AwaitableReady<void>{};
return ready_awaitable<>{};
}
void unhandled_exception() noexcept {
......@@ -174,11 +173,11 @@ class AsyncGeneratorPromise {
}
auto await_transform(folly::coro::co_current_executor_t) noexcept {
return AwaitableReady<folly::Executor*>{executor_.get()};
return ready_awaitable<folly::Executor*>{executor_.get()};
}
auto await_transform(folly::coro::co_current_cancellation_token_t) noexcept {
return AwaitableReady<const folly::CancellationToken&>{cancelToken_};
return ready_awaitable<const folly::CancellationToken&>{cancelToken_};
}
void setCancellationToken(folly::CancellationToken cancelToken) noexcept {
......
......@@ -16,7 +16,14 @@
#pragma once
#include <type_traits>
#if __has_include(<variant>)
#include <variant>
#endif
#include <folly/Portability.h>
#include <folly/Utility.h>
#if FOLLY_HAS_COROUTINES
......@@ -24,6 +31,14 @@
#endif // FOLLY_HAS_COROUTINES
// A place for foundational vocabulary types.
//
// This header reexports the foundational vocabulary coroutine-helper types
// from the standard, and exports several new foundational vocabulary types
// as well.
//
// Types which are non-foundational and non-vocabulary should go elsewhere.
#if FOLLY_HAS_COROUTINES
namespace folly::coro {
......@@ -36,6 +51,111 @@ using std::experimental::noop_coroutine_promise;
using std::experimental::suspend_always;
using std::experimental::suspend_never;
// ready_awaitable
//
// An awaitable which is immediately ready with a value. Suspension is no-op.
// Resumption returns the value.
//
// The value type is permitted to be a reference.
template <typename T = void>
class ready_awaitable {
static_assert(!std::is_void<T>::value, "base template unsuitable for void");
public:
explicit ready_awaitable(T value) //
noexcept(noexcept(T(FOLLY_DECLVAL(T&&))))
: value_(static_cast<T&&>(value)) {}
bool await_ready() noexcept { return true; }
void await_suspend(coroutine_handle<>) noexcept {}
T await_resume() noexcept(noexcept(T(FOLLY_DECLVAL(T&&)))) {
return static_cast<T&&>(value_);
}
private:
T value_;
};
// ready_awaitable
//
// An awaitable type which is immediately ready. Suspension is a no-op.
template <>
class ready_awaitable<void> {
public:
ready_awaitable() noexcept = default;
bool await_ready() noexcept { return true; }
void await_suspend(coroutine_handle<>) noexcept {}
void await_resume() noexcept {}
};
namespace detail {
// await_suspend_return_coroutine_fn
// await_suspend_return_coroutine
//
// The special member await_suspend has three forms, differing in their return
// types. It may return void, bool, or coroutine_handle<>. This invokes member
// await_suspend on the argument, conspiring always to return coroutine_handle
// no matter the underlying form of member await_suspend on the argument.
struct await_suspend_return_coroutine_fn {
template <typename A, typename P>
coroutine_handle<> operator()(A& a, coroutine_handle<P> coro) const
noexcept(noexcept(a.await_suspend(coro))) {
using result = decltype(a.await_suspend(coro));
if constexpr (std::is_same<void, result>::value) {
a.await_suspend(coro);
return noop_coroutine();
} else if constexpr (std::is_same<bool, result>::value) {
return a.await_suspend(coro) ? noop_coroutine() : coro;
} else {
return a.await_suspend(coro);
}
}
};
inline constexpr await_suspend_return_coroutine_fn
await_suspend_return_coroutine{};
} // namespace detail
#if __has_include(<variant>)
// variant_awaitable
//
// An awaitable type which is backed by one of several possible underlying
// awaitables.
template <typename... A>
class variant_awaitable : private std::variant<A...> {
private:
using base = std::variant<A...>;
template <typename Visitor>
auto visit(Visitor v) {
return std::visit(v, static_cast<base&>(*this));
}
public:
// imports the base-class constructors wholesale for implementation simplicity
using base::base; // assume there are no valueless-by-exception instances
auto await_ready() noexcept(
(noexcept(FOLLY_DECLVAL(A&).await_ready()) && ...)) {
return visit([&](auto& a) { return a.await_ready(); });
}
template <typename P>
auto await_suspend(coroutine_handle<P> coro) noexcept(
(noexcept(FOLLY_DECLVAL(A&).await_suspend(coro)) && ...)) {
auto impl = detail::await_suspend_return_coroutine;
return visit([&](auto& a) { return impl(a, coro); });
}
auto await_resume() noexcept(
(noexcept(FOLLY_DECLVAL(A&).await_resume()) && ...)) {
return visit([&](auto& a) { return a.await_resume(); });
}
};
#endif // __has_include(<variant>)
} // namespace folly::coro
#endif
......@@ -32,7 +32,6 @@
#include <folly/experimental/coro/Invoke.h>
#include <folly/experimental/coro/Result.h>
#include <folly/experimental/coro/Traits.h>
#include <folly/experimental/coro/Utils.h>
#include <folly/experimental/coro/ViaIfAsync.h>
#include <folly/experimental/coro/WithAsyncStack.h>
#include <folly/experimental/coro/WithCancellation.h>
......@@ -79,12 +78,12 @@ class TaskPromiseBase {
TaskPromiseBase() noexcept {}
template <typename Promise>
AwaitableVariant<FinalAwaiter, AwaitableReady<void>> do_safe_point(
variant_awaitable<FinalAwaiter, ready_awaitable<>> do_safe_point(
Promise& promise) noexcept {
if (cancelToken_.isCancellationRequested()) {
return promise.yield_value(co_cancelled);
}
return AwaitableReady<void>{};
return ready_awaitable<>{};
}
public:
......@@ -109,11 +108,11 @@ class TaskPromiseBase {
}
auto await_transform(co_current_executor_t) noexcept {
return AwaitableReady<folly::Executor*>{executor_.get()};
return ready_awaitable<folly::Executor*>{executor_.get()};
}
auto await_transform(co_current_cancellation_token_t) noexcept {
return AwaitableReady<const folly::CancellationToken&>{cancelToken_};
return ready_awaitable<const folly::CancellationToken&>{cancelToken_};
}
void setCancelToken(const folly::CancellationToken& cancelToken) noexcept {
......
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* 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 <type_traits>
#include <variant>
#include <folly/Utility.h>
#include <folly/experimental/coro/Coroutine.h>
#if FOLLY_HAS_COROUTINES
namespace folly {
namespace coro {
template <typename T>
class AwaitableReady {
public:
explicit AwaitableReady(T value) noexcept(
std::is_nothrow_move_constructible<T>::value)
: value_(static_cast<T&&>(value)) {}
bool await_ready() noexcept { return true; }
void await_suspend(coroutine_handle<>) noexcept {}
T await_resume() noexcept(std::is_nothrow_move_constructible<T>::value) {
return static_cast<T&&>(value_);
}
private:
T value_;
};
template <>
class AwaitableReady<void> {
public:
AwaitableReady() noexcept = default;
bool await_ready() noexcept { return true; }
void await_suspend(coroutine_handle<>) noexcept {}
void await_resume() noexcept {}
};
namespace detail {
struct await_suspend_return_coroutine_fn {
template <typename A, typename P>
coroutine_handle<> operator()(A& a, coroutine_handle<P> coro) const
noexcept(noexcept(a.await_suspend(coro))) {
using result = decltype(a.await_suspend(coro));
if constexpr (std::is_same_v<void, result>) {
a.await_suspend(coro);
return noop_coroutine();
} else if constexpr (std::is_same_v<bool, result>) {
return a.await_suspend(coro) ? noop_coroutine() : coro;
} else {
return a.await_suspend(coro);
}
}
};
inline constexpr await_suspend_return_coroutine_fn
await_suspend_return_coroutine{};
template <typename... A>
class AwaitableVariant : private std::variant<A...> {
private:
using base = std::variant<A...>;
template <typename Visitor>
auto visit(Visitor v) {
return std::visit(v, static_cast<base&>(*this));
}
public:
using base::base; // assume there are no valueless-by-exception instances
auto await_ready() noexcept(
(noexcept(FOLLY_DECLVAL(A&).await_ready()) && ...)) {
return visit([&](auto& a) { return a.await_ready(); });
}
template <typename P>
auto await_suspend(coroutine_handle<P> coro) noexcept(
(noexcept(FOLLY_DECLVAL(A&).await_suspend(coro)) && ...)) {
auto impl = await_suspend_return_coroutine;
return visit([&](auto& a) { return impl(a, coro); });
}
auto await_resume() noexcept(
(noexcept(FOLLY_DECLVAL(A&).await_resume()) && ...)) {
return visit([&](auto& a) { return a.await_resume(); });
}
};
} // namespace detail
} // namespace coro
} // namespace folly
#endif // FOLLY_HAS_COROUTINES
......@@ -18,7 +18,7 @@
#include <folly/Portability.h>
#include <folly/experimental/coro/BlockingWait.h>
#include <folly/experimental/coro/Utils.h>
#include <folly/experimental/coro/Coroutine.h>
#include <string>
......@@ -27,7 +27,7 @@
BENCHMARK(blockingWaitRVOInt, iters) {
for (size_t iter = 0; iter < iters; ++iter) {
auto result =
folly::coro::blockingWait(folly::coro::AwaitableReady<int>(42));
folly::coro::blockingWait(folly::coro::ready_awaitable<int>(42));
if (result != 42) {
std::abort();
}
......@@ -41,7 +41,7 @@ constexpr folly::StringPiece longString =
BENCHMARK(blockingWaitRVOStrings, iters) {
for (size_t iter = 0; iter < iters; ++iter) {
auto result = folly::coro::blockingWait(
folly::coro::AwaitableReady<std::string>(longString.str()));
folly::coro::ready_awaitable<std::string>(longString.str()));
if (result.size() != longString.size()) {
std::abort();
}
......@@ -66,7 +66,7 @@ struct Matrix {
};
BENCHMARK(blockingWaitRVO, iters) {
folly::coro::AwaitableReady<Matrix> identityAwaitable{IdentityMatrix{}};
folly::coro::ready_awaitable<Matrix> identityAwaitable{IdentityMatrix{}};
for (size_t iter = 0; iter < iters; ++iter) {
auto result = folly::coro::blockingWait(identityAwaitable);
if (result.values_[3][3] != 1) {
......
......@@ -21,8 +21,8 @@
#include <folly/executors/ManualExecutor.h>
#include <folly/experimental/coro/Baton.h>
#include <folly/experimental/coro/BlockingWait.h>
#include <folly/experimental/coro/Coroutine.h>
#include <folly/experimental/coro/Invoke.h>
#include <folly/experimental/coro/Utils.h>
#include <folly/fibers/FiberManager.h>
#include <folly/fibers/FiberManagerMap.h>
#include <folly/portability/GTest.h>
......@@ -35,25 +35,25 @@
static_assert(
std::is_same<
decltype(folly::coro::blockingWait(
std::declval<folly::coro::AwaitableReady<void>>())),
std::declval<folly::coro::ready_awaitable<>>())),
void>::value,
"");
static_assert(
std::is_same<
decltype(folly::coro::blockingWait(
std::declval<folly::coro::AwaitableReady<int>>())),
std::declval<folly::coro::ready_awaitable<int>>())),
int>::value,
"");
static_assert(
std::is_same<
decltype(folly::coro::blockingWait(
std::declval<folly::coro::AwaitableReady<int&>>())),
std::declval<folly::coro::ready_awaitable<int&>>())),
int&>::value,
"");
static_assert(
std::is_same<
decltype(folly::coro::blockingWait(
std::declval<folly::coro::AwaitableReady<int&&>>())),
std::declval<folly::coro::ready_awaitable<int&&>>())),
int>::value,
"blockingWait() should convert rvalue-reference-returning awaitables "
"into a returned prvalue to avoid potential lifetime issues since "
......@@ -64,22 +64,22 @@ static_assert(
class BlockingWaitTest : public testing::Test {};
TEST_F(BlockingWaitTest, SynchronousCompletionVoidResult) {
folly::coro::blockingWait(folly::coro::AwaitableReady<void>{});
folly::coro::blockingWait(folly::coro::ready_awaitable<>{});
}
TEST_F(BlockingWaitTest, SynchronousCompletionPRValueResult) {
EXPECT_EQ(
123, folly::coro::blockingWait(folly::coro::AwaitableReady<int>{123}));
123, folly::coro::blockingWait(folly::coro::ready_awaitable<int>{123}));
EXPECT_EQ(
"hello",
folly::coro::blockingWait(
folly::coro::AwaitableReady<std::string>("hello")));
folly::coro::ready_awaitable<std::string>("hello")));
}
TEST_F(BlockingWaitTest, SynchronousCompletionLValueResult) {
int value = 123;
int& result =
folly::coro::blockingWait(folly::coro::AwaitableReady<int&>{value});
folly::coro::blockingWait(folly::coro::ready_awaitable<int&>{value});
EXPECT_EQ(&value, &result);
EXPECT_EQ(123, result);
}
......@@ -91,7 +91,7 @@ TEST_F(BlockingWaitTest, SynchronousCompletionRValueResult) {
// Should return a prvalue which will lifetime-extend when assigned to an
// auto&& local variable.
auto&& result = folly::coro::blockingWait(
folly::coro::AwaitableReady<std::unique_ptr<int>&&>{std::move(p)});
folly::coro::ready_awaitable<std::unique_ptr<int>&&>{std::move(p)});
EXPECT_EQ(ptr, result.get());
EXPECT_FALSE(p);
......@@ -203,11 +203,11 @@ TEST_F(BlockingWaitTest, WaitOnMoveOnlyAsyncPromise) {
}
TEST_F(BlockingWaitTest, moveCountingAwaitableReady) {
folly::coro::AwaitableReady<MoveCounting> awaitable{MoveCounting{}};
folly::coro::ready_awaitable<MoveCounting> awaitable{MoveCounting{}};
auto result = folly::coro::blockingWait(awaitable);
// Moves:
// 1. Move value into AwaitableReady
// 1. Move value into ready_awaitable
// 2. Move value to await_resume() return-value
// 3. Move value to Try<T>
// 4. Move value to blockingWait() return-value
......
......@@ -22,13 +22,13 @@
#include <folly/experimental/coro/Baton.h>
#include <folly/experimental/coro/BlockingWait.h>
#include <folly/experimental/coro/Collect.h>
#include <folly/experimental/coro/Coroutine.h>
#include <folly/experimental/coro/CurrentExecutor.h>
#include <folly/experimental/coro/DetachOnCancel.h>
#include <folly/experimental/coro/Invoke.h>
#include <folly/experimental/coro/Sleep.h>
#include <folly/experimental/coro/Task.h>
#include <folly/experimental/coro/TimedWait.h>
#include <folly/experimental/coro/Utils.h>
#include <folly/experimental/coro/WithCancellation.h>
#include <folly/fibers/Semaphore.h>
#include <folly/futures/Future.h>
......
......@@ -19,7 +19,6 @@
#include <folly/Range.h>
#include <folly/SocketAddress.h>
#include <folly/experimental/coro/Task.h>
#include <folly/experimental/coro/Utils.h>
#include <folly/io/IOBufQueue.h>
#include <folly/io/async/AsyncSocket.h>
#include <folly/io/async/AsyncTimeout.h>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment