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