Commit 1213fc18 authored by Tom Jackson's avatar Tom Jackson Committed by Facebook GitHub Bot

Debug checks for catching use of empty coro::Task<>

Summary: It took me a while to find that I'd `co_await`'d the same `Task<>` twice when working in a loop. This would've made it easier to find.

Reviewed By: andriigrynenko

Differential Revision: D24718615

fbshipit-source-id: 263c0aae208eab37ab198f875dc72a03287785c1
parent 85e4e439
...@@ -395,6 +395,7 @@ class FOLLY_NODISCARD TaskWithExecutor { ...@@ -395,6 +395,7 @@ class FOLLY_NODISCARD TaskWithExecutor {
template <typename Promise> template <typename Promise>
FOLLY_NOINLINE void await_suspend( FOLLY_NOINLINE void await_suspend(
std::experimental::coroutine_handle<Promise> continuation) noexcept { std::experimental::coroutine_handle<Promise> continuation) noexcept {
DCHECK(coro_);
auto& promise = coro_.promise(); auto& promise = coro_.promise();
DCHECK(!promise.continuation_); DCHECK(!promise.continuation_);
DCHECK(promise.executor_); DCHECK(promise.executor_);
...@@ -426,6 +427,7 @@ class FOLLY_NODISCARD TaskWithExecutor { ...@@ -426,6 +427,7 @@ class FOLLY_NODISCARD TaskWithExecutor {
} }
T await_resume() { T await_resume() {
DCHECK(coro_);
// 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(); };
return std::move(coro_.promise().result()).value(); return std::move(coro_.promise().result()).value();
...@@ -458,6 +460,7 @@ class FOLLY_NODISCARD TaskWithExecutor { ...@@ -458,6 +460,7 @@ class FOLLY_NODISCARD TaskWithExecutor {
template <typename Promise> template <typename Promise>
FOLLY_NOINLINE std::experimental::coroutine_handle<> await_suspend( FOLLY_NOINLINE std::experimental::coroutine_handle<> await_suspend(
std::experimental::coroutine_handle<Promise> continuation) { std::experimental::coroutine_handle<Promise> continuation) {
DCHECK(coro_);
auto& promise = coro_.promise(); auto& promise = coro_.promise();
DCHECK(!promise.continuation_); DCHECK(!promise.continuation_);
DCHECK(promise.executor_); DCHECK(promise.executor_);
...@@ -478,6 +481,7 @@ class FOLLY_NODISCARD TaskWithExecutor { ...@@ -478,6 +481,7 @@ class FOLLY_NODISCARD TaskWithExecutor {
} }
folly::Try<StorageType> await_resume() { folly::Try<StorageType> await_resume() {
DCHECK(coro_);
// 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(); };
return std::move(coro_.promise().result()); return std::move(coro_.promise().result());
...@@ -495,12 +499,14 @@ class FOLLY_NODISCARD TaskWithExecutor { ...@@ -495,12 +499,14 @@ class FOLLY_NODISCARD TaskWithExecutor {
public: public:
Awaiter operator co_await() && noexcept { Awaiter operator co_await() && noexcept {
DCHECK(coro_);
return Awaiter{std::exchange(coro_, {})}; return Awaiter{std::exchange(coro_, {})};
} }
friend TaskWithExecutor co_withCancellation( friend TaskWithExecutor co_withCancellation(
const folly::CancellationToken& cancelToken, const folly::CancellationToken& cancelToken,
TaskWithExecutor&& task) noexcept { TaskWithExecutor&& task) noexcept {
DCHECK(task.coro_);
task.coro_.promise().setCancelToken(cancelToken); task.coro_.promise().setCancelToken(cancelToken);
return std::move(task); return std::move(task);
} }
...@@ -553,6 +559,7 @@ class FOLLY_NODISCARD Task { ...@@ -553,6 +559,7 @@ class FOLLY_NODISCARD Task {
using handle_t = std::experimental::coroutine_handle<promise_type>; using handle_t = std::experimental::coroutine_handle<promise_type>;
void setExecutor(folly::Executor::KeepAlive<>&& e) noexcept { void setExecutor(folly::Executor::KeepAlive<>&& e) noexcept {
DCHECK(coro_);
coro_.promise().executor_ = std::move(e); coro_.promise().executor_ = std::move(e);
} }
...@@ -581,6 +588,7 @@ class FOLLY_NODISCARD Task { ...@@ -581,6 +588,7 @@ class FOLLY_NODISCARD Task {
FOLLY_NODISCARD FOLLY_NODISCARD
TaskWithExecutor<T> scheduleOn(Executor::KeepAlive<> executor) && noexcept { TaskWithExecutor<T> scheduleOn(Executor::KeepAlive<> executor) && noexcept {
setExecutor(std::move(executor)); setExecutor(std::move(executor));
DCHECK(coro_);
return TaskWithExecutor<T>{std::exchange(coro_, {})}; return TaskWithExecutor<T>{std::exchange(coro_, {})};
} }
...@@ -595,6 +603,7 @@ class FOLLY_NODISCARD Task { ...@@ -595,6 +603,7 @@ class FOLLY_NODISCARD Task {
friend auto co_viaIfAsync( friend auto co_viaIfAsync(
Executor::KeepAlive<> executor, Executor::KeepAlive<> executor,
Task<T>&& t) noexcept { Task<T>&& t) noexcept {
DCHECK(t.coro_);
// Child task inherits the awaiting task's executor // Child task inherits the awaiting task's executor
t.setExecutor(std::move(executor)); t.setExecutor(std::move(executor));
return Awaiter{std::exchange(t.coro_, {})}; return Awaiter{std::exchange(t.coro_, {})};
...@@ -603,6 +612,7 @@ class FOLLY_NODISCARD Task { ...@@ -603,6 +612,7 @@ class FOLLY_NODISCARD Task {
friend Task co_withCancellation( friend Task co_withCancellation(
const folly::CancellationToken& cancelToken, const folly::CancellationToken& cancelToken,
Task&& task) noexcept { Task&& task) noexcept {
DCHECK(task.coro_);
task.coro_.promise().setCancelToken(cancelToken); task.coro_.promise().setCancelToken(cancelToken);
return std::move(task); return std::move(task);
} }
...@@ -635,6 +645,7 @@ class FOLLY_NODISCARD Task { ...@@ -635,6 +645,7 @@ class FOLLY_NODISCARD Task {
template <typename Promise> template <typename Promise>
FOLLY_NOINLINE auto await_suspend( FOLLY_NOINLINE auto await_suspend(
std::experimental::coroutine_handle<Promise> continuation) noexcept { std::experimental::coroutine_handle<Promise> continuation) noexcept {
DCHECK(coro_);
auto& promise = coro_.promise(); auto& promise = coro_.promise();
promise.continuation_ = continuation; promise.continuation_ = continuation;
...@@ -653,11 +664,13 @@ class FOLLY_NODISCARD Task { ...@@ -653,11 +664,13 @@ class FOLLY_NODISCARD Task {
} }
T await_resume() { T await_resume() {
DCHECK(coro_);
SCOPE_EXIT { std::exchange(coro_, {}).destroy(); }; SCOPE_EXIT { std::exchange(coro_, {}).destroy(); };
return std::move(coro_.promise().result()).value(); return std::move(coro_.promise().result()).value();
} }
folly::Try<StorageType> await_resume_try() { folly::Try<StorageType> await_resume_try() {
DCHECK(coro_);
SCOPE_EXIT { std::exchange(coro_, {}).destroy(); }; SCOPE_EXIT { std::exchange(coro_, {}).destroy(); };
return std::move(coro_.promise().result()); return std::move(coro_.promise().result());
} }
......
...@@ -574,4 +574,16 @@ TEST_F(TaskTest, CoAwaitTryWithScheduleOnAndCancellation) { ...@@ -574,4 +574,16 @@ TEST_F(TaskTest, CoAwaitTryWithScheduleOnAndCancellation) {
}()); }());
} }
TEST_F(TaskTest, Moved) {
if (folly::kIsDebug) {
ASSERT_DEATH_IF_SUPPORTED(
folly::coro::blockingWait([]() -> folly::coro::Task<void> {
folly::coro::Task<int> task = folly::coro::makeTask(1);
co_await std::move(task);
co_await std::move(task);
}()),
"task\\.coro_");
}
}
#endif #endif
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