Skip to content

Commit 287fc74

Browse files
committed
Use symmetric transfer in PCSX::Coroutine final_suspend
Resuming the awaiting coroutine from return_void/return_value meant destroy() could be called on the inner coroutine before it reached final_suspend, which is UB. Move the handoff to final_suspend's await_suspend using symmetric transfer. Signed-off-by: Nicolas 'Pixel' Noble <nicolas@nobis-crew.org>
1 parent 5d495f9 commit 287fc74

1 file changed

Lines changed: 19 additions & 12 deletions

File tree

src/support/coroutine.h

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -123,43 +123,50 @@ struct Coroutine {
123123

124124
private:
125125
struct PromiseVoid {
126+
struct FinalAwaiter {
127+
bool await_ready() const noexcept { return false; }
128+
std::coroutine_handle<> await_suspend(std::coroutine_handle<PromiseVoid> h) const noexcept {
129+
auto next = h.promise().m_awaitingCoroutine;
130+
return next ? next : std::noop_coroutine();
131+
}
132+
void await_resume() const noexcept {}
133+
};
126134
Coroutine<> get_return_object() {
127135
return Coroutine<>{std::move(std::coroutine_handle<Promise>::from_promise(*this))};
128136
}
129137
std::suspend_always initial_suspend() { return {}; }
130-
std::suspend_always final_suspend() noexcept { return {}; }
138+
FinalAwaiter final_suspend() noexcept { return {}; }
131139
void unhandled_exception() {}
132140
template <typename From>
133141
From yield_value(From &&from) {
134142
return std::forward<From>(from);
135143
}
136-
void return_void() {
137-
if (m_awaitingCoroutine) {
138-
m_awaitingCoroutine.resume();
139-
m_awaitingCoroutine = nullptr;
140-
}
141-
}
144+
void return_void() {}
142145
[[no_unique_address]] Empty m_value;
143146
std::coroutine_handle<> m_awaitingCoroutine;
144147
};
145148

146149
struct PromiseValue {
150+
struct FinalAwaiter {
151+
bool await_ready() const noexcept { return false; }
152+
std::coroutine_handle<> await_suspend(std::coroutine_handle<PromiseValue> h) const noexcept {
153+
auto next = h.promise().m_awaitingCoroutine;
154+
return next ? next : std::noop_coroutine();
155+
}
156+
void await_resume() const noexcept {}
157+
};
147158
Coroutine<T> get_return_object() {
148159
return Coroutine{std::move(std::coroutine_handle<Promise>::from_promise(*this))};
149160
}
150161
std::suspend_always initial_suspend() { return {}; }
151-
std::suspend_always final_suspend() noexcept { return {}; }
162+
FinalAwaiter final_suspend() noexcept { return {}; }
152163
void unhandled_exception() {}
153164
template <typename From>
154165
From yield_value(From &&from) {
155166
return std::forward<From>(from);
156167
}
157168
void return_value(T &&value) {
158169
m_value = std::move(value);
159-
if (m_awaitingCoroutine) {
160-
m_awaitingCoroutine.resume();
161-
m_awaitingCoroutine = nullptr;
162-
}
163170
}
164171
T m_value;
165172
std::coroutine_handle<> m_awaitingCoroutine;

0 commit comments

Comments
 (0)