Commit c05ce04c authored by Lewis Baker's avatar Lewis Baker Committed by Facebook Github Bot

Fix potential deadlock in AsyncGenerator when used with blockingWait()

Summary:
The AsyncGenerator was not clearing its Executor::KeepAlive when it ran to completion.

This meant that when you call `blockingWait(gen.next())` and this would result in the end-of-stream (error or done) then `blockingWait()` would never return because the destructor of the local executor created in the call was blocked waiting for all KeepAlive instances to be destroyed - but this would not happen until the AsyncGenerator was destroyed.

AsyncGenerator now clears the caller context (which includes the KeepAlive) when the coroutine runs to completion.

Reviewed By: kirkshoop

Differential Revision: D19625741

fbshipit-source-id: f3254181af14828b32bfa8f7ef5d400d53c04efa
parent 05fe3c7b
......@@ -68,6 +68,7 @@ class AsyncGeneratorPromise {
YieldAwaiter final_suspend() noexcept {
DCHECK(!hasValue_);
clearContext();
return {};
}
......
......@@ -447,4 +447,30 @@ TEST_F(AsyncGeneratorTest, CancellationTokenPropagatesFromConsumer) {
}());
}
TEST_F(AsyncGeneratorTest, BlockingWaitOnFinalNextDoesNotDeadlock) {
auto gen = []() -> folly::coro::AsyncGenerator<int> { co_yield 42; };
auto g = gen();
auto val1 = folly::coro::blockingWait(g.next());
CHECK_EQ(42, val1.value());
auto val2 = folly::coro::blockingWait(g.next());
CHECK(!val2.has_value());
}
TEST_F(AsyncGeneratorTest, BlockingWaitOnThrowingFinalNextDoesNotDeadlock) {
auto gen = []() -> folly::coro::AsyncGenerator<int> {
co_yield 42;
throw SomeError{};
};
auto g = gen();
auto val1 = folly::coro::blockingWait(g.next());
CHECK_EQ(42, val1.value());
try {
folly::coro::blockingWait(g.next());
CHECK(false);
} catch (const SomeError&) {
}
}
#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