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 @@ ...@@ -24,7 +24,6 @@
#include <folly/experimental/coro/CurrentExecutor.h> #include <folly/experimental/coro/CurrentExecutor.h>
#include <folly/experimental/coro/Invoke.h> #include <folly/experimental/coro/Invoke.h>
#include <folly/experimental/coro/Result.h> #include <folly/experimental/coro/Result.h>
#include <folly/experimental/coro/Utils.h>
#include <folly/experimental/coro/ViaIfAsync.h> #include <folly/experimental/coro/ViaIfAsync.h>
#include <folly/experimental/coro/WithAsyncStack.h> #include <folly/experimental/coro/WithAsyncStack.h>
#include <folly/experimental/coro/WithCancellation.h> #include <folly/experimental/coro/WithCancellation.h>
...@@ -146,12 +145,12 @@ class AsyncGeneratorPromise { ...@@ -146,12 +145,12 @@ class AsyncGeneratorPromise {
} }
} }
AwaitableVariant<YieldAwaiter, AwaitableReady<void>> await_transform( variant_awaitable<YieldAwaiter, ready_awaitable<>> await_transform(
co_safe_point_t) noexcept { co_safe_point_t) noexcept {
if (cancelToken_.isCancellationRequested()) { if (cancelToken_.isCancellationRequested()) {
return yield_value(co_cancelled); return yield_value(co_cancelled);
} }
return AwaitableReady<void>{}; return ready_awaitable<>{};
} }
void unhandled_exception() noexcept { void unhandled_exception() noexcept {
...@@ -174,11 +173,11 @@ class AsyncGeneratorPromise { ...@@ -174,11 +173,11 @@ class AsyncGeneratorPromise {
} }
auto await_transform(folly::coro::co_current_executor_t) noexcept { 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 { 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 { void setCancellationToken(folly::CancellationToken cancelToken) noexcept {
......
...@@ -16,7 +16,14 @@ ...@@ -16,7 +16,14 @@
#pragma once #pragma once
#include <type_traits>
#if __has_include(<variant>)
#include <variant>
#endif
#include <folly/Portability.h> #include <folly/Portability.h>
#include <folly/Utility.h>
#if FOLLY_HAS_COROUTINES #if FOLLY_HAS_COROUTINES
...@@ -24,6 +31,14 @@ ...@@ -24,6 +31,14 @@
#endif // FOLLY_HAS_COROUTINES #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 #if FOLLY_HAS_COROUTINES
namespace folly::coro { namespace folly::coro {
...@@ -36,6 +51,111 @@ using std::experimental::noop_coroutine_promise; ...@@ -36,6 +51,111 @@ using std::experimental::noop_coroutine_promise;
using std::experimental::suspend_always; using std::experimental::suspend_always;
using std::experimental::suspend_never; 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 } // namespace folly::coro
#endif #endif
...@@ -32,7 +32,6 @@ ...@@ -32,7 +32,6 @@
#include <folly/experimental/coro/Invoke.h> #include <folly/experimental/coro/Invoke.h>
#include <folly/experimental/coro/Result.h> #include <folly/experimental/coro/Result.h>
#include <folly/experimental/coro/Traits.h> #include <folly/experimental/coro/Traits.h>
#include <folly/experimental/coro/Utils.h>
#include <folly/experimental/coro/ViaIfAsync.h> #include <folly/experimental/coro/ViaIfAsync.h>
#include <folly/experimental/coro/WithAsyncStack.h> #include <folly/experimental/coro/WithAsyncStack.h>
#include <folly/experimental/coro/WithCancellation.h> #include <folly/experimental/coro/WithCancellation.h>
...@@ -79,12 +78,12 @@ class TaskPromiseBase { ...@@ -79,12 +78,12 @@ class TaskPromiseBase {
TaskPromiseBase() noexcept {} TaskPromiseBase() noexcept {}
template <typename Promise> template <typename Promise>
AwaitableVariant<FinalAwaiter, AwaitableReady<void>> do_safe_point( variant_awaitable<FinalAwaiter, ready_awaitable<>> do_safe_point(
Promise& promise) noexcept { Promise& promise) noexcept {
if (cancelToken_.isCancellationRequested()) { if (cancelToken_.isCancellationRequested()) {
return promise.yield_value(co_cancelled); return promise.yield_value(co_cancelled);
} }
return AwaitableReady<void>{}; return ready_awaitable<>{};
} }
public: public:
...@@ -109,11 +108,11 @@ class TaskPromiseBase { ...@@ -109,11 +108,11 @@ class TaskPromiseBase {
} }
auto await_transform(co_current_executor_t) noexcept { 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 { 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 { 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 @@ ...@@ -18,7 +18,7 @@
#include <folly/Portability.h> #include <folly/Portability.h>
#include <folly/experimental/coro/BlockingWait.h> #include <folly/experimental/coro/BlockingWait.h>
#include <folly/experimental/coro/Utils.h> #include <folly/experimental/coro/Coroutine.h>
#include <string> #include <string>
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
BENCHMARK(blockingWaitRVOInt, iters) { BENCHMARK(blockingWaitRVOInt, iters) {
for (size_t iter = 0; iter < iters; ++iter) { for (size_t iter = 0; iter < iters; ++iter) {
auto result = auto result =
folly::coro::blockingWait(folly::coro::AwaitableReady<int>(42)); folly::coro::blockingWait(folly::coro::ready_awaitable<int>(42));
if (result != 42) { if (result != 42) {
std::abort(); std::abort();
} }
...@@ -41,7 +41,7 @@ constexpr folly::StringPiece longString = ...@@ -41,7 +41,7 @@ constexpr folly::StringPiece longString =
BENCHMARK(blockingWaitRVOStrings, iters) { BENCHMARK(blockingWaitRVOStrings, iters) {
for (size_t iter = 0; iter < iters; ++iter) { for (size_t iter = 0; iter < iters; ++iter) {
auto result = folly::coro::blockingWait( 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()) { if (result.size() != longString.size()) {
std::abort(); std::abort();
} }
...@@ -66,7 +66,7 @@ struct Matrix { ...@@ -66,7 +66,7 @@ struct Matrix {
}; };
BENCHMARK(blockingWaitRVO, iters) { BENCHMARK(blockingWaitRVO, iters) {
folly::coro::AwaitableReady<Matrix> identityAwaitable{IdentityMatrix{}}; folly::coro::ready_awaitable<Matrix> identityAwaitable{IdentityMatrix{}};
for (size_t iter = 0; iter < iters; ++iter) { for (size_t iter = 0; iter < iters; ++iter) {
auto result = folly::coro::blockingWait(identityAwaitable); auto result = folly::coro::blockingWait(identityAwaitable);
if (result.values_[3][3] != 1) { if (result.values_[3][3] != 1) {
......
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
#include <folly/executors/ManualExecutor.h> #include <folly/executors/ManualExecutor.h>
#include <folly/experimental/coro/Baton.h> #include <folly/experimental/coro/Baton.h>
#include <folly/experimental/coro/BlockingWait.h> #include <folly/experimental/coro/BlockingWait.h>
#include <folly/experimental/coro/Coroutine.h>
#include <folly/experimental/coro/Invoke.h> #include <folly/experimental/coro/Invoke.h>
#include <folly/experimental/coro/Utils.h>
#include <folly/fibers/FiberManager.h> #include <folly/fibers/FiberManager.h>
#include <folly/fibers/FiberManagerMap.h> #include <folly/fibers/FiberManagerMap.h>
#include <folly/portability/GTest.h> #include <folly/portability/GTest.h>
...@@ -35,25 +35,25 @@ ...@@ -35,25 +35,25 @@
static_assert( static_assert(
std::is_same< std::is_same<
decltype(folly::coro::blockingWait( decltype(folly::coro::blockingWait(
std::declval<folly::coro::AwaitableReady<void>>())), std::declval<folly::coro::ready_awaitable<>>())),
void>::value, void>::value,
""); "");
static_assert( static_assert(
std::is_same< std::is_same<
decltype(folly::coro::blockingWait( decltype(folly::coro::blockingWait(
std::declval<folly::coro::AwaitableReady<int>>())), std::declval<folly::coro::ready_awaitable<int>>())),
int>::value, int>::value,
""); "");
static_assert( static_assert(
std::is_same< std::is_same<
decltype(folly::coro::blockingWait( decltype(folly::coro::blockingWait(
std::declval<folly::coro::AwaitableReady<int&>>())), std::declval<folly::coro::ready_awaitable<int&>>())),
int&>::value, int&>::value,
""); "");
static_assert( static_assert(
std::is_same< std::is_same<
decltype(folly::coro::blockingWait( decltype(folly::coro::blockingWait(
std::declval<folly::coro::AwaitableReady<int&&>>())), std::declval<folly::coro::ready_awaitable<int&&>>())),
int>::value, int>::value,
"blockingWait() should convert rvalue-reference-returning awaitables " "blockingWait() should convert rvalue-reference-returning awaitables "
"into a returned prvalue to avoid potential lifetime issues since " "into a returned prvalue to avoid potential lifetime issues since "
...@@ -64,22 +64,22 @@ static_assert( ...@@ -64,22 +64,22 @@ static_assert(
class BlockingWaitTest : public testing::Test {}; class BlockingWaitTest : public testing::Test {};
TEST_F(BlockingWaitTest, SynchronousCompletionVoidResult) { TEST_F(BlockingWaitTest, SynchronousCompletionVoidResult) {
folly::coro::blockingWait(folly::coro::AwaitableReady<void>{}); folly::coro::blockingWait(folly::coro::ready_awaitable<>{});
} }
TEST_F(BlockingWaitTest, SynchronousCompletionPRValueResult) { TEST_F(BlockingWaitTest, SynchronousCompletionPRValueResult) {
EXPECT_EQ( EXPECT_EQ(
123, folly::coro::blockingWait(folly::coro::AwaitableReady<int>{123})); 123, folly::coro::blockingWait(folly::coro::ready_awaitable<int>{123}));
EXPECT_EQ( EXPECT_EQ(
"hello", "hello",
folly::coro::blockingWait( folly::coro::blockingWait(
folly::coro::AwaitableReady<std::string>("hello"))); folly::coro::ready_awaitable<std::string>("hello")));
} }
TEST_F(BlockingWaitTest, SynchronousCompletionLValueResult) { TEST_F(BlockingWaitTest, SynchronousCompletionLValueResult) {
int value = 123; int value = 123;
int& result = 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(&value, &result);
EXPECT_EQ(123, result); EXPECT_EQ(123, result);
} }
...@@ -91,7 +91,7 @@ TEST_F(BlockingWaitTest, SynchronousCompletionRValueResult) { ...@@ -91,7 +91,7 @@ TEST_F(BlockingWaitTest, SynchronousCompletionRValueResult) {
// Should return a prvalue which will lifetime-extend when assigned to an // Should return a prvalue which will lifetime-extend when assigned to an
// auto&& local variable. // auto&& local variable.
auto&& result = folly::coro::blockingWait( 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_EQ(ptr, result.get());
EXPECT_FALSE(p); EXPECT_FALSE(p);
...@@ -203,11 +203,11 @@ TEST_F(BlockingWaitTest, WaitOnMoveOnlyAsyncPromise) { ...@@ -203,11 +203,11 @@ TEST_F(BlockingWaitTest, WaitOnMoveOnlyAsyncPromise) {
} }
TEST_F(BlockingWaitTest, moveCountingAwaitableReady) { TEST_F(BlockingWaitTest, moveCountingAwaitableReady) {
folly::coro::AwaitableReady<MoveCounting> awaitable{MoveCounting{}}; folly::coro::ready_awaitable<MoveCounting> awaitable{MoveCounting{}};
auto result = folly::coro::blockingWait(awaitable); auto result = folly::coro::blockingWait(awaitable);
// Moves: // Moves:
// 1. Move value into AwaitableReady // 1. Move value into ready_awaitable
// 2. Move value to await_resume() return-value // 2. Move value to await_resume() return-value
// 3. Move value to Try<T> // 3. Move value to Try<T>
// 4. Move value to blockingWait() return-value // 4. Move value to blockingWait() return-value
......
...@@ -22,13 +22,13 @@ ...@@ -22,13 +22,13 @@
#include <folly/experimental/coro/Baton.h> #include <folly/experimental/coro/Baton.h>
#include <folly/experimental/coro/BlockingWait.h> #include <folly/experimental/coro/BlockingWait.h>
#include <folly/experimental/coro/Collect.h> #include <folly/experimental/coro/Collect.h>
#include <folly/experimental/coro/Coroutine.h>
#include <folly/experimental/coro/CurrentExecutor.h> #include <folly/experimental/coro/CurrentExecutor.h>
#include <folly/experimental/coro/DetachOnCancel.h> #include <folly/experimental/coro/DetachOnCancel.h>
#include <folly/experimental/coro/Invoke.h> #include <folly/experimental/coro/Invoke.h>
#include <folly/experimental/coro/Sleep.h> #include <folly/experimental/coro/Sleep.h>
#include <folly/experimental/coro/Task.h> #include <folly/experimental/coro/Task.h>
#include <folly/experimental/coro/TimedWait.h> #include <folly/experimental/coro/TimedWait.h>
#include <folly/experimental/coro/Utils.h>
#include <folly/experimental/coro/WithCancellation.h> #include <folly/experimental/coro/WithCancellation.h>
#include <folly/fibers/Semaphore.h> #include <folly/fibers/Semaphore.h>
#include <folly/futures/Future.h> #include <folly/futures/Future.h>
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include <folly/Range.h> #include <folly/Range.h>
#include <folly/SocketAddress.h> #include <folly/SocketAddress.h>
#include <folly/experimental/coro/Task.h> #include <folly/experimental/coro/Task.h>
#include <folly/experimental/coro/Utils.h>
#include <folly/io/IOBufQueue.h> #include <folly/io/IOBufQueue.h>
#include <folly/io/async/AsyncSocket.h> #include <folly/io/async/AsyncSocket.h>
#include <folly/io/async/AsyncTimeout.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