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