From 836cbfd1ceca127f34f49e56ba91c76493557bd2 Mon Sep 17 00:00:00 2001 From: Andrii Grynenko Date: Sat, 19 Oct 2024 17:52:11 -0700 Subject: [PATCH] AsyncGenerator drain safety docs Reviewed By: wangyishuo123, jzzhao-fb, mbasmanova Differential Revision: D64626519 fbshipit-source-id: fa55b32e66583741dea573149301547eb4027b63 --- folly/coro/AsyncGenerator.h | 56 +++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/folly/coro/AsyncGenerator.h b/folly/coro/AsyncGenerator.h index 8882c3e4aeb..2dff26dc393 100644 --- a/folly/coro/AsyncGenerator.h +++ b/folly/coro/AsyncGenerator.h @@ -149,6 +149,62 @@ class AsyncGeneratorPromise; * * There is an alias CleanableAsyncGenerator for AsyncGenerator with * RequiresCleanup set to true. + * + * Drain safety + * ------------ + * One significant difference between AsyncGenerator and folly::coro::Task is + * that AsyncGenerator may be destroyed between next() calls - i.e. destroyed + * without being fully drained. + * + * For example: + * + * AsyncGenerator gen() { + * SCOPE_EXIT { + * LOG(INFO) << "Step 4"; + * }; + * LOG(INFO) << "Step 1"; + * co_yield 41; + * SCOPE_EXIT { + * LOG(INFO) << "Step 3"; + * }; + * LOG(INFO) << "Step 2"; + * co_yield 42; + * SCOPE_EXIT { + * LOG(INFO) << "Never reached"; + * }; + * LOG(INFO) << "Never reached"; + * co_yield 43; + * } + * + * { + * AsyncGenerator g = gen(); + * while (auto next = co_await g.next()) { + * LOG(INFO) << *next; + * if (*next == 42) { + * break; + * // ^^^ this may trigger generator destruction before it is drained. + * } + * } + * } + * + * This means that when writing an AsyncGenerator, you should always document + * whether such AsyncGenerator requires draining before destruction (drain + * unsafe). When possible you should always aim to make AsyncGenerator not + * require draining before destruction (drain safe). + * + * If an AsyncGenerator is drain unsafe, always mention this in the + * documentation and ideally include some assertions that help detect cases + * where such AsyncGenerator is destroyed without being fully drained. + * + * Example: + * + * AsyncGenerator gen() { + * auto drainGuard = makeGuard([] { LOG(FATAL) << "I shall be drained!"; }); + * co_yield 41; + * co_yield 42; + * co_yield 43; + * drainGuard.dismiss(); + * } */ template < typename Reference,