804 lines21.2 KB
Newer
Older
-
+
commited
{line.log.rev}
on
9 months ago
4
1
#include "coroutine.h"
2
#include <assert.h>
3
#include <setjmp.h>
4
#include <stdbool.h>
5
#include <stddef.h>
8 months ago
6
#include "cor_platform.h"
9 months ago
4
7
7 months ago
8
// see CPython again, this time from ctypes.h
9
#if (defined (__SVR4) && defined (__sun)) || defined(COROUTINE_HAVE_ALLOCA_H)
10
# include <alloca.h>
11
#elif defined(MS_WIN32)
12
# include <malloc.h>
13
#endif
9 months ago
4
14
7 months ago
15
/* If the system does not define alloca(), we have to hope for a compiler builtin. */
16
#ifndef alloca
17
# if defined __GNUC__ || (__clang_major__ >= 4)
18
# define alloca __builtin_alloca
19
# else
20
# error "Could not define alloca() on your platform."
21
# endif
22
#endif
23
8 months ago
24
static void Coroutine_RunNext(void);
8 months ago
25
static void _Coroutine_Continue(Coroutine *cor, void *value, bool early);
7 months ago
26
static unsigned char *StackTopNow(void);
9 months ago
27
9 months ago
4
28
///////////////////////////////////////////////////////////////////////////////
29
// 2-way linked lists...
30
//
9 months ago
31
// Brought inline here to avoid namespace polution
9 months ago
4
32
///////////////////////////////////////////////////////////////////////////////
33
34
typedef struct List_Link List_Link;
35
struct List_Link {
36
List_Link *next;
37
List_Link *prev;
38
};
39
40
typedef struct List_Head List_Head;
41
struct List_Head {
42
union {
43
struct {
44
List_Link link;
45
List_Link *filler;
46
} fwd;
47
struct {
48
List_Link *filler;
49
List_Link link;
50
} back;
51
};
52
};
53
9 months ago
54
55
static inline bool List_IsEmpty(
56
const List_Head *list
57
){
9 months ago
4
58
return list->fwd.link.next == &list->back.link;
59
}
60
9 months ago
61
62
static inline List_Link *List_GetHead(
63
const List_Head *list
64
){
9 months ago
4
65
return List_IsEmpty(list) ? NULL : list->fwd.link.next;
66
}
9 months ago
67
68
69
// static inline List_Link *List_GetTail(
70
// const List_Head *list
71
// ){
9 months ago
72
// return List_IsEmpty(list) ? NULL : list->back.link.prev;
73
// }
9 months ago
74
75
9 months ago
4
76
#define OFFSETOF(Container, Field) ((char *)&((Container *)4)->Field - (char *)(Container *)4)
77
#define List_Link_Container(Container, Link, link) ((Container *)((char *)(link) - OFFSETOF(Container, Link)))
78
9 months ago
79
80
static inline void List_Init(
81
List_Head *list
82
){
9 months ago
4
83
list->fwd.link.next = &list->back.link;
84
list->fwd.link.prev = NULL;
85
list->back.link.prev = &list->fwd.link;
86
}
87
9 months ago
88
89
static inline void List_AddHead(
90
List_Head *list,
91
List_Link *link
92
){
9 months ago
4
93
List_Link *first = list->fwd.link.next;
94
link->next = first;
95
link->prev = &list->fwd.link;
96
first->prev = link;
97
list->fwd.link.next = link;
98
}
99
9 months ago
100
101
static inline void List_AddTail(
102
List_Head *list,
103
List_Link *link
104
){
9 months ago
4
105
List_Link *last = list->back.link.prev;
106
link->prev = last;
107
link->next = &list->back.link;
108
last->next = link;
109
list->back.link.prev = link;
110
}
111
9 months ago
112
113
static inline void List_Remove(
114
List_Link *link
115
){
9 months ago
4
116
link->prev->next = link->next;
117
link->next->prev = link->prev;
118
}
119
120
///////////////////////////////////////////////////////////////////////////////
121
// ...2-way linked lists
122
///////////////////////////////////////////////////////////////////////////////
123
9 months ago
124
typedef struct Coroutines Coroutines;
9 months ago
4
125
126
enum {
127
Coroutines_Idle,
128
Coroutines_Starting,
129
Coroutines_Started,
130
Coroutines_Active,
131
Coroutines_Stopping
132
};
133
134
enum {
135
Chunk_Initial,
136
Chunk_Create,
137
Chunk_Enter
138
};
139
9 months ago
140
typedef enum Coroutine_State {
9 months ago
4
141
Coroutine_Free,
142
Coroutine_Idle,
143
Coroutine_Running,
144
Coroutine_Waiting,
145
Coroutine_Complete
9 months ago
146
} Coroutine_State;
9 months ago
4
147
148
enum {
149
Coroutines_Init,
150
Coroutines_AllocatedChunk,
151
Coroutines_CoroutineComplete,
152
};
153
154
struct Coroutine {
7 months ago
155
Coroutines *coroutines; // so can work with it off-thread
156
List_Link link; // for whichever list it's on
157
jmp_buf buf; // how to get back to it
158
unsigned char *guard; // where the stack overrun guard is
159
Coroutine_Start start; // entry point
160
void *entry_param; // to pass to start
161
void *value; // yielded/returned
162
unsigned char *stack_top; // recorded at yield
9 months ago
163
Coroutine_State state;
9 months ago
4
164
};
165
166
struct Coroutines {
8 months ago
167
_Cor_Mutex mutex;
9 months ago
168
jmp_buf controller; // to return from Coroutine_Run
169
jmp_buf chunk_allocated;// for chunk allocation
170
unsigned char *guard; // the stack guard for the startup sequence
9 months ago
4
171
172
// singletons
173
Coroutine *tip; // top of stack chunk
174
Coroutine *active; // currently running coroutine
175
Coroutine *primary; // Coroutine_Run coroutine
7 months ago
176
unsigned char *stack_limit; // when not NULL, where the stack finishes
9 months ago
4
177
178
// lists
179
List_Head free;
180
List_Head inactive; // idle or complete
181
List_Head runable; // running or waiting to run
182
List_Head waiting; // yielded / waiting to run
8 months ago
183
_Cor_Mutex waiting_mutex;
9 months ago
4
184
9 months ago
185
// Summary of the system
186
Coroutine_Report report;
187
9 months ago
4
188
// state
189
char state;
190
};
191
8 months ago
192
_Cor_thread_local Coroutines g_c;
9 months ago
4
193
7 months ago
194
static void stack_chunk_chunk(Coroutine *parent, size_t chunk_size);
7 months ago
195
static void stack_chunk_base(void);
9 months ago
4
196
9 months ago
197
7 months ago
198
#define GUARD_PATTERN_SIZE (4)
9 months ago
199
// Check whether the guard is intact
200
static inline bool Check_Guard(
201
unsigned char *guard
202
){
7 months ago
203
return !guard ||
204
(guard[0] == 0xde &&
205
guard[1] == 0xad &&
206
guard[2] == 0xbe &&
207
guard[3] == 0xef);
9 months ago
208
}
209
210
7 months ago
211
static inline void Apply_Guard(unsigned char *guard){
212
guard[0] = 0xde;
213
guard[1] = 0xad;
214
guard[2] = 0xbe;
215
guard[3] = 0xef;
216
}
217
218
7 months ago
219
static bool Coroutine_StackHasNotOverrun(void){
7 months ago
220
unsigned char *stack_top = StackTopNow();
221
unsigned char *stack_limit = g_c.stack_limit;
222
if (stack_limit && stack_top < stack_limit){
223
// current stack top is beyond limit - we are overrunning NOW
224
return false;
225
}
226
Coroutine *me = g_c.active;
227
if (!me){
228
return true;
229
}
230
if (me->guard){
231
return Check_Guard(me->guard);
232
}
233
unsigned char *coroutine_limit;
234
if (!stack_limit || stack_limit <= (unsigned char *)me - 2*COROUTINE_STACK_SIZE){
235
// no stack limit, or can start a coroutine, so limit ourselves to one unit of coroutine stack
236
coroutine_limit = (unsigned char *)me - 1*COROUTINE_STACK_SIZE + GUARD_PATTERN_SIZE;
237
} else {
238
// can't start coroutine, and have a stack limit - use that
239
coroutine_limit = stack_limit;
240
}
241
return stack_top >= coroutine_limit;
242
}
243
244
8 months ago
245
static void Coroutine_PrimeStackChunks(void)
9 months ago
4
246
{
7 months ago
247
unsigned char chunk_of_stack[COROUTINE_STARTUP_STACK_SIZE + GUARD_PATTERN_SIZE];
248
Apply_Guard(chunk_of_stack);
9 months ago
249
assert(Check_Guard(chunk_of_stack));
250
251
// Stacks grow down in memory (almost always), so if the caller of this function changes
252
// the guard before entering the coroutine system, it has overrun the startup stack
8 months ago
253
g_c.guard = chunk_of_stack;
9 months ago
254
7 months ago
255
stack_chunk_base();
9 months ago
4
256
}
257
9 months ago
258
259
static void stack_chunk_chunk(
7 months ago
260
Coroutine *parent,
261
size_t chunk_size
9 months ago
262
){
7 months ago
263
unsigned char *chunk_of_stack = alloca(chunk_size);
264
#if COROUTINE_RECORD_LOWEST_HEADROOM
265
for (size_t i = 0; i <= chunk_size-GUARD_PATTERN_SIZE; i += GUARD_PATTERN_SIZE){
266
Apply_Guard(&chunk_of_stack[i]);
267
}
268
#else
7 months ago
269
Apply_Guard(chunk_of_stack);
7 months ago
270
#endif
271
parent->guard = chunk_of_stack;
272
stack_chunk_base();
9 months ago
4
273
}
274
9 months ago
275
276
static void stack_chunk_base(
7 months ago
277
void
9 months ago
278
){
9 months ago
4
279
Coroutine here;
7 months ago
280
here.state = Coroutine_Free;
281
here.guard = NULL;
282
here.coroutines = &g_c;
283
List_AddHead(&g_c.free, &here.link);
284
g_c.report.coroutines_pool_size += 1;
285
g_c.tip = &here;
7 months ago
286
for(;;){
287
switch (setjmp(here.buf)) {
288
case Chunk_Initial:
7 months ago
289
if (here.state == Coroutine_Free){
7 months ago
290
// return to the coroutine allocator
291
longjmp(g_c.chunk_allocated, 1);
292
} else {
293
assert(here.state == Coroutine_Complete);
294
// we finish here to ensure the setjmp is redone
295
if (g_c.primary == &here) {
296
// if primary coroutine - return to Coroutine_Run
297
longjmp(g_c.controller, Coroutines_CoroutineComplete);
298
}
299
_Cor_Mutex_Unlock(&g_c.mutex);
300
Coroutine_RunNext();
301
assert(false);
302
}
303
case Chunk_Create:
7 months ago
304
// Request to create a new chunk on the stack
305
// We're here if the coroutine is:
306
// Allocated, but not 'run' (Coroutine_Idle)
307
// Run, but not not entered yet (Coroutine_Running)
308
// Completed (Coroutine_Complete)
309
assert(here.state == Coroutine_Idle || here.state == Coroutine_Running || here.state == Coroutine_Complete);
310
unsigned char *ideal_limit = (unsigned char *)&here - COROUTINE_STACK_SIZE;
311
stack_chunk_chunk(&here, StackTopNow() - ideal_limit);
7 months ago
312
assert(false);
313
case Chunk_Enter:
314
// request to start a coroutine (ie use the chunk for a coroutine)
315
// arrive here with mutex locked
316
assert(here.state == Coroutine_Running);
317
g_c.active = &here;
318
_Cor_Mutex_Unlock(&g_c.mutex);
319
here.value = here.start(here.entry_param);
9 months ago
320
7 months ago
321
// check the guard
322
assert(Check_Guard(here.guard));
9 months ago
323
7 months ago
324
_Cor_Mutex_Lock(&g_c.mutex);
325
g_c.active = NULL;
326
assert(here.state == Coroutine_Running);
327
List_Remove(&here.link);
328
here.state = Coroutine_Complete;
329
List_AddTail(&g_c.inactive, &here.link);
330
// Coroutine has completed
331
// Loop round to redo the setjmp() - if this coroutine yielded, then the setjmp will
332
// need reseting
9 months ago
4
333
}
334
}
335
}
336
9 months ago
337
8 months ago
338
static void Coroutine_RunNext(void)
9 months ago
339
{
340
// arrive here with mutex unlocked
8 months ago
341
_Cor_Mutex_Lock(&g_c.waiting_mutex);
342
_Cor_Mutex_Lock(&g_c.mutex);
343
Coroutine *next = List_Link_Container(Coroutine, link, List_GetHead(&g_c.runable));
9 months ago
344
assert(next->state == Coroutine_Running);
345
longjmp(next->buf, Chunk_Enter);
346
assert(false);
347
}
348
349
8 months ago
350
void Coroutine_StartSystem(void)
9 months ago
4
351
{
8 months ago
352
assert(g_c.state == Coroutines_Idle);
353
g_c.state = Coroutines_Starting;
9 months ago
354
8 months ago
355
_Cor_Mutex_ctor(&g_c.mutex);
9 months ago
356
8 months ago
357
g_c.tip = NULL;
358
g_c.active = NULL;
9 months ago
359
8 months ago
360
List_Init(&g_c.free);
361
List_Init(&g_c.inactive);
362
List_Init(&g_c.runable);
363
List_Init(&g_c.waiting);
364
_Cor_Mutex_ctor(&g_c.waiting_mutex);
365
_Cor_Mutex_Lock(&g_c.waiting_mutex);
9 months ago
4
366
8 months ago
367
g_c.report.coroutines_created = 0;
368
g_c.report.coroutines_pool_size = 0;
9 months ago
4
369
370
// prime the chunk system
8 months ago
371
if (!setjmp(g_c.chunk_allocated)){
9 months ago
4
372
Coroutine_PrimeStackChunks();
373
assert(false);
374
}
9 months ago
375
8 months ago
376
assert(g_c.state == Coroutines_Starting);
377
g_c.state = Coroutines_Started;
9 months ago
4
378
}
379
9 months ago
380
7 months ago
381
void Coroutine_SetStackLimit(void *limit){
7 months ago
382
assert(!limit || !(g_c.state == Coroutines_Started || g_c.state == Coroutines_Active) || (unsigned char *)limit < (unsigned char *)g_c.tip);
7 months ago
383
g_c.stack_limit = limit;
384
}
385
386
8 months ago
387
Coroutine_Report Coroutine_StopSystem(void)
9 months ago
4
388
{
8 months ago
389
_Cor_Mutex_Lock(&g_c.mutex);
390
assert(g_c.state == Coroutines_Started);
391
g_c.state = Coroutines_Stopping;
9 months ago
4
392
7 months ago
393
uintptr_t stackminheadroom;;
394
#if COROUTINE_RECORD_LOWEST_HEADROOM
395
stackminheadroom = COROUTINE_STACK_SIZE;
8 months ago
396
for (List_Link *link = g_c.free.fwd.link.next; link->next; link = link->next){
9 months ago
397
Coroutine *cor = List_Link_Container(Coroutine, link, link);
7 months ago
398
if (cor->guard){
399
for (uintptr_t i = 4; i < COROUTINE_STACK_SIZE-3; i += 4){
400
if (!Check_Guard(&cor->guard[i])){
401
stackminheadroom = i < stackminheadroom ? i : stackminheadroom;
402
break;
403
}
9 months ago
404
}
405
}
406
}
7 months ago
407
#else
408
stackminheadroom = 0;
409
#endif
8 months ago
410
g_c.report.lowest_headroom = stackminheadroom;
9 months ago
411
8 months ago
412
assert(List_IsEmpty(&g_c.inactive));
413
_Cor_Mutex_Unlock(&g_c.waiting_mutex);
414
_Cor_Mutex_dtor(&g_c.waiting_mutex);
9 months ago
4
415
8 months ago
416
assert(g_c.state == Coroutines_Stopping);
417
g_c.state = Coroutines_Idle;
418
_Cor_Mutex_Unlock(&g_c.mutex);
419
_Cor_Mutex_dtor(&g_c.mutex);
9 months ago
420
8 months ago
421
return g_c.report;
9 months ago
4
422
}
423
9 months ago
424
9 months ago
425
void Coroutine_Run_Coroutine(
426
Coroutine *cor,
9 months ago
427
void *value
428
){
429
Coroutines *cors = cor->coroutines;
8 months ago
430
assert(&g_c == cors);
8 months ago
431
_Cor_Mutex_Lock(&cors->mutex);
9 months ago
432
assert(cors->state == Coroutines_Started);
433
cors->state = Coroutines_Active;
434
cors->primary = cor;
9 months ago
435
8 months ago
436
_Coroutine_Continue(cor, value, true);
9 months ago
4
437
9 months ago
438
if (!setjmp(cors->controller)){
8 months ago
439
_Cor_Mutex_Unlock(&cors->mutex);
9 months ago
440
441
// check the guard
8 months ago
442
assert(Check_Guard(cors->guard));
9 months ago
443
9 months ago
4
444
// start the first coroutine
445
Coroutine_RunNext();
446
}
9 months ago
447
// arrive here with mutex locked
9 months ago
448
assert(List_IsEmpty(&cors->runable));
449
assert(List_IsEmpty(&cors->waiting));
450
assert(cors->state == Coroutines_Active);
451
cors->state = Coroutines_Started;
8 months ago
452
_Cor_Mutex_Unlock(&cors->mutex);
9 months ago
453
}
454
455
6 months ago
456
bool Coroutine_Run(
9 months ago
457
Coroutine_Start start,
6 months ago
458
void *value,
459
void **result
9 months ago
460
){
7 months ago
461
if (g_c.active){
6 months ago
462
void *res = start(value);
463
if (result){
464
*result = res;
465
}
466
// no failures, so...
467
return false;
7 months ago
468
}
469
assert(g_c.state == Coroutines_Idle || g_c.state == Coroutines_Started);
470
bool need_start = g_c.state == Coroutines_Idle;
471
if (need_start){
472
Coroutine_StartSystem();
473
}
9 months ago
474
Coroutine *cor = Coroutine_New(start);
6 months ago
475
if (!cor){
476
// that didn't work
477
return true;
478
}
9 months ago
479
Coroutine_Run_Coroutine(cor, value);
6 months ago
480
if (result){
481
*result = Coroutine_GetValue(cor);
482
}
9 months ago
483
Coroutine_Delete(cor);
7 months ago
484
if (need_start){
485
Coroutine_StopSystem();
486
}
6 months ago
487
// no failures, so...
488
return false;
9 months ago
4
489
}
490
491
9 months ago
492
Coroutine *Coroutine_New(
493
Coroutine_Start start
494
){
8 months ago
495
assert((g_c.state == Coroutines_Started && List_IsEmpty(&g_c.inactive)) || g_c.state == Coroutines_Active);
7 months ago
496
assert(Coroutine_StackHasNotOverrun());
9 months ago
497
9 months ago
4
498
// if none free - add one
8 months ago
499
if (List_IsEmpty(&g_c.free)){
6 months ago
500
// no free stack blocks
501
if (g_c.stack_limit && g_c.stack_limit > (unsigned char *)g_c.tip - 2*COROUTINE_STACK_SIZE){
502
// no space for a new stack block
503
return NULL;
504
}
7 months ago
505
Coroutine *tip = g_c.tip;
7 months ago
506
Coroutine *me = g_c.active;
507
if (tip == me) {
508
if (!setjmp(g_c.chunk_allocated)){
509
unsigned char *ideal_limit = (unsigned char *)me - COROUTINE_STACK_SIZE;
510
stack_chunk_chunk(me, StackTopNow() - ideal_limit);
511
}
512
} else {
513
if (!setjmp(g_c.chunk_allocated)){
514
longjmp(tip->buf, Chunk_Create);
515
}
9 months ago
4
516
}
517
}
518
8 months ago
519
Coroutine *cor = List_Link_Container(Coroutine, link, List_GetHead(&g_c.free));
9 months ago
4
520
assert(cor->state == Coroutine_Free);
521
cor->state = Coroutine_Idle;
522
cor->start = start;
523
cor->value = NULL;
524
List_Remove(&cor->link);
8 months ago
525
List_AddHead(&g_c.inactive, &cor->link);
9 months ago
4
526
8 months ago
527
g_c.report.coroutines_created += 1;
9 months ago
528
9 months ago
4
529
return cor;
530
}
531
9 months ago
532
533
void Coroutine_Delete(
534
Coroutine *cor
535
){
7 months ago
536
assert(Coroutine_StackHasNotOverrun());
6 months ago
537
if (cor){
538
Coroutines *cors = cor->coroutines;
539
_Cor_Mutex_Lock(&cors->mutex);
540
assert(cor->state == Coroutine_Idle || cor->state == Coroutine_Complete);
541
cor->state = Coroutine_Free;
542
List_Remove(&cor->link);
543
List_AddTail(&cors->free, &cor->link);
544
_Cor_Mutex_Unlock(&cors->mutex);
545
}
9 months ago
4
546
}
547
9 months ago
548
8 months ago
549
// Coroutine_Continue, assuming the mutex is claimed
550
static void _Coroutine_Continue(
9 months ago
551
Coroutine *cor,
552
void *value,
553
bool early
554
){
555
Coroutines *cors = cor->coroutines;
9 months ago
4
556
assert(cor->state == Coroutine_Idle || cor->state == Coroutine_Waiting);
557
cor->entry_param = value;
558
cor->state = Coroutine_Running;
559
List_Remove(&cor->link);
560
if ( early ) {
9 months ago
561
List_AddHead(&cors->runable, &cor->link);
9 months ago
4
562
} else {
9 months ago
563
List_AddTail(&cors->runable, &cor->link);
9 months ago
4
564
}
8 months ago
565
_Cor_Mutex_Unlock(&cors->waiting_mutex);
8 months ago
566
}
567
568
569
void Coroutine_Continue(
570
Coroutine *cor,
571
void *value,
572
bool early
573
){
7 months ago
574
assert(Coroutine_StackHasNotOverrun());
8 months ago
575
Coroutines *cors = cor->coroutines;
8 months ago
576
_Cor_Mutex_Lock(&cors->mutex);
8 months ago
577
_Coroutine_Continue(cor, value, early);
8 months ago
578
_Cor_Mutex_Unlock(&cors->mutex);
9 months ago
4
579
}
580
9 months ago
581
582
void *Coroutine_Yield(
583
void *value,
584
Coroutine_YieldCallback on_yield,
9 months ago
585
void *yield_me
9 months ago
586
){
8 months ago
587
Coroutine *me = g_c.active;
7 months ago
588
assert(me);
589
assert(Coroutine_StackHasNotOverrun());
9 months ago
590
8 months ago
591
_Cor_Mutex_Lock(&g_c.mutex);
9 months ago
592
Coroutines *cors = me->coroutines;
8 months ago
593
assert(me && me->state == Coroutine_Running && cors == &g_c);
7 months ago
594
me->stack_top = StackTopNow();
9 months ago
4
595
me->value = value;
596
me->state = Coroutine_Waiting;
9 months ago
597
9 months ago
4
598
List_Remove(&me->link);
8 months ago
599
if (!List_IsEmpty(&cors->runable)){
8 months ago
600
_Cor_Mutex_Unlock(&cors->waiting_mutex);
8 months ago
601
}
9 months ago
602
List_AddTail(&cors->waiting, &me->link);
9 months ago
603
9 months ago
604
switch (setjmp(me->buf)){
605
case Chunk_Initial:
8 months ago
606
_Cor_Mutex_Unlock(&cors->mutex);
9 months ago
607
on_yield(yield_me);
9 months ago
4
608
Coroutine_RunNext();
7 months ago
609
assert(false);
9 months ago
610
case Chunk_Create:
7 months ago
611
assert(me == g_c.tip);
612
unsigned char *ideal_limit = (unsigned char *)me - COROUTINE_STACK_SIZE;
613
stack_chunk_chunk(me, me->stack_top - ideal_limit);
9 months ago
614
assert(false);
615
case Chunk_Enter:
616
// arrive here with mutex locked
9 months ago
617
cors->active = me;
7 months ago
618
assert(Coroutine_StackHasNotOverrun());
9 months ago
619
// when we return here - we are running again
620
assert(me->state == Coroutine_Running);
621
void *res = me->entry_param;
8 months ago
622
_Cor_Mutex_Unlock(&cors->mutex);
9 months ago
623
return res;
9 months ago
4
624
}
9 months ago
625
return NULL;
9 months ago
4
626
}
627
9 months ago
628
629
void *Coroutine_GetValue(
630
Coroutine *cor
631
){
9 months ago
4
632
return cor->value;
633
}
634
9 months ago
635
8 months ago
636
Coroutine *Coroutine_GetActive(void)
9 months ago
4
637
{
8 months ago
638
return g_c.active;
9 months ago
4
639
}
640
9 months ago
641
8 months ago
642
intptr_t Coroutine_GetStackHeadroom(void){
7 months ago
643
assert(Coroutine_StackHasNotOverrun());
644
Coroutine *me = g_c.active;
645
if (!me){
646
// no active coroutine
647
unsigned char *stack_limit = g_c.stack_limit;
648
if (stack_limit){
649
// no stack limit - assume we'll use COROUTINE_STACK_SIZE
650
return StackTopNow() - stack_limit;
651
} else {
652
// no information where the stack ends - return something
653
return COROUTINE_STACK_SIZE;
654
}
655
}
656
unsigned char *stack_top = StackTopNow();
657
if (me->guard){
658
// guard established - that's where we'll measure to
659
return stack_top - me->guard;
660
}
661
intptr_t used = (unsigned char *)me - stack_top;
662
unsigned char *stack_limit = g_c.stack_limit;
663
if (!stack_limit){
664
// no stack limit - assume we'll use COROUTINE_STACK_SIZE
665
return COROUTINE_STACK_SIZE - used;
666
}
667
intptr_t available = (unsigned char *)me - stack_limit;
7 months ago
668
if (available < (intptr_t)(2*COROUTINE_STACK_SIZE)){
7 months ago
669
// can't start another coroutine, so whatever's left in the C stack is what we've got
670
return available - used;
671
}
672
// can start another coroutine, so limit ourselves to a coroutine stack size's worth
673
return COROUTINE_STACK_SIZE - used;
9 months ago
674
}
675
676
7 months ago
677
// This is used to avoid compiler warnings about returning the address of a local
678
static inline void *StopAddressWarnings(void *p)
679
{
680
return p;
9 months ago
681
}
682
683
7 months ago
684
void *Coroutine_GetStackHWM(void){
685
assert(g_c.state == Coroutines_Active);
7 months ago
686
assert(Coroutine_StackHasNotOverrun());
7 months ago
687
// Find where the guards end
688
unsigned char *guard;
689
for (guard = g_c.active->guard; Check_Guard(guard); guard += 4){
690
// do nothing
691
}
692
return guard;
693
}
8 months ago
694
695
7 months ago
696
void Coroutine_ClearStackForHWM(void){
697
assert(g_c.state == Coroutines_Active);
7 months ago
698
assert(Coroutine_StackHasNotOverrun());
7 months ago
699
unsigned char *end = StackTopNow() - GUARD_PATTERN_SIZE;
700
for (unsigned char *guard = g_c.active->guard+GUARD_PATTERN_SIZE; guard <= end; guard += GUARD_PATTERN_SIZE){
701
Apply_Guard(guard);
7 months ago
702
}
703
}
704
705
7 months ago
706
bool Coroutine_CanStartCoroutine(void){
7 months ago
707
assert(g_c.state == Coroutines_Started || g_c.state == Coroutines_Active);
708
assert(Coroutine_StackHasNotOverrun());
7 months ago
709
if (!List_IsEmpty(&g_c.free)){
710
return true;
711
}
7 months ago
712
713
return !g_c.stack_limit || g_c.stack_limit <= (unsigned char *)g_c.tip - 2*COROUTINE_STACK_SIZE;
7 months ago
714
}
715
716
8 months ago
717
void *Coroutine_GetCStackTop(void){
7 months ago
718
assert(Coroutine_StackHasNotOverrun());
719
if ((g_c.state == Coroutines_Started || g_c.state == Coroutines_Active) && g_c.tip != g_c.active) {
720
return g_c.tip->stack_top;
721
} else {
722
return StackTopNow();
723
}
9 months ago
724
}
725
726
7 months ago
727
static unsigned char *StackTopNow(void){
7 months ago
728
unsigned char here[4];
729
return StopAddressWarnings(here);
730
}
731
732
9 months ago
733
struct Coroutine_ChainParam {
734
Coroutine_Start start;
735
void *value;
736
Coroutine *ret;
737
};
738
739
740
static void *Coroutine_ChainFn(
741
void *param
742
){
743
struct Coroutine_ChainParam *params = (struct Coroutine_ChainParam *)param;
744
Coroutine_Continue(params->ret, params->start(params->value), true);
745
return NULL;
746
}
747
748
749
static void Coroutine_ChainYield(
750
void *unused
751
){
752
(void)unused;
753
}
754
755
6 months ago
756
bool Coroutine_Chain(
9 months ago
757
Coroutine_Start start,
6 months ago
758
void *value,
759
void **result
9 months ago
760
){
8 months ago
761
assert(Check_Guard(Coroutine_GetActive()->guard));
9 months ago
762
Coroutine *cor = Coroutine_New(Coroutine_ChainFn);
6 months ago
763
if (!cor){
764
// failed
765
return true;
766
}
9 months ago
767
struct Coroutine_ChainParam params = {
768
start,
769
value,
770
Coroutine_GetActive()
771
};
772
Coroutine_Continue(cor, &params, true);
773
void *res = Coroutine_Yield(NULL, Coroutine_ChainYield, NULL);
774
Coroutine_Delete(cor);
6 months ago
775
if (result){
776
*result = res;
777
}
778
// success!
779
return false;
9 months ago
780
}
781
782
9 months ago
783
bool Coroutine_IsRunning(
784
Coroutine *cor
785
)
9 months ago
4
786
{
9 months ago
787
int state = cor->state;
788
return state == Coroutine_Running || state == Coroutine_Waiting;
9 months ago
4
789
}
9 months ago
790
791
7 months ago
792
bool Coroutine_IsComplete(
793
Coroutine *cor
794
)
795
{
796
int state = cor->state;
797
return state == Coroutine_Complete;
798
}
799
800
8 months ago
801
bool Coroutine_IsStarted(void){
8 months ago
802
return g_c.state == Coroutines_Active || g_c.state == Coroutines_Started;
9 months ago
803
}
804