9
9
namespace conduit ::mixin {
10
10
enum suspend : bool { always = true , never = false };
11
11
12
+ #if CONDUIT_USE_GCC_EXCEPTION_WORKAROUND
13
+ namespace detail {
14
+ using remuse_coro_t = void (*)();
15
+ using destroy_coro_t = void (*)();
16
+ struct frame_header_t {
17
+ remuse_coro_t resume_coro;
18
+ destroy_coro_t destroy_coro;
19
+ };
20
+ } // namespace detail
21
+ template <bool suspend>
22
+ struct InitialSuspend {
23
+ // If CONDUIT_USE_GCC_EXCEPTION_WORKAROUND is defined, then we need to keep
24
+ // track of this value in order to destroy the frame manually. This value is
25
+ // recorded inside initial_suspend_t
26
+ detail::destroy_coro_t destroy_coro = nullptr ;
27
+
28
+ struct initial_suspend_t {
29
+ detail::destroy_coro_t & destroy_coro_ref;
30
+
31
+ inline constexpr bool await_ready () { return false ; }
32
+ inline bool await_suspend (std::coroutine_handle<> h) {
33
+ destroy_coro_ref =
34
+ ((detail::frame_header_t *)h.address ())->destroy_coro ;
35
+ return suspend; // The coroutine is resumed if suspend is false
36
+ }
37
+ inline constexpr void await_resume () noexcept {}
38
+ };
39
+
40
+ inline constexpr auto initial_suspend () noexcept {
41
+ return initial_suspend_t {destroy_coro};
42
+ }
43
+ };
44
+ #else
12
45
template <bool suspend>
13
46
struct InitialSuspend {
14
47
inline constexpr auto initial_suspend () noexcept {
@@ -19,6 +52,7 @@ struct InitialSuspend {
19
52
}
20
53
}
21
54
};
55
+ #endif
22
56
template <bool suspend>
23
57
struct FinalSuspend {
24
58
inline constexpr auto final_suspend () noexcept {
@@ -32,13 +66,25 @@ struct FinalSuspend {
32
66
struct ReturnVoid {
33
67
inline constexpr void return_void () noexcept {}
34
68
};
35
- template <bool IsNoexcept = true >
69
+
70
+ template <class DerivedPromise >
36
71
struct UnhandledException {
37
- [[noreturn]] void unhandled_exception () noexcept { std::terminate (); }
38
- };
39
- template <>
40
- struct UnhandledException <false > {
41
- void unhandled_exception () noexcept {}
72
+ void unhandled_exception () {
73
+ // NB: for some reason, GCC doesn't destroy the coroutine frame if
74
+ // there's an exception raised inside the coroutine. As a result, if
75
+ // we're on GCC, we need to destroy it manually.
76
+
77
+ #ifdef CONDUIT_USE_GCC_EXCEPTION_WORKAROUND
78
+ DerivedPromise& promise = static_cast <DerivedPromise&>(*this );
79
+ auto coro_frame = static_cast <detail::frame_header_t *>(
80
+ std::coroutine_handle<DerivedPromise>::from_promise (promise)
81
+ .address ());
82
+ coro_frame->destroy_coro = promise.destroy_coro ;
83
+ std::coroutine_handle<>::from_address (coro_frame).destroy ();
84
+ #endif
85
+
86
+ std::rethrow_exception (std::current_exception ());
87
+ }
42
88
};
43
89
template <class Promise , bool IsNoexcept = true >
44
90
struct GetReturnObject ;
0 commit comments