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