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
235 constexpr bool await_ready() { return m_handle.done(); }
236 template <typename U>
237 constexpr void await_suspend(std::coroutine_handle<U> h) {
238 m_handle.promise().m_awaitingCoroutine = h;
239 resume();
240 }
241 constexpr T await_resume() {
242 if constexpr (std::is_void<T>::value) {
243 return;
244 } else {
245 return eastl::move(m_handle.promise().m_value);
246 }
247 }
248};
249
251 protected:
252 void initializeInternal(eastl::function<void()> &&func, void *ss_sp, unsigned ss_size);
253 void resume();
254 void yield();
255 [[nodiscard]] bool isAlive() const { return m_isAlive; }
256
257 StackfulBase() = default;
258 StackfulBase(const StackfulBase &) = delete;
260
261 private:
262 static void trampoline(void *arg) {
263 StackfulBase *self = static_cast<StackfulBase *>(arg);
264 self->trampoline();
265 }
266 void trampoline();
267 ucontext_t m_coroutine;
268 ucontext_t m_return;
269 eastl::function<void()> m_func;
270 bool m_isAlive = false;
271};
272
286template <unsigned StackSize = 0x10000>
287class Stackful : public StackfulBase {
288 public:
289 static constexpr unsigned c_stackSize = (StackSize + 7) & ~7;
290
291 Stackful() = default;
292 Stackful(const Stackful &) = delete;
293 Stackful &operator=(const Stackful &) = delete;
294
301 void initialize(eastl::function<void()> &&func) {
302 initializeInternal(eastl::move(func), m_stack.data, c_stackSize);
303 }
304
315
325
334 [[nodiscard]] bool isAlive() const { return StackfulBase::isAlive(); }
335
336 private:
337 struct alignas(8) Stack {
338 uint8_t data[c_stackSize];
339 };
340 Stack m_stack;
341};
342
343} // namespace psyqo
Definition coroutine.hh:250
void yield()
Definition coroutine.cpp:49
bool isAlive() const
Definition coroutine.hh:255
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:287
Stackful(const Stackful &)=delete
bool isAlive() const
Check if the coroutine is currently alive.
Definition coroutine.hh:334
void yield()
Yield the coroutine.
Definition coroutine.hh:324
Stackful()=default
void resume()
Resume the coroutine.
Definition coroutine.hh:314
void initialize(eastl::function< void()> &&func)
Initialize the coroutine with a function and an argument.
Definition coroutine.hh:301
static constexpr unsigned c_stackSize
Definition coroutine.hh:289
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
Definition coroutine.hh:55
A suitable type to hold and return a C++20 coroutine.
Definition coroutine.hh:54
Coroutine(Coroutine const &)=delete
Awaiter awaiter()
Creates an Awaiter object.
Definition coroutine.hh:131
~Coroutine()
Definition coroutine.hh:93
Coroutine & operator=(Coroutine const &)=delete
constexpr T await_resume()
Definition coroutine.hh:241
constexpr bool await_ready()
Definition coroutine.hh:235
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
bool done()
Returns the status of the coroutine.
Definition coroutine.hh:161
constexpr void await_suspend(std::coroutine_handle< U > h)
Definition coroutine.hh:237
Definition ucontext.h:45
static int ret
Definition syscalls.h:72
void void(ptr, size)