1588 lines39.0 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>
1 weeks ago
7
#include <stdlib.h>
8 months ago
8
#include "cor_platform.h"
9 months ago
4
9
9 hours 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
9 hours 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
1 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
{ \
9 hours 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
1 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 {
1 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
1 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;
1 weeks ago
344
345
int sequence;
9 months ago
4
346
};
347
348
struct Coroutines {
8 months ago
349
_Cor_Mutex mutex;
9 hours ago
350
jmp_buf controller; // to return from Coroutine_Run
9 months ago
351
jmp_buf chunk_allocated;// for chunk allocation
1 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
9 hours 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
1 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
1 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;
1 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
1 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
1 weeks ago
402
TrimmableSize(
403
Coroutine *cor
404
){
1 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;
1 weeks ago
409
}
410
411
if (cor->limit){
1 weeks ago
412
if (Coroutine_Size(cor) < min_size + COROUTINE_MINIMUM_STACK_SIZE){
1 weeks ago
413
return 0;
414
}
415
}
416
return min_size;
417
}
418
419
7 months ago
420
#define GUARD_PATTERN_SIZE (4)
1 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 ||
1 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
1 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){
1 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
1 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
1 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
9 hours 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
1 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
9 hours 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;
1 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
9 hours 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){
1 weeks ago
531
if (StackPointerDiff(me->guard, stack_top) < 0){
7 days ago
532
// Stack top beyond active stack limit
1 weeks ago
533
return Coroutine_Err_StackOverrun;
534
}
535
if (!Guard_Pattern_OK(me->guard)){
7 days ago
536
// Guard pattern trampled
1 weeks ago
537
return Coroutine_Err_StackOverrun;
Last month
538
}
7 months ago
539
}
1 weeks ago
540
return Coroutine_OK;
7 months ago
541
}
542
Last month
543
#ifndef NDEBUG
1 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
9 hours ago
547
Coroutine_CheckIntegrity(
Last month
548
void
549
){
9 hours ago
550
Coroutine_Err err = Coroutine_StackHasOverrun();
Last month
551
#if !COROUTINE_CHECK_INTEGRITY_ON_STACK_CHECK
552
if (!err && g_c){
9 hours 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);
1 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){
1 weeks ago
573
Apply_Guard(StackPointerAdd(guard, -i));
7 months ago
574
}
575
#else
1 weeks ago
576
Apply_Guard(guard);
7 months ago
577
#endif
6 months ago
578
if (parent){
1 weeks ago
579
parent->limit = limit;
580
parent->guard = guard;
6 months ago
581
}
1 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
){
1 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){
1 weeks ago
603
here->guard = StackPointerAdd(limit, -GUARD_PATTERN_SIZE);
6 months ago
604
Apply_Guard(limit);
1 weeks ago
605
} else {
606
here->guard = NULL;
6 months ago
607
}
608
6 months ago
609
// insert into all list
610
if (parent){
1 weeks ago
611
Link_AddAfter(&here->all_link, &parent->all_link);
6 months ago
612
} else {
1 weeks ago
613
List_AddHead(&cors->all, &here->all_link);
6 months ago
614
}
6 months ago
615
// add to free list
1 weeks ago
616
List_AddTail(&cors->free, &here->link);
6 months ago
617
618
cors->report.coroutines_pool_size += 1;
619
1 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(;;){
1 weeks ago
625
switch (setjmp(here->buf)) {
7 months ago
626
case Chunk_Initial:
1 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 {
1 weeks ago
632
MyAssert(here->state == Coroutine_Complete);
7 months ago
633
// we finish here to ensure the setjmp is redone
1 weeks ago
634
if (cors->primary == here) {
9 hours 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);
9 hours ago
639
Coroutine_RunNext();
7 months ago
640
}
3 months ago
641
MyAssert(false);
642
break;
6 months ago
643
case Chunk_Split:
1 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
1 weeks ago
652
MyAssert(here->state == Coroutine_Running);
653
here->coroutines->active = here;
6 months ago
654
_Cor_Mutex_Unlock(&cors->mutex);
1 weeks ago
655
here->value = here->start(here->entry_param);
9 months ago
656
7 months ago
657
// check the guard
1 weeks ago
658
MyAssert(Guard_Pattern_OK(here->guard));
9 months ago
659
1 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
9 hours 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
1 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(
1 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)){
1 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)){
1 weeks ago
724
goto error1;
4 months ago
725
}
726
if (_Cor_Mutex_Lock(&cors->waiting_mutex)){
1 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;
1 weeks ago
733
cors->spare = NULL;
734
cors->sequence = 0;
6 months ago
735
1 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
}
1 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
1 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;
1 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));
1 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
9 hours ago
800
Coroutine_RunSystem(
1 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;
1 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;
1 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
9 hours ago
822
Coroutine_SetStackLimit(
Last month
823
void *limit
824
){
7 days ago
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;
1 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
9 hours 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){
1 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
9 hours 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;
9 hours 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
4 months ago
887
#ifndef NDEBUG
Last month
888
static void
9 hours ago
889
Coroutine_ReportNonEmptyList(
4 months ago
890
List_Head const *head,
891
char const *tag
892
){
893
List_Link *link;
894
for (link = List_Begin(head); Link_NextIsLink(link); link = Link_Next(link)){
895
Coroutine *cor = List_Link_Container(Coroutine, link, link);
896
printf("%s: %p %p %p\n", tag, cor, cor->start, cor->entry_param);
897
}
898
}
899
#endif
900
Last month
901
Coroutine_Err
9 hours ago
902
Coroutine_Run_Coroutine(
9 months ago
903
Coroutine *cor,
9 months ago
904
void *value
905
){
4 months ago
906
CHECK_SYSTEM_RUNNING
907
CHECK_COROUTINE_THREAD
908
CHECK_NO_COROUTINE_RUNNING
909
9 months ago
910
Coroutines *cors = cor->coroutines;
8 months ago
911
_Cor_Mutex_Lock(&cors->mutex);
9 months ago
912
cors->primary = cor;
9 months ago
913
9 hours ago
914
Coroutine_Continue_(cors, cor, value, true);
9 months ago
4
915
9 months ago
916
if (!setjmp(cors->controller)){
Last month
917
ready_jmp_buf(cors->controller);
8 months ago
918
_Cor_Mutex_Unlock(&cors->mutex);
9 months ago
919
9 months ago
4
920
// start the first coroutine
9 hours ago
921
Coroutine_RunNext();
9 months ago
4
922
}
9 months ago
923
// arrive here with mutex locked
4 months ago
924
if (!List_IsEmpty(&cors->runable) || !List_IsEmpty(&cors->waiting)){
4 months ago
925
#ifndef NDEBUG
9 hours ago
926
Coroutine_ReportNonEmptyList(&cors->runable, "runable");
927
Coroutine_ReportNonEmptyList(&cors->waiting, "waiting");
4 months ago
928
#endif
929
return Coroutine_Err_ExitWithRunningCoroutines;
4 months ago
930
}
8 months ago
931
_Cor_Mutex_Unlock(&cors->mutex);
4 months ago
932
933
return Coroutine_OK;
9 months ago
934
}
935
936
Last month
937
struct Coroutine_Run_Params {
4 months ago
938
Coroutine_Start start;
939
void *value;
940
void **result;
941
};
942
Last month
943
static Coroutine_Err
9 hours ago
944
Coroutine_Run_Starter(
1 weeks ago
945
void *_params,
946
Coroutine *root
Last month
947
){
1 weeks ago
948
(void)root;
Last month
949
struct Coroutine_Run_Params *params = (struct Coroutine_Run_Params *)_params;
4 months ago
950
1 weeks ago
951
void *res = params->start(params->value);
952
if (params->result){
953
*params->result = res;
4 months ago
954
}
1 weeks ago
955
956
return Coroutine_OK;
4 months ago
957
}
958
959
9 hours ago
960
Coroutine_Err Coroutine_Run(
1 weeks ago
961
size_t min_size,
962
size_t min_headroom,
9 months ago
963
Coroutine_Start start,
6 months ago
964
void *value,
965
void **result
9 months ago
966
){
4 months ago
967
if (!g_c){
1 weeks ago
968
struct Coroutine_Run_Params params = {start, value, result};
9 hours ago
969
return Coroutine_RunSystem(min_size, min_headroom, Coroutine_Run_Starter, &params);
4 months ago
970
}
971
972
// We are in an active coroutine, so call start() directly
973
CHECK_STACK_OVERRUN
974
void *res = start(value);
6 months ago
975
if (result){
4 months ago
976
*result = res;
6 months ago
977
}
4 months ago
978
6 months ago
979
// no failures, so...
4 months ago
980
return Coroutine_OK;
9 months ago
4
981
}
982
983
9 hours ago
984
static Coroutine *Coroutine_New_Lock_Assumed(
1 weeks ago
985
size_t min_size,
986
size_t min_headroom,
6 months ago
987
Coroutine_Start start
988
){
989
List_Link *link;
990
1 weeks ago
991
if (!EnsureSpare(g_c)){
992
return NULL;
6 months ago
993
}
994
6 months ago
995
Coroutine *cor = NULL;
6 months ago
996
for (link = List_Begin(&g_c->free); Link_NextIsLink(link); link = Link_Next(link)){
6 months ago
997
Coroutine *candidate = List_Link_Container(Coroutine, link, link);
4 months ago
998
MyAssert(candidate->coroutines == g_c);
1 weeks ago
999
MyAssert(candidate->state == Coroutine_Free);
1000
if (candidate->limit){
1001
size_t candidate_size = Coroutine_Size(candidate);
1002
if (candidate_size < min_size){
1003
continue;
7 months ago
1004
}
1 weeks ago
1005
}
1006
// candidate has no limit, or is big enough
6 months ago
1007
1 weeks ago
1008
// check if it's 'better' (lower in the C stack) than cor
1009
if (!cor || StackPointerDiff(candidate->base, cor->base) < 0){
6 months ago
1010
// chunk big enough, and a better choice than cor
1011
cor = candidate;
1012
}
1013
}
1014
1 weeks ago
1015
if (!cor){
1016
return NULL;
1017
}
1018
1019
// use the whole block - the tail will be freed in (New) or (Yield)
1020
cor->min_size = min_size;
1021
cor->min_headroom = min_headroom;
1022
cor->state = Coroutine_Idle;
1023
cor->start = start;
1024
cor->value = NULL;
1025
Link_Remove(&cor->link);
1026
List_AddHead(&g_c->inactive, &cor->link);
1027
1028
// trim down to an appropriate size
1029
size_t trimmable_size = TrimmableSize(cor);
1030
if (trimmable_size){
1031
// split block into two
1032
if (EnsureSpare(g_c)){
1033
g_c->size_to_retain = trimmable_size;
6 months ago
1034
if (!setjmp(g_c->chunk_allocated)){
Last month
1035
ready_jmp_buf(g_c->chunk_allocated);
6 months ago
1036
longjmp(cor->buf, Chunk_Split);
6 months ago
1037
}
1038
}
9 months ago
4
1039
}
1040
1 weeks ago
1041
g_c->report.coroutines_created += 1;
6 months ago
1042
1 weeks ago
1043
return cor;
1044
}
1045
1046
1047
static void
1048
TrimActiveIfPossible
1049
(
1050
void
1051
){
1052
MyAssert(g_c);
1053
1054
// If we're the active coroutine, free the tail
1055
Coroutine *active = g_c->active;
1056
MyAssert(active);
1057
1058
active->stack_top = (unsigned char *)StackTopNow();
1059
ptrdiff_t timmablesize = TrimmableSize(active);
1060
if (!timmablesize){
1061
return;
6 months ago
1062
}
1 weeks ago
1063
1064
if (!EnsureSpare(g_c)){
1065
return;
6 months ago
1066
}
1067
1 weeks ago
1068
// enough space for a second coroutine so free the unused stack space from active
1069
if (!setjmp(g_c->chunk_allocated)){
1070
ready_jmp_buf(g_c->chunk_allocated);
1071
ReserveStackSpace(g_c, active, timmablesize - StackPointerDiff(active->stack_top, active->base), active->limit);
1072
MyAssert(false);
1073
}
1074
}
1075
1076
1077
static void
1078
EnlargeActiveIfPossible(
1079
void
1080
){
1081
MyAssert(g_c);
1082
Coroutine *active = g_c->active;
1083
MyAssert(active);
1084
1085
List_Link *next = Link_Next(&active->all_link);
1086
if (!Link_NextIsLink(next)){
1087
return;
1088
}
1089
Coroutine *cor = List_Link_Container(Coroutine, all_link, next);
1090
if (cor->state != Coroutine_Free){
1091
return;
1092
}
1093
1094
// Following block is free, merge with this
1095
active->limit = cor->limit;
1096
active->guard = cor->guard;
6 months ago
1097
Link_Remove(&cor->link);
1 weeks ago
1098
Link_Remove(&cor->all_link);
1099
if (g_c->tip == cor){
1100
g_c->tip = active;
1101
}
1102
if (g_c->spare){
1103
free(cor);
1104
} else {
1105
g_c->spare = cor;
1106
}
6 months ago
1107
}
9 months ago
1108
6 months ago
1109
Last month
1110
Coroutine *
9 hours ago
1111
Coroutine_New(
1 weeks ago
1112
size_t min_size,
1113
size_t min_headroom,
6 months ago
1114
Coroutine_Start start
1115
){
7 days ago
1116
MyAssert(g_c && g_c->state == Coroutines_Started);
9 hours ago
1117
MyAssert(!Coroutine_StackHasOverrun());
6 months ago
1118
1 weeks ago
1119
// Make the paramaters make sense
1120
if (min_size < min_headroom){
1121
min_size = min_headroom;
1122
}
1123
6 months ago
1124
_Cor_Mutex_Lock(&g_c->mutex);
1125
1 weeks ago
1126
TrimActiveIfPossible();
6 months ago
1127
9 hours ago
1128
Coroutine *cor = Coroutine_New_Lock_Assumed(min_size, min_headroom, start);
1 weeks ago
1129
1130
if (cor && Coroutine_Size(cor) > g_c->report.largest_stack){
1131
g_c->report.largest_stack = Coroutine_Size(cor);
6 months ago
1132
}
1133
1 weeks ago
1134
EnlargeActiveIfPossible();
1135
6 months ago
1136
_Cor_Mutex_Unlock(&g_c->mutex);
1137
9 months ago
4
1138
return cor;
1139
}
1140
9 months ago
1141
Last month
1142
void
9 hours ago
1143
Coroutine_Delete(
9 months ago
1144
Coroutine *cor
1145
){
9 hours ago
1146
MyAssert(!Coroutine_StackHasOverrun());
6 months ago
1147
if (cor){
1148
Coroutines *cors = cor->coroutines;
1149
_Cor_Mutex_Lock(&cors->mutex);
Last month
1150
MyAssert(cor->state == Coroutine_Idle || cor->state == Coroutine_Complete);
4 months ago
1151
1152
#if COROUTINE_RECORD_LOWEST_HEADROOM
1153
if (cor->guard){
1 weeks ago
1154
unsigned char *guard = cor->guard;
1155
ptrdiff_t chunk_size = Coroutine_Size(cor);
1156
ptrdiff_t myheadroom;
1157
for (myheadroom = 0; myheadroom <= chunk_size-(ptrdiff_t)GUARD_PATTERN_SIZE; myheadroom += GUARD_PATTERN_SIZE){
1158
if (!Guard_Pattern_OK(StackPointerAdd(guard, -myheadroom))){
4 months ago
1159
break;
1160
}
1161
}
1 weeks ago
1162
if (myheadroom < (ptrdiff_t)g_c->report.lowest_headroom || g_c->report.lowest_headroom == 0){
4 months ago
1163
g_c->report.lowest_headroom = myheadroom;
1164
}
1165
}
1166
#endif
1167
Last month
1168
cor->state = Coroutine_Free;
6 months ago
1169
Link_Remove(&cor->link);
1170
6 months ago
1171
// insert into free list
1172
List_AddHead(&cors->free, &cor->link);
6 months ago
1173
1174
// Check for merge with following Coroutine
6 months ago
1175
List_Link *link = Link_Next(&cor->all_link);
6 months ago
1176
if (Link_NextIsLink(link)){
6 months ago
1177
Coroutine *listcor = List_Link_Container(Coroutine, all_link, link);
Last month
1178
if (listcor->state == Coroutine_Free){
6 months ago
1179
// merge
1180
cor->limit = listcor->limit;
1181
cor->guard = listcor->guard;
6 months ago
1182
Link_Remove(&listcor->all_link);
6 months ago
1183
Link_Remove(&listcor->link);
6 months ago
1184
if (g_c->tip == listcor){
1185
g_c->tip = cor;
1186
}
1 weeks ago
1187
1188
// free up the now unused struct
1189
if (g_c->spare){
1190
free(listcor);
1191
} else {
1192
g_c->spare = listcor;
1193
}
6 months ago
1194
}
1195
}
1196
1197
// check for merge with prev coroutine
6 months ago
1198
link = Link_Prev(&cor->all_link);
6 months ago
1199
if (Link_PrevIsLink(link)){
6 months ago
1200
Coroutine *listcor = List_Link_Container(Coroutine, all_link, link);
Last month
1201
if (listcor->state == Coroutine_Free){
6 months ago
1202
// merge
1203
listcor->limit = cor->limit;
1204
listcor->guard = cor->guard;
6 months ago
1205
Link_Remove(&cor->all_link);
6 months ago
1206
Link_Remove(&cor->link);
6 months ago
1207
if (g_c->tip == cor){
1208
g_c->tip = listcor;
1209
}
1 weeks ago
1210
1211
// free up the now unused struct
1212
if (g_c->spare){
1213
free(cor);
1214
} else {
1215
g_c->spare = cor;
1216
}
6 months ago
1217
}
1218
}
1 weeks ago
1219
1220
EnlargeActiveIfPossible();
6 months ago
1221
6 months ago
1222
_Cor_Mutex_Unlock(&cors->mutex);
1223
}
9 months ago
4
1224
}
1225
9 months ago
1226
9 hours ago
1227
// Coroutine_Continue, assuming the mutex is claimed
4 months ago
1228
// return false for success, true for something went wrong
Last month
1229
static Coroutine_Err
9 hours ago
1230
Coroutine_Continue_(
4 months ago
1231
Coroutines *cors,
9 months ago
1232
Coroutine *cor,
1233
void *value,
1234
bool early
1235
){
Last month
1236
if (cor->state == Coroutine_Running){
4 months ago
1237
// already running
4 months ago
1238
return Coroutine_OK;
4 months ago
1239
}
Last month
1240
if (cor->state != Coroutine_Idle && cor->state != Coroutine_Waiting){
4 months ago
1241
return Coroutine_Err_WrongState;
1242
}
9 months ago
4
1243
cor->entry_param = value;
Last month
1244
cor->state = Coroutine_Running;
6 months ago
1245
Link_Remove(&cor->link);
9 months ago
4
1246
if ( early ) {
9 months ago
1247
List_AddHead(&cors->runable, &cor->link);
9 months ago
4
1248
} else {
9 months ago
1249
List_AddTail(&cors->runable, &cor->link);
9 months ago
4
1250
}
8 months ago
1251
_Cor_Mutex_Unlock(&cors->waiting_mutex);
4 months ago
1252
return Coroutine_OK;
8 months ago
1253
}
1254
1255
Last month
1256
Coroutine_Err
9 hours ago
1257
Coroutine_Continue(
8 months ago
1258
Coroutine *cor,
1259
void *value,
1260
bool early
1261
){
9 hours ago
1262
MyAssert(!Coroutine_StackHasOverrun());
8 months ago
1263
Coroutines *cors = cor->coroutines;
8 months ago
1264
_Cor_Mutex_Lock(&cors->mutex);
9 hours ago
1265
Coroutine_Err err = Coroutine_Continue_(cors, cor, value, early);
8 months ago
1266
_Cor_Mutex_Unlock(&cors->mutex);
4 months ago
1267
return err;
9 months ago
4
1268
}
1269
9 months ago
1270
Last month
1271
void *
9 hours ago
1272
Coroutine_Yield(
9 months ago
1273
void *value,
Last month
1274
Coroutine_YieldCallback on_yield,
9 months ago
1275
void *yield_me
9 months ago
1276
){
4 months ago
1277
MyAssert(g_c);
6 months ago
1278
Coroutine *me = g_c->active;
4 months ago
1279
MyAssert(me);
9 hours ago
1280
MyAssert(!Coroutine_StackHasOverrun());
9 months ago
1281
6 months ago
1282
_Cor_Mutex_Lock(&g_c->mutex);
9 months ago
1283
Coroutines *cors = me->coroutines;
Last month
1284
MyAssert(me && me->state == Coroutine_Running && cors == g_c);
Last month
1285
me->stack_top = (unsigned char *)StackTopNow();
9 months ago
4
1286
me->value = value;
Last month
1287
me->state = Coroutine_Waiting;
9 months ago
1288
1 weeks ago
1289
TrimActiveIfPossible();
1290
6 months ago
1291
Link_Remove(&me->link);
8 months ago
1292
if (!List_IsEmpty(&cors->runable)){
8 months ago
1293
_Cor_Mutex_Unlock(&cors->waiting_mutex);
8 months ago
1294
}
9 months ago
1295
List_AddTail(&cors->waiting, &me->link);
9 months ago
1296
9 months ago
1297
switch (setjmp(me->buf)){
1298
case Chunk_Initial:
Last month
1299
ready_jmp_buf(me->buf);
8 months ago
1300
_Cor_Mutex_Unlock(&cors->mutex);
9 months ago
1301
on_yield(yield_me);
9 hours ago
1302
Coroutine_RunNext();
4 months ago
1303
MyAssert(false);
3 months ago
1304
break;
9 months ago
1305
case Chunk_Enter:
1306
// arrive here with mutex locked
9 months ago
1307
cors->active = me;
9 hours ago
1308
MyAssert(!Coroutine_StackHasOverrun());
9 months ago
1309
// when we return here - we are running again
Last month
1310
MyAssert(me->state == Coroutine_Running);
1 weeks ago
1311
EnlargeActiveIfPossible();
9 months ago
1312
void *res = me->entry_param;
8 months ago
1313
_Cor_Mutex_Unlock(&cors->mutex);
9 months ago
1314
return res;
9 months ago
4
1315
}
3 months ago
1316
MyAssert(false);
9 months ago
1317
return NULL;
9 months ago
4
1318
}
1319
9 months ago
1320
Last month
1321
void *
9 hours ago
1322
Coroutine_GetValue(
9 months ago
1323
Coroutine *cor
1324
){
9 months ago
4
1325
return cor->value;
1326
}
1327
9 months ago
1328
Last month
1329
Coroutine *
9 hours ago
1330
Coroutine_GetActive(
Last month
1331
void
1332
){
6 months ago
1333
return g_c ? g_c->active : NULL;
9 months ago
4
1334
}
1335
9 months ago
1336
1 weeks ago
1337
ptrdiff_t
9 hours ago
1338
Coroutine_GetStackHeadroom(
Last month
1339
void
1340
){
4 months ago
1341
Coroutine *me = g_c ? g_c->active : NULL;
7 months ago
1342
if (!me){
1343
// no active coroutine
4 months ago
1344
if (g_stack_limit){
1 weeks ago
1345
return StackPointerDiff(g_stack_limit, (unsigned char *)StackTopNow());
7 months ago
1346
} else {
1347
// no information where the stack ends - return something
1 weeks ago
1348
// The biggest ptrdiff_t possible
1349
return PTRDIFF_MAX;
7 months ago
1350
}
1 weeks ago
1351
} else {
1352
if (me->guard){
1353
return StackPointerDiff(me->guard, (unsigned char *)StackTopNow());
1354
} else {
1 weeks ago
1355
// The biggest ptrdiff_t possible
1356
return PTRDIFF_MAX;
1 weeks ago
1357
}
7 months ago
1358
}
9 months ago
1359
}
1360
1361
Last month
1362
void *
9 hours ago
1363
Coroutine_GetStackHWM(
Last month
1364
void
1365
){
4 months ago
1366
MyAssert(g_c);
7 days ago
1367
MyAssert(g_c->state == Coroutines_Started);
9 hours ago
1368
MyAssert(!Coroutine_StackHasOverrun());
7 months ago
1369
// Find where the guards end
1 weeks ago
1370
unsigned char *guard = g_c->active->guard;
1371
if (guard){
9 hours ago
1372
ptrdiff_t headroom = Coroutine_GetStackHeadroom();
1 weeks ago
1373
for (ptrdiff_t i = 0; i <= headroom; i += GUARD_PATTERN_SIZE){
1374
if (!Guard_Pattern_OK(StackPointerAdd(guard, -i))){
1375
return StackPointerAdd(guard, i);
1376
}
1377
}
7 months ago
1378
}
1379
return guard;
1380
}
8 months ago
1381
1382
Last month
1383
void
9 hours ago
1384
Coroutine_ClearStackForHWM(
Last month
1385
void
1386
){
4 months ago
1387
MyAssert(g_c);
9 hours ago
1388
MyAssert(!Coroutine_StackHasOverrun());
1 weeks ago
1389
unsigned char *guard = g_c->active->guard;
1390
if (guard){
9 hours ago
1391
ptrdiff_t headroom = Coroutine_GetStackHeadroom();
1 weeks ago
1392
for (ptrdiff_t i = 0; i <= headroom; i += GUARD_PATTERN_SIZE){
1393
Apply_Guard(StackPointerAdd(guard, -i));
1394
}
7 months ago
1395
}
1396
}
1397
1398
Last month
1399
static bool
9 hours ago
1400
Coroutine_CanStartCoroutine_Lock_Assumed(
1 weeks ago
1401
size_t min_size
6 months ago
1402
){
1403
if (!g_c->stack_limit){
7 months ago
1404
return true;
1405
}
7 months ago
1406
1 weeks ago
1407
// check free list
6 months ago
1408
List_Link *link;
1409
for (link = List_Begin(&g_c->free); Link_NextIsLink(link); link = Link_Next(link)){
1410
Coroutine *cor = List_Link_Container(Coroutine, link, link);
1 weeks ago
1411
size_t cor_size = Coroutine_Size(cor);
1412
if (cor_size >= min_size){
6 months ago
1413
return true;
1414
}
1415
}
1416
1 weeks ago
1417
// Check if this coroutine has enough size
1418
Coroutine *me = g_c->active;
1419
me->stack_top = (unsigned char *)StackTopNow();
1420
ptrdiff_t reduced_size = TrimmableSize(g_c->active);
1421
if (reduced_size && StackPointerDiff(g_c->active->limit, g_c->active->base) - reduced_size > (ptrdiff_t)min_size){
1422
return true;
1423
}
1424
6 months ago
1425
return false;
7 months ago
1426
}
1427
1428
Last month
1429
bool
9 hours ago
1430
Coroutine_CanStartCoroutine(
6 months ago
1431
size_t size
1432
){
7 days ago
1433
MyAssert(g_c && g_c->state == Coroutines_Started);
9 hours ago
1434
MyAssert(!Coroutine_StackHasOverrun());
4 months ago
1435
6 months ago
1436
_Cor_Mutex_Lock(&g_c->mutex);
1437
9 hours ago
1438
bool result = Coroutine_CanStartCoroutine_Lock_Assumed(size);
6 months ago
1439
1440
_Cor_Mutex_Unlock(&g_c->mutex);
1441
1442
return result;
1443
}
1444
Last month
1445
void *
9 hours ago
1446
Coroutine_GetCStackTop(
Last month
1447
void
1448
){
1 weeks ago
1449
if (!g_c || g_c->tip == g_c->active){
1450
return (void *)StackTopNow();
7 months ago
1451
}
1 weeks ago
1452
1453
return g_c->tip->stack_top;
9 months ago
1454
}
1455
1456
Last month
1457
// Inspired by cpython...
1458
#ifdef __has_builtin
1459
# define Coroutine__has_builtin(x) __has_builtin(x)
1460
#else
1461
# define Coroutine__has_builtin(x) 0
1462
#endif
1463
1464
#if !Coroutine__has_builtin(__builtin_frame_address) && !defined(__GNUC__) && !defined(_MSC_VER)
1465
static uintptr_t return_pointer_as_int(char* p) {
1466
return (uintptr_t)p;
7 months ago
1467
}
Last month
1468
#endif
7 months ago
1469
Last month
1470
static inline uintptr_t
1471
StackTopNow(void) {
1472
#if Coroutine__has_builtin(__builtin_frame_address) || defined(__GNUC__)
1473
return (uintptr_t)__builtin_frame_address(0);
1474
#elif defined(_MSC_VER)
1475
return (uintptr_t)_AddressOfReturnAddress();
1476
#else
1477
char here;
1478
/* Avoid compiler warning about returning stack address */
1479
return return_pointer_as_int(&here);
1480
#endif
1481
}
1482
// ...inspired by cpython
7 months ago
1483
Last month
1484
Last month
1485
struct Coroutine_ChainParam {
9 months ago
1486
Coroutine_Start start;
1487
void *value;
1488
Coroutine *ret;
1489
};
1490
1491
Last month
1492
static void *
9 hours ago
1493
Coroutine_ChainFn(
9 months ago
1494
void *param
1495
){
Last month
1496
struct Coroutine_ChainParam *params = (struct Coroutine_ChainParam *)param;
7 days ago
1497
void *res = params->start(params->value);
9 hours ago
1498
return (void *)(uintptr_t)Coroutine_Continue(params->ret, res, true);
9 months ago
1499
}
1500
1501
Last month
1502
static void
9 hours ago
1503
Coroutine_ChainYield(
9 months ago
1504
void *unused
1505
){
1506
(void)unused;
1507
}
1508
1509
Last month
1510
Coroutine_Err
9 hours ago
1511
Coroutine_Chain(
1 weeks ago
1512
size_t min_size,
1513
size_t min_headroom,
9 months ago
1514
Coroutine_Start start,
6 months ago
1515
void *value,
1516
void **result
9 months ago
1517
){
9 hours ago
1518
MyAssert(!Coroutine_StackHasOverrun());
1519
Coroutine *cor = Coroutine_New(min_size, min_headroom, Coroutine_ChainFn);
6 months ago
1520
if (!cor){
1521
// failed
4 months ago
1522
return Coroutine_Err_NoStack;
6 months ago
1523
}
Last month
1524
struct Coroutine_ChainParam params = {
9 months ago
1525
start,
1526
value,
9 hours ago
1527
Coroutine_GetActive()
9 months ago
1528
};
9 hours ago
1529
Coroutine_Err err = Coroutine_Continue(cor, &params, true);
4 months ago
1530
if (err){
7 days ago
1531
goto error;
4 months ago
1532
}
9 hours ago
1533
void *res = Coroutine_Yield(NULL, Coroutine_ChainYield, NULL);
1534
err = (Coroutine_Err)(uintptr_t)Coroutine_GetValue(cor);
7 days ago
1535
error:
9 hours ago
1536
Coroutine_Delete(cor);
4 months ago
1537
if (!err && result){
6 months ago
1538
*result = res;
1539
}
4 months ago
1540
// success! ...probably
1541
return err;
9 months ago
1542
}
1543
1544
Last month
1545
bool
9 hours ago
1546
Coroutine_IsRunning(
9 months ago
1547
Coroutine *cor
Last month
1548
){
9 months ago
1549
int state = cor->state;
Last month
1550
return state == Coroutine_Running || state == Coroutine_Waiting;
9 months ago
4
1551
}
9 months ago
1552
1553
9 hours ago
1554
bool Coroutine_IsComplete(
7 months ago
1555
Coroutine *cor
Last month
1556
){
7 months ago
1557
int state = cor->state;
Last month
1558
return state == Coroutine_Complete;
7 months ago
1559
}
1560
1561
Last month
1562
bool
9 hours ago
1563
Coroutine_IsStarted(
Last month
1564
void
1565
){
7 days ago
1566
return g_c && g_c->state == Coroutines_Started;
9 months ago
1567
}
6 months ago
1568
Last month
1569
void
9 hours ago
1570
Coroutine_Dump_(
Last month
1571
void
1572
){
6 months ago
1573
char *state_to_text[] = {
1574
"Free",
1575
"Idle",
1576
"Running",
1577
"Waiting",
1578
"Complete"
1579
};
1580
unsigned idx = 0;
1581
List_Link *link;
1582
for (link = List_Begin(&g_c->all); Link_NextIsLink(link); link = Link_Next(link)){
1583
Coroutine *cor = List_Link_Container(Coroutine, all_link, link);
1 weeks ago
1584
printf("%d) %p (%s) %s\n", idx++, cor, state_to_text[cor->state], cor == g_c->tip ? " (TIP)" : "");
6 months ago
1585
}
1586
}
9 hours ago
1587
#include "coroutine_names_undef.h"
1588