From: Avi Kivity' via seastar-dev <
seast...@googlegroups.com>
Committer: Tomasz Grabiec <
tgra...@scylladb.com>
Branch: master
coroutine: explain and mitigate the lambda coroutine fiasco
Due to the way lambda coroutines are defined and how Seastar
continuations work, it is easy to have a use-after-free with lambda
coroutines.
Fortunately, it is easy to mitigate.
This patch documents the problem, provides a lambda coroutine wrapper
and describes how to use it, and documents APIs that are safe for lambda
coroutines without the wrapper.
A unit test is included.
Message-Id: <
2022090715243...@scylladb.com>
---
diff --git a/doc/lambda-coroutine-fiasco.md b/doc/lambda-coroutine-fiasco.md
--- a/doc/lambda-coroutine-fiasco.md
--- a/doc/tutorial.md
+++ b/doc/tutorial.md
@@ -372,6 +372,39 @@ In #4, we call a function that returns a `seastar::future<>`. In this case, the
diff --git a/include/seastar/core/coroutine.hh b/include/seastar/core/coroutine.hh
--- a/include/seastar/core/coroutine.hh
+++ b/include/seastar/core/coroutine.hh
@@ -225,6 +225,41 @@ template<typename T> struct SEASTAR_NODISCARD without_preemption_check<T> : publ
diff --git a/include/seastar/coroutine/all.hh b/include/seastar/coroutine/all.hh
--- a/include/seastar/coroutine/all.hh
+++ b/include/seastar/coroutine/all.hh
@@ -110,6 +110,8 @@ using value_tuple_for_non_void_futures = typename value_tuple_for_non_void_futur
/// co_return a + b;
/// };
/// ```
+///
+/// Safe for use with lambda coroutines.
template <typename... Futures>
requires (sizeof ...(Futures) > 0)
class [[nodiscard("must co_await an all() object")]] all {
diff --git a/include/seastar/coroutine/parallel_for_each.hh b/include/seastar/coroutine/parallel_for_each.hh
--- a/include/seastar/coroutine/parallel_for_each.hh
+++ b/include/seastar/coroutine/parallel_for_each.hh
@@ -51,6 +51,8 @@ namespace seastar::coroutine {
/// co_return sum;
/// };
/// ```
+///
+/// Safe for use with lambda coroutines.
template <typename Func>
// constaints for Func are defined at the parallel_for_each constructor
class [[nodiscard("must co_await an parallel_for_each() object")]] parallel_for_each final : continuation_base<> {
diff --git a/tests/unit/coroutines_test.cc b/tests/unit/coroutines_test.cc
--- a/tests/unit/coroutines_test.cc
+++ b/tests/unit/coroutines_test.cc
@@ -894,4 +894,31 @@ SEASTAR_TEST_CASE(test_async_generator_throws_from_consumer_unbuffered) {