Commit 505affdc authored by Lewis Baker's avatar Lewis Baker Committed by Facebook GitHub Bot

Add support for co_awaitTry(task.scheduleOn())

Summary:
This adds support for applying the co_awaitTry() algorithm to a
TaskWithExecutor type.

This required a bit of a refactor of the `co_awaitTry()` implementtion
to not require that it's passed a SemiAwaitable but and to allow
Awaitable objects to also be passed to `co_awaitTry()`.

Renamed TaskWithExecutor::InlineAwaiter to InlineTryAwaitable and make
it private to better reflect its purpose.

No longer implement `Task::Awaiter::await_resume()` in terms of the
`await_resume_try()` method. This should avoid a call to the Try<T>
move-constructor by extracting the T result directly from the Try<T>
stored in the promise.

Reviewed By: andriigrynenko

Differential Revision: D24602729

fbshipit-source-id: d352e717877769502cc55a01731eb165ddad9ad8
parent 018a9487
...@@ -288,7 +288,7 @@ class FOLLY_NODISCARD TaskWithExecutor { ...@@ -288,7 +288,7 @@ class FOLLY_NODISCARD TaskWithExecutor {
[](TaskWithExecutor task, [](TaskWithExecutor task,
std::decay_t<F> cb) -> detail::InlineTaskDetached { std::decay_t<F> cb) -> detail::InlineTaskDetached {
try { try {
cb(co_await std::move(task).co_awaitTry()); cb(co_await folly::coro::co_awaitTry(std::move(task)));
} catch (const std::exception& e) { } catch (const std::exception& e) {
cb(Try<StorageType>(exception_wrapper(std::current_exception(), e))); cb(Try<StorageType>(exception_wrapper(std::current_exception(), e)));
} catch (...) { } catch (...) {
...@@ -311,7 +311,7 @@ class FOLLY_NODISCARD TaskWithExecutor { ...@@ -311,7 +311,7 @@ class FOLLY_NODISCARD TaskWithExecutor {
[](TaskWithExecutor task, [](TaskWithExecutor task,
std::decay_t<F> cb) -> detail::InlineTaskDetached { std::decay_t<F> cb) -> detail::InlineTaskDetached {
try { try {
cb(co_await InlineAwaiter{std::exchange(task.coro_, {})}); cb(co_await InlineTryAwaitable{std::exchange(task.coro_, {})});
} catch (const std::exception& e) { } catch (const std::exception& e) {
cb(Try<StorageType>(exception_wrapper(std::current_exception(), e))); cb(Try<StorageType>(exception_wrapper(std::current_exception(), e)));
} catch (...) { } catch (...) {
...@@ -336,7 +336,7 @@ class FOLLY_NODISCARD TaskWithExecutor { ...@@ -336,7 +336,7 @@ class FOLLY_NODISCARD TaskWithExecutor {
return sf; return sf;
} }
template <typename ResultCreator> private:
class Awaiter { class Awaiter {
public: public:
explicit Awaiter(handle_t coro) noexcept : coro_(coro) {} explicit Awaiter(handle_t coro) noexcept : coro_(coro) {}
...@@ -372,22 +372,26 @@ class FOLLY_NODISCARD TaskWithExecutor { ...@@ -372,22 +372,26 @@ class FOLLY_NODISCARD TaskWithExecutor {
}); });
} }
decltype(auto) await_resume() { T await_resume() {
// Eagerly destroy the coroutine-frame once we have retrieved the result. // Eagerly destroy the coroutine-frame once we have retrieved the result.
SCOPE_EXIT { std::exchange(coro_, {}).destroy(); }; SCOPE_EXIT { std::exchange(coro_, {}).destroy(); };
ResultCreator resultCreator; return std::move(coro_.promise().result()).value();
return resultCreator(std::move(coro_.promise().result())); }
folly::Try<StorageType> await_resume_try() {
SCOPE_EXIT { std::exchange(coro_, {}).destroy(); };
return std::move(coro_.promise().result());
} }
private: private:
handle_t coro_; handle_t coro_;
}; };
class InlineAwaiter { class InlineTryAwaitable {
public: public:
InlineAwaiter(handle_t coro) noexcept : coro_(coro) {} InlineTryAwaitable(handle_t coro) noexcept : coro_(coro) {}
~InlineAwaiter() { ~InlineTryAwaitable() {
if (coro_) { if (coro_) {
coro_.destroy(); coro_.destroy();
} }
...@@ -414,22 +418,9 @@ class FOLLY_NODISCARD TaskWithExecutor { ...@@ -414,22 +418,9 @@ class FOLLY_NODISCARD TaskWithExecutor {
handle_t coro_; handle_t coro_;
}; };
struct ValueCreator { public:
T operator()(Try<StorageType>&& t) const { return std::move(t).value(); } Awaiter operator co_await() && noexcept {
}; return Awaiter{std::exchange(coro_, {})};
struct TryCreator {
Try<StorageType> operator()(Try<StorageType>&& t) const {
return std::move(t);
}
};
auto operator co_await() && noexcept {
return Awaiter<ValueCreator>{std::exchange(coro_, {})};
}
auto co_awaitTry() && noexcept {
return Awaiter<TryCreator>{std::exchange(coro_, {})};
} }
friend TaskWithExecutor co_withCancellation( friend TaskWithExecutor co_withCancellation(
...@@ -566,9 +557,12 @@ class FOLLY_NODISCARD Task { ...@@ -566,9 +557,12 @@ class FOLLY_NODISCARD Task {
return coro_; return coro_;
} }
T await_resume() { return await_resume_try().value(); } T await_resume() {
SCOPE_EXIT { std::exchange(coro_, {}).destroy(); };
return std::move(coro_.promise().result()).value();
}
auto await_resume_try() { folly::Try<StorageType> await_resume_try() {
SCOPE_EXIT { std::exchange(coro_, {}).destroy(); }; SCOPE_EXIT { std::exchange(coro_, {}).destroy(); };
return std::move(coro_.promise().result()); return std::move(coro_.promise().result());
} }
......
...@@ -397,62 +397,106 @@ using semi_await_result_t = await_result_t<decltype(folly::coro::co_viaIfAsync( ...@@ -397,62 +397,106 @@ using semi_await_result_t = await_result_t<decltype(folly::coro::co_viaIfAsync(
namespace detail { namespace detail {
template <typename Awaiter> template <typename Awaitable>
class TryAwaiter { class TryAwaiter {
using Awaiter = awaiter_type_t<Awaitable>;
public: public:
TryAwaiter(Awaiter&& awaiter) : awaiter_(std::move(awaiter)) {} explicit TryAwaiter(Awaitable&& awaiter)
: awaiter_(folly::coro::get_awaiter(static_cast<Awaitable&&>(awaiter))) {}
bool await_ready() { return awaiter_.await_ready(); } auto await_ready() noexcept(noexcept(std::declval<Awaiter&>().await_ready()))
-> decltype(std::declval<Awaiter&>().await_ready()) {
return awaiter_.await_ready();
}
template <typename Promise> template <typename Promise>
auto await_suspend(std::experimental::coroutine_handle<Promise> coro) { auto
await_suspend(std::experimental::coroutine_handle<Promise> coro) noexcept(
noexcept(std::declval<Awaiter&>().await_suspend(coro)))
-> decltype(std::declval<Awaiter&>().await_suspend(coro)) {
return awaiter_.await_suspend(coro); return awaiter_.await_suspend(coro);
} }
auto await_resume() { return awaiter_.await_resume_try(); } auto await_resume() noexcept(
noexcept(std::declval<Awaiter&>().await_resume_try()))
-> decltype(std::declval<Awaiter&>().await_resume_try()) {
return awaiter_.await_resume_try();
}
private: private:
Awaiter awaiter_; Awaiter awaiter_;
}; };
template <typename Awaiter> template <typename T>
auto makeTryAwaiter(Awaiter&& awaiter) { class TryAwaitable {
return TryAwaiter<std::decay_t<Awaiter>>(std::move(awaiter));
}
template <typename SemiAwaitable>
class TrySemiAwaitable {
public: public:
explicit TrySemiAwaitable(SemiAwaitable&& semiAwaitable) template <typename T2>
: semiAwaitable_(std::move(semiAwaitable)) {} explicit TryAwaitable(T2&& awaitable) noexcept(
std::is_nothrow_constructible_v<T, T2>)
friend auto co_viaIfAsync( : inner_(static_cast<T2&&>(awaitable)) {}
Executor::KeepAlive<> executor,
TrySemiAwaitable&& self) noexcept { template <typename Factory>
return makeTryAwaiter(get_awaiter( explicit TryAwaitable(std::in_place_t, Factory&& factory)
co_viaIfAsync(std::move(executor), std::move(self.semiAwaitable_)))); : inner_(factory()) {}
template <
typename Self,
std::enable_if_t<
std::is_same_v<remove_cvref_t<Self>, TryAwaitable>,
int> = 0,
typename T2 = like_t<Self, T>,
std::enable_if_t<is_awaitable_v<T2>, int> = 0>
friend TryAwaiter<T2> operator co_await(Self&& self) {
return TryAwaiter<T2>{static_cast<Self&&>(self).inner_};
}
template <
typename T2 = T,
typename Result = decltype(folly::coro::co_withCancellation(
std::declval<const folly::CancellationToken&>(),
std::declval<T2>()))>
friend TryAwaitable<Result> co_withCancellation(
const folly::CancellationToken& cancelToken,
TryAwaitable&& awaitable) {
return TryAwaitable<Result>{std::in_place, [&]() -> decltype(auto) {
return folly::coro::co_withCancellation(
cancelToken,
static_cast<T&&>(awaitable.inner_));
}};
} }
friend auto co_withCancellation( template <
const CancellationToken& cancelToken, typename T2 = T,
TrySemiAwaitable&& awaitable) { typename Result = decltype(folly::coro::co_viaIfAsync(
auto cancelAwaitable = folly::coro::co_withCancellation( std::declval<folly::Executor::KeepAlive<>>(),
std::move(cancelToken), std::declval<T2>()))>
static_cast<SemiAwaitable&&>(awaitable.semiAwaitable_)); friend TryAwaitable<Result> co_viaIfAsync(
return TrySemiAwaitable<decltype(cancelAwaitable)>( folly::Executor::KeepAlive<> executor,
std::move(cancelAwaitable)); TryAwaitable&&
awaitable) noexcept(noexcept(folly::coro::
co_viaIfAsync(
std::declval<folly::Executor::
KeepAlive<>>(),
std::declval<T2>()))) {
return TryAwaitable<Result>{std::in_place, [&]() -> decltype(auto) {
return folly::coro::co_viaIfAsync(
std::move(executor),
static_cast<T&&>(awaitable.inner_));
}};
} }
private: private:
SemiAwaitable semiAwaitable_; T inner_;
}; };
} // namespace detail } // namespace detail
template < template <typename Awaitable>
typename SemiAwaitable, detail::TryAwaitable<remove_cvref_t<Awaitable>> co_awaitTry(
typename = std::enable_if_t<is_semi_awaitable_v<SemiAwaitable>>> Awaitable&& awaitable) {
auto co_awaitTry(SemiAwaitable&& semiAwaitable) { return detail::TryAwaitable<remove_cvref_t<Awaitable>>{
return detail::TrySemiAwaitable<SemiAwaitable>(std::move(semiAwaitable)); static_cast<Awaitable&&>(awaitable)};
} }
} // namespace coro } // namespace coro
......
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