1 contributor
1076 lines28.6 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
6 months ago
69
static inline List_Link *List_Begin(
70
const List_Head *list
71
){
72
return list->fwd.link.next;
73
}
74
75
76
static inline bool Link_NextIsLink(
77
const List_Link *link
78
){
79
return link->next != NULL;
80
}
81
82
83
static inline List_Link *Link_Next(
84
List_Link *link
85
){
86
return link->next;
87
}
88
89
90
static inline bool Link_PrevIsLink(
91
const List_Link *link
92
){
93
return link->prev != NULL;
94
}
95
96
97
static inline List_Link *Link_Prev(
98
List_Link *link
99
){
100
return link->prev;
101
}
102
9 months ago
103
// static inline List_Link *List_GetTail(
104
// const List_Head *list
105
// ){
9 months ago
106
// return List_IsEmpty(list) ? NULL : list->back.link.prev;
107
// }
9 months ago
108
109
9 months ago
4
110
#define OFFSETOF(Container, Field) ((char *)&((Container *)4)->Field - (char *)(Container *)4)
111
#define List_Link_Container(Container, Link, link) ((Container *)((char *)(link) - OFFSETOF(Container, Link)))
112
9 months ago
113
114
static inline void List_Init(
115
List_Head *list
116
){
9 months ago
4
117
list->fwd.link.next = &list->back.link;
118
list->fwd.link.prev = NULL;
119
list->back.link.prev = &list->fwd.link;
120
}
121
9 months ago
122
6 months ago
123
static inline void Link_AddAfter(
124
List_Link *link,
125
List_Link *after
126
){
127
link->next = after->next;
128
link->prev = after;
129
after->next->prev = link;
130
after->next = link;
131
}
132
133
9 months ago
134
static inline void List_AddHead(
135
List_Head *list,
136
List_Link *link
137
){
6 months ago
138
Link_AddAfter(link, &list->fwd.link);
9 months ago
4
139
}
140
9 months ago
141
6 months ago
142
static inline void Link_AddBefore(
143
List_Link *link,
144
List_Link *before
145
){
146
link->prev = before->prev;
147
link->next = before;
148
before->prev->next = link;
149
before->prev = link;
150
}
151
152
9 months ago
153
static inline void List_AddTail(
154
List_Head *list,
155
List_Link *link
156
){
6 months ago
157
Link_AddBefore(link, &list->back.link);
9 months ago
4
158
}
159
9 months ago
160
6 months ago
161
static inline void Link_Remove(
9 months ago
162
List_Link *link
163
){
9 months ago
4
164
link->prev->next = link->next;
165
link->next->prev = link->prev;
166
}
167
168
///////////////////////////////////////////////////////////////////////////////
169
// ...2-way linked lists
170
///////////////////////////////////////////////////////////////////////////////
171
9 months ago
172
typedef struct Coroutines Coroutines;
9 months ago
4
173
174
enum {
175
Coroutines_Starting,
176
Coroutines_Started,
177
Coroutines_Active,
178
Coroutines_Stopping
179
};
180
181
enum {
182
Chunk_Initial,
183
Chunk_Create,
6 months ago
184
Chunk_Split,
9 months ago
4
185
Chunk_Enter
186
};
187
9 months ago
188
typedef enum Coroutine_State {
9 months ago
4
189
Coroutine_Free,
190
Coroutine_Idle,
191
Coroutine_Running,
192
Coroutine_Waiting,
193
Coroutine_Complete
9 months ago
194
} Coroutine_State;
9 months ago
4
195
196
enum {
197
Coroutines_Init,
198
Coroutines_AllocatedChunk,
199
Coroutines_CoroutineComplete,
200
};
201
202
struct Coroutine {
7 months ago
203
Coroutines *coroutines; // so can work with it off-thread
204
List_Link link; // for whichever list it's on
205
jmp_buf buf; // how to get back to it
6 months ago
206
unsigned char *prev_limit; // the previous Coroutine's stack limit
207
unsigned char *base; // where the base (high address) of this Coroutine's stack is
208
unsigned char *limit; // where the limit (low address) of this Coroutine's stack is
7 months ago
209
unsigned char *guard; // where the stack overrun guard is
6 months ago
210
size_t size;
7 months ago
211
Coroutine_Start start; // entry point
212
void *entry_param; // to pass to start
213
void *value; // yielded/returned
214
unsigned char *stack_top; // recorded at yield
9 months ago
215
Coroutine_State state;
9 months ago
4
216
};
217
218
struct Coroutines {
8 months ago
219
_Cor_Mutex mutex;
9 months ago
220
jmp_buf controller; // to return from Coroutine_Run
221
jmp_buf chunk_allocated;// for chunk allocation
222
unsigned char *guard; // the stack guard for the startup sequence
6 months ago
223
size_t gap_before; // bytes between previous's stack_top and next's Coroutine
224
size_t gap_after; // bytes between Coroutine and stack_base
9 months ago
4
225
226
// singletons
227
Coroutine *tip; // top of stack chunk
228
Coroutine *active; // currently running coroutine
229
Coroutine *primary; // Coroutine_Run coroutine
7 months ago
230
unsigned char *stack_limit; // when not NULL, where the stack finishes
9 months ago
4
231
232
// lists
233
List_Head free;
234
List_Head inactive; // idle or complete
235
List_Head runable; // running or waiting to run
236
List_Head waiting; // yielded / waiting to run
8 months ago
237
_Cor_Mutex waiting_mutex;
9 months ago
4
238
9 months ago
239
// Summary of the system
240
Coroutine_Report report;
241
9 months ago
4
242
// state
243
char state;
244
};
245
6 months ago
246
_Cor_thread_local Coroutines *g_c;
9 months ago
4
247
6 months ago
248
static void ReserveStackSpace(Coroutines *cors, Coroutine *parent, size_t chunk_size, unsigned char *childs_limit);
249
static void stack_chunk_base(Coroutines *cors, unsigned char *prev_limit, unsigned char *limit);
9 months ago
4
250
9 months ago
251
7 months ago
252
#define GUARD_PATTERN_SIZE (4)
9 months ago
253
// Check whether the guard is intact
254
static inline bool Check_Guard(
255
unsigned char *guard
256
){
7 months ago
257
return !guard ||
258
(guard[0] == 0xde &&
259
guard[1] == 0xad &&
260
guard[2] == 0xbe &&
261
guard[3] == 0xef);
9 months ago
262
}
263
264
7 months ago
265
static inline void Apply_Guard(unsigned char *guard){
266
guard[0] = 0xde;
267
guard[1] = 0xad;
268
guard[2] = 0xbe;
269
guard[3] = 0xef;
270
}
271
272
6 months ago
273
static bool Coroutine_StackHasOverrun(void){
7 months ago
274
unsigned char *stack_top = StackTopNow();
6 months ago
275
unsigned char *stack_limit = g_c ? g_c->stack_limit : NULL;
7 months ago
276
if (stack_limit && stack_top < stack_limit){
277
// current stack top is beyond limit - we are overrunning NOW
6 months ago
278
return true;
7 months ago
279
}
6 months ago
280
Coroutine *me = g_c ? g_c->active : NULL;
7 months ago
281
if (!me){
6 months ago
282
return false;
7 months ago
283
}
284
if (me->guard){
6 months ago
285
return !Check_Guard(me->guard);
7 months ago
286
}
6 months ago
287
return stack_top < me->limit;
7 months ago
288
}
289
290
6 months ago
291
static void ReserveStackSpace(
292
Coroutines *cors,
7 months ago
293
Coroutine *parent,
6 months ago
294
size_t chunk_size,
295
unsigned char *childs_limit
9 months ago
296
){
7 months ago
297
unsigned char *chunk_of_stack = alloca(chunk_size);
298
#if COROUTINE_RECORD_LOWEST_HEADROOM
299
for (size_t i = 0; i <= chunk_size-GUARD_PATTERN_SIZE; i += GUARD_PATTERN_SIZE){
300
Apply_Guard(&chunk_of_stack[i]);
301
}
302
#else
7 months ago
303
Apply_Guard(chunk_of_stack);
7 months ago
304
#endif
6 months ago
305
if (parent){
306
parent->guard = chunk_of_stack;
307
parent->limit = chunk_of_stack;
308
parent->base = chunk_of_stack + chunk_size;
309
}
310
stack_chunk_base(cors, chunk_of_stack, childs_limit);
9 months ago
4
311
}
312
9 months ago
313
314
static void stack_chunk_base(
6 months ago
315
Coroutines *cors,
316
unsigned char *prev_limit,
317
unsigned char *limit
9 months ago
318
){
9 months ago
4
319
Coroutine here;
6 months ago
320
here.coroutines = cors;
7 months ago
321
here.state = Coroutine_Free;
6 months ago
322
here.prev_limit = prev_limit;
323
here.size = 0;
324
here.base = NULL;
325
here.guard = limit;
326
here.limit = limit;
327
if (limit){
328
here.base = (unsigned char *)&here - cors->gap_after;
329
here.size = here.base - here.limit;
330
Apply_Guard(limit);
331
}
332
333
// find place to insert into free list
334
List_Link *link;
335
for (link = List_Begin(&cors->free); Link_NextIsLink(link); link = Link_Next(link)){
336
Coroutine *listcor = List_Link_Container(Coroutine, link, link);
337
if (listcor < &here){
338
break;
339
}
340
}
341
Link_AddBefore(&here.link, link);
342
343
cors->report.coroutines_pool_size += 1;
344
345
if (!cors->tip || &here < cors->tip){
346
cors->tip = &here;
347
}
348
7 months ago
349
for(;;){
350
switch (setjmp(here.buf)) {
351
case Chunk_Initial:
7 months ago
352
if (here.state == Coroutine_Free){
7 months ago
353
// return to the coroutine allocator
6 months ago
354
longjmp(cors->chunk_allocated, 1);
7 months ago
355
} else {
356
assert(here.state == Coroutine_Complete);
357
// we finish here to ensure the setjmp is redone
6 months ago
358
if (cors->primary == &here) {
7 months ago
359
// if primary coroutine - return to Coroutine_Run
6 months ago
360
longjmp(cors->controller, Coroutines_CoroutineComplete);
7 months ago
361
}
6 months ago
362
_Cor_Mutex_Unlock(&cors->mutex);
7 months ago
363
Coroutine_RunNext();
364
assert(false);
365
}
366
case Chunk_Create:
7 months ago
367
// Request to create a new chunk on the stack
368
// We're here if the coroutine is:
369
// Allocated, but not 'run' (Coroutine_Idle)
370
// Run, but not not entered yet (Coroutine_Running)
371
// Completed (Coroutine_Complete)
6 months ago
372
// Free, and the coroutines system is starting - we're characterising the system
373
assert(here.state == Coroutine_Idle ||
374
here.state == Coroutine_Running ||
375
here.state == Coroutine_Complete ||
376
(here.state == Coroutine_Free && cors->state == Coroutines_Starting));
377
ReserveStackSpace(here.coroutines, &here, here.size, NULL);
7 months ago
378
assert(false);
6 months ago
379
case Chunk_Split:
380
// Request to split this free block into two
381
// here.size will be set to our shorter size
382
ReserveStackSpace(here.coroutines, &here, here.size, here.limit);
383
assert(false);
7 months ago
384
case Chunk_Enter:
385
// request to start a coroutine (ie use the chunk for a coroutine)
386
// arrive here with mutex locked
387
assert(here.state == Coroutine_Running);
6 months ago
388
here.coroutines->active = &here;
389
_Cor_Mutex_Unlock(&cors->mutex);
7 months ago
390
here.value = here.start(here.entry_param);
9 months ago
391
7 months ago
392
// check the guard
393
assert(Check_Guard(here.guard));
9 months ago
394
6 months ago
395
_Cor_Mutex_Lock(&here.coroutines->mutex);
396
here.coroutines->active = NULL;
7 months ago
397
assert(here.state == Coroutine_Running);
6 months ago
398
Link_Remove(&here.link);
7 months ago
399
here.state = Coroutine_Complete;
6 months ago
400
List_AddTail(&here.coroutines->inactive, &here.link);
7 months ago
401
// Coroutine has completed
402
// Loop round to redo the setjmp() - if this coroutine yielded, then the setjmp will
403
// need reseting
9 months ago
4
404
}
405
}
406
}
407
9 months ago
408
8 months ago
409
static void Coroutine_RunNext(void)
9 months ago
410
{
411
// arrive here with mutex unlocked
6 months ago
412
_Cor_Mutex_Lock(&g_c->waiting_mutex);
413
_Cor_Mutex_Lock(&g_c->mutex);
414
Coroutine *next = List_Link_Container(Coroutine, link, List_GetHead(&g_c->runable));
9 months ago
415
assert(next->state == Coroutine_Running);
416
longjmp(next->buf, Chunk_Enter);
417
assert(false);
418
}
419
420
8 months ago
421
void Coroutine_StartSystem(void)
9 months ago
4
422
{
6 months ago
423
assert(!g_c);
9 months ago
424
6 months ago
425
Coroutines *cors = _Cor_Malloc(sizeof(*g_c));
426
assert(cors);
9 months ago
427
6 months ago
428
cors->state = Coroutines_Starting;
429
_Cor_Mutex_ctor(&cors->mutex);
9 months ago
430
6 months ago
431
cors->tip = NULL;
432
cors->active = NULL;
9 months ago
4
433
6 months ago
434
List_Init(&cors->free);
435
List_Init(&cors->inactive);
436
List_Init(&cors->runable);
437
List_Init(&cors->waiting);
438
_Cor_Mutex_ctor(&cors->waiting_mutex);
439
_Cor_Mutex_Lock(&cors->waiting_mutex);
9 months ago
4
440
6 months ago
441
cors->report.coroutines_created = 0;
442
cors->report.coroutines_pool_size = 0;
443
cors->report.largest_stack = 0;
444
445
// Charactersize the system...
446
if (!setjmp(cors->chunk_allocated)){
447
ReserveStackSpace(cors, NULL, COROUTINE_STARTUP_STACK_SIZE, NULL);
9 months ago
4
448
}
6 months ago
449
Coroutine *cor = List_Link_Container(Coroutine, link, List_GetHead(&cors->free));
450
cor->size = COROUTINE_STARTUP_STACK_SIZE;
451
if (!setjmp(cors->chunk_allocated)){
452
longjmp(cor->buf, Chunk_Create);
453
}
454
cors->gap_before = cor->prev_limit - (unsigned char *)cor;
455
cors->gap_after = (unsigned char *)cor - cor->base;
456
// ...charactersize the system
9 months ago
457
6 months ago
458
// discard what we've just created
459
List_Init(&cors->free);
460
cors->tip = NULL;
461
462
cors->state = Coroutines_Started;
463
464
g_c = cors;
9 months ago
4
465
}
466
9 months ago
467
7 months ago
468
void Coroutine_SetStackLimit(void *limit){
6 months ago
469
assert(g_c);
470
assert(!limit || !(g_c->state == Coroutines_Started || g_c->state == Coroutines_Active) || (unsigned char *)limit < (unsigned char *)g_c->tip);
471
g_c->stack_limit = limit;
7 months ago
472
}
473
474
8 months ago
475
Coroutine_Report Coroutine_StopSystem(void)
9 months ago
4
476
{
6 months ago
477
assert(g_c);
478
assert(g_c->state == Coroutines_Started);
479
_Cor_Mutex_Lock(&g_c->mutex);
480
g_c->state = Coroutines_Stopping;
9 months ago
4
481
6 months ago
482
uintptr_t stackminheadroom;
7 months ago
483
#if COROUTINE_RECORD_LOWEST_HEADROOM
6 months ago
484
stackminheadroom = g_c->report.largest_stack;
485
for (List_Link *link = g_c->free.fwd.link.next; Link_NextIsLink(link); link = Link_Next(link)){
9 months ago
486
Coroutine *cor = List_Link_Container(Coroutine, link, link);
7 months ago
487
if (cor->guard){
6 months ago
488
for (uintptr_t i = 4; i < cor->size-3; i += 4){
7 months ago
489
if (!Check_Guard(&cor->guard[i])){
490
stackminheadroom = i < stackminheadroom ? i : stackminheadroom;
491
break;
492
}
9 months ago
493
}
494
}
495
}
7 months ago
496
#else
497
stackminheadroom = 0;
498
#endif
6 months ago
499
g_c->report.lowest_headroom = stackminheadroom;
9 months ago
500
6 months ago
501
assert(List_IsEmpty(&g_c->inactive));
502
_Cor_Mutex_Unlock(&g_c->waiting_mutex);
503
_Cor_Mutex_dtor(&g_c->waiting_mutex);
9 months ago
4
504
6 months ago
505
assert(g_c->state == Coroutines_Stopping);
506
_Cor_Mutex_Unlock(&g_c->mutex);
507
_Cor_Mutex_dtor(&g_c->mutex);
9 months ago
508
6 months ago
509
Coroutine_Report ret = g_c->report;
510
511
_Cor_Free(g_c);
512
g_c = NULL;
513
514
return ret;
9 months ago
4
515
}
516
9 months ago
517
9 months ago
518
void Coroutine_Run_Coroutine(
519
Coroutine *cor,
9 months ago
520
void *value
521
){
522
Coroutines *cors = cor->coroutines;
6 months ago
523
524
// Can't Coroutine_Run_Coroutine() off-thread
525
assert(g_c == cors);
526
8 months ago
527
_Cor_Mutex_Lock(&cors->mutex);
9 months ago
528
assert(cors->state == Coroutines_Started);
529
cors->state = Coroutines_Active;
530
cors->primary = cor;
9 months ago
531
8 months ago
532
_Coroutine_Continue(cor, value, true);
9 months ago
4
533
9 months ago
534
if (!setjmp(cors->controller)){
8 months ago
535
_Cor_Mutex_Unlock(&cors->mutex);
9 months ago
536
537
// check the guard
8 months ago
538
assert(Check_Guard(cors->guard));
9 months ago
539
9 months ago
4
540
// start the first coroutine
541
Coroutine_RunNext();
542
}
9 months ago
543
// arrive here with mutex locked
9 months ago
544
assert(List_IsEmpty(&cors->runable));
545
assert(List_IsEmpty(&cors->waiting));
546
assert(cors->state == Coroutines_Active);
547
cors->state = Coroutines_Started;
8 months ago
548
_Cor_Mutex_Unlock(&cors->mutex);
9 months ago
549
}
550
551
6 months ago
552
bool Coroutine_Run(
6 months ago
553
size_t stack,
9 months ago
554
Coroutine_Start start,
6 months ago
555
void *value,
556
void **result
9 months ago
557
){
6 months ago
558
if (g_c && g_c->active){
6 months ago
559
void *res = start(value);
560
if (result){
561
*result = res;
562
}
563
// no failures, so...
564
return false;
7 months ago
565
}
6 months ago
566
assert(!g_c || g_c->state == Coroutines_Started);
567
bool need_start = !g_c;
7 months ago
568
if (need_start){
569
Coroutine_StartSystem();
570
}
6 months ago
571
Coroutine *cor = Coroutine_New(stack, start);
6 months ago
572
if (!cor){
573
// that didn't work
574
return true;
575
}
9 months ago
576
Coroutine_Run_Coroutine(cor, value);
6 months ago
577
if (result){
578
*result = Coroutine_GetValue(cor);
579
}
9 months ago
580
Coroutine_Delete(cor);
7 months ago
581
if (need_start){
582
Coroutine_StopSystem();
583
}
6 months ago
584
// no failures, so...
585
return false;
9 months ago
4
586
}
587
588
6 months ago
589
static void Coroutine_FreeToIdle(
590
Coroutine *cor,
9 months ago
591
Coroutine_Start start
592
){
6 months ago
593
assert(cor->state == Coroutine_Free);
594
cor->state = Coroutine_Idle;
595
cor->start = start;
596
cor->value = NULL;
597
Link_Remove(&cor->link);
598
List_AddHead(&g_c->inactive, &cor->link);
9 months ago
599
6 months ago
600
g_c->report.coroutines_created += 1;
601
}
602
603
604
static void Coroutine_FreeToIdleSize(
605
Coroutine *cor,
606
Coroutine_Start start,
607
size_t size
608
){
609
assert(!cor->guard);
610
cor->size = size;
611
cor->base = (unsigned char *)cor - g_c->gap_after;
612
cor->limit = cor->base - cor->size;
613
Coroutine_FreeToIdle(cor, start);
614
}
615
616
617
static Coroutine *Coroutine_New_Lock_Assumed(
618
size_t size,
619
Coroutine_Start start
620
){
621
List_Link *link;
622
623
if (!g_c->tip){
624
// no tip - time to create one
625
626
// we're the non-Coroutine which starts the Coroutine system.
627
// Add a single free block
628
if (!setjmp(g_c->chunk_allocated)){
629
ReserveStackSpace(g_c, NULL, COROUTINE_STARTUP_STACK_SIZE, NULL);
6 months ago
630
}
6 months ago
631
}
632
633
for (link = List_Begin(&g_c->free); Link_NextIsLink(link); link = Link_Next(link)){
634
Coroutine *cor = List_Link_Container(Coroutine, link, link);
635
if (!cor->guard) {
636
// this must be the tip
637
assert(cor == g_c->tip);
638
639
// If this is the only Coroutine in the system, go ahead and use it regardless of size.
640
// Note: there can only be one free block if there's no other sort of blocks as we merge on free
641
if (List_IsEmpty(&g_c->inactive) &&
642
List_IsEmpty(&g_c->runable) &&
643
List_IsEmpty(&g_c->waiting) ){
644
if (g_c->stack_limit){
645
size_t available = (unsigned char *)cor - g_c->stack_limit - g_c->gap_after;
646
size = available < size ? available : size;
647
}
648
Coroutine_FreeToIdleSize(cor, start, size);
649
return cor;
7 months ago
650
}
6 months ago
651
652
// Not the only coroutine in the system - check size
653
if (g_c->stack_limit){
654
// there's a limit - see what that space allows....
655
size_t available = (unsigned char *)cor - g_c->stack_limit - g_c->gap_after;
656
657
if (available < size){
658
// not enough space for this coroutine
659
return NULL;
660
}
661
662
if (available >= size + g_c->gap_before + g_c->gap_after + COROUTINE_MINIMUM_STACK_SIZE) {
663
// not enough space for another coroutine - use all the space for this one
664
size = available;
665
}
7 months ago
666
}
6 months ago
667
Coroutine_FreeToIdleSize(cor, start, size);
668
return cor;
9 months ago
4
669
}
6 months ago
670
if (cor->size >= size){
671
// chunk big enough - work out whether we're splitting or using the whole chunk
672
if (cor->size >= size + g_c->gap_before + g_c->gap_after + COROUTINE_MINIMUM_STACK_SIZE){
673
// enough space for a second coroutine so split this free block
674
cor->size = size;
675
if (!setjmp(g_c->chunk_allocated)){
676
longjmp(cor->buf, Chunk_Split);
677
}
678
}
679
// cor now ready to use
680
Coroutine_FreeToIdle(cor, start);
681
return cor;
682
}
9 months ago
4
683
}
684
6 months ago
685
// No big-enough free blocks - check if there's space beyond the tip block
686
687
if (g_c->stack_limit && g_c->stack_limit > (unsigned char *)g_c->tip->limit - sizeof(Coroutine) - size){
688
// no space for a new stack block
689
return NULL;
690
}
691
Coroutine *tip = g_c->tip;
692
Coroutine *me = g_c->active;
693
if (tip == me) {
694
if (!setjmp(g_c->chunk_allocated)){
695
ReserveStackSpace(g_c, me, StackTopNow() - me->limit, NULL);
696
}
697
} else {
698
if (!setjmp(g_c->chunk_allocated)){
699
longjmp(tip->buf, Chunk_Create);
700
}
701
}
702
703
Coroutine *cor = List_Link_Container(Coroutine, link, List_GetHead(&g_c->free));
9 months ago
4
704
assert(cor->state == Coroutine_Free);
6 months ago
705
cor->size = size;
706
cor->limit = (unsigned char *)cor - g_c->gap_after - size;
9 months ago
4
707
cor->state = Coroutine_Idle;
708
cor->start = start;
709
cor->value = NULL;
6 months ago
710
Link_Remove(&cor->link);
711
List_AddHead(&g_c->inactive, &cor->link);
9 months ago
4
712
6 months ago
713
g_c->report.coroutines_created += 1;
714
return cor;
715
}
9 months ago
716
6 months ago
717
718
Coroutine *Coroutine_New(
719
size_t stack,
720
Coroutine_Start start
721
){
722
assert(g_c);
723
assert((g_c->state == Coroutines_Started && List_IsEmpty(&g_c->inactive)) || g_c->state == Coroutines_Active);
724
assert(!Coroutine_StackHasOverrun());
725
726
_Cor_Mutex_Lock(&g_c->mutex);
727
728
Coroutine *cor = Coroutine_New_Lock_Assumed(stack, start);
729
730
if (cor && cor->size > g_c->report.largest_stack){
731
g_c->report.largest_stack = cor->size;
732
}
733
734
_Cor_Mutex_Unlock(&g_c->mutex);
735
9 months ago
4
736
return cor;
737
}
738
9 months ago
739
740
void Coroutine_Delete(
741
Coroutine *cor
742
){
6 months ago
743
assert(!Coroutine_StackHasOverrun());
6 months ago
744
if (cor){
745
Coroutines *cors = cor->coroutines;
746
_Cor_Mutex_Lock(&cors->mutex);
747
assert(cor->state == Coroutine_Idle || cor->state == Coroutine_Complete);
748
cor->state = Coroutine_Free;
6 months ago
749
Link_Remove(&cor->link);
750
751
// Find place to insert into free list
752
List_Link *link;
753
for (link = List_Begin(&cors->free); Link_NextIsLink(link); link = Link_Next(link)){
754
Coroutine *listcor = List_Link_Container(Coroutine, link, link);
755
if (listcor < cor){
756
break;
757
}
758
}
759
Link_AddBefore(&cor->link, link);
760
761
// Check for merge with following Coroutine
762
if (Link_NextIsLink(link)){
763
Coroutine *listcor = List_Link_Container(Coroutine, link, link);
764
if (cor->limit == listcor->prev_limit){
765
// merge
766
cor->size += listcor->limit - cor->limit;
767
cor->limit = listcor->limit;
768
cor->guard = listcor->guard;
769
Link_Remove(&listcor->link);
770
}
771
}
772
773
// check for merge with prev coroutine
774
link = Link_Prev(&cor->link);
775
if (Link_PrevIsLink(link)){
776
Coroutine *listcor = List_Link_Container(Coroutine, link, link);
777
if (listcor->limit == cor->prev_limit){
778
// merge
779
listcor->size += cor->limit - listcor->limit;
780
listcor->limit = cor->limit;
781
listcor->guard = cor->guard;
782
Link_Remove(&cor->link);
783
}
784
}
785
6 months ago
786
_Cor_Mutex_Unlock(&cors->mutex);
787
}
9 months ago
4
788
}
789
9 months ago
790
8 months ago
791
// Coroutine_Continue, assuming the mutex is claimed
792
static void _Coroutine_Continue(
9 months ago
793
Coroutine *cor,
794
void *value,
795
bool early
796
){
797
Coroutines *cors = cor->coroutines;
9 months ago
4
798
assert(cor->state == Coroutine_Idle || cor->state == Coroutine_Waiting);
799
cor->entry_param = value;
800
cor->state = Coroutine_Running;
6 months ago
801
Link_Remove(&cor->link);
9 months ago
4
802
if ( early ) {
9 months ago
803
List_AddHead(&cors->runable, &cor->link);
9 months ago
4
804
} else {
9 months ago
805
List_AddTail(&cors->runable, &cor->link);
9 months ago
4
806
}
8 months ago
807
_Cor_Mutex_Unlock(&cors->waiting_mutex);
8 months ago
808
}
809
810
811
void Coroutine_Continue(
812
Coroutine *cor,
813
void *value,
814
bool early
815
){
6 months ago
816
assert(!Coroutine_StackHasOverrun());
8 months ago
817
Coroutines *cors = cor->coroutines;
8 months ago
818
_Cor_Mutex_Lock(&cors->mutex);
8 months ago
819
_Coroutine_Continue(cor, value, early);
8 months ago
820
_Cor_Mutex_Unlock(&cors->mutex);
9 months ago
4
821
}
822
9 months ago
823
824
void *Coroutine_Yield(
825
void *value,
826
Coroutine_YieldCallback on_yield,
9 months ago
827
void *yield_me
9 months ago
828
){
6 months ago
829
assert(g_c);
830
Coroutine *me = g_c->active;
7 months ago
831
assert(me);
6 months ago
832
assert(!Coroutine_StackHasOverrun());
9 months ago
833
6 months ago
834
_Cor_Mutex_Lock(&g_c->mutex);
9 months ago
835
Coroutines *cors = me->coroutines;
6 months ago
836
assert(me && me->state == Coroutine_Running && cors == g_c);
7 months ago
837
me->stack_top = StackTopNow();
9 months ago
4
838
me->value = value;
839
me->state = Coroutine_Waiting;
9 months ago
840
6 months ago
841
Link_Remove(&me->link);
8 months ago
842
if (!List_IsEmpty(&cors->runable)){
8 months ago
843
_Cor_Mutex_Unlock(&cors->waiting_mutex);
8 months ago
844
}
9 months ago
845
List_AddTail(&cors->waiting, &me->link);
9 months ago
846
9 months ago
847
switch (setjmp(me->buf)){
848
case Chunk_Initial:
8 months ago
849
_Cor_Mutex_Unlock(&cors->mutex);
9 months ago
850
on_yield(yield_me);
9 months ago
4
851
Coroutine_RunNext();
7 months ago
852
assert(false);
9 months ago
853
case Chunk_Create:
6 months ago
854
assert(me == g_c->tip);
855
ReserveStackSpace(me->coroutines, me, me->stack_top - me->limit, NULL);
9 months ago
856
assert(false);
857
case Chunk_Enter:
858
// arrive here with mutex locked
9 months ago
859
cors->active = me;
6 months ago
860
assert(!Coroutine_StackHasOverrun());
9 months ago
861
// when we return here - we are running again
862
assert(me->state == Coroutine_Running);
863
void *res = me->entry_param;
8 months ago
864
_Cor_Mutex_Unlock(&cors->mutex);
9 months ago
865
return res;
9 months ago
4
866
}
9 months ago
867
return NULL;
9 months ago
4
868
}
869
9 months ago
870
871
void *Coroutine_GetValue(
872
Coroutine *cor
873
){
9 months ago
4
874
return cor->value;
875
}
876
9 months ago
877
8 months ago
878
Coroutine *Coroutine_GetActive(void)
9 months ago
4
879
{
6 months ago
880
return g_c ? g_c->active : NULL;
9 months ago
4
881
}
882
9 months ago
883
8 months ago
884
intptr_t Coroutine_GetStackHeadroom(void){
6 months ago
885
assert(g_c);
886
assert(!Coroutine_StackHasOverrun());
887
Coroutine *me = g_c->active;
7 months ago
888
if (!me){
889
// no active coroutine
6 months ago
890
unsigned char *stack_limit = g_c->stack_limit;
7 months ago
891
if (stack_limit){
892
// no stack limit - assume we'll use COROUTINE_STACK_SIZE
893
return StackTopNow() - stack_limit;
894
} else {
895
// no information where the stack ends - return something
6 months ago
896
return COROUTINE_MINIMUM_STACK_SIZE;
7 months ago
897
}
898
}
6 months ago
899
return StackTopNow() - me->limit;
9 months ago
900
}
901
902
7 months ago
903
// This is used to avoid compiler warnings about returning the address of a local
904
static inline void *StopAddressWarnings(void *p)
905
{
906
return p;
9 months ago
907
}
908
909
7 months ago
910
void *Coroutine_GetStackHWM(void){
6 months ago
911
assert(g_c);
912
assert(g_c->state == Coroutines_Active);
913
assert(!Coroutine_StackHasOverrun());
7 months ago
914
// Find where the guards end
915
unsigned char *guard;
6 months ago
916
for (guard = g_c->active->guard; Check_Guard(guard); guard += 4){
7 months ago
917
// do nothing
918
}
919
return guard;
920
}
8 months ago
921
922
7 months ago
923
void Coroutine_ClearStackForHWM(void){
6 months ago
924
assert(g_c);
925
assert(g_c->state == Coroutines_Active);
926
assert(!Coroutine_StackHasOverrun());
7 months ago
927
unsigned char *end = StackTopNow() - GUARD_PATTERN_SIZE;
6 months ago
928
for (unsigned char *guard = g_c->active->guard+GUARD_PATTERN_SIZE; guard <= end; guard += GUARD_PATTERN_SIZE){
7 months ago
929
Apply_Guard(guard);
7 months ago
930
}
931
}
932
933
6 months ago
934
static bool Coroutine_CanStartCoroutine_Lock_Assumed(
935
size_t size
936
){
937
assert(g_c);
938
assert(g_c->state == Coroutines_Started || g_c->state == Coroutines_Active);
939
assert(!Coroutine_StackHasOverrun());
940
941
if (!g_c->stack_limit){
7 months ago
942
return true;
943
}
7 months ago
944
6 months ago
945
if (!g_c->tip){
946
return true;
947
}
948
949
if (g_c->tip->state == Coroutine_Free){
950
// last block is free
951
if ((unsigned char *)g_c->tip - g_c->stack_limit >= (ptrdiff_t)(g_c->gap_after + size)){
952
// enough room in free block, which is the last block
953
return true;
954
}
955
} else {
956
// last block is allocated
957
if (g_c->tip->limit - g_c->stack_limit >= (ptrdiff_t)(g_c->gap_before + g_c->gap_after + size)){
958
// enough room after the last block, which is allocated
959
return true;
960
}
961
}
962
963
// not enough room between allocated blocks and stack limit, so check free list
964
List_Link *link;
965
for (link = List_Begin(&g_c->free); Link_NextIsLink(link); link = Link_Next(link)){
966
Coroutine *cor = List_Link_Container(Coroutine, link, link);
967
if (cor->size >= size){
968
return true;
969
}
970
}
971
972
return false;
7 months ago
973
}
974
975
6 months ago
976
bool Coroutine_CanStartCoroutine(
977
size_t size
978
){
979
_Cor_Mutex_Lock(&g_c->mutex);
980
981
bool result = Coroutine_CanStartCoroutine_Lock_Assumed(size);
982
983
_Cor_Mutex_Unlock(&g_c->mutex);
984
985
return result;
986
}
987
8 months ago
988
void *Coroutine_GetCStackTop(void){
6 months ago
989
assert(!Coroutine_StackHasOverrun());
990
if ((g_c->state == Coroutines_Started || g_c->state == Coroutines_Active) && g_c->tip != g_c->active) {
991
return g_c->tip->stack_top;
7 months ago
992
} else {
993
return StackTopNow();
994
}
9 months ago
995
}
996
997
7 months ago
998
static unsigned char *StackTopNow(void){
7 months ago
999
unsigned char here[4];
1000
return StopAddressWarnings(here);
1001
}
1002
1003
9 months ago
1004
struct Coroutine_ChainParam {
1005
Coroutine_Start start;
1006
void *value;
1007
Coroutine *ret;
1008
};
1009
1010
1011
static void *Coroutine_ChainFn(
1012
void *param
1013
){
1014
struct Coroutine_ChainParam *params = (struct Coroutine_ChainParam *)param;
1015
Coroutine_Continue(params->ret, params->start(params->value), true);
1016
return NULL;
1017
}
1018
1019
1020
static void Coroutine_ChainYield(
1021
void *unused
1022
){
1023
(void)unused;
1024
}
1025
1026
6 months ago
1027
bool Coroutine_Chain(
6 months ago
1028
size_t size,
9 months ago
1029
Coroutine_Start start,
6 months ago
1030
void *value,
1031
void **result
9 months ago
1032
){
8 months ago
1033
assert(Check_Guard(Coroutine_GetActive()->guard));
6 months ago
1034
Coroutine *cor = Coroutine_New(size, Coroutine_ChainFn);
6 months ago
1035
if (!cor){
1036
// failed
1037
return true;
1038
}
9 months ago
1039
struct Coroutine_ChainParam params = {
1040
start,
1041
value,
1042
Coroutine_GetActive()
1043
};
1044
Coroutine_Continue(cor, &params, true);
1045
void *res = Coroutine_Yield(NULL, Coroutine_ChainYield, NULL);
1046
Coroutine_Delete(cor);
6 months ago
1047
if (result){
1048
*result = res;
1049
}
1050
// success!
1051
return false;
9 months ago
1052
}
1053
1054
9 months ago
1055
bool Coroutine_IsRunning(
1056
Coroutine *cor
1057
)
9 months ago
4
1058
{
9 months ago
1059
int state = cor->state;
1060
return state == Coroutine_Running || state == Coroutine_Waiting;
9 months ago
4
1061
}
9 months ago
1062
1063
7 months ago
1064
bool Coroutine_IsComplete(
1065
Coroutine *cor
1066
)
1067
{
1068
int state = cor->state;
1069
return state == Coroutine_Complete;
1070
}
1071
1072
8 months ago
1073
bool Coroutine_IsStarted(void){
6 months ago
1074
return g_c && (g_c->state == Coroutines_Active || g_c->state == Coroutines_Started);
9 months ago
1075
}
1076