Skip to content

Commit fc849e5

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 505de5f commit fc849e5

File tree

1 file changed

+31
-2
lines changed

1 file changed

+31
-2
lines changed

include/seastar/core/coroutine.hh

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,19 @@
2929

3030

3131
#include <coroutine>
32+
#include <new>
33+
#include <cstdlib>
34+
35+
#if !defined(__GLIBC__) || !__GLIBC_PREREQ(2, 43)
36+
37+
extern "C" {
38+
39+
void free_sized(void* ptr, size_t size);
40+
41+
}
42+
43+
#endif
44+
3245

3346
namespace seastar {
3447

@@ -48,10 +61,26 @@ execute_involving_handle_destruction_in_await_suspend(std::invocable<> auto&& fu
4861
}
4962

5063

64+
class coroutine_allocators {
65+
public:
66+
static void* operator new(size_t size) {
67+
memory::scoped_critical_alloc_section _;
68+
return ::malloc(size);
69+
}
70+
static void operator delete(void* ptr) noexcept {
71+
::free(ptr);
72+
}
73+
#ifdef __cpp_sized_deallocation
74+
static void operator delete(void* ptr, std::size_t sz) noexcept {
75+
::free_sized(ptr, sz);
76+
}
77+
#endif
78+
};
79+
5180
template <typename T = void>
5281
class coroutine_traits_base {
5382
public:
54-
class promise_type final : public seastar::task {
83+
class promise_type final : public seastar::task, public coroutine_allocators {
5584
seastar::promise<T> _promise;
5685
public:
5786
promise_type() = default;
@@ -103,7 +132,7 @@ public:
103132
template <>
104133
class coroutine_traits_base<> {
105134
public:
106-
class promise_type final : public seastar::task {
135+
class promise_type final : public seastar::task, public coroutine_allocators {
107136
seastar::promise<> _promise;
108137
public:
109138
promise_type() = default;

0 commit comments

Comments
 (0)