@@ -34,6 +34,7 @@ SOFTWARE.
34
34
35
35
#include " common/psxlibc/ucontext.h"
36
36
#include " common/syscalls/syscalls.h"
37
+ #include " psyqo/kernel.hh"
37
38
38
39
namespace psyqo {
39
40
@@ -43,7 +44,7 @@ namespace psyqo {
43
44
* @details C++20 introduced the concept of coroutines in the language. This
44
45
* type can be used to properly hold a coroutine and yield and resume it
45
46
* within psyqo. An important caveat of using coroutines is that the language
46
- * insist on calling `new` and `delete` silently within the coroutine object.
47
+ * insists on calling `new` and `delete` silently within the coroutine object.
47
48
* This may be a problem for users who don't want to use the heap.
48
49
*
49
50
* @tparam T The type the coroutine returns. `void` by default.
@@ -55,16 +56,50 @@ struct Coroutine {
55
56
typedef typename std::conditional<std::is_void<T>::value, Empty, T>::type SafeT;
56
57
57
58
Coroutine () = default ;
58
- Coroutine (Coroutine &&other) = default ;
59
- Coroutine &operator =(Coroutine &&other) = default ;
59
+
60
+ Coroutine (Coroutine &&other) {
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
+
74
+ Coroutine &operator =(Coroutine &&other) {
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
+
60
91
Coroutine (Coroutine const &) = delete ;
61
92
Coroutine &operator =(Coroutine const &) = delete ;
93
+ ~Coroutine () {
94
+ if (m_handle) m_handle.destroy ();
95
+ m_handle = nullptr ;
96
+ }
62
97
63
98
/* *
64
99
* @brief The awaiter type.
65
100
*
66
101
* @details The awaiter type is the type that is used to suspend the coroutine
67
- * after scheduling an asychronous operation. The keyword `co_await` can be used
102
+ * after scheduling an asynchronous operation. The keyword `co_await` can be used
68
103
* on an instance of the object to suspend the current coroutine. Creating an
69
104
* instance of this object is done by calling `coroutine.awaiter()`.
70
105
*/
@@ -158,16 +193,15 @@ struct Coroutine {
158
193
std::suspend_always final_suspend () noexcept { return {}; }
159
194
void unhandled_exception () {}
160
195
void return_void () {
161
- auto awaitingCoroutine = m_awaitingCoroutine;
162
- if (awaitingCoroutine) {
163
- // This doesn't feel right, but I don't know how to do it otherwise,
164
- // since the coroutine is a template and I can't forward the type.
165
- __builtin_coro_resume (awaitingCoroutine);
196
+ if (m_awaitingCoroutine) {
197
+ Kernel::queueCallback ([h = m_awaitingCoroutine]() { h.resume (); });
198
+ m_awaitingCoroutine = nullptr ;
166
199
}
167
200
}
168
201
[[no_unique_address]] Empty m_value;
169
- void * m_awaitingCoroutine = nullptr ;
202
+ std::coroutine_handle<> m_awaitingCoroutine;
170
203
};
204
+
171
205
struct PromiseValue {
172
206
Coroutine<T> get_return_object () {
173
207
return Coroutine{eastl::move (std::coroutine_handle<Promise>::from_promise (*this ))};
@@ -177,21 +211,21 @@ struct Coroutine {
177
211
void unhandled_exception () {}
178
212
void return_value (T &&value) {
179
213
m_value = eastl::move (value);
180
- auto awaitingCoroutine = m_awaitingCoroutine;
181
- if (awaitingCoroutine) {
182
- // This doesn't feel right, but I don't know how to do it otherwise,
183
- // since the coroutine is a template and I can't forward the type.
184
- __builtin_coro_resume (awaitingCoroutine);
214
+ if (m_awaitingCoroutine) {
215
+ Kernel::queueCallback ([h = m_awaitingCoroutine]() { h.resume (); });
216
+ m_awaitingCoroutine = nullptr ;
185
217
}
186
218
}
187
219
T m_value;
188
- void * m_awaitingCoroutine = nullptr ;
220
+ std::coroutine_handle<> m_awaitingCoroutine;
189
221
};
222
+
190
223
typedef typename std::conditional<std::is_void<T>::value, PromiseVoid, PromiseValue>::type Promise;
224
+
191
225
Coroutine (std::coroutine_handle<Promise> &&handle) : m_handle(eastl::move(handle)) {}
226
+
192
227
std::coroutine_handle<Promise> m_handle;
193
228
[[no_unique_address]] SafeT m_value;
194
- void *m_awaitingCoroutine = nullptr ;
195
229
bool m_suspended = true ;
196
230
bool m_earlyResume = false ;
197
231
@@ -201,31 +235,32 @@ struct Coroutine {
201
235
constexpr bool await_ready () { return m_handle.done (); }
202
236
template <typename U>
203
237
constexpr void await_suspend (std::coroutine_handle<U> h) {
204
- auto &promise = m_handle.promise ();
205
- promise.m_awaitingCoroutine = h.address ();
238
+ m_handle.promise ().m_awaitingCoroutine = h;
206
239
resume ();
207
240
}
208
- constexpr SafeT await_resume () {
209
- SafeT value = eastl::move (m_handle.promise ().m_value );
210
- m_handle.destroy ();
211
- return value;
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
+ }
212
247
}
213
248
};
214
249
215
250
class StackfulBase {
216
251
protected:
217
- void initializeInternal (eastl::function<void ()>&& func, void* ss_sp, unsigned ss_size);
252
+ void initializeInternal (eastl::function<void ()> && func, void * ss_sp, unsigned ss_size);
218
253
void resume ();
219
254
void yield ();
220
255
[[nodiscard]] bool isAlive () const { return m_isAlive; }
221
256
222
257
StackfulBase () = default ;
223
- StackfulBase (const StackfulBase&) = delete ;
224
- StackfulBase& operator =(const StackfulBase&) = delete ;
258
+ StackfulBase (const StackfulBase &) = delete ;
259
+ StackfulBase & operator =(const StackfulBase &) = delete ;
225
260
226
261
private:
227
- static void trampoline (void * arg) {
228
- StackfulBase* self = static_cast <StackfulBase*>(arg);
262
+ static void trampoline (void * arg) {
263
+ StackfulBase * self = static_cast <StackfulBase *>(arg);
229
264
self->trampoline ();
230
265
}
231
266
void trampoline ();
@@ -254,16 +289,16 @@ class Stackful : public StackfulBase {
254
289
static constexpr unsigned c_stackSize = (StackSize + 7 ) & ~7 ;
255
290
256
291
Stackful () = default ;
257
- Stackful (const Stackful&) = delete ;
258
- Stackful& operator =(const Stackful&) = delete ;
292
+ Stackful (const Stackful &) = delete ;
293
+ Stackful & operator =(const Stackful &) = delete ;
259
294
260
295
/* *
261
296
* @brief Initialize the coroutine with a function and an argument.
262
297
*
263
298
* @param func Function to be executed by the coroutine.
264
299
* @param arg Argument to be passed to the function.
265
300
*/
266
- void initialize (eastl::function<void ()>&& func) {
301
+ void initialize (eastl::function<void ()> && func) {
267
302
initializeInternal (eastl::move (func), m_stack.data , c_stackSize);
268
303
}
269
304
0 commit comments