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