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