Nugget
Loading...
Searching...
No Matches
coroutine.hh
Go to the documentation of this file.
1/*
2
3MIT License
4
5Copyright (c) 2022 PCSX-Redux authors
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26
27#pragma once
28
29#include <EASTL/functional.h>
30#include <EASTL/utility.h>
31
32#include <coroutine>
33#include <type_traits>
34
37#include "psyqo/kernel.hh"
38
39namespace psyqo {
40
53template <typename T = void>
54struct Coroutine {
55 struct Empty {};
56 typedef typename std::conditional<std::is_void<T>::value, Empty, T>::type SafeT;
57
58 Coroutine() = default;
59
61 if (m_handle) m_handle.destroy();
62 m_handle = nullptr;
63 m_handle = other.m_handle;
64 m_value = eastl::move(other.m_value);
65 m_suspended = other.m_suspended;
66 m_earlyResume = other.m_earlyResume;
67
68 other.m_handle = nullptr;
69 other.m_value = SafeT{};
70 other.m_suspended = true;
71 other.m_earlyResume = false;
72 }
73
75 if (this != &other) {
76 if (m_handle) m_handle.destroy();
77 m_handle = nullptr;
78 m_handle = other.m_handle;
79 m_value = eastl::move(other.m_value);
80 m_suspended = other.m_suspended;
81 m_earlyResume = other.m_earlyResume;
82
83 other.m_handle = nullptr;
84 other.m_value = SafeT{};
85 other.m_suspended = true;
86 other.m_earlyResume = false;
87 }
88 return *this;
89 }
90
91 Coroutine(Coroutine const &) = delete;
92 Coroutine &operator=(Coroutine const &) = delete;
94 if (m_handle) m_handle.destroy();
95 m_handle = nullptr;
96 }
97
106 struct Awaiter {
107 Awaiter(Awaiter &&other) = default;
108 Awaiter &operator=(Awaiter &&other) = default;
109 Awaiter(Awaiter const &) = default;
110 Awaiter &operator=(Awaiter const &) = default;
111 constexpr bool await_ready() const noexcept {
112 bool ret = m_coroutine->m_earlyResume;
113 m_coroutine->m_earlyResume = false;
114 return ret;
115 }
116 constexpr void await_suspend(std::coroutine_handle<> h) { m_coroutine->m_suspended = true; }
117 constexpr void await_resume() const noexcept {}
118
119 private:
120 Awaiter(Coroutine *coroutine) : m_coroutine(coroutine) {}
121 Coroutine *m_coroutine;
122 friend struct Coroutine;
123 };
124
131 Awaiter awaiter() & { return Awaiter(this); }
132
142 void resume() {
143 if (!m_handle) return;
144 if (!m_suspended) {
145 m_earlyResume = true;
146 return;
147 }
148 m_suspended = false;
149 m_handle.resume();
150 }
151
161 bool done() {
162 if (!m_handle) return true;
163 bool isDone = m_handle.done();
164 if (isDone) {
165 if constexpr (!std::is_void<T>::value) {
166 m_value = eastl::move(m_handle.promise().m_value);
167 }
168 m_handle.destroy();
169 m_handle = nullptr;
170 }
171 return isDone;
172 }
173
185 const SafeT &value() const { return m_value; }
186
187 private:
188 struct PromiseVoid {
189 Coroutine<> get_return_object() {
190 return Coroutine<>{eastl::move(std::coroutine_handle<Promise>::from_promise(*this))};
191 }
192 std::suspend_always initial_suspend() { return {}; }
193 std::suspend_always final_suspend() noexcept { return {}; }
194 void unhandled_exception() {}
195 void return_void() {
196 if (m_awaitingCoroutine) {
197 Kernel::queueCallback([h = m_awaitingCoroutine]() { h.resume(); });
198 m_awaitingCoroutine = nullptr;
199 }
200 }
201 [[no_unique_address]] Empty m_value;
202 std::coroutine_handle<> m_awaitingCoroutine;
203 };
204
205 struct PromiseValue {
206 Coroutine<T> get_return_object() {
207 return Coroutine{eastl::move(std::coroutine_handle<Promise>::from_promise(*this))};
208 }
209 std::suspend_always initial_suspend() { return {}; }
210 std::suspend_always final_suspend() noexcept { return {}; }
211 void unhandled_exception() {}
212 void return_value(T &&value) {
213 m_value = eastl::move(value);
214 if (m_awaitingCoroutine) {
215 Kernel::queueCallback([h = m_awaitingCoroutine]() { h.resume(); });
216 m_awaitingCoroutine = nullptr;
217 }
218 }
219 T m_value;
220 std::coroutine_handle<> m_awaitingCoroutine;
221 };
222
223 typedef typename std::conditional<std::is_void<T>::value, PromiseVoid, PromiseValue>::type Promise;
224
225 Coroutine(std::coroutine_handle<Promise> &&handle) : m_handle(eastl::move(handle)) {}
226
227 std::coroutine_handle<Promise> m_handle;
228 [[no_unique_address]] SafeT m_value;
229 bool m_suspended = true;
230 bool m_earlyResume = false;
231
232 public:
233 using promise_type = Promise;
234
244 std::coroutine_handle<Promise> handle;
245
246 explicit ChainAwaiter(std::coroutine_handle<Promise> h) : handle(h) {}
248 if (handle) handle.destroy();
249 }
250
251 ChainAwaiter(ChainAwaiter &&other) : handle(other.handle) { other.handle = nullptr; }
253 ChainAwaiter(const ChainAwaiter &) = delete;
255
256 constexpr bool await_ready() { return handle.done(); }
257
258 void await_suspend(std::coroutine_handle<> h) {
259 handle.promise().m_awaitingCoroutine = h;
260 if (!handle.done()) handle.resume();
261 }
262
263 constexpr T await_resume() {
264 if constexpr (std::is_void<T>::value) {
265 handle.destroy();
266 handle = nullptr;
267 return;
268 } else {
269 auto val = eastl::move(handle.promise().m_value);
270 handle.destroy();
271 handle = nullptr;
272 return val;
273 }
274 }
275 };
276
277 ChainAwaiter operator co_await() && {
278 auto h = m_handle;
279 m_handle = nullptr;
280 return ChainAwaiter{h};
281 }
282};
283
285 protected:
286 void initializeInternal(eastl::function<void()> &&func, void *ss_sp, unsigned ss_size);
287 void resume();
288 void yield();
289 [[nodiscard]] bool isAlive() const { return m_isAlive; }
290
291 StackfulBase() = default;
292 StackfulBase(const StackfulBase &) = delete;
294
295 private:
296 static void trampoline(void *arg) {
297 StackfulBase *self = static_cast<StackfulBase *>(arg);
298 self->trampoline();
299 }
300 void trampoline();
301 ucontext_t m_coroutine;
302 ucontext_t m_return;
303 eastl::function<void()> m_func;
304 bool m_isAlive = false;
305};
306
320template <unsigned StackSize = 0x10000>
321class Stackful : public StackfulBase {
322 public:
323 static constexpr unsigned c_stackSize = (StackSize + 7) & ~7;
324
325 Stackful() = default;
326 Stackful(const Stackful &) = delete;
327 Stackful &operator=(const Stackful &) = delete;
328
335 void initialize(eastl::function<void()> &&func) {
336 initializeInternal(eastl::move(func), m_stack.data, c_stackSize);
337 }
338
349
359
368 [[nodiscard]] bool isAlive() const { return StackfulBase::isAlive(); }
369
370 private:
371 struct alignas(8) Stack {
372 uint8_t data[c_stackSize];
373 };
374 Stack m_stack;
375};
376
377} // namespace psyqo
Definition coroutine.hh:284
void yield()
Definition coroutine.cpp:49
bool isAlive() const
Definition coroutine.hh:289
StackfulBase & operator=(const StackfulBase &)=delete
void resume()
Definition coroutine.cpp:44
StackfulBase()=default
StackfulBase(const StackfulBase &)=delete
void initializeInternal(eastl::function< void()> &&func, void *ss_sp, unsigned ss_size)
Definition coroutine.cpp:29
Stackful coroutine class.
Definition coroutine.hh:321
Stackful(const Stackful &)=delete
bool isAlive() const
Check if the coroutine is currently alive.
Definition coroutine.hh:368
void yield()
Yield the coroutine.
Definition coroutine.hh:358
Stackful()=default
void resume()
Resume the coroutine.
Definition coroutine.hh:348
void initialize(eastl::function< void()> &&func)
Initialize the coroutine with a function and an argument.
Definition coroutine.hh:335
static constexpr unsigned c_stackSize
Definition coroutine.hh:323
Stackful & operator=(const Stackful &)=delete
constexpr uint32_t move(Reg tgt, Reg src)
Definition encoder.hh:243
void queueCallback(eastl::function< void()> &&lambda)
Queues a callback to be called from the main thead.
Definition kernel.cpp:394
Definition lua.hh:38
The awaiter type.
Definition coroutine.hh:106
Awaiter(Awaiter &&other)=default
Awaiter(Awaiter const &)=default
constexpr void await_suspend(std::coroutine_handle<> h)
Definition coroutine.hh:116
Awaiter & operator=(Awaiter const &)=default
constexpr void await_resume() const noexcept
Definition coroutine.hh:117
constexpr bool await_ready() const noexcept
Definition coroutine.hh:111
Awaiter & operator=(Awaiter &&other)=default
The awaiter type for coroutine-to-coroutine chaining via co_await.
Definition coroutine.hh:243
constexpr T await_resume()
Definition coroutine.hh:263
constexpr bool await_ready()
Definition coroutine.hh:256
void await_suspend(std::coroutine_handle<> h)
Definition coroutine.hh:258
std::coroutine_handle< Promise > handle
Definition coroutine.hh:244
ChainAwaiter & operator=(const ChainAwaiter &)=delete
ChainAwaiter & operator=(ChainAwaiter &&)=delete
~ChainAwaiter()
Definition coroutine.hh:247
ChainAwaiter(ChainAwaiter &&other)
Definition coroutine.hh:251
ChainAwaiter(const ChainAwaiter &)=delete
ChainAwaiter(std::coroutine_handle< Promise > h)
Definition coroutine.hh:246
Definition coroutine.hh:55
A suitable type to hold and return a C++20 coroutine.
Definition coroutine.hh:54
Coroutine(Coroutine const &)=delete
~Coroutine()
Definition coroutine.hh:93
Coroutine & operator=(Coroutine const &)=delete
Promise promise_type
Definition coroutine.hh:233
const SafeT & value() const
Returns the value returned by the coroutine.
Definition coroutine.hh:185
Coroutine(Coroutine &&other)
Definition coroutine.hh:60
Coroutine()=default
std::conditional< std::is_void< T >::value, Empty, T >::type SafeT
Definition coroutine.hh:56
Coroutine & operator=(Coroutine &&other)
Definition coroutine.hh:74
void resume()
Resumes the coroutine.
Definition coroutine.hh:142
Awaiter awaiter() &
Creates an Awaiter object.
Definition coroutine.hh:131
bool done()
Returns the status of the coroutine.
Definition coroutine.hh:161
Definition ucontext.h:45
static int ret
Definition syscalls.h:72
void void(ptr, size)
uint16_t val
Definition timers.c:319