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