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