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