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