Skip to content

Commit ef91a68

Browse files
committed
implemented stack overflow prevention
1 parent 5e2c478 commit ef91a68

File tree

2 files changed

+72
-29
lines changed

2 files changed

+72
-29
lines changed

examples/stackoverflow.cpp

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,24 @@ struct task {
1212

1313
struct base {
1414
virtual void complete_value() noexcept = 0;
15+
virtual void complete_stopped() noexcept = 0;
1516
};
1617

1718
struct promise_type {
1819
struct final_awaiter {
1920
base* data;
2021
bool await_ready() noexcept { return false; }
21-
auto await_suspend(auto h) noexcept {
22-
std::cout << "final_awaiter\n";
23-
this->data->complete_value();
24-
std::cout << "completed\n";
25-
};
22+
auto await_suspend(auto h) noexcept { this->data->complete_value(); };
2623
void await_resume() noexcept {}
2724
};
2825
std::suspend_always initial_suspend() const noexcept { return {}; }
2926
final_awaiter final_suspend() const noexcept { return {this->data}; }
3027
void unhandled_exception() const noexcept {}
31-
std::coroutine_handle<> unhandled_stopped() { return std::coroutine_handle<>(); }
28+
std::coroutine_handle<> unhandled_stopped() {
29+
30+
this->data->complete_stopped();
31+
return std::noop_coroutine();
32+
}
3233
auto return_void() {}
3334
auto get_return_object() { return task{std::coroutine_handle<promise_type>::from_promise(*this)}; }
3435
template <::beman::execution::sender Sender>
@@ -51,7 +52,14 @@ struct task {
5152
this->handle.promise().data = this;
5253
this->handle.resume();
5354
}
54-
void complete_value() noexcept override { ex::set_value(std::move(this->r)); }
55+
void complete_value() noexcept override {
56+
this->handle.destroy();
57+
ex::set_value(std::move(this->r));
58+
}
59+
void complete_stopped() noexcept override {
60+
this->handle.destroy();
61+
ex::set_stopped(std::move(this->r));
62+
}
5563
};
5664

5765
std::coroutine_handle<promise_type> handle;
@@ -63,11 +71,23 @@ struct task {
6371
};
6472

6573
int main(int ac, char*[]) {
74+
std::cout << std::unitbuf;
75+
using on_exit = std::unique_ptr<const char, decltype([](auto msg) { std::cout << msg << "\n"; })>;
6676
static_assert(ex::sender<task>);
6777
ex::sync_wait([](int n) -> task {
68-
for (int i{}; i < n; ++i) {
69-
std::cout << "await=" << (co_await ex::just(i)) << "\n";
70-
}
71-
co_return;
78+
on_exit msg("coro run to the end");
79+
if constexpr (true)
80+
for (int i{}; i < n; ++i) {
81+
std::cout << "await just=" << (co_await ex::just(i)) << "\n";
82+
}
83+
if constexpr (false)
84+
for (int i{}; i < n; ++i) {
85+
try {
86+
co_await ex::just_error(i);
87+
} catch (int x) {
88+
std::cout << "await error=" << x << "\n";
89+
}
90+
}
91+
co_await ex::just_stopped();
7292
}(ac < 2 ? 3 : 30000));
7393
}

include/beman/execution/detail/sender_awaitable.hpp

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include <type_traits>
2323
#include <utility>
2424
#include <variant>
25+
#include <tuple>
26+
#include <atomic>
2527

2628
namespace beman::execution::detail {
2729
template <class Sndr, class Promise>
@@ -31,58 +33,79 @@ class sender_awaitable {
3133
::beman::execution::detail::single_sender_value_type<Sndr, ::beman::execution::env_of_t<Promise>>;
3234
using result_type = ::std::conditional_t<::std::is_void_v<value_type>, unit, value_type>;
3335
using variant_type = ::std::variant<::std::monostate, result_type, ::std::exception_ptr>;
36+
using data_type = ::std::tuple<variant_type, ::std::atomic<bool>, ::std::coroutine_handle<Promise>>;
37+
3438
struct awaitable_receiver {
3539
using receiver_concept = ::beman::execution::receiver_t;
3640

41+
void resume() {
42+
if (::std::get<1>(*result_ptr_).exchange(true, std::memory_order_acq_rel)) {
43+
::std::get<2>(*result_ptr_).resume();
44+
}
45+
}
46+
3747
template <class... Args>
3848
requires ::std::constructible_from<result_type, Args...>
3949
void set_value(Args&&... args) && noexcept {
4050
try {
41-
result_ptr_->template emplace<1>(::std::forward<Args>(args)...);
51+
::std::get<0>(*result_ptr_).template emplace<1>(::std::forward<Args>(args)...);
4252
} catch (...) {
43-
result_ptr_->template emplace<2>(::std::current_exception());
53+
::std::get<0>(*result_ptr_).template emplace<2>(::std::current_exception());
4454
}
45-
continuation_.resume();
55+
this->resume();
4656
}
47-
4857
template <class Error>
4958
void set_error(Error&& error) && noexcept {
50-
result_ptr_->template emplace<2>(::beman::execution::detail::as_except_ptr(::std::forward<Error>(error)));
51-
continuation_.resume();
59+
::std::get<0>(*result_ptr_)
60+
.template emplace<2>(::beman::execution::detail::as_except_ptr(::std::forward<Error>(error)));
61+
this->resume();
5262
}
5363

5464
void set_stopped() && noexcept {
55-
static_cast<::std::coroutine_handle<>>(continuation_.promise().unhandled_stopped()).resume();
65+
if (::std::get<1>(*result_ptr_).exchange(true, ::std::memory_order_acq_rel)) {
66+
static_cast<::std::coroutine_handle<>>(::std::get<2>(*result_ptr_).promise().unhandled_stopped())
67+
.resume();
68+
}
5669
}
5770

5871
auto get_env() const noexcept {
59-
return ::beman::execution::detail::fwd_env{::beman::execution::get_env(continuation_.promise())};
72+
return ::beman::execution::detail::fwd_env{
73+
::beman::execution::get_env(::std::get<2>(*result_ptr_).promise())};
6074
}
6175

62-
variant_type* result_ptr_;
63-
::std::coroutine_handle<Promise> continuation_;
76+
data_type* result_ptr_;
6477
};
6578
using op_state_type = ::beman::execution::connect_result_t<Sndr, awaitable_receiver>;
6679

67-
variant_type result{};
80+
data_type result{};
6881
op_state_type state;
6982

7083
public:
7184
sender_awaitable(Sndr&& sndr, Promise& p)
72-
: state{::beman::execution::connect(
73-
::std::forward<Sndr>(sndr),
74-
awaitable_receiver{::std::addressof(result), ::std::coroutine_handle<Promise>::from_promise(p)})} {}
85+
: result{::std::monostate{}, false, ::std::coroutine_handle<Promise>::from_promise(p)},
86+
state{::beman::execution::connect(::std::forward<Sndr>(sndr),
87+
sender_awaitable::awaitable_receiver{::std::addressof(result)})} {}
7588

7689
static constexpr bool await_ready() noexcept { return false; }
77-
void await_suspend(::std::coroutine_handle<Promise>) noexcept { ::beman::execution::start(state); }
90+
::std::coroutine_handle<> await_suspend(::std::coroutine_handle<Promise> handle) noexcept {
91+
::beman::execution::start(state);
92+
if (::std::get<1>(this->result).exchange(true, std::memory_order_acq_rel)) {
93+
if (::std::holds_alternative<::std::monostate>(::std::get<0>(this->result))) {
94+
return static_cast<::std::coroutine_handle<>>(
95+
::std::get<2>(this->result).promise().unhandled_stopped());
96+
}
97+
return handle;
98+
}
99+
return ::std::noop_coroutine();
100+
}
78101
value_type await_resume() {
79-
if (::std::holds_alternative<::std::exception_ptr>(result)) {
80-
::std::rethrow_exception(::std::get<::std::exception_ptr>(result));
102+
if (::std::holds_alternative<::std::exception_ptr>(::std::get<0>(result))) {
103+
::std::rethrow_exception(::std::get<::std::exception_ptr>(::std::get<0>(result)));
81104
}
82105
if constexpr (::std::is_void_v<value_type>) {
83106
return;
84107
} else {
85-
return ::std::get<value_type>(std::move(result));
108+
return ::std::get<value_type>(std::move(::std::get<0>(result)));
86109
}
87110
}
88111
};

0 commit comments

Comments
 (0)