8 months ago |
4 |
1 |
#include "coroutine.h" | ||
2 |
#include <assert.h> | ||||
3 |
#include <setjmp.h> | ||||
4 |
#include <stdbool.h> | ||||
5 |
#include <stddef.h> | ||||
5 months ago |
6 |
#include <stdio.h> | |||
7 months ago |
7 |
#include "cor_platform.h" | |||
8 months ago |
4 |
8 |
|||
6 months ago |
9 |
// see CPython again, this time from ctypes.h | |||
10 |
#if (defined (__SVR4) && defined (__sun)) || defined(COROUTINE_HAVE_ALLOCA_H) | ||||
11 |
# include <alloca.h> | ||||
12 |
#elif defined(MS_WIN32) | ||||
13 |
# include <malloc.h> | ||||
14 |
#endif | ||||
8 months ago |
4 |
15 |
|||
6 months ago |
16 |
/* If the system does not define alloca(), we have to hope for a compiler builtin. */ | |||
17 |
#ifndef alloca | ||||
18 |
# if defined __GNUC__ || (__clang_major__ >= 4) | ||||
19 |
# define alloca __builtin_alloca | ||||
20 |
# else | ||||
21 |
# error "Could not define alloca() on your platform." | ||||
22 |
# endif | ||||
23 |
#endif | ||||
24 |
|||||
3 months ago |
25 |
typedef struct Coroutines Coroutines; | |||
26 |
|||||
6 days ago |
27 |
static void Coroutine_NS(RunNext)(void); | |||
28 |
static Coroutine_Err Coroutine_NS(Continue_)(Coroutines *cors, Coroutine *cor, void *value, bool early); | ||||
6 days ago |
29 |
static uintptr_t StackTopNow(void); | |||
8 months ago |
30 |
||||
3 months ago |
31 |
#ifndef NDEBUG | |||
32 |
// In debug builds, use the built-in assert | ||||
33 |
#define MyAssert assert | ||||
34 |
#else | ||||
35 |
#if 1 | ||||
36 |
// In non-debug builds, normally use this - all the asserts are disabled | ||||
37 |
#define MyAssert(cond) | ||||
38 |
#else | ||||
39 |
// In non-debug builds with stack problems, you can use this. | ||||
40 |
// This activates all the asserts, and gives a line to put a | ||||
41 |
// breakpoint in your debugger. | ||||
42 |
static void _MyAssert(bool cond, char const *msg) | ||||
43 |
{ | ||||
44 |
if (!cond){ | ||||
45 |
fputs("Assertion failed: ", stdout); | ||||
46 |
fputs(msg, stdout); | ||||
47 |
fputs("\n", stdout); | ||||
48 |
} | ||||
49 |
} | ||||
50 |
#define MyAssert(cond) _MyAssert(cond, #cond) | ||||
51 |
#endif | ||||
52 |
#endif | ||||
53 |
|||||
3 months ago |
54 |
#define CHECK_SYSTEM_RUNNING \ | |||
55 |
if (!g_c){ \ | ||||
56 |
return Coroutine_Err_SystemNotRunning; \ | ||||
57 |
} | ||||
58 |
#define CHECK_SYSTEM_NOT_RUNNING \ | ||||
59 |
if (g_c){ \ | ||||
60 |
return Coroutine_Err_SystemRunning; \ | ||||
61 |
} | ||||
62 |
#define CHECK_COROUTINE_THREAD \ | ||||
63 |
if (cor->coroutines != g_c){ \ | ||||
64 |
return Coroutine_Err_CoroutineFromWrongThread; \ | ||||
65 |
} | ||||
66 |
#define CHECK_NO_COROUTINE_RUNNING \ | ||||
67 |
if (g_c->state != Coroutines_Started){ \ | ||||
68 |
return Coroutine_Err_ACoroutineIsAlreadyRunning; \ | ||||
69 |
} | ||||
70 |
#define CHECK_STACK_OVERRUN \ | ||||
71 |
{ \ | ||||
6 days ago |
72 |
Coroutine_Err err = Coroutine_NS(StackHasOverrun)(); \ | |||
3 months ago |
73 |
if (err){ \ | |||
74 |
return err; \ | ||||
75 |
} \ | ||||
76 |
} while (0); | ||||
77 |
|||||
2 days ago |
78 |
||||
79 |
static inline void ready_jmp_buf(jmp_buf buf) { | ||||
80 |
#if defined(_M_X64) || defined(_M_ARM64) | ||||
81 |
// Win64: | ||||
82 |
// Set Frame to 0 on Windows 64 bit to prevent C++ stack unwinding in longjmp(). | ||||
83 |
// Win32: | ||||
84 |
// Doesn't do this, so only needed on the 2 64 bit Windows versions | ||||
85 |
((_JUMP_BUFFER*)buf)->Frame = 0; | ||||
86 |
#endif | ||||
87 |
} | ||||
88 |
|||||
8 months ago |
4 |
89 |
/////////////////////////////////////////////////////////////////////////////// | ||
90 |
// 2-way linked lists... | ||||
91 |
// | ||||
8 months ago |
92 |
// Brought inline here to avoid namespace polution | |||
8 months ago |
4 |
93 |
/////////////////////////////////////////////////////////////////////////////// | ||
94 |
|||||
95 |
typedef struct List_Link List_Link; | ||||
96 |
struct List_Link { | ||||
97 |
List_Link *next; | ||||
98 |
List_Link *prev; | ||||
99 |
}; | ||||
100 |
|||||
101 |
typedef struct List_Head List_Head; | ||||
102 |
struct List_Head { | ||||
103 |
union { | ||||
104 |
struct { | ||||
105 |
List_Link link; | ||||
106 |
List_Link *filler; | ||||
107 |
} fwd; | ||||
108 |
struct { | ||||
109 |
List_Link *filler; | ||||
110 |
List_Link link; | ||||
111 |
} back; | ||||
112 |
}; | ||||
113 |
}; | ||||
114 |
|||||
8 months ago |
115 |
||||
116 |
static inline bool List_IsEmpty( | ||||
117 |
const List_Head *list | ||||
118 |
){ | ||||
8 months ago |
4 |
119 |
return list->fwd.link.next == &list->back.link; | ||
120 |
} | ||||
121 |
|||||
8 months ago |
122 |
||||
123 |
static inline List_Link *List_GetHead( | ||||
124 |
const List_Head *list | ||||
125 |
){ | ||||
8 months ago |
4 |
126 |
return List_IsEmpty(list) ? NULL : list->fwd.link.next; | ||
127 |
} | ||||
8 months ago |
128 |
||||
129 |
|||||
5 months ago |
130 |
static inline List_Link *List_Begin( | |||
131 |
const List_Head *list | ||||
132 |
){ | ||||
133 |
return list->fwd.link.next; | ||||
134 |
} | ||||
135 |
|||||
136 |
|||||
137 |
static inline bool Link_NextIsLink( | ||||
138 |
const List_Link *link | ||||
139 |
){ | ||||
140 |
return link->next != NULL; | ||||
141 |
} | ||||
142 |
|||||
143 |
|||||
144 |
static inline List_Link *Link_Next( | ||||
145 |
List_Link *link | ||||
146 |
){ | ||||
147 |
return link->next; | ||||
148 |
} | ||||
149 |
|||||
150 |
|||||
151 |
static inline bool Link_PrevIsLink( | ||||
152 |
const List_Link *link | ||||
153 |
){ | ||||
154 |
return link->prev != NULL; | ||||
155 |
} | ||||
156 |
|||||
157 |
|||||
158 |
static inline List_Link *Link_Prev( | ||||
159 |
List_Link *link | ||||
160 |
){ | ||||
161 |
return link->prev; | ||||
162 |
} | ||||
163 |
|||||
5 months ago |
164 |
static inline List_Link *List_GetTail( | |||
165 |
const List_Head *list | ||||
166 |
){ | ||||
167 |
return List_IsEmpty(list) ? NULL : list->back.link.prev; | ||||
168 |
} | ||||
8 months ago |
169 |
||||
170 |
|||||
8 months ago |
4 |
171 |
#define OFFSETOF(Container, Field) ((char *)&((Container *)4)->Field - (char *)(Container *)4) | ||
172 |
#define List_Link_Container(Container, Link, link) ((Container *)((char *)(link) - OFFSETOF(Container, Link))) | ||||
173 |
|||||
8 months ago |
174 |
||||
6 days ago |
175 |
static inline void | |||
176 |
List_Init( | ||||
8 months ago |
177 |
List_Head *list | |||
178 |
){ | ||||
8 months ago |
4 |
179 |
list->fwd.link.next = &list->back.link; | ||
180 |
list->fwd.link.prev = NULL; | ||||
181 |
list->back.link.prev = &list->fwd.link; | ||||
182 |
} | ||||
183 |
|||||
8 months ago |
184 |
||||
6 days ago |
185 |
static inline void | |||
186 |
Link_AddAfter( | ||||
5 months ago |
187 |
List_Link *link, | |||
188 |
List_Link *after | ||||
189 |
){ | ||||
190 |
link->next = after->next; | ||||
191 |
link->prev = after; | ||||
192 |
after->next->prev = link; | ||||
193 |
after->next = link; | ||||
194 |
} | ||||
195 |
|||||
196 |
|||||
6 days ago |
197 |
static inline void | |||
198 |
List_AddHead( | ||||
8 months ago |
199 |
List_Head *list, | |||
200 |
List_Link *link | ||||
201 |
){ | ||||
5 months ago |
202 |
Link_AddAfter(link, &list->fwd.link); | |||
8 months ago |
4 |
203 |
} | ||
204 |
|||||
8 months ago |
205 |
||||
6 days ago |
206 |
static inline void | |||
207 |
Link_AddBefore( | ||||
5 months ago |
208 |
List_Link *link, | |||
209 |
List_Link *before | ||||
210 |
){ | ||||
211 |
link->prev = before->prev; | ||||
212 |
link->next = before; | ||||
213 |
before->prev->next = link; | ||||
214 |
before->prev = link; | ||||
215 |
} | ||||
216 |
|||||
217 |
|||||
6 days ago |
218 |
static inline void | |||
219 |
List_AddTail( | ||||
8 months ago |
220 |
List_Head *list, | |||
221 |
List_Link *link | ||||
222 |
){ | ||||
5 months ago |
223 |
Link_AddBefore(link, &list->back.link); | |||
8 months ago |
4 |
224 |
} | ||
225 |
|||||
8 months ago |
226 |
||||
6 days ago |
227 |
static inline void | |||
228 |
Link_Remove( | ||||
8 months ago |
229 |
List_Link *link | |||
230 |
){ | ||||
8 months ago |
4 |
231 |
link->prev->next = link->next; | ||
232 |
link->next->prev = link->prev; | ||||
233 |
} | ||||
234 |
|||||
235 |
/////////////////////////////////////////////////////////////////////////////// | ||||
236 |
// ...2-way linked lists | ||||
237 |
/////////////////////////////////////////////////////////////////////////////// | ||||
238 |
|||||
239 |
enum { | ||||
240 |
Coroutines_Starting, | ||||
241 |
Coroutines_Started, | ||||
242 |
Coroutines_Active, | ||||
243 |
Coroutines_Stopping | ||||
244 |
}; | ||||
245 |
|||||
246 |
enum { | ||||
247 |
Chunk_Initial, | ||||
248 |
Chunk_Create, | ||||
5 months ago |
249 |
Chunk_Split, | |||
8 months ago |
4 |
250 |
Chunk_Enter | ||
251 |
}; | ||||
252 |
|||||
8 months ago |
253 |
typedef enum Coroutine_State { | |||
2 days ago |
254 |
Coroutine_Free, | |||
255 |
Coroutine_Idle, | ||||
256 |
Coroutine_Running, | ||||
257 |
Coroutine_Waiting, | ||||
258 |
Coroutine_Complete | ||||
8 months ago |
259 |
} Coroutine_State; | |||
8 months ago |
4 |
260 |
|||
261 |
enum { | ||||
262 |
Coroutines_Init, | ||||
263 |
Coroutines_AllocatedChunk, | ||||
264 |
Coroutines_CoroutineComplete, | ||||
265 |
}; | ||||
266 |
|||||
267 |
struct Coroutine { | ||||
6 months ago |
268 |
Coroutines *coroutines; // so can work with it off-thread | |||
269 |
List_Link link; // for whichever list it's on | ||||
5 months ago |
270 |
List_Link all_link; // list of all Coroutines | |||
6 months ago |
271 |
jmp_buf buf; // how to get back to it | |||
5 months ago |
272 |
unsigned char *prev_limit; // the previous Coroutine's stack limit | |||
273 |
unsigned char *base; // where the base (high address) of this Coroutine's stack is | ||||
274 |
unsigned char *limit; // where the limit (low address) of this Coroutine's stack is | ||||
6 months ago |
275 |
unsigned char *guard; // where the stack overrun guard is | |||
5 months ago |
276 |
size_t size; | |||
6 months ago |
277 |
Coroutine_Start start; // entry point | |||
278 |
void *entry_param; // to pass to start | ||||
279 |
void *value; // yielded/returned | ||||
280 |
unsigned char *stack_top; // recorded at yield | ||||
8 months ago |
281 |
Coroutine_State state; | |||
8 months ago |
4 |
282 |
}; | ||
283 |
|||||
284 |
struct Coroutines { | ||||
7 months ago |
285 |
_Cor_Mutex mutex; | |||
6 days ago |
286 |
jmp_buf controller; // to return from Coroutine_NS(Run) | |||
8 months ago |
287 |
jmp_buf chunk_allocated;// for chunk allocation | |||
5 months ago |
288 |
size_t gap_before; // bytes between previous's stack_top and next's Coroutine | |||
289 |
size_t gap_after; // bytes between Coroutine and stack_base | ||||
8 months ago |
4 |
290 |
|||
291 |
// singletons | ||||
292 |
Coroutine *tip; // top of stack chunk | ||||
293 |
Coroutine *active; // currently running coroutine | ||||
6 days ago |
294 |
Coroutine *primary; // Coroutine_NS(Run) coroutine | |||
6 months ago |
295 |
unsigned char *stack_limit; // when not NULL, where the stack finishes | |||
8 months ago |
4 |
296 |
|||
297 |
// lists | ||||
5 months ago |
298 |
List_Head all; // all Coroutines (in address order) | |||
299 |
List_Head free; // free Coroutines | ||||
8 months ago |
4 |
300 |
List_Head inactive; // idle or complete | ||
301 |
List_Head runable; // running or waiting to run | ||||
302 |
List_Head waiting; // yielded / waiting to run | ||||
7 months ago |
303 |
_Cor_Mutex waiting_mutex; | |||
8 months ago |
4 |
304 |
|||
8 months ago |
305 |
// Summary of the system | |||
306 |
Coroutine_Report report; | ||||
307 |
|||||
8 months ago |
4 |
308 |
// state | ||
309 |
char state; | ||||
310 |
}; | ||||
311 |
|||||
5 months ago |
312 |
_Cor_thread_local Coroutines *g_c; | |||
4 months ago |
313 |
_Cor_thread_local unsigned char *g_stack_limit; | |||
8 months ago |
4 |
314 |
|||
5 months ago |
315 |
static void ReserveStackSpace(Coroutines *cors, Coroutine *parent, size_t chunk_size, unsigned char *childs_limit); | |||
5 months ago |
316 |
static void stack_chunk_base(Coroutines *cors, Coroutine *parent, unsigned char *prev_limit, unsigned char *limit); | |||
8 months ago |
4 |
317 |
|||
8 months ago |
318 |
||||
6 months ago |
319 |
#define GUARD_PATTERN_SIZE (4) | |||
8 months ago |
320 |
// Check whether the guard is intact | |||
6 days ago |
321 |
static inline bool | |||
322 |
Guard_Pattern_OK( | ||||
8 months ago |
323 |
unsigned char *guard | |||
324 |
){ | ||||
6 months ago |
325 |
return !guard || | |||
326 |
(guard[0] == 0xde && | ||||
327 |
guard[1] == 0xad && | ||||
328 |
guard[2] == 0xbe && | ||||
329 |
guard[3] == 0xef); | ||||
8 months ago |
330 |
} | |||
331 |
|||||
332 |
|||||
6 days ago |
333 |
static inline void | |||
334 |
Apply_Guard(unsigned char *guard){ | ||||
6 months ago |
335 |
guard[0] = 0xde; | |||
336 |
guard[1] = 0xad; | ||||
337 |
guard[2] = 0xbe; | ||||
338 |
guard[3] = 0xef; | ||||
339 |
} | ||||
340 |
|||||
341 |
|||||
3 months ago |
342 |
#ifndef NDEBUG | |||
6 days ago |
343 |
static Coroutine_Err | |||
344 |
CheckListIntegrity( | ||||
345 |
List_Head *head, | ||||
346 |
Coroutine_State state1, | ||||
347 |
Coroutine_State state2 | ||||
348 |
){ | ||||
3 months ago |
349 |
for (List_Link *link = List_Begin(head); Link_NextIsLink(link); link = Link_Next(link)){ | |||
350 |
Coroutine *candidate = List_Link_Container(Coroutine, link, link); | ||||
3 months ago |
351 |
if (candidate->coroutines != g_c){ | |||
352 |
return Coroutine_Err_InternalInsistency; | ||||
353 |
} | ||||
354 |
if(candidate->state != state1 && candidate->state != state2){ | ||||
355 |
return Coroutine_Err_InternalInsistency; | ||||
356 |
} | ||||
3 months ago |
357 |
bool found = false; | |||
358 |
for (List_Link *link = List_Begin(&g_c->all); Link_NextIsLink(link); link = Link_Next(link)){ | ||||
359 |
Coroutine *candidate2 = List_Link_Container(Coroutine, all_link, link); | ||||
360 |
if (candidate == candidate2){ | ||||
361 |
found = true; | ||||
362 |
} | ||||
363 |
} | ||||
3 months ago |
364 |
if (!found){ | |||
365 |
return Coroutine_Err_InternalInsistency; | ||||
366 |
} | ||||
3 months ago |
367 |
} | |||
3 months ago |
368 |
return Coroutine_OK; | |||
3 months ago |
369 |
} | |||
370 |
|||||
371 |
|||||
6 days ago |
372 |
static Coroutine_Err | |||
373 |
Coroutine_NS(CheckIntegrity_)( | ||||
374 |
void | ||||
375 |
){ | ||||
3 months ago |
376 |
Coroutine_Err err; | |||
2 days ago |
377 |
err = CheckListIntegrity(&g_c->free, Coroutine_Free, Coroutine_Free); | |||
3 months ago |
378 |
if (err){ | |||
379 |
return err; | ||||
380 |
} | ||||
2 days ago |
381 |
err = CheckListIntegrity(&g_c->inactive, Coroutine_Idle, Coroutine_Complete); | |||
3 months ago |
382 |
if (err){ | |||
383 |
return err; | ||||
384 |
} | ||||
2 days ago |
385 |
err = CheckListIntegrity(&g_c->runable, Coroutine_Running, Coroutine_Running); | |||
3 months ago |
386 |
if (err){ | |||
387 |
return err; | ||||
388 |
} | ||||
2 days ago |
389 |
err = CheckListIntegrity(&g_c->waiting, Coroutine_Waiting, Coroutine_Waiting); | |||
3 months ago |
390 |
return err; | |||
3 months ago |
391 |
} | |||
3 months ago |
392 |
#endif | |||
393 |
|||||
394 |
|||||
6 days ago |
395 |
static Coroutine_Err | |||
396 |
Coroutine_NS(StackHasOverrun)( | ||||
397 |
void | ||||
398 |
){ | ||||
6 days ago |
399 |
unsigned char *stack_top = (unsigned char *)StackTopNow(); | |||
5 months ago |
400 |
unsigned char *stack_limit = g_c ? g_c->stack_limit : NULL; | |||
6 months ago |
401 |
if (stack_limit && stack_top < stack_limit){ | |||
3 months ago |
402 |
// printf("top %p < limit %p\n", stack_top, stack_limit); | |||
6 months ago |
403 |
// current stack top is beyond limit - we are overrunning NOW | |||
3 months ago |
404 |
return Coroutine_Err_StackOverrun; | |||
6 months ago |
405 |
} | |||
3 months ago |
406 |
// if (stack_limit && stack_top < stack_limit+2048){ | |||
407 |
// printf("Stack LOW hazard\n"); | ||||
408 |
// } | ||||
5 months ago |
409 |
Coroutine *me = g_c ? g_c->active : NULL; | |||
6 months ago |
410 |
if (!me){ | |||
3 months ago |
411 |
return Coroutine_OK; | |||
6 months ago |
412 |
} | |||
Last week |
413 |
Coroutine_Err err; | |||
3 months ago |
414 |
#if COROUTINE_CHECK_INTEGRITY_ON_STACK_CHECK | |||
415 |
// Check all coroutines integrity | ||||
2 days ago |
416 |
err = Coroutine_NS(CheckIntegrity_)(); | |||
3 months ago |
417 |
if (err){ | |||
418 |
return err; | ||||
419 |
} | ||||
3 months ago |
420 |
#endif | |||
6 months ago |
421 |
if (me->guard){ | |||
Last week |
422 |
err = Guard_Pattern_OK(me->guard) ? Coroutine_OK : Coroutine_Err_StackOverrun; | |||
423 |
if (err){ | ||||
424 |
printf("Guard pattern trampled\n"); | ||||
425 |
} | ||||
426 |
return err; | ||||
6 months ago |
427 |
} | |||
Last week |
428 |
err = stack_top >= me->limit ? Coroutine_OK : Coroutine_Err_StackOverrun; | |||
429 |
if (err){ | ||||
430 |
printf("Stack top beyond active stack limit\n"); | ||||
431 |
} | ||||
432 |
return err; | ||||
6 months ago |
433 |
} | |||
434 |
|||||
Last week |
435 |
#ifndef NDEBUG | |||
6 days ago |
436 |
Coroutine_Err | |||
437 |
Coroutine_NS(CheckIntegrity)( | ||||
438 |
void | ||||
439 |
){ | ||||
440 |
Coroutine_Err err = Coroutine_NS(StackHasOverrun)(); | ||||
Last week |
441 |
#if !COROUTINE_CHECK_INTEGRITY_ON_STACK_CHECK | |||
442 |
if (!err && g_c){ | ||||
6 days ago |
443 |
err = Coroutine_NS(CheckIntegrity_)(); | |||
Last week |
444 |
} | |||
445 |
#endif | ||||
446 |
return err; | ||||
447 |
} | ||||
448 |
#endif | ||||
6 months ago |
449 |
||||
Last week |
450 |
||||
6 days ago |
451 |
static void | |||
452 |
ReserveStackSpace( | ||||
5 months ago |
453 |
Coroutines *cors, | |||
6 months ago |
454 |
Coroutine *parent, | |||
5 months ago |
455 |
size_t chunk_size, | |||
456 |
unsigned char *childs_limit | ||||
8 months ago |
457 |
){ | |||
6 months ago |
458 |
unsigned char *chunk_of_stack = alloca(chunk_size); | |||
459 |
#if COROUTINE_RECORD_LOWEST_HEADROOM | ||||
460 |
for (size_t i = 0; i <= chunk_size-GUARD_PATTERN_SIZE; i += GUARD_PATTERN_SIZE){ | ||||
461 |
Apply_Guard(&chunk_of_stack[i]); | ||||
462 |
} | ||||
463 |
#else | ||||
6 months ago |
464 |
Apply_Guard(chunk_of_stack); | |||
6 months ago |
465 |
#endif | |||
5 months ago |
466 |
if (parent){ | |||
467 |
parent->guard = chunk_of_stack; | ||||
468 |
parent->limit = chunk_of_stack; | ||||
469 |
parent->base = chunk_of_stack + chunk_size; | ||||
470 |
} | ||||
5 months ago |
471 |
stack_chunk_base(cors, parent, chunk_of_stack, childs_limit); | |||
8 months ago |
4 |
472 |
} | ||
473 |
|||||
8 months ago |
474 |
||||
6 days ago |
475 |
static void | |||
476 |
stack_chunk_base( | ||||
5 months ago |
477 |
Coroutines *cors, | |||
5 months ago |
478 |
Coroutine *parent, | |||
5 months ago |
479 |
unsigned char *prev_limit, | |||
480 |
unsigned char *limit | ||||
8 months ago |
481 |
){ | |||
8 months ago |
4 |
482 |
Coroutine here; | ||
5 months ago |
483 |
here.coroutines = cors; | |||
2 days ago |
484 |
here.state = Coroutine_Free; | |||
5 months ago |
485 |
here.prev_limit = prev_limit; | |||
486 |
here.size = 0; | ||||
487 |
here.base = NULL; | ||||
488 |
here.guard = limit; | ||||
489 |
here.limit = limit; | ||||
490 |
if (limit){ | ||||
491 |
here.base = (unsigned char *)&here - cors->gap_after; | ||||
492 |
here.size = here.base - here.limit; | ||||
493 |
Apply_Guard(limit); | ||||
494 |
} | ||||
495 |
|||||
5 months ago |
496 |
// insert into all list | |||
497 |
if (parent){ | ||||
498 |
Link_AddAfter(&here.all_link, &parent->all_link); | ||||
499 |
} else { | ||||
500 |
List_AddHead(&cors->all, &here.all_link); | ||||
5 months ago |
501 |
} | |||
5 months ago |
502 |
// add to free list | |||
503 |
List_AddTail(&cors->free, &here.link); | ||||
5 months ago |
504 |
||||
505 |
cors->report.coroutines_pool_size += 1; | ||||
506 |
|||||
507 |
if (!cors->tip || &here < cors->tip){ | ||||
508 |
cors->tip = &here; | ||||
509 |
} | ||||
510 |
|||||
6 months ago |
511 |
for(;;){ | |||
512 |
switch (setjmp(here.buf)) { | ||||
513 |
case Chunk_Initial: | ||||
2 days ago |
514 |
ready_jmp_buf(here.buf); | |||
2 days ago |
515 |
if (here.state == Coroutine_Free){ | |||
6 months ago |
516 |
// return to the coroutine allocator | |||
5 months ago |
517 |
longjmp(cors->chunk_allocated, 1); | |||
6 months ago |
518 |
} else { | |||
2 days ago |
519 |
MyAssert(here.state == Coroutine_Complete); | |||
6 months ago |
520 |
// we finish here to ensure the setjmp is redone | |||
5 months ago |
521 |
if (cors->primary == &here) { | |||
6 days ago |
522 |
// if primary coroutine - return to Coroutine_NS(Run) | |||
5 months ago |
523 |
longjmp(cors->controller, Coroutines_CoroutineComplete); | |||
6 months ago |
524 |
} | |||
5 months ago |
525 |
_Cor_Mutex_Unlock(&cors->mutex); | |||
6 days ago |
526 |
Coroutine_NS(RunNext)(); | |||
6 months ago |
527 |
} | |||
2 months ago |
528 |
MyAssert(false); | |||
529 |
break; | ||||
6 months ago |
530 |
case Chunk_Create: | |||
6 months ago |
531 |
// Request to create a new chunk on the stack | |||
532 |
// We're here if the coroutine is: | ||||
6 days ago |
533 |
// Allocated, but not 'run' (Coroutine_NS(Idle)) | |||
534 |
// Run, but not not entered yet (Coroutine_NS(Running)) | ||||
535 |
// Completed (Coroutine_NS(Complete)) | ||||
5 months ago |
536 |
// Free, and the coroutines system is starting - we're characterising the system | |||
2 days ago |
537 |
MyAssert(here.state == Coroutine_Idle || | |||
538 |
here.state == Coroutine_Running || | ||||
539 |
here.state == Coroutine_Complete || | ||||
540 |
(here.state == Coroutine_Free && cors->state == Coroutines_Starting)); | ||||
5 months ago |
541 |
ReserveStackSpace(here.coroutines, &here, here.size, NULL); | |||
3 months ago |
542 |
MyAssert(false); | |||
2 months ago |
543 |
break; | |||
5 months ago |
544 |
case Chunk_Split: | |||
545 |
// Request to split this free block into two | ||||
546 |
// here.size will be set to our shorter size | ||||
547 |
ReserveStackSpace(here.coroutines, &here, here.size, here.limit); | ||||
3 months ago |
548 |
MyAssert(false); | |||
2 months ago |
549 |
break; | |||
6 months ago |
550 |
case Chunk_Enter: | |||
551 |
// request to start a coroutine (ie use the chunk for a coroutine) | ||||
552 |
// arrive here with mutex locked | ||||
2 days ago |
553 |
MyAssert(here.state == Coroutine_Running); | |||
5 months ago |
554 |
here.coroutines->active = &here; | |||
555 |
_Cor_Mutex_Unlock(&cors->mutex); | ||||
6 months ago |
556 |
here.value = here.start(here.entry_param); | |||
8 months ago |
557 |
||||
6 months ago |
558 |
// check the guard | |||
3 months ago |
559 |
MyAssert(Guard_Pattern_OK(here.guard)); | |||
8 months ago |
560 |
||||
5 months ago |
561 |
_Cor_Mutex_Lock(&here.coroutines->mutex); | |||
562 |
here.coroutines->active = NULL; | ||||
2 days ago |
563 |
MyAssert(here.state == Coroutine_Running); | |||
5 months ago |
564 |
Link_Remove(&here.link); | |||
2 days ago |
565 |
here.state = Coroutine_Complete; | |||
5 months ago |
566 |
List_AddTail(&here.coroutines->inactive, &here.link); | |||
6 months ago |
567 |
// Coroutine has completed | |||
568 |
// Loop round to redo the setjmp() - if this coroutine yielded, then the setjmp will | ||||
569 |
// need reseting | ||||
2 months ago |
570 |
break; | |||
8 months ago |
4 |
571 |
} | ||
572 |
} | ||||
573 |
} | ||||
574 |
|||||
8 months ago |
575 |
||||
6 days ago |
576 |
static void | |||
577 |
Coroutine_NS(RunNext)( | ||||
578 |
void | ||||
579 |
){ | ||||
8 months ago |
580 |
// arrive here with mutex unlocked | |||
5 months ago |
581 |
_Cor_Mutex_Lock(&g_c->waiting_mutex); | |||
582 |
_Cor_Mutex_Lock(&g_c->mutex); | ||||
583 |
Coroutine *next = List_Link_Container(Coroutine, link, List_GetHead(&g_c->runable)); | ||||
2 days ago |
584 |
MyAssert(next->state == Coroutine_Running); | |||
8 months ago |
585 |
longjmp(next->buf, Chunk_Enter); | |||
3 months ago |
586 |
MyAssert(false); | |||
8 months ago |
587 |
} | |||
588 |
|||||
589 |
|||||
6 days ago |
590 |
static Coroutine_Err | |||
591 |
Coroutines_ctor( | ||||
592 |
Coroutines *cors | ||||
593 |
){ | ||||
5 months ago |
594 |
cors->state = Coroutines_Starting; | |||
3 months ago |
595 |
if (_Cor_Mutex_ctor(&cors->mutex)){ | |||
596 |
return Coroutine_Err_CouldNotInitialiseSystem; | ||||
597 |
} | ||||
5 months ago |
598 |
cors->tip = NULL; | |||
599 |
cors->active = NULL; | ||||
5 months ago |
600 |
cors->primary = NULL; | |||
4 months ago |
601 |
cors->stack_limit = g_stack_limit; | |||
8 months ago |
4 |
602 |
|||
5 months ago |
603 |
List_Init(&cors->all); | |||
5 months ago |
604 |
List_Init(&cors->free); | |||
605 |
List_Init(&cors->inactive); | ||||
606 |
List_Init(&cors->runable); | ||||
607 |
List_Init(&cors->waiting); | ||||
3 months ago |
608 |
if (_Cor_Mutex_ctor(&cors->waiting_mutex)){ | |||
609 |
_Cor_Mutex_dtor(&cors->mutex); | ||||
610 |
return Coroutine_Err_CouldNotInitialiseSystem; | ||||
611 |
} | ||||
612 |
if (_Cor_Mutex_Lock(&cors->waiting_mutex)){ | ||||
613 |
_Cor_Mutex_dtor(&cors->waiting_mutex); | ||||
614 |
_Cor_Mutex_dtor(&cors->mutex); | ||||
615 |
return Coroutine_Err_CouldNotInitialiseSystem; | ||||
616 |
} | ||||
8 months ago |
4 |
617 |
|||
5 months ago |
618 |
cors->report.coroutines_created = 0; | |||
619 |
cors->report.coroutines_pool_size = 0; | ||||
620 |
cors->report.largest_stack = 0; | ||||
621 |
|||||
622 |
// Charactersize the system... | ||||
623 |
if (!setjmp(cors->chunk_allocated)){ | ||||
2 days ago |
624 |
ready_jmp_buf(cors->chunk_allocated); | |||
5 months ago |
625 |
ReserveStackSpace(cors, NULL, COROUTINE_STARTUP_STACK_SIZE, NULL); | |||
8 months ago |
4 |
626 |
} | ||
5 months ago |
627 |
Coroutine *cor = List_Link_Container(Coroutine, link, List_GetHead(&cors->free)); | |||
628 |
cor->size = COROUTINE_STARTUP_STACK_SIZE; | ||||
629 |
if (!setjmp(cors->chunk_allocated)){ | ||||
2 days ago |
630 |
ready_jmp_buf(cors->chunk_allocated); | |||
5 months ago |
631 |
longjmp(cor->buf, Chunk_Create); | |||
632 |
} | ||||
633 |
cors->gap_before = cor->prev_limit - (unsigned char *)cor; | ||||
634 |
cors->gap_after = (unsigned char *)cor - cor->base; | ||||
635 |
// ...charactersize the system | ||||
8 months ago |
636 |
||||
5 months ago |
637 |
// discard what we've just created | |||
5 months ago |
638 |
List_Init(&cors->all); | |||
5 months ago |
639 |
List_Init(&cors->free); | |||
640 |
cors->tip = NULL; | ||||
641 |
|||||
642 |
cors->state = Coroutines_Started; | ||||
3 months ago |
643 |
return Coroutine_OK; | |||
644 |
} | ||||
5 months ago |
645 |
||||
6 days ago |
646 |
static void | |||
647 |
Coroutines_dtor( | ||||
648 |
Coroutines *cors | ||||
649 |
){ | ||||
3 months ago |
650 |
_Cor_Mutex_Lock(&cors->mutex); | |||
651 |
cors->state = Coroutines_Stopping; | ||||
652 |
|||||
3 months ago |
653 |
MyAssert(List_IsEmpty(&cors->inactive)); | |||
3 months ago |
654 |
_Cor_Mutex_Unlock(&cors->waiting_mutex); | |||
655 |
_Cor_Mutex_dtor(&cors->waiting_mutex); | ||||
656 |
|||||
3 months ago |
657 |
MyAssert(cors->state == Coroutines_Stopping); | |||
3 months ago |
658 |
_Cor_Mutex_Unlock(&cors->mutex); | |||
659 |
_Cor_Mutex_dtor(&cors->mutex); | ||||
8 months ago |
4 |
660 |
} | ||
661 |
|||||
8 months ago |
662 |
||||
6 days ago |
663 |
Coroutine_Err | |||
664 |
Coroutine_NS(RunSystem)( | ||||
665 |
Coroutine_SystemStart start, | ||||
666 |
void *value | ||||
667 |
){ | ||||
3 months ago |
668 |
CHECK_SYSTEM_NOT_RUNNING | |||
669 |
|||||
670 |
Coroutines cors; | ||||
671 |
Coroutine_Err err = Coroutines_ctor(&cors); | ||||
672 |
if (err){ | ||||
673 |
return err; | ||||
674 |
} | ||||
675 |
g_c = &cors; | ||||
676 |
err = start(value); | ||||
677 |
g_c = NULL; | ||||
678 |
Coroutines_dtor(&cors); | ||||
679 |
return err; | ||||
680 |
} | ||||
681 |
|||||
682 |
|||||
6 days ago |
683 |
void | |||
684 |
Coroutine_NS(SetStackLimit)( | ||||
685 |
void *limit | ||||
686 |
){ | ||||
3 months ago |
687 |
MyAssert(!limit || !g_c || !(g_c->state == Coroutines_Started || g_c->state == Coroutines_Active) || (unsigned char *)limit < (unsigned char *)g_c->tip || !g_c->tip); | |||
4 months ago |
688 |
g_stack_limit = limit; | |||
689 |
if (g_c){ | ||||
690 |
g_c->stack_limit = limit; | ||||
691 |
} | ||||
6 months ago |
692 |
} | |||
693 |
|||||
694 |
|||||
3 months ago |
695 |
#if COROUTINE_RECORD_LOWEST_HEADROOM | |||
6 days ago |
696 |
static size_t | |||
697 |
Coroutine_NS(UpdateMinimumHeadroom)( | ||||
698 |
List_Head *list, | ||||
699 |
size_t headroom | ||||
700 |
){ | ||||
3 months ago |
701 |
for (List_Link *link = List_Begin(list); Link_NextIsLink(link); link = Link_Next(link)){ | |||
8 months ago |
702 |
Coroutine *cor = List_Link_Container(Coroutine, link, link); | |||
6 months ago |
703 |
if (cor->guard){ | |||
5 months ago |
704 |
for (uintptr_t i = 4; i < cor->size-3; i += 4){ | |||
3 months ago |
705 |
if (!Guard_Pattern_OK(&cor->guard[i])){ | |||
3 months ago |
706 |
headroom = i < headroom ? i : headroom; | |||
6 months ago |
707 |
break; | |||
708 |
} | ||||
8 months ago |
709 |
} | |||
710 |
} | ||||
711 |
} | ||||
3 months ago |
712 |
return headroom; | |||
713 |
} | ||||
714 |
#endif | ||||
715 |
|||||
716 |
|||||
6 days ago |
717 |
Coroutine_Report | |||
718 |
Coroutine_NS(GetReport)( | ||||
719 |
void | ||||
720 |
){ | ||||
3 months ago |
721 |
if (g_c){ | |||
722 |
size_t headroom; | ||||
3 months ago |
723 |
#if COROUTINE_RECORD_LOWEST_HEADROOM | |||
3 months ago |
724 |
_Cor_Mutex_Lock(&g_c->mutex); | |||
725 |
headroom = g_c->report.lowest_headroom; | ||||
6 days ago |
726 |
headroom = Coroutine_NS(UpdateMinimumHeadroom)(&g_c->inactive, headroom); | |||
727 |
headroom = Coroutine_NS(UpdateMinimumHeadroom)(&g_c->runable, headroom); | ||||
728 |
headroom = Coroutine_NS(UpdateMinimumHeadroom)(&g_c->waiting, headroom); | ||||
3 months ago |
729 |
_Cor_Mutex_Unlock(&g_c->mutex); | |||
6 months ago |
730 |
#else | |||
3 months ago |
731 |
headroom = 0; | |||
6 months ago |
732 |
#endif | |||
3 months ago |
733 |
g_c->report.lowest_headroom = headroom; | |||
8 months ago |
734 |
||||
3 months ago |
735 |
return g_c->report; | |||
736 |
} else { | ||||
737 |
Coroutine_Report ret = {0, 0, 0, 0}; | ||||
738 |
return ret; | ||||
739 |
} | ||||
3 months ago |
740 |
} | |||
741 |
|||||
742 |
|||||
3 months ago |
743 |
#ifndef NDEBUG | |||
6 days ago |
744 |
static void | |||
745 |
Coroutine_NS(ReportNonEmptyList)( | ||||
3 months ago |
746 |
List_Head const *head, | |||
747 |
char const *tag | ||||
748 |
){ | ||||
749 |
List_Link *link; | ||||
750 |
for (link = List_Begin(head); Link_NextIsLink(link); link = Link_Next(link)){ | ||||
751 |
Coroutine *cor = List_Link_Container(Coroutine, link, link); | ||||
752 |
printf("%s: %p %p %p\n", tag, cor, cor->start, cor->entry_param); | ||||
753 |
} | ||||
754 |
} | ||||
755 |
#endif | ||||
756 |
|||||
6 days ago |
757 |
Coroutine_Err | |||
758 |
Coroutine_NS(Run_Coroutine)( | ||||
8 months ago |
759 |
Coroutine *cor, | |||
8 months ago |
760 |
void *value | |||
761 |
){ | ||||
3 months ago |
762 |
CHECK_SYSTEM_RUNNING | |||
763 |
CHECK_COROUTINE_THREAD | ||||
764 |
CHECK_NO_COROUTINE_RUNNING | ||||
765 |
|||||
8 months ago |
766 |
Coroutines *cors = cor->coroutines; | |||
7 months ago |
767 |
_Cor_Mutex_Lock(&cors->mutex); | |||
8 months ago |
768 |
cors->state = Coroutines_Active; | |||
769 |
cors->primary = cor; | ||||
8 months ago |
770 |
||||
6 days ago |
771 |
Coroutine_NS(Continue_)(cors, cor, value, true); | |||
8 months ago |
4 |
772 |
|||
8 months ago |
773 |
if (!setjmp(cors->controller)){ | |||
2 days ago |
774 |
ready_jmp_buf(cors->controller); | |||
7 months ago |
775 |
_Cor_Mutex_Unlock(&cors->mutex); | |||
8 months ago |
776 |
||||
8 months ago |
4 |
777 |
// start the first coroutine | ||
6 days ago |
778 |
Coroutine_NS(RunNext)(); | |||
8 months ago |
4 |
779 |
} | ||
8 months ago |
780 |
// arrive here with mutex locked | |||
3 months ago |
781 |
if (!List_IsEmpty(&cors->runable) || !List_IsEmpty(&cors->waiting)){ | |||
3 months ago |
782 |
#ifndef NDEBUG | |||
6 days ago |
783 |
Coroutine_NS(ReportNonEmptyList)(&cors->runable, "runable"); | |||
784 |
Coroutine_NS(ReportNonEmptyList)(&cors->waiting, "waiting"); | ||||
3 months ago |
785 |
#endif | |||
786 |
return Coroutine_Err_ExitWithRunningCoroutines; | ||||
3 months ago |
787 |
} | |||
3 months ago |
788 |
MyAssert(cors->state == Coroutines_Active); | |||
8 months ago |
789 |
cors->state = Coroutines_Started; | |||
7 months ago |
790 |
_Cor_Mutex_Unlock(&cors->mutex); | |||
3 months ago |
791 |
||||
792 |
return Coroutine_OK; | ||||
8 months ago |
793 |
} | |||
794 |
|||||
795 |
|||||
2 days ago |
796 |
struct Coroutine_Run_Params { | |||
3 months ago |
797 |
size_t stack; | |||
798 |
Coroutine_Start start; | ||||
799 |
void *value; | ||||
800 |
void **result; | ||||
801 |
}; | ||||
802 |
|||||
6 days ago |
803 |
static Coroutine_Err | |||
804 |
Coroutine_NS(Run_Starter)( | ||||
805 |
void *_params | ||||
806 |
){ | ||||
2 days ago |
807 |
struct Coroutine_Run_Params *params = (struct Coroutine_Run_Params *)_params; | |||
3 months ago |
808 |
||||
6 days ago |
809 |
Coroutine *cor = Coroutine_NS(New)(params->stack, params->start); | |||
3 months ago |
810 |
if (!cor){ | |||
811 |
// that didn't work | ||||
812 |
return Coroutine_Err_NoStack; | ||||
813 |
} | ||||
6 days ago |
814 |
Coroutine_Err ret = Coroutine_NS(Run_Coroutine)(cor, params->value); | |||
3 months ago |
815 |
if (!ret && params->result){ | |||
6 days ago |
816 |
*params->result = Coroutine_NS(GetValue)(cor); | |||
3 months ago |
817 |
} | |||
6 days ago |
818 |
Coroutine_NS(Delete)(cor); | |||
3 months ago |
819 |
return ret; | |||
820 |
} | ||||
821 |
|||||
822 |
|||||
6 days ago |
823 |
Coroutine_Err Coroutine_NS(Run)( | |||
5 months ago |
824 |
size_t stack, | |||
8 months ago |
825 |
Coroutine_Start start, | |||
5 months ago |
826 |
void *value, | |||
827 |
void **result | ||||
8 months ago |
828 |
){ | |||
3 months ago |
829 |
if (!g_c){ | |||
2 days ago |
830 |
struct Coroutine_Run_Params params = {stack, start, value, result}; | |||
6 days ago |
831 |
return Coroutine_NS(RunSystem)(Coroutine_NS(Run_Starter), ¶ms); | |||
3 months ago |
832 |
} | |||
833 |
if (!g_c->active) | ||||
834 |
{ | ||||
835 |
// system running, but no active coroutine | ||||
6 days ago |
836 |
Coroutine *cor = Coroutine_NS(New)(stack, start); | |||
3 months ago |
837 |
if (!cor){ | |||
838 |
// that didn't work | ||||
839 |
return Coroutine_Err_NoStack; | ||||
5 months ago |
840 |
} | |||
6 days ago |
841 |
Coroutine_Err err = Coroutine_NS(Run_Coroutine)(cor, value); | |||
3 months ago |
842 |
if (!err && result){ | |||
6 days ago |
843 |
*result = Coroutine_NS(GetValue)(cor); | |||
3 months ago |
844 |
} | |||
6 days ago |
845 |
Coroutine_NS(Delete)(cor); | |||
3 months ago |
846 |
return err; | |||
6 months ago |
847 |
} | |||
3 months ago |
848 |
||||
849 |
// We are in an active coroutine, so call start() directly | ||||
850 |
CHECK_STACK_OVERRUN | ||||
851 |
void *res = start(value); | ||||
5 months ago |
852 |
if (result){ | |||
3 months ago |
853 |
*result = res; | |||
5 months ago |
854 |
} | |||
3 months ago |
855 |
||||
5 months ago |
856 |
// no failures, so... | |||
3 months ago |
857 |
return Coroutine_OK; | |||
8 months ago |
4 |
858 |
} | ||
859 |
|||||
860 |
|||||
6 days ago |
861 |
static void Coroutine_NS(FreeToIdle)( | |||
5 months ago |
862 |
Coroutine *cor, | |||
8 months ago |
863 |
Coroutine_Start start | |||
864 |
){ | ||||
2 days ago |
865 |
MyAssert(cor->state == Coroutine_Free); | |||
866 |
cor->state = Coroutine_Idle; | ||||
5 months ago |
867 |
cor->start = start; | |||
868 |
cor->value = NULL; | ||||
869 |
Link_Remove(&cor->link); | ||||
870 |
List_AddHead(&g_c->inactive, &cor->link); | ||||
8 months ago |
871 |
||||
5 months ago |
872 |
g_c->report.coroutines_created += 1; | |||
873 |
} | ||||
874 |
|||||
875 |
|||||
6 days ago |
876 |
static void Coroutine_NS(FreeToIdleSize)( | |||
5 months ago |
877 |
Coroutine *cor, | |||
878 |
Coroutine_Start start, | ||||
879 |
size_t size | ||||
880 |
){ | ||||
3 months ago |
881 |
MyAssert(!cor->guard); | |||
5 months ago |
882 |
cor->size = size; | |||
883 |
cor->base = (unsigned char *)cor - g_c->gap_after; | ||||
884 |
cor->limit = cor->base - cor->size; | ||||
6 days ago |
885 |
Coroutine_NS(FreeToIdle)(cor, start); | |||
5 months ago |
886 |
} | |||
887 |
|||||
888 |
|||||
6 days ago |
889 |
static Coroutine *Coroutine_NS(New_Lock_Assumed)( | |||
5 months ago |
890 |
size_t size, | |||
891 |
Coroutine_Start start | ||||
892 |
){ | ||||
893 |
List_Link *link; | ||||
894 |
|||||
895 |
if (!g_c->tip){ | ||||
896 |
// no tip - time to create one | ||||
897 |
|||||
898 |
// we're the non-Coroutine which starts the Coroutine system. | ||||
899 |
// Add a single free block | ||||
900 |
if (!setjmp(g_c->chunk_allocated)){ | ||||
2 days ago |
901 |
ready_jmp_buf(g_c->chunk_allocated); | |||
5 months ago |
902 |
ReserveStackSpace(g_c, NULL, COROUTINE_STARTUP_STACK_SIZE, NULL); | |||
5 months ago |
903 |
} | |||
5 months ago |
904 |
} | |||
905 |
|||||
5 months ago |
906 |
Coroutine *cor = NULL; | |||
5 months ago |
907 |
for (link = List_Begin(&g_c->free); Link_NextIsLink(link); link = Link_Next(link)){ | |||
5 months ago |
908 |
Coroutine *candidate = List_Link_Container(Coroutine, link, link); | |||
3 months ago |
909 |
MyAssert(candidate->coroutines == g_c); | |||
5 months ago |
910 |
if (!candidate->guard) { | |||
5 months ago |
911 |
// this must be the tip | |||
3 months ago |
912 |
MyAssert(candidate == g_c->tip); | |||
5 months ago |
913 |
||||
Last week |
914 |
size_t size_to_use; | |||
5 months ago |
915 |
// If this is the only Coroutine in the system, go ahead and use it regardless of size. | |||
916 |
// Note: there can only be one free block if there's no other sort of blocks as we merge on free | ||||
917 |
if (List_IsEmpty(&g_c->inactive) && | ||||
918 |
List_IsEmpty(&g_c->runable) && | ||||
919 |
List_IsEmpty(&g_c->waiting) ){ | ||||
920 |
if (g_c->stack_limit){ | ||||
5 months ago |
921 |
size_t available = (unsigned char *)candidate - g_c->stack_limit - g_c->gap_after; | |||
Last week |
922 |
size_to_use = available < size ? available : size; | |||
923 |
} else { | ||||
924 |
size_to_use = size; | ||||
5 months ago |
925 |
} | |||
6 days ago |
926 |
Coroutine_NS(FreeToIdleSize)(candidate, start, size_to_use); | |||
5 months ago |
927 |
return candidate; | |||
6 months ago |
928 |
} | |||
5 months ago |
929 |
||||
930 |
// Not the only coroutine in the system - check size | ||||
931 |
if (g_c->stack_limit){ | ||||
932 |
// there's a limit - see what that space allows.... | ||||
5 months ago |
933 |
size_t available = (unsigned char *)candidate - g_c->stack_limit - g_c->gap_after; | |||
5 months ago |
934 |
||||
935 |
if (available < size){ | ||||
936 |
// not enough space for this coroutine | ||||
3 months ago |
937 |
// printf("Not enough stack space (A) %ld\n", available); | |||
5 months ago |
938 |
return NULL; | |||
939 |
} | ||||
940 |
|||||
4 months ago |
941 |
if (available < size + g_c->gap_before + g_c->gap_after + COROUTINE_MINIMUM_STACK_SIZE) { | |||
5 months ago |
942 |
// not enough space for another coroutine - use all the space for this one | |||
Last week |
943 |
size_to_use = available; | |||
944 |
} else { | ||||
945 |
size_to_use = size; | ||||
5 months ago |
946 |
} | |||
Last week |
947 |
} else { | |||
948 |
size_to_use = size; | ||||
6 months ago |
949 |
} | |||
6 days ago |
950 |
Coroutine_NS(FreeToIdleSize)(candidate, start, size_to_use); | |||
5 months ago |
951 |
return candidate; | |||
8 months ago |
4 |
952 |
} | ||
5 months ago |
953 |
if (candidate->size >= size && candidate > cor){ | |||
954 |
// chunk big enough, and a better choice than cor | ||||
955 |
cor = candidate; | ||||
956 |
} | ||||
957 |
} | ||||
958 |
|||||
959 |
if (cor){ | ||||
960 |
// - work out whether we're splitting or using the whole chunk | ||||
961 |
if (cor->size >= size + g_c->gap_before + g_c->gap_after + COROUTINE_MINIMUM_STACK_SIZE){ | ||||
962 |
// enough space for a second coroutine so split this free block | ||||
963 |
cor->size = size; | ||||
964 |
if (!setjmp(g_c->chunk_allocated)){ | ||||
2 days ago |
965 |
ready_jmp_buf(g_c->chunk_allocated); | |||
5 months ago |
966 |
longjmp(cor->buf, Chunk_Split); | |||
5 months ago |
967 |
} | |||
968 |
} | ||||
5 months ago |
969 |
// cor now ready to use | |||
6 days ago |
970 |
Coroutine_NS(FreeToIdle)(cor, start); | |||
5 months ago |
971 |
return cor; | |||
8 months ago |
4 |
972 |
} | ||
973 |
|||||
5 months ago |
974 |
// No big-enough free blocks - check if there's space beyond the tip block | |||
975 |
|||||
4 months ago |
976 |
if (g_c->stack_limit) { | |||
977 |
ptrdiff_t available = (unsigned char *)g_c->tip->limit - g_c->gap_before - g_c->gap_after - g_c->stack_limit; | ||||
978 |
if (available < (ptrdiff_t)size){ | ||||
979 |
// no space for a new stack block | ||||
3 months ago |
980 |
// printf("Not enough stack space (B) %p %zu %zu %p %ld\n", g_c->tip->limit, g_c->gap_before, g_c->gap_after, g_c->stack_limit, available); | |||
981 |
// printf("g_c->tip = %p; tip-limit = %ld; tip->size = %zu\n", g_c->tip, (unsigned char *)g_c->tip - g_c->tip->limit, g_c->tip->size); | ||||
4 months ago |
982 |
return NULL; | |||
983 |
} | ||||
5 months ago |
984 |
} | |||
985 |
Coroutine *tip = g_c->tip; | ||||
986 |
Coroutine *me = g_c->active; | ||||
987 |
if (tip == me) { | ||||
988 |
if (!setjmp(g_c->chunk_allocated)){ | ||||
2 days ago |
989 |
ready_jmp_buf(g_c->chunk_allocated); | |||
6 days ago |
990 |
ReserveStackSpace(g_c, me, (unsigned char *)StackTopNow() - me->limit, NULL); | |||
5 months ago |
991 |
} | |||
992 |
} else { | ||||
993 |
if (!setjmp(g_c->chunk_allocated)){ | ||||
2 days ago |
994 |
ready_jmp_buf(g_c->chunk_allocated); | |||
5 months ago |
995 |
longjmp(tip->buf, Chunk_Create); | |||
996 |
} | ||||
997 |
} | ||||
998 |
|||||
5 months ago |
999 |
cor = List_Link_Container(Coroutine, link, List_GetTail(&g_c->free)); | |||
2 days ago |
1000 |
MyAssert(cor->state == Coroutine_Free); | |||
5 months ago |
1001 |
cor->size = size; | |||
1002 |
cor->limit = (unsigned char *)cor - g_c->gap_after - size; | ||||
2 days ago |
1003 |
cor->state = Coroutine_Idle; | |||
8 months ago |
4 |
1004 |
cor->start = start; | ||
1005 |
cor->value = NULL; | ||||
5 months ago |
1006 |
Link_Remove(&cor->link); | |||
1007 |
List_AddHead(&g_c->inactive, &cor->link); | ||||
8 months ago |
4 |
1008 |
|||
5 months ago |
1009 |
g_c->report.coroutines_created += 1; | |||
1010 |
return cor; | ||||
1011 |
} | ||||
8 months ago |
1012 |
||||
5 months ago |
1013 |
||||
6 days ago |
1014 |
Coroutine * | |||
1015 |
Coroutine_NS(New)( | ||||
5 months ago |
1016 |
size_t stack, | |||
1017 |
Coroutine_Start start | ||||
1018 |
){ | ||||
3 months ago |
1019 |
MyAssert(g_c); | |||
1020 |
MyAssert((g_c->state == Coroutines_Started && List_IsEmpty(&g_c->inactive)) || g_c->state == Coroutines_Active); | ||||
6 days ago |
1021 |
MyAssert(!Coroutine_NS(StackHasOverrun)()); | |||
5 months ago |
1022 |
||||
1023 |
_Cor_Mutex_Lock(&g_c->mutex); | ||||
1024 |
|||||
6 days ago |
1025 |
Coroutine *cor = Coroutine_NS(New_Lock_Assumed)(stack, start); | |||
5 months ago |
1026 |
||||
1027 |
if (cor && cor->size > g_c->report.largest_stack){ | ||||
1028 |
g_c->report.largest_stack = cor->size; | ||||
1029 |
} | ||||
1030 |
|||||
1031 |
_Cor_Mutex_Unlock(&g_c->mutex); | ||||
1032 |
|||||
8 months ago |
4 |
1033 |
return cor; | ||
1034 |
} | ||||
1035 |
|||||
8 months ago |
1036 |
||||
6 days ago |
1037 |
void | |||
1038 |
Coroutine_NS(Delete)( | ||||
8 months ago |
1039 |
Coroutine *cor | |||
1040 |
){ | ||||
6 days ago |
1041 |
MyAssert(!Coroutine_NS(StackHasOverrun)()); | |||
5 months ago |
1042 |
if (cor){ | |||
1043 |
Coroutines *cors = cor->coroutines; | ||||
1044 |
_Cor_Mutex_Lock(&cors->mutex); | ||||
2 days ago |
1045 |
MyAssert(cor->state == Coroutine_Idle || cor->state == Coroutine_Complete); | |||
3 months ago |
1046 |
||||
1047 |
#if COROUTINE_RECORD_LOWEST_HEADROOM | ||||
1048 |
if (cor->guard){ | ||||
1049 |
unsigned char *base = cor->base; | ||||
1050 |
unsigned char *rover; | ||||
1051 |
for (rover = cor->limit+4; rover<base; rover += 4){ | ||||
3 months ago |
1052 |
if (!Guard_Pattern_OK(rover)){ | |||
3 months ago |
1053 |
break; | |||
1054 |
} | ||||
1055 |
} | ||||
1056 |
size_t myheadroom = (size_t)(rover - cor->limit); | ||||
1057 |
if (myheadroom < g_c->report.lowest_headroom || g_c->report.lowest_headroom == 0){ | ||||
1058 |
g_c->report.lowest_headroom = myheadroom; | ||||
1059 |
} | ||||
1060 |
} | ||||
1061 |
#endif | ||||
1062 |
|||||
2 days ago |
1063 |
cor->state = Coroutine_Free; | |||
5 months ago |
1064 |
Link_Remove(&cor->link); | |||
1065 |
|||||
5 months ago |
1066 |
// insert into free list | |||
1067 |
List_AddHead(&cors->free, &cor->link); | ||||
5 months ago |
1068 |
||||
1069 |
// Check for merge with following Coroutine | ||||
5 months ago |
1070 |
List_Link *link = Link_Next(&cor->all_link); | |||
5 months ago |
1071 |
if (Link_NextIsLink(link)){ | |||
5 months ago |
1072 |
Coroutine *listcor = List_Link_Container(Coroutine, all_link, link); | |||
2 days ago |
1073 |
if (listcor->state == Coroutine_Free){ | |||
5 months ago |
1074 |
// merge | |||
5 months ago |
1075 |
cor->size += cor->limit - listcor->limit; | |||
5 months ago |
1076 |
cor->limit = listcor->limit; | |||
1077 |
cor->guard = listcor->guard; | ||||
5 months ago |
1078 |
Link_Remove(&listcor->all_link); | |||
5 months ago |
1079 |
Link_Remove(&listcor->link); | |||
5 months ago |
1080 |
if (g_c->tip == listcor){ | |||
1081 |
g_c->tip = cor; | ||||
1082 |
} | ||||
5 months ago |
1083 |
} | |||
1084 |
} | ||||
1085 |
|||||
1086 |
// check for merge with prev coroutine | ||||
5 months ago |
1087 |
link = Link_Prev(&cor->all_link); | |||
5 months ago |
1088 |
if (Link_PrevIsLink(link)){ | |||
5 months ago |
1089 |
Coroutine *listcor = List_Link_Container(Coroutine, all_link, link); | |||
2 days ago |
1090 |
if (listcor->state == Coroutine_Free){ | |||
5 months ago |
1091 |
// merge | |||
5 months ago |
1092 |
listcor->size += listcor->limit - cor->limit; | |||
5 months ago |
1093 |
listcor->limit = cor->limit; | |||
1094 |
listcor->guard = cor->guard; | ||||
5 months ago |
1095 |
Link_Remove(&cor->all_link); | |||
5 months ago |
1096 |
Link_Remove(&cor->link); | |||
5 months ago |
1097 |
if (g_c->tip == cor){ | |||
1098 |
g_c->tip = listcor; | ||||
1099 |
} | ||||
5 months ago |
1100 |
} | |||
1101 |
} | ||||
1102 |
|||||
5 months ago |
1103 |
_Cor_Mutex_Unlock(&cors->mutex); | |||
1104 |
} | ||||
8 months ago |
4 |
1105 |
} | ||
1106 |
|||||
8 months ago |
1107 |
||||
6 days ago |
1108 |
// Coroutine_NS(Continue), assuming the mutex is claimed | |||
3 months ago |
1109 |
// return false for success, true for something went wrong | |||
6 days ago |
1110 |
static Coroutine_Err | |||
1111 |
Coroutine_NS(Continue_)( | ||||
3 months ago |
1112 |
Coroutines *cors, | |||
8 months ago |
1113 |
Coroutine *cor, | |||
1114 |
void *value, | ||||
1115 |
bool early | ||||
1116 |
){ | ||||
2 days ago |
1117 |
if (cor->state == Coroutine_Running){ | |||
3 months ago |
1118 |
// already running | |||
3 months ago |
1119 |
return Coroutine_OK; | |||
3 months ago |
1120 |
} | |||
2 days ago |
1121 |
if (cor->state != Coroutine_Idle && cor->state != Coroutine_Waiting){ | |||
3 months ago |
1122 |
return Coroutine_Err_WrongState; | |||
1123 |
} | ||||
8 months ago |
4 |
1124 |
cor->entry_param = value; | ||
2 days ago |
1125 |
cor->state = Coroutine_Running; | |||
5 months ago |
1126 |
Link_Remove(&cor->link); | |||
8 months ago |
4 |
1127 |
if ( early ) { | ||
8 months ago |
1128 |
List_AddHead(&cors->runable, &cor->link); | |||
8 months ago |
4 |
1129 |
} else { | ||
8 months ago |
1130 |
List_AddTail(&cors->runable, &cor->link); | |||
8 months ago |
4 |
1131 |
} | ||
7 months ago |
1132 |
_Cor_Mutex_Unlock(&cors->waiting_mutex); | |||
3 months ago |
1133 |
return Coroutine_OK; | |||
7 months ago |
1134 |
} | |||
1135 |
|||||
1136 |
|||||
6 days ago |
1137 |
Coroutine_Err | |||
1138 |
Coroutine_NS(Continue)( | ||||
7 months ago |
1139 |
Coroutine *cor, | |||
1140 |
void *value, | ||||
1141 |
bool early | ||||
1142 |
){ | ||||
6 days ago |
1143 |
MyAssert(!Coroutine_NS(StackHasOverrun)()); | |||
7 months ago |
1144 |
Coroutines *cors = cor->coroutines; | |||
7 months ago |
1145 |
_Cor_Mutex_Lock(&cors->mutex); | |||
6 days ago |
1146 |
Coroutine_Err err = Coroutine_NS(Continue_)(cors, cor, value, early); | |||
7 months ago |
1147 |
_Cor_Mutex_Unlock(&cors->mutex); | |||
3 months ago |
1148 |
return err; | |||
8 months ago |
4 |
1149 |
} | ||
1150 |
|||||
8 months ago |
1151 |
||||
6 days ago |
1152 |
void * | |||
1153 |
Coroutine_NS(Yield)( | ||||
8 months ago |
1154 |
void *value, | |||
2 days ago |
1155 |
Coroutine_YieldCallback on_yield, | |||
8 months ago |
1156 |
void *yield_me | |||
8 months ago |
1157 |
){ | |||
3 months ago |
1158 |
MyAssert(g_c); | |||
5 months ago |
1159 |
Coroutine *me = g_c->active; | |||
3 months ago |
1160 |
MyAssert(me); | |||
6 days ago |
1161 |
MyAssert(!Coroutine_NS(StackHasOverrun)()); | |||
8 months ago |
1162 |
||||
5 months ago |
1163 |
_Cor_Mutex_Lock(&g_c->mutex); | |||
8 months ago |
1164 |
Coroutines *cors = me->coroutines; | |||
2 days ago |
1165 |
MyAssert(me && me->state == Coroutine_Running && cors == g_c); | |||
6 days ago |
1166 |
me->stack_top = (unsigned char *)StackTopNow(); | |||
8 months ago |
4 |
1167 |
me->value = value; | ||
2 days ago |
1168 |
me->state = Coroutine_Waiting; | |||
8 months ago |
1169 |
||||
5 months ago |
1170 |
Link_Remove(&me->link); | |||
7 months ago |
1171 |
if (!List_IsEmpty(&cors->runable)){ | |||
7 months ago |
1172 |
_Cor_Mutex_Unlock(&cors->waiting_mutex); | |||
7 months ago |
1173 |
} | |||
8 months ago |
1174 |
List_AddTail(&cors->waiting, &me->link); | |||
8 months ago |
1175 |
||||
8 months ago |
1176 |
switch (setjmp(me->buf)){ | |||
1177 |
case Chunk_Initial: | ||||
2 days ago |
1178 |
ready_jmp_buf(me->buf); | |||
7 months ago |
1179 |
_Cor_Mutex_Unlock(&cors->mutex); | |||
8 months ago |
1180 |
on_yield(yield_me); | |||
6 days ago |
1181 |
Coroutine_NS(RunNext)(); | |||
3 months ago |
1182 |
MyAssert(false); | |||
2 months ago |
1183 |
break; | |||
8 months ago |
1184 |
case Chunk_Create: | |||
3 months ago |
1185 |
MyAssert(me == g_c->tip); | |||
5 months ago |
1186 |
ReserveStackSpace(me->coroutines, me, me->stack_top - me->limit, NULL); | |||
3 months ago |
1187 |
MyAssert(false); | |||
2 months ago |
1188 |
break; | |||
8 months ago |
1189 |
case Chunk_Enter: | |||
1190 |
// arrive here with mutex locked | ||||
8 months ago |
1191 |
cors->active = me; | |||
6 days ago |
1192 |
MyAssert(!Coroutine_NS(StackHasOverrun)()); | |||
8 months ago |
1193 |
// when we return here - we are running again | |||
2 days ago |
1194 |
MyAssert(me->state == Coroutine_Running); | |||
8 months ago |
1195 |
void *res = me->entry_param; | |||
7 months ago |
1196 |
_Cor_Mutex_Unlock(&cors->mutex); | |||
8 months ago |
1197 |
return res; | |||
8 months ago |
4 |
1198 |
} | ||
2 months ago |
1199 |
MyAssert(false); | |||
8 months ago |
1200 |
return NULL; | |||
8 months ago |
4 |
1201 |
} | ||
1202 |
|||||
8 months ago |
1203 |
||||
6 days ago |
1204 |
void * | |||
1205 |
Coroutine_NS(GetValue)( | ||||
8 months ago |
1206 |
Coroutine *cor | |||
1207 |
){ | ||||
8 months ago |
4 |
1208 |
return cor->value; | ||
1209 |
} | ||||
1210 |
|||||
8 months ago |
1211 |
||||
6 days ago |
1212 |
Coroutine * | |||
1213 |
Coroutine_NS(GetActive)( | ||||
1214 |
void | ||||
1215 |
){ | ||||
5 months ago |
1216 |
return g_c ? g_c->active : NULL; | |||
8 months ago |
4 |
1217 |
} | ||
1218 |
|||||
8 months ago |
1219 |
||||
6 days ago |
1220 |
intptr_t | |||
1221 |
Coroutine_NS(GetStackHeadroom)( | ||||
1222 |
void | ||||
1223 |
){ | ||||
3 months ago |
1224 |
Coroutine *me = g_c ? g_c->active : NULL; | |||
6 months ago |
1225 |
if (!me){ | |||
1226 |
// no active coroutine | ||||
3 months ago |
1227 |
if (g_stack_limit){ | |||
6 days ago |
1228 |
return (unsigned char *)StackTopNow() - g_stack_limit; | |||
6 months ago |
1229 |
} else { | |||
1230 |
// no information where the stack ends - return something | ||||
5 months ago |
1231 |
return COROUTINE_MINIMUM_STACK_SIZE; | |||
6 months ago |
1232 |
} | |||
1233 |
} | ||||
6 days ago |
1234 |
return (unsigned char *)StackTopNow() - me->limit; | |||
8 months ago |
1235 |
} | |||
1236 |
|||||
1237 |
|||||
6 days ago |
1238 |
void * | |||
1239 |
Coroutine_NS(GetStackHWM)( | ||||
1240 |
void | ||||
1241 |
){ | ||||
3 months ago |
1242 |
MyAssert(g_c); | |||
1243 |
MyAssert(g_c->state == Coroutines_Active); | ||||
6 days ago |
1244 |
MyAssert(!Coroutine_NS(StackHasOverrun)()); | |||
6 months ago |
1245 |
// Find where the guards end | |||
1246 |
unsigned char *guard; | ||||
2 months ago |
1247 |
for (guard = g_c->active->limit; Guard_Pattern_OK(guard); guard += 4){ | |||
6 months ago |
1248 |
// do nothing | |||
1249 |
} | ||||
1250 |
return guard; | ||||
1251 |
} | ||||
7 months ago |
1252 |
||||
1253 |
|||||
6 days ago |
1254 |
void | |||
1255 |
Coroutine_NS(ClearStackForHWM)( | ||||
1256 |
void | ||||
1257 |
){ | ||||
3 months ago |
1258 |
MyAssert(g_c); | |||
1259 |
MyAssert(g_c->state == Coroutines_Active); | ||||
6 days ago |
1260 |
MyAssert(!Coroutine_NS(StackHasOverrun)()); | |||
6 days ago |
1261 |
unsigned char *end = (unsigned char *)StackTopNow() - GUARD_PATTERN_SIZE; | |||
2 months ago |
1262 |
for (unsigned char *guard = g_c->active->limit; guard <= end; guard += GUARD_PATTERN_SIZE){ | |||
6 months ago |
1263 |
Apply_Guard(guard); | |||
6 months ago |
1264 |
} | |||
1265 |
} | ||||
1266 |
|||||
1267 |
|||||
6 days ago |
1268 |
static bool | |||
1269 |
Coroutine_NS(CanStartCoroutine_Lock_Assumed)( | ||||
5 months ago |
1270 |
size_t size | |||
1271 |
){ | ||||
1272 |
if (!g_c->stack_limit){ | ||||
6 months ago |
1273 |
return true; | |||
1274 |
} | ||||
6 months ago |
1275 |
||||
5 months ago |
1276 |
if (!g_c->tip){ | |||
1277 |
return true; | ||||
1278 |
} | ||||
1279 |
|||||
2 days ago |
1280 |
if (g_c->tip->state == Coroutine_Free){ | |||
5 months ago |
1281 |
// last block is free | |||
1282 |
if ((unsigned char *)g_c->tip - g_c->stack_limit >= (ptrdiff_t)(g_c->gap_after + size)){ | ||||
1283 |
// enough room in free block, which is the last block | ||||
1284 |
return true; | ||||
1285 |
} | ||||
1286 |
} else { | ||||
1287 |
// last block is allocated | ||||
1288 |
if (g_c->tip->limit - g_c->stack_limit >= (ptrdiff_t)(g_c->gap_before + g_c->gap_after + size)){ | ||||
1289 |
// enough room after the last block, which is allocated | ||||
1290 |
return true; | ||||
1291 |
} | ||||
1292 |
} | ||||
1293 |
|||||
1294 |
// not enough room between allocated blocks and stack limit, so check free list | ||||
1295 |
List_Link *link; | ||||
1296 |
for (link = List_Begin(&g_c->free); Link_NextIsLink(link); link = Link_Next(link)){ | ||||
1297 |
Coroutine *cor = List_Link_Container(Coroutine, link, link); | ||||
1298 |
if (cor->size >= size){ | ||||
1299 |
return true; | ||||
1300 |
} | ||||
1301 |
} | ||||
1302 |
|||||
1303 |
return false; | ||||
6 months ago |
1304 |
} | |||
1305 |
|||||
1306 |
|||||
6 days ago |
1307 |
bool | |||
1308 |
Coroutine_NS(CanStartCoroutine)( | ||||
5 months ago |
1309 |
size_t size | |||
1310 |
){ | ||||
3 months ago |
1311 |
MyAssert(g_c); | |||
1312 |
MyAssert(g_c->state == Coroutines_Started || g_c->state == Coroutines_Active); | ||||
6 days ago |
1313 |
MyAssert(!Coroutine_NS(StackHasOverrun)()); | |||
3 months ago |
1314 |
||||
5 months ago |
1315 |
_Cor_Mutex_Lock(&g_c->mutex); | |||
1316 |
|||||
6 days ago |
1317 |
bool result = Coroutine_NS(CanStartCoroutine_Lock_Assumed)(size); | |||
5 months ago |
1318 |
||||
1319 |
_Cor_Mutex_Unlock(&g_c->mutex); | ||||
1320 |
|||||
1321 |
return result; | ||||
1322 |
} | ||||
1323 |
|||||
6 days ago |
1324 |
void * | |||
1325 |
Coroutine_NS(GetCStackTop)( | ||||
1326 |
void | ||||
1327 |
){ | ||||
1328 |
MyAssert(!Coroutine_NS(StackHasOverrun)()); | ||||
5 months ago |
1329 |
if ((g_c->state == Coroutines_Started || g_c->state == Coroutines_Active) && g_c->tip != g_c->active) { | |||
1330 |
return g_c->tip->stack_top; | ||||
6 months ago |
1331 |
} else { | |||
6 days ago |
1332 |
return (unsigned char *)StackTopNow(); | |||
6 months ago |
1333 |
} | |||
8 months ago |
1334 |
} | |||
1335 |
|||||
1336 |
|||||
6 days ago |
1337 |
// Inspired by cpython... | |||
1338 |
#ifdef __has_builtin | ||||
1339 |
# define Coroutine__has_builtin(x) __has_builtin(x) | ||||
1340 |
#else | ||||
1341 |
# define Coroutine__has_builtin(x) 0 | ||||
1342 |
#endif | ||||
1343 |
|||||
1344 |
#if !Coroutine__has_builtin(__builtin_frame_address) && !defined(__GNUC__) && !defined(_MSC_VER) | ||||
1345 |
static uintptr_t return_pointer_as_int(char* p) { | ||||
1346 |
return (uintptr_t)p; | ||||
6 months ago |
1347 |
} | |||
6 days ago |
1348 |
#endif | |||
6 months ago |
1349 |
||||
6 days ago |
1350 |
static inline uintptr_t | |||
1351 |
StackTopNow(void) { | ||||
1352 |
#if Coroutine__has_builtin(__builtin_frame_address) || defined(__GNUC__) | ||||
1353 |
return (uintptr_t)__builtin_frame_address(0); | ||||
1354 |
#elif defined(_MSC_VER) | ||||
1355 |
return (uintptr_t)_AddressOfReturnAddress(); | ||||
1356 |
#else | ||||
1357 |
char here; | ||||
1358 |
/* Avoid compiler warning about returning stack address */ | ||||
1359 |
return return_pointer_as_int(&here); | ||||
1360 |
#endif | ||||
1361 |
} | ||||
1362 |
// ...inspired by cpython | ||||
6 months ago |
1363 |
||||
6 days ago |
1364 |
||||
2 days ago |
1365 |
struct Coroutine_ChainParam { | |||
8 months ago |
1366 |
Coroutine_Start start; | |||
1367 |
void *value; | ||||
1368 |
Coroutine *ret; | ||||
1369 |
}; | ||||
1370 |
|||||
1371 |
|||||
6 days ago |
1372 |
static void * | |||
1373 |
Coroutine_NS(ChainFn)( | ||||
8 months ago |
1374 |
void *param | |||
1375 |
){ | ||||
2 days ago |
1376 |
struct Coroutine_ChainParam *params = (struct Coroutine_ChainParam *)param; | |||
6 days ago |
1377 |
return (void *)(uintptr_t)Coroutine_NS(Continue)(params->ret, params->start(params->value), true); | |||
8 months ago |
1378 |
} | |||
1379 |
|||||
1380 |
|||||
6 days ago |
1381 |
static void | |||
1382 |
Coroutine_NS(ChainYield)( | ||||
8 months ago |
1383 |
void *unused | |||
1384 |
){ | ||||
1385 |
(void)unused; | ||||
1386 |
} | ||||
1387 |
|||||
1388 |
|||||
6 days ago |
1389 |
Coroutine_Err | |||
1390 |
Coroutine_NS(Chain)( | ||||
5 months ago |
1391 |
size_t size, | |||
8 months ago |
1392 |
Coroutine_Start start, | |||
5 months ago |
1393 |
void *value, | |||
1394 |
void **result | ||||
8 months ago |
1395 |
){ | |||
6 days ago |
1396 |
MyAssert(!Coroutine_NS(StackHasOverrun)()); | |||
1397 |
Coroutine *cor = Coroutine_NS(New)(size, Coroutine_NS(ChainFn)); | ||||
5 months ago |
1398 |
if (!cor){ | |||
1399 |
// failed | ||||
3 months ago |
1400 |
return Coroutine_Err_NoStack; | |||
5 months ago |
1401 |
} | |||
2 days ago |
1402 |
struct Coroutine_ChainParam params = { | |||
8 months ago |
1403 |
start, | |||
1404 |
value, | ||||
6 days ago |
1405 |
Coroutine_NS(GetActive)() | |||
8 months ago |
1406 |
}; | |||
6 days ago |
1407 |
Coroutine_Err err = Coroutine_NS(Continue)(cor, ¶ms, true); | |||
3 months ago |
1408 |
if (err){ | |||
1409 |
return err; | ||||
1410 |
} | ||||
6 days ago |
1411 |
void *res = Coroutine_NS(Yield)(NULL, Coroutine_NS(ChainYield), NULL); | |||
1412 |
err = (Coroutine_Err)(uintptr_t)Coroutine_NS(GetValue)(cor); | ||||
1413 |
Coroutine_NS(Delete)(cor); | ||||
3 months ago |
1414 |
if (!err && result){ | |||
5 months ago |
1415 |
*result = res; | |||
1416 |
} | ||||
3 months ago |
1417 |
// success! ...probably | |||
1418 |
return err; | ||||
8 months ago |
1419 |
} | |||
1420 |
|||||
1421 |
|||||
6 days ago |
1422 |
bool | |||
1423 |
Coroutine_NS(IsRunning)( | ||||
8 months ago |
1424 |
Coroutine *cor | |||
6 days ago |
1425 |
){ | |||
8 months ago |
1426 |
int state = cor->state; | |||
2 days ago |
1427 |
return state == Coroutine_Running || state == Coroutine_Waiting; | |||
8 months ago |
4 |
1428 |
} | ||
8 months ago |
1429 |
||||
1430 |
|||||
6 days ago |
1431 |
bool Coroutine_NS(IsComplete)( | |||
6 months ago |
1432 |
Coroutine *cor | |||
6 days ago |
1433 |
){ | |||
6 months ago |
1434 |
int state = cor->state; | |||
2 days ago |
1435 |
return state == Coroutine_Complete; | |||
6 months ago |
1436 |
} | |||
1437 |
|||||
1438 |
|||||
6 days ago |
1439 |
bool | |||
1440 |
Coroutine_NS(IsStarted)( | ||||
1441 |
void | ||||
1442 |
){ | ||||
5 months ago |
1443 |
return g_c && (g_c->state == Coroutines_Active || g_c->state == Coroutines_Started); | |||
8 months ago |
1444 |
} | |||
5 months ago |
1445 |
||||
6 days ago |
1446 |
void | |||
1447 |
Coroutine_NS(Dump_)( | ||||
1448 |
void | ||||
1449 |
){ | ||||
5 months ago |
1450 |
char *state_to_text[] = { | |||
1451 |
"Free", | ||||
1452 |
"Idle", | ||||
1453 |
"Running", | ||||
1454 |
"Waiting", | ||||
1455 |
"Complete" | ||||
1456 |
}; | ||||
1457 |
unsigned idx = 0; | ||||
1458 |
List_Link *link; | ||||
1459 |
for (link = List_Begin(&g_c->all); Link_NextIsLink(link); link = Link_Next(link)){ | ||||
1460 |
Coroutine *cor = List_Link_Container(Coroutine, all_link, link); | ||||
1461 |
printf("%d) %p (%s) %ld%s\n", idx++, cor, state_to_text[cor->state], cor->size, cor == g_c->tip ? " (TIP)" : ""); | ||||
1462 |
} | ||||
1463 |
} | ||||
1464 |