Commit be621139 authored by Andrii Grynenko's avatar Andrii Grynenko Committed by Facebook Github Bot

Add co_awaitTry support for folly::Future

Summary: This also makes co_awaitTry to work for any SemiAwaitable that implements await_resume_try in it's awaiter.

Reviewed By: yfeldblum

Differential Revision: D15170076

fbshipit-source-id: 7f6ce0e2fa762acc5bcc4a32ed29d189787bf1ef
parent ec7519ab
...@@ -321,9 +321,7 @@ class FOLLY_NODISCARD Task { ...@@ -321,9 +321,7 @@ class FOLLY_NODISCARD Task {
using StorageType = typename promise_type::StorageType; using StorageType = typename promise_type::StorageType;
private: private:
template <typename ResultCreator>
class Awaiter; class Awaiter;
class TrySemiAwaitable;
using handle_t = std::experimental::coroutine_handle<promise_type>; using handle_t = std::experimental::coroutine_handle<promise_type>;
public: public:
...@@ -364,32 +362,18 @@ class FOLLY_NODISCARD Task { ...@@ -364,32 +362,18 @@ class FOLLY_NODISCARD Task {
}); });
} }
// Returns a SemiAwaitable<folly::Try<T>> type that when co_awaited will
// produce a Try<T> instead of the value T.
//
// eg.
// auto result = co_await std::move(someTask).co_awaitTry();
// if (result.hasValue()) {
// use(result.value());
// }
auto co_awaitTry() && noexcept {
return TrySemiAwaitable{std::exchange(coro_, {})};
}
friend auto co_viaIfAsync( friend auto co_viaIfAsync(
Executor::KeepAlive<> executor, Executor::KeepAlive<> executor,
Task<T>&& t) noexcept { Task<T>&& t) noexcept {
// Child task inherits the awaiting task's executor // Child task inherits the awaiting task's executor
t.coro_.promise().executor_ = std::move(executor); t.coro_.promise().executor_ = std::move(executor);
return Awaiter<typename TaskWithExecutor<T>::ValueCreator>{ return Awaiter{std::exchange(t.coro_, {})};
std::exchange(t.coro_, {})};
} }
private: private:
friend class detail::TaskPromiseBase; friend class detail::TaskPromiseBase;
friend class detail::TaskPromise<T>; friend class detail::TaskPromise<T>;
template <typename ResultCreator>
class Awaiter { class Awaiter {
public: public:
explicit Awaiter(handle_t coro) noexcept : coro_(coro) {} explicit Awaiter(handle_t coro) noexcept : coro_(coro) {}
...@@ -414,49 +398,18 @@ class FOLLY_NODISCARD Task { ...@@ -414,49 +398,18 @@ class FOLLY_NODISCARD Task {
return coro_; return coro_;
} }
decltype(auto) await_resume() { T await_resume() {
return await_resume_try().value();
}
auto await_resume_try() {
SCOPE_EXIT { SCOPE_EXIT {
std::exchange(coro_, {}).destroy(); std::exchange(coro_, {}).destroy();
}; };
return ResultCreator{}(std::move(coro_.promise().result())); return std::move(coro_.promise().result());
}
private:
handle_t coro_;
};
class TrySemiAwaitable {
public:
TrySemiAwaitable(TrySemiAwaitable&& other) noexcept
: coro_(std::exchange(other.coro_, {})) {}
~TrySemiAwaitable() {
if (coro_) {
coro_.destroy();
}
}
TrySemiAwaitable& operator=(TrySemiAwaitable&& other) noexcept {
auto oldCoro = std::exchange(coro_, std::exchange(other.coro_, {}));
if (oldCoro) {
oldCoro.destroy();
}
return *this;
}
friend auto co_viaIfAsync(
Executor::KeepAlive<> executor,
TrySemiAwaitable&& awaitable) noexcept {
awaitable.coro_.promise().executor_ = std::move(executor);
return Awaiter<typename TaskWithExecutor<T>::TryCreator>{
std::exchange(awaitable.coro_, {})};
} }
private: private:
friend Task<T>;
explicit TrySemiAwaitable(handle_t coro) noexcept : coro_(coro) {}
handle_t coro_; handle_t coro_;
}; };
......
...@@ -388,5 +388,59 @@ using semi_await_result_t = await_result_t<decltype(folly::coro::co_viaIfAsync( ...@@ -388,5 +388,59 @@ using semi_await_result_t = await_result_t<decltype(folly::coro::co_viaIfAsync(
std::declval<folly::Executor::KeepAlive<>>(), std::declval<folly::Executor::KeepAlive<>>(),
std::declval<T>()))>; std::declval<T>()))>;
namespace detail {
template <typename Awaiter>
class TryAwaiter {
public:
TryAwaiter(Awaiter&& awaiter) : awaiter_(std::move(awaiter)) {}
bool await_ready() {
return awaiter_.await_ready();
}
template <typename Promise>
auto await_suspend(std::experimental::coroutine_handle<Promise> coro) {
return awaiter_.await_suspend(coro);
}
auto await_resume() {
return awaiter_.await_resume_try();
}
private:
Awaiter awaiter_;
};
template <typename Awaiter>
auto makeTryAwaiter(Awaiter&& awaiter) {
return TryAwaiter<std::decay_t<Awaiter>>(std::move(awaiter));
}
template <typename SemiAwaitable>
class TrySemiAwaitable {
public:
explicit TrySemiAwaitable(SemiAwaitable&& semiAwaitable)
: semiAwaitable_(std::move(semiAwaitable)) {}
friend auto co_viaIfAsync(
Executor::KeepAlive<> executor,
TrySemiAwaitable&& self) noexcept {
return makeTryAwaiter(get_awaiter(
co_viaIfAsync(std::move(executor), std::move(self.semiAwaitable_))));
}
private:
SemiAwaitable semiAwaitable_;
};
} // namespace detail
template <
typename SemiAwaitable,
typename = std::enable_if_t<is_semi_awaitable_v<SemiAwaitable>>>
auto co_awaitTry(SemiAwaitable&& semiAwaitable) {
return detail::TrySemiAwaitable<SemiAwaitable>(std::move(semiAwaitable));
}
} // namespace coro } // namespace coro
} // namespace folly } // namespace folly
...@@ -459,4 +459,33 @@ TEST(Coro, Semaphore) { ...@@ -459,4 +459,33 @@ TEST(Coro, Semaphore) {
} }
} }
TEST(Coro, FutureTry) {
folly::coro::blockingWait([]() -> folly::coro::Task<void> {
{
auto result = co_await folly::coro::co_awaitTry(task42().semi());
EXPECT_TRUE(result.hasValue());
EXPECT_EQ(42, result.value());
}
{
auto result = co_await folly::coro::co_awaitTry(taskException().semi());
EXPECT_TRUE(result.hasException());
}
{
auto result = co_await folly::coro::co_awaitTry(
task42().semi().via(co_await folly::coro::co_current_executor));
EXPECT_TRUE(result.hasValue());
EXPECT_EQ(42, result.value());
}
{
auto result =
co_await folly::coro::co_awaitTry(taskException().semi().via(
co_await folly::coro::co_current_executor));
EXPECT_TRUE(result.hasException());
}
}());
}
#endif #endif
...@@ -370,7 +370,7 @@ TEST(Task, TaskOfLvalueReference) { ...@@ -370,7 +370,7 @@ TEST(Task, TaskOfLvalueReference) {
TEST(Task, TaskOfLvalueReferenceAsTry) { TEST(Task, TaskOfLvalueReferenceAsTry) {
folly::coro::blockingWait([]() -> folly::coro::Task<void> { folly::coro::blockingWait([]() -> folly::coro::Task<void> {
int value = 123; int value = 123;
auto&& result = co_await returnIntRef(value).co_awaitTry(); auto&& result = co_await co_awaitTry(returnIntRef(value));
CHECK(result.hasValue()); CHECK(result.hasValue());
CHECK_EQ(&value, &result.value().get()); CHECK_EQ(&value, &result.value().get());
......
...@@ -1900,6 +1900,10 @@ class FutureAwaitable { ...@@ -1900,6 +1900,10 @@ class FutureAwaitable {
return std::move(result_).value(); return std::move(result_).value();
} }
Try<T> await_resume_try() {
return std::move(result_);
}
FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES void await_suspend( FOLLY_CORO_AWAIT_SUSPEND_NONTRIVIAL_ATTRIBUTES void await_suspend(
std::experimental::coroutine_handle<> h) { std::experimental::coroutine_handle<> h) {
// FutureAwaitable may get destroyed as soon as the callback is executed. // FutureAwaitable may get destroyed as soon as the callback is executed.
......
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