Skip to content

Commit 21770c2

Browse files
committed
coroutine: allocate coroutine frame in a critical section
Like continuations, coroutines are glue that cannot be allowed to fail. For example, if cleanup coroutine fails to allocate and returns an exception future, cleanup will not be done and the system will enter an undefined state. Better to crash. This conflicts with test code that tries to inject memory failures and see how they are handled (file_io_test.cc handle_bad_alloc_test). The coroutine will throw std::bad_alloc (since we don't define get_return_object_on_allocation_failure()), and since it's declared noexcept, will call std::terminate. To avoid all this, follow future::schedule() and prevent memory allocation failure injection from interfering with coroutine frame allocation. In this regard a coroutine frame is just like a continuation. This is done by injecting class-specific operator new and operator delete. Sized deallocation is also defined if supported by the compiler.
1 parent d4c7806 commit 21770c2

File tree

1 file changed

+20
-2
lines changed

1 file changed

+20
-2
lines changed

include/seastar/core/coroutine.hh

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@
2424

2525
#include <seastar/core/future.hh>
2626
#include <seastar/core/make_task.hh>
27+
#include <seastar/core/sized-free.hh>
2728
#include <seastar/coroutine/exception.hh>
2829
#include <seastar/util/modules.hh>
2930
#include <seastar/util/std-compat.hh>
3031

3132

3233
#ifndef SEASTAR_MODULE
3334
#include <coroutine>
35+
#include <new>
3436
#endif
3537

3638
namespace seastar {
@@ -51,10 +53,26 @@ execute_involving_handle_destruction_in_await_suspend(std::invocable<> auto&& fu
5153
}
5254

5355

56+
class coroutine_allocators {
57+
public:
58+
static void* operator new(size_t size) {
59+
memory::scoped_critical_alloc_section _;
60+
return ::malloc(size);
61+
}
62+
static void operator delete(void* ptr) noexcept {
63+
::free(ptr);
64+
}
65+
#ifdef __cpp_sized_deallocation
66+
static void operator delete(void* ptr, std::size_t sz) noexcept {
67+
memory::free(ptr, sz);
68+
}
69+
#endif
70+
};
71+
5472
template <typename T = void>
5573
class coroutine_traits_base {
5674
public:
57-
class promise_type final : public seastar::task {
75+
class promise_type final : public seastar::task, public coroutine_allocators {
5876
seastar::promise<T> _promise;
5977
public:
6078
promise_type() = default;
@@ -106,7 +124,7 @@ public:
106124
template <>
107125
class coroutine_traits_base<> {
108126
public:
109-
class promise_type final : public seastar::task {
127+
class promise_type final : public seastar::task, public coroutine_allocators {
110128
seastar::promise<> _promise;
111129
public:
112130
promise_type() = default;

0 commit comments

Comments
 (0)