1434 lines37.9 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 months ago
29
static unsigned char *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
8 months ago
4
78
///////////////////////////////////////////////////////////////////////////////
79
// 2-way linked lists...
80
//
8 months ago
81
// Brought inline here to avoid namespace polution
8 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
8 months ago
104
105
static inline bool List_IsEmpty(
106
const List_Head *list
107
){
8 months ago
4
108
return list->fwd.link.next == &list->back.link;
109
}
110
8 months ago
111
112
static inline List_Link *List_GetHead(
113
const List_Head *list
114
){
8 months ago
4
115
return List_IsEmpty(list) ? NULL : list->fwd.link.next;
116
}
8 months ago
117
118
5 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
5 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
}
8 months ago
158
159
8 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
8 months ago
163
6 days ago
164
static inline void
165
List_Init(
8 months ago
166
List_Head *list
167
){
8 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
8 months ago
173
6 days ago
174
static inline void
175
Link_AddAfter(
5 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
6 days ago
186
static inline void
187
List_AddHead(
8 months ago
188
List_Head *list,
189
List_Link *link
190
){
5 months ago
191
Link_AddAfter(link, &list->fwd.link);
8 months ago
4
192
}
193
8 months ago
194
6 days ago
195
static inline void
196
Link_AddBefore(
5 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
6 days ago
207
static inline void
208
List_AddTail(
8 months ago
209
List_Head *list,
210
List_Link *link
211
){
5 months ago
212
Link_AddBefore(link, &list->back.link);
8 months ago
4
213
}
214
8 months ago
215
6 days ago
216
static inline void
217
Link_Remove(
8 months ago
218
List_Link *link
219
){
8 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,
5 months ago
238
Chunk_Split,
8 months ago
4
239
Chunk_Enter
240
};
241
8 months ago
242
typedef enum Coroutine_State {
6 days ago
243
Coroutine_NS(Free),
244
Coroutine_NS(Idle),
245
Coroutine_NS(Running),
246
Coroutine_NS(Waiting),
247
Coroutine_NS(Complete)
8 months ago
248
} Coroutine_State;
8 months ago
4
249
250
enum {
251
Coroutines_Init,
252
Coroutines_AllocatedChunk,
253
Coroutines_CoroutineComplete,
254
};
255
256
struct Coroutine {
6 months ago
257
Coroutines *coroutines; // so can work with it off-thread
258
List_Link link; // for whichever list it's on
5 months ago
259
List_Link all_link; // list of all Coroutines
6 months ago
260
jmp_buf buf; // how to get back to it
5 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
6 months ago
264
unsigned char *guard; // where the stack overrun guard is
5 months ago
265
size_t size;
6 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
8 months ago
270
Coroutine_State state;
8 months ago
4
271
};
272
273
struct Coroutines {
7 months ago
274
_Cor_Mutex mutex;
6 days ago
275
jmp_buf controller; // to return from Coroutine_NS(Run)
8 months ago
276
jmp_buf chunk_allocated;// for chunk allocation
5 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
8 months ago
4
279
280
// singletons
281
Coroutine *tip; // top of stack chunk
282
Coroutine *active; // currently running coroutine
6 days ago
283
Coroutine *primary; // Coroutine_NS(Run) coroutine
6 months ago
284
unsigned char *stack_limit; // when not NULL, where the stack finishes
8 months ago
4
285
286
// lists
5 months ago
287
List_Head all; // all Coroutines (in address order)
288
List_Head free; // free Coroutines
8 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
7 months ago
292
_Cor_Mutex waiting_mutex;
8 months ago
4
293
8 months ago
294
// Summary of the system
295
Coroutine_Report report;
296
8 months ago
4
297
// state
298
char state;
299
};
300
5 months ago
301
_Cor_thread_local Coroutines *g_c;
4 months ago
302
_Cor_thread_local unsigned char *g_stack_limit;
8 months ago
4
303
5 months ago
304
static void ReserveStackSpace(Coroutines *cors, Coroutine *parent, size_t chunk_size, unsigned char *childs_limit);
5 months ago
305
static void stack_chunk_base(Coroutines *cors, Coroutine *parent, unsigned char *prev_limit, unsigned char *limit);
8 months ago
4
306
8 months ago
307
6 months ago
308
#define GUARD_PATTERN_SIZE (4)
8 months ago
309
// Check whether the guard is intact
6 days ago
310
static inline bool
311
Guard_Pattern_OK(
8 months ago
312
unsigned char *guard
313
){
6 months ago
314
return !guard ||
315
(guard[0] == 0xde &&
316
guard[1] == 0xad &&
317
guard[2] == 0xbe &&
318
guard[3] == 0xef);
8 months ago
319
}
320
321
6 days ago
322
static inline void
323
Apply_Guard(unsigned char *guard){
6 months ago
324
guard[0] = 0xde;
325
guard[1] = 0xad;
326
guard[2] = 0xbe;
327
guard[3] = 0xef;
328
}
329
330
3 months ago
331
#ifndef NDEBUG
6 days ago
332
static Coroutine_Err
333
CheckListIntegrity(
334
List_Head *head,
335
Coroutine_State state1,
336
Coroutine_State state2
337
){
3 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);
3 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
}
3 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
}
3 months ago
353
if (!found){
354
return Coroutine_Err_InternalInsistency;
355
}
3 months ago
356
}
3 months ago
357
return Coroutine_OK;
3 months ago
358
}
359
360
6 days ago
361
static Coroutine_Err
362
Coroutine_NS(CheckIntegrity_)(
363
void
364
){
3 months ago
365
Coroutine_Err err;
6 days ago
366
err = CheckListIntegrity(&g_c->free, Coroutine_NS(Free), Coroutine_NS(Free));
3 months ago
367
if (err){
368
return err;
369
}
6 days ago
370
err = CheckListIntegrity(&g_c->inactive, Coroutine_NS(Idle), Coroutine_NS(Complete));
3 months ago
371
if (err){
372
return err;
373
}
6 days ago
374
err = CheckListIntegrity(&g_c->runable, Coroutine_NS(Running), Coroutine_NS(Running));
3 months ago
375
if (err){
376
return err;
377
}
6 days ago
378
err = CheckListIntegrity(&g_c->waiting, Coroutine_NS(Waiting), Coroutine_NS(Waiting));
3 months ago
379
return err;
3 months ago
380
}
3 months ago
381
#endif
382
383
6 days ago
384
static Coroutine_Err
385
Coroutine_NS(StackHasOverrun)(
386
void
387
){
6 months ago
388
unsigned char *stack_top = StackTopNow();
5 months ago
389
unsigned char *stack_limit = g_c ? g_c->stack_limit : NULL;
6 months ago
390
if (stack_limit && stack_top < stack_limit){
3 months ago
391
// printf("top %p < limit %p\n", stack_top, stack_limit);
6 months ago
392
// current stack top is beyond limit - we are overrunning NOW
3 months ago
393
return Coroutine_Err_StackOverrun;
6 months ago
394
}
3 months ago
395
// if (stack_limit && stack_top < stack_limit+2048){
396
// printf("Stack LOW hazard\n");
397
// }
5 months ago
398
Coroutine *me = g_c ? g_c->active : NULL;
6 months ago
399
if (!me){
3 months ago
400
return Coroutine_OK;
6 months ago
401
}
Last week
402
Coroutine_Err err;
3 months ago
403
#if COROUTINE_CHECK_INTEGRITY_ON_STACK_CHECK
404
// Check all coroutines integrity
6 days ago
405
err = _Coroutine_NS(CheckIntegrity)();
3 months ago
406
if (err){
407
return err;
408
}
3 months ago
409
#endif
6 months ago
410
if (me->guard){
Last week
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;
6 months ago
416
}
Last week
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;
6 months ago
422
}
423
Last week
424
#ifndef NDEBUG
6 days ago
425
Coroutine_Err
426
Coroutine_NS(CheckIntegrity)(
427
void
428
){
429
Coroutine_Err err = Coroutine_NS(StackHasOverrun)();
Last week
430
#if !COROUTINE_CHECK_INTEGRITY_ON_STACK_CHECK
431
if (!err && g_c){
6 days ago
432
err = Coroutine_NS(CheckIntegrity_)();
Last week
433
}
434
#endif
435
return err;
436
}
437
#endif
6 months ago
438
Last week
439
6 days ago
440
static void
441
ReserveStackSpace(
5 months ago
442
Coroutines *cors,
6 months ago
443
Coroutine *parent,
5 months ago
444
size_t chunk_size,
445
unsigned char *childs_limit
8 months ago
446
){
6 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
6 months ago
453
Apply_Guard(chunk_of_stack);
6 months ago
454
#endif
5 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
}
5 months ago
460
stack_chunk_base(cors, parent, chunk_of_stack, childs_limit);
8 months ago
4
461
}
462
8 months ago
463
6 days ago
464
static void
465
stack_chunk_base(
5 months ago
466
Coroutines *cors,
5 months ago
467
Coroutine *parent,
5 months ago
468
unsigned char *prev_limit,
469
unsigned char *limit
8 months ago
470
){
8 months ago
4
471
Coroutine here;
5 months ago
472
here.coroutines = cors;
6 days ago
473
here.state = Coroutine_NS(Free);
5 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
5 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);
5 months ago
490
}
5 months ago
491
// add to free list
492
List_AddTail(&cors->free, &here.link);
5 months ago
493
494
cors->report.coroutines_pool_size += 1;
495
496
if (!cors->tip || &here < cors->tip){
497
cors->tip = &here;
498
}
499
6 months ago
500
for(;;){
501
switch (setjmp(here.buf)) {
502
case Chunk_Initial:
6 days ago
503
if (here.state == Coroutine_NS(Free)){
6 months ago
504
// return to the coroutine allocator
5 months ago
505
longjmp(cors->chunk_allocated, 1);
6 months ago
506
} else {
6 days ago
507
MyAssert(here.state == Coroutine_NS(Complete));
6 months ago
508
// we finish here to ensure the setjmp is redone
5 months ago
509
if (cors->primary == &here) {
6 days ago
510
// if primary coroutine - return to Coroutine_NS(Run)
5 months ago
511
longjmp(cors->controller, Coroutines_CoroutineComplete);
6 months ago
512
}
5 months ago
513
_Cor_Mutex_Unlock(&cors->mutex);
6 days ago
514
Coroutine_NS(RunNext)();
6 months ago
515
}
2 months ago
516
MyAssert(false);
517
break;
6 months ago
518
case Chunk_Create:
6 months ago
519
// Request to create a new chunk on the stack
520
// We're here if the coroutine is:
6 days ago
521
// Allocated, but not 'run' (Coroutine_NS(Idle))
522
// Run, but not not entered yet (Coroutine_NS(Running))
523
// Completed (Coroutine_NS(Complete))
5 months ago
524
// Free, and the coroutines system is starting - we're characterising the system
6 days ago
525
MyAssert(here.state == Coroutine_NS(Idle) ||
526
here.state == Coroutine_NS(Running) ||
527
here.state == Coroutine_NS(Complete) ||
528
(here.state == Coroutine_NS(Free) && cors->state == Coroutines_Starting));
5 months ago
529
ReserveStackSpace(here.coroutines, &here, here.size, NULL);
3 months ago
530
MyAssert(false);
2 months ago
531
break;
5 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);
3 months ago
536
MyAssert(false);
2 months ago
537
break;
6 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
6 days ago
541
MyAssert(here.state == Coroutine_NS(Running));
5 months ago
542
here.coroutines->active = &here;
543
_Cor_Mutex_Unlock(&cors->mutex);
6 months ago
544
here.value = here.start(here.entry_param);
8 months ago
545
6 months ago
546
// check the guard
3 months ago
547
MyAssert(Guard_Pattern_OK(here.guard));
8 months ago
548
5 months ago
549
_Cor_Mutex_Lock(&here.coroutines->mutex);
550
here.coroutines->active = NULL;
6 days ago
551
MyAssert(here.state == Coroutine_NS(Running));
5 months ago
552
Link_Remove(&here.link);
6 days ago
553
here.state = Coroutine_NS(Complete);
5 months ago
554
List_AddTail(&here.coroutines->inactive, &here.link);
6 months ago
555
// Coroutine has completed
556
// Loop round to redo the setjmp() - if this coroutine yielded, then the setjmp will
557
// need reseting
2 months ago
558
break;
8 months ago
4
559
}
560
}
561
}
562
8 months ago
563
6 days ago
564
static void
565
Coroutine_NS(RunNext)(
566
void
567
){
8 months ago
568
// arrive here with mutex unlocked
5 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));
6 days ago
572
MyAssert(next->state == Coroutine_NS(Running));
8 months ago
573
longjmp(next->buf, Chunk_Enter);
3 months ago
574
MyAssert(false);
8 months ago
575
}
576
577
6 days ago
578
static Coroutine_Err
579
Coroutines_ctor(
580
Coroutines *cors
581
){
5 months ago
582
cors->state = Coroutines_Starting;
3 months ago
583
if (_Cor_Mutex_ctor(&cors->mutex)){
584
return Coroutine_Err_CouldNotInitialiseSystem;
585
}
5 months ago
586
cors->tip = NULL;
587
cors->active = NULL;
5 months ago
588
cors->primary = NULL;
4 months ago
589
cors->stack_limit = g_stack_limit;
8 months ago
4
590
5 months ago
591
List_Init(&cors->all);
5 months ago
592
List_Init(&cors->free);
593
List_Init(&cors->inactive);
594
List_Init(&cors->runable);
595
List_Init(&cors->waiting);
3 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
}
8 months ago
4
605
5 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);
8 months ago
4
613
}
5 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
8 months ago
622
5 months ago
623
// discard what we've just created
5 months ago
624
List_Init(&cors->all);
5 months ago
625
List_Init(&cors->free);
626
cors->tip = NULL;
627
628
cors->state = Coroutines_Started;
3 months ago
629
return Coroutine_OK;
630
}
5 months ago
631
6 days ago
632
static void
633
Coroutines_dtor(
634
Coroutines *cors
635
){
3 months ago
636
_Cor_Mutex_Lock(&cors->mutex);
637
cors->state = Coroutines_Stopping;
638
3 months ago
639
MyAssert(List_IsEmpty(&cors->inactive));
3 months ago
640
_Cor_Mutex_Unlock(&cors->waiting_mutex);
641
_Cor_Mutex_dtor(&cors->waiting_mutex);
642
3 months ago
643
MyAssert(cors->state == Coroutines_Stopping);
3 months ago
644
_Cor_Mutex_Unlock(&cors->mutex);
645
_Cor_Mutex_dtor(&cors->mutex);
8 months ago
4
646
}
647
8 months ago
648
6 days ago
649
Coroutine_Err
650
Coroutine_NS(RunSystem)(
651
Coroutine_SystemStart start,
652
void *value
653
){
3 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
6 days ago
669
void
670
Coroutine_NS(SetStackLimit)(
671
void *limit
672
){
3 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);
4 months ago
674
g_stack_limit = limit;
675
if (g_c){
676
g_c->stack_limit = limit;
677
}
6 months ago
678
}
679
680
3 months ago
681
#if COROUTINE_RECORD_LOWEST_HEADROOM
6 days ago
682
static size_t
683
Coroutine_NS(UpdateMinimumHeadroom)(
684
List_Head *list,
685
size_t headroom
686
){
3 months ago
687
for (List_Link *link = List_Begin(list); Link_NextIsLink(link); link = Link_Next(link)){
8 months ago
688
Coroutine *cor = List_Link_Container(Coroutine, link, link);
6 months ago
689
if (cor->guard){
5 months ago
690
for (uintptr_t i = 4; i < cor->size-3; i += 4){
3 months ago
691
if (!Guard_Pattern_OK(&cor->guard[i])){
3 months ago
692
headroom = i < headroom ? i : headroom;
6 months ago
693
break;
694
}
8 months ago
695
}
696
}
697
}
3 months ago
698
return headroom;
699
}
700
#endif
701
702
6 days ago
703
Coroutine_Report
704
Coroutine_NS(GetReport)(
705
void
706
){
3 months ago
707
if (g_c){
708
size_t headroom;
3 months ago
709
#if COROUTINE_RECORD_LOWEST_HEADROOM
3 months ago
710
_Cor_Mutex_Lock(&g_c->mutex);
711
headroom = g_c->report.lowest_headroom;
6 days ago
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);
3 months ago
715
_Cor_Mutex_Unlock(&g_c->mutex);
6 months ago
716
#else
3 months ago
717
headroom = 0;
6 months ago
718
#endif
3 months ago
719
g_c->report.lowest_headroom = headroom;
8 months ago
720
3 months ago
721
return g_c->report;
722
} else {
723
Coroutine_Report ret = {0, 0, 0, 0};
724
return ret;
725
}
3 months ago
726
}
727
728
3 months ago
729
#ifndef NDEBUG
6 days ago
730
static void
731
Coroutine_NS(ReportNonEmptyList)(
3 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
6 days ago
743
Coroutine_Err
744
Coroutine_NS(Run_Coroutine)(
8 months ago
745
Coroutine *cor,
8 months ago
746
void *value
747
){
3 months ago
748
CHECK_SYSTEM_RUNNING
749
CHECK_COROUTINE_THREAD
750
CHECK_NO_COROUTINE_RUNNING
751
8 months ago
752
Coroutines *cors = cor->coroutines;
7 months ago
753
_Cor_Mutex_Lock(&cors->mutex);
8 months ago
754
cors->state = Coroutines_Active;
755
cors->primary = cor;
8 months ago
756
6 days ago
757
Coroutine_NS(Continue_)(cors, cor, value, true);
8 months ago
4
758
8 months ago
759
if (!setjmp(cors->controller)){
7 months ago
760
_Cor_Mutex_Unlock(&cors->mutex);
8 months ago
761
8 months ago
4
762
// start the first coroutine
6 days ago
763
Coroutine_NS(RunNext)();
8 months ago
4
764
}
8 months ago
765
// arrive here with mutex locked
3 months ago
766
if (!List_IsEmpty(&cors->runable) || !List_IsEmpty(&cors->waiting)){
3 months ago
767
#ifndef NDEBUG
6 days ago
768
Coroutine_NS(ReportNonEmptyList)(&cors->runable, "runable");
769
Coroutine_NS(ReportNonEmptyList)(&cors->waiting, "waiting");
3 months ago
770
#endif
771
return Coroutine_Err_ExitWithRunningCoroutines;
3 months ago
772
}
3 months ago
773
MyAssert(cors->state == Coroutines_Active);
8 months ago
774
cors->state = Coroutines_Started;
7 months ago
775
_Cor_Mutex_Unlock(&cors->mutex);
3 months ago
776
777
return Coroutine_OK;
8 months ago
778
}
779
780
6 days ago
781
struct Coroutine_NS(Run_Params) {
3 months ago
782
size_t stack;
783
Coroutine_Start start;
784
void *value;
785
void **result;
786
};
787
6 days ago
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;
3 months ago
793
6 days ago
794
Coroutine *cor = Coroutine_NS(New)(params->stack, params->start);
3 months ago
795
if (!cor){
796
// that didn't work
797
return Coroutine_Err_NoStack;
798
}
6 days ago
799
Coroutine_Err ret = Coroutine_NS(Run_Coroutine)(cor, params->value);
3 months ago
800
if (!ret && params->result){
6 days ago
801
*params->result = Coroutine_NS(GetValue)(cor);
3 months ago
802
}
6 days ago
803
Coroutine_NS(Delete)(cor);
3 months ago
804
return ret;
805
}
806
807
6 days ago
808
Coroutine_Err Coroutine_NS(Run)(
5 months ago
809
size_t stack,
8 months ago
810
Coroutine_Start start,
5 months ago
811
void *value,
812
void **result
8 months ago
813
){
3 months ago
814
if (!g_c){
6 days ago
815
struct Coroutine_NS(Run_Params) params = {stack, start, value, result};
816
return Coroutine_NS(RunSystem)(Coroutine_NS(Run_Starter), &params);
3 months ago
817
}
818
if (!g_c->active)
819
{
820
// system running, but no active coroutine
6 days ago
821
Coroutine *cor = Coroutine_NS(New)(stack, start);
3 months ago
822
if (!cor){
823
// that didn't work
824
return Coroutine_Err_NoStack;
5 months ago
825
}
6 days ago
826
Coroutine_Err err = Coroutine_NS(Run_Coroutine)(cor, value);
3 months ago
827
if (!err && result){
6 days ago
828
*result = Coroutine_NS(GetValue)(cor);
3 months ago
829
}
6 days ago
830
Coroutine_NS(Delete)(cor);
3 months ago
831
return err;
6 months ago
832
}
3 months ago
833
834
// We are in an active coroutine, so call start() directly
835
CHECK_STACK_OVERRUN
836
void *res = start(value);
5 months ago
837
if (result){
3 months ago
838
*result = res;
5 months ago
839
}
3 months ago
840
5 months ago
841
// no failures, so...
3 months ago
842
return Coroutine_OK;
8 months ago
4
843
}
844
845
6 days ago
846
static void Coroutine_NS(FreeToIdle)(
5 months ago
847
Coroutine *cor,
8 months ago
848
Coroutine_Start start
849
){
6 days ago
850
MyAssert(cor->state == Coroutine_NS(Free));
851
cor->state = Coroutine_NS(Idle);
5 months ago
852
cor->start = start;
853
cor->value = NULL;
854
Link_Remove(&cor->link);
855
List_AddHead(&g_c->inactive, &cor->link);
8 months ago
856
5 months ago
857
g_c->report.coroutines_created += 1;
858
}
859
860
6 days ago
861
static void Coroutine_NS(FreeToIdleSize)(
5 months ago
862
Coroutine *cor,
863
Coroutine_Start start,
864
size_t size
865
){
3 months ago
866
MyAssert(!cor->guard);
5 months ago
867
cor->size = size;
868
cor->base = (unsigned char *)cor - g_c->gap_after;
869
cor->limit = cor->base - cor->size;
6 days ago
870
Coroutine_NS(FreeToIdle)(cor, start);
5 months ago
871
}
872
873
6 days ago
874
static Coroutine *Coroutine_NS(New_Lock_Assumed)(
5 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);
5 months ago
887
}
5 months ago
888
}
889
5 months ago
890
Coroutine *cor = NULL;
5 months ago
891
for (link = List_Begin(&g_c->free); Link_NextIsLink(link); link = Link_Next(link)){
5 months ago
892
Coroutine *candidate = List_Link_Container(Coroutine, link, link);
3 months ago
893
MyAssert(candidate->coroutines == g_c);
5 months ago
894
if (!candidate->guard) {
5 months ago
895
// this must be the tip
3 months ago
896
MyAssert(candidate == g_c->tip);
5 months ago
897
Last week
898
size_t size_to_use;
5 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){
5 months ago
905
size_t available = (unsigned char *)candidate - g_c->stack_limit - g_c->gap_after;
Last week
906
size_to_use = available < size ? available : size;
907
} else {
908
size_to_use = size;
5 months ago
909
}
6 days ago
910
Coroutine_NS(FreeToIdleSize)(candidate, start, size_to_use);
5 months ago
911
return candidate;
6 months ago
912
}
5 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....
5 months ago
917
size_t available = (unsigned char *)candidate - g_c->stack_limit - g_c->gap_after;
5 months ago
918
919
if (available < size){
920
// not enough space for this coroutine
3 months ago
921
// printf("Not enough stack space (A) %ld\n", available);
5 months ago
922
return NULL;
923
}
924
4 months ago
925
if (available < size + g_c->gap_before + g_c->gap_after + COROUTINE_MINIMUM_STACK_SIZE) {
5 months ago
926
// not enough space for another coroutine - use all the space for this one
Last week
927
size_to_use = available;
928
} else {
929
size_to_use = size;
5 months ago
930
}
Last week
931
} else {
932
size_to_use = size;
6 months ago
933
}
6 days ago
934
Coroutine_NS(FreeToIdleSize)(candidate, start, size_to_use);
5 months ago
935
return candidate;
8 months ago
4
936
}
5 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);
5 months ago
950
}
951
}
5 months ago
952
// cor now ready to use
6 days ago
953
Coroutine_NS(FreeToIdle)(cor, start);
5 months ago
954
return cor;
8 months ago
4
955
}
956
5 months ago
957
// No big-enough free blocks - check if there's space beyond the tip block
958
4 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
3 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);
4 months ago
965
return NULL;
966
}
5 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)){
972
ReserveStackSpace(g_c, me, StackTopNow() - me->limit, NULL);
973
}
974
} else {
975
if (!setjmp(g_c->chunk_allocated)){
976
longjmp(tip->buf, Chunk_Create);
977
}
978
}
979
5 months ago
980
cor = List_Link_Container(Coroutine, link, List_GetTail(&g_c->free));
6 days ago
981
MyAssert(cor->state == Coroutine_NS(Free));
5 months ago
982
cor->size = size;
983
cor->limit = (unsigned char *)cor - g_c->gap_after - size;
6 days ago
984
cor->state = Coroutine_NS(Idle);
8 months ago
4
985
cor->start = start;
986
cor->value = NULL;
5 months ago
987
Link_Remove(&cor->link);
988
List_AddHead(&g_c->inactive, &cor->link);
8 months ago
4
989
5 months ago
990
g_c->report.coroutines_created += 1;
991
return cor;
992
}
8 months ago
993
5 months ago
994
6 days ago
995
Coroutine *
996
Coroutine_NS(New)(
5 months ago
997
size_t stack,
998
Coroutine_Start start
999
){
3 months ago
1000
MyAssert(g_c);
1001
MyAssert((g_c->state == Coroutines_Started && List_IsEmpty(&g_c->inactive)) || g_c->state == Coroutines_Active);
6 days ago
1002
MyAssert(!Coroutine_NS(StackHasOverrun)());
5 months ago
1003
1004
_Cor_Mutex_Lock(&g_c->mutex);
1005
6 days ago
1006
Coroutine *cor = Coroutine_NS(New_Lock_Assumed)(stack, start);
5 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
8 months ago
4
1014
return cor;
1015
}
1016
8 months ago
1017
6 days ago
1018
void
1019
Coroutine_NS(Delete)(
8 months ago
1020
Coroutine *cor
1021
){
6 days ago
1022
MyAssert(!Coroutine_NS(StackHasOverrun)());
5 months ago
1023
if (cor){
1024
Coroutines *cors = cor->coroutines;
1025
_Cor_Mutex_Lock(&cors->mutex);
6 days ago
1026
MyAssert(cor->state == Coroutine_NS(Idle) || cor->state == Coroutine_NS(Complete));
3 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){
3 months ago
1033
if (!Guard_Pattern_OK(rover)){
3 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
6 days ago
1044
cor->state = Coroutine_NS(Free);
5 months ago
1045
Link_Remove(&cor->link);
1046
5 months ago
1047
// insert into free list
1048
List_AddHead(&cors->free, &cor->link);
5 months ago
1049
1050
// Check for merge with following Coroutine
5 months ago
1051
List_Link *link = Link_Next(&cor->all_link);
5 months ago
1052
if (Link_NextIsLink(link)){
5 months ago
1053
Coroutine *listcor = List_Link_Container(Coroutine, all_link, link);
6 days ago
1054
if (listcor->state == Coroutine_NS(Free)){
5 months ago
1055
// merge
5 months ago
1056
cor->size += cor->limit - listcor->limit;
5 months ago
1057
cor->limit = listcor->limit;
1058
cor->guard = listcor->guard;
5 months ago
1059
Link_Remove(&listcor->all_link);
5 months ago
1060
Link_Remove(&listcor->link);
5 months ago
1061
if (g_c->tip == listcor){
1062
g_c->tip = cor;
1063
}
5 months ago
1064
}
1065
}
1066
1067
// check for merge with prev coroutine
5 months ago
1068
link = Link_Prev(&cor->all_link);
5 months ago
1069
if (Link_PrevIsLink(link)){
5 months ago
1070
Coroutine *listcor = List_Link_Container(Coroutine, all_link, link);
6 days ago
1071
if (listcor->state == Coroutine_NS(Free)){
5 months ago
1072
// merge
5 months ago
1073
listcor->size += listcor->limit - cor->limit;
5 months ago
1074
listcor->limit = cor->limit;
1075
listcor->guard = cor->guard;
5 months ago
1076
Link_Remove(&cor->all_link);
5 months ago
1077
Link_Remove(&cor->link);
5 months ago
1078
if (g_c->tip == cor){
1079
g_c->tip = listcor;
1080
}
5 months ago
1081
}
1082
}
1083
5 months ago
1084
_Cor_Mutex_Unlock(&cors->mutex);
1085
}
8 months ago
4
1086
}
1087
8 months ago
1088
6 days ago
1089
// Coroutine_NS(Continue), assuming the mutex is claimed
3 months ago
1090
// return false for success, true for something went wrong
6 days ago
1091
static Coroutine_Err
1092
Coroutine_NS(Continue_)(
3 months ago
1093
Coroutines *cors,
8 months ago
1094
Coroutine *cor,
1095
void *value,
1096
bool early
1097
){
6 days ago
1098
if (cor->state == Coroutine_NS(Running)){
3 months ago
1099
// already running
3 months ago
1100
return Coroutine_OK;
3 months ago
1101
}
6 days ago
1102
if (cor->state != Coroutine_NS(Idle) && cor->state != Coroutine_NS(Waiting)){
3 months ago
1103
return Coroutine_Err_WrongState;
1104
}
8 months ago
4
1105
cor->entry_param = value;
6 days ago
1106
cor->state = Coroutine_NS(Running);
5 months ago
1107
Link_Remove(&cor->link);
8 months ago
4
1108
if ( early ) {
8 months ago
1109
List_AddHead(&cors->runable, &cor->link);
8 months ago
4
1110
} else {
8 months ago
1111
List_AddTail(&cors->runable, &cor->link);
8 months ago
4
1112
}
7 months ago
1113
_Cor_Mutex_Unlock(&cors->waiting_mutex);
3 months ago
1114
return Coroutine_OK;
7 months ago
1115
}
1116
1117
6 days ago
1118
Coroutine_Err
1119
Coroutine_NS(Continue)(
7 months ago
1120
Coroutine *cor,
1121
void *value,
1122
bool early
1123
){
6 days ago
1124
MyAssert(!Coroutine_NS(StackHasOverrun)());
7 months ago
1125
Coroutines *cors = cor->coroutines;
7 months ago
1126
_Cor_Mutex_Lock(&cors->mutex);
6 days ago
1127
Coroutine_Err err = Coroutine_NS(Continue_)(cors, cor, value, early);
7 months ago
1128
_Cor_Mutex_Unlock(&cors->mutex);
3 months ago
1129
return err;
8 months ago
4
1130
}
1131
8 months ago
1132
6 days ago
1133
void *
1134
Coroutine_NS(Yield)(
8 months ago
1135
void *value,
6 days ago
1136
Coroutine_NS(YieldCallback) on_yield,
8 months ago
1137
void *yield_me
8 months ago
1138
){
3 months ago
1139
MyAssert(g_c);
5 months ago
1140
Coroutine *me = g_c->active;
3 months ago
1141
MyAssert(me);
6 days ago
1142
MyAssert(!Coroutine_NS(StackHasOverrun)());
8 months ago
1143
5 months ago
1144
_Cor_Mutex_Lock(&g_c->mutex);
8 months ago
1145
Coroutines *cors = me->coroutines;
6 days ago
1146
MyAssert(me && me->state == Coroutine_NS(Running) && cors == g_c);
6 months ago
1147
me->stack_top = StackTopNow();
8 months ago
4
1148
me->value = value;
6 days ago
1149
me->state = Coroutine_NS(Waiting);
8 months ago
1150
5 months ago
1151
Link_Remove(&me->link);
7 months ago
1152
if (!List_IsEmpty(&cors->runable)){
7 months ago
1153
_Cor_Mutex_Unlock(&cors->waiting_mutex);
7 months ago
1154
}
8 months ago
1155
List_AddTail(&cors->waiting, &me->link);
8 months ago
1156
8 months ago
1157
switch (setjmp(me->buf)){
1158
case Chunk_Initial:
7 months ago
1159
_Cor_Mutex_Unlock(&cors->mutex);
8 months ago
1160
on_yield(yield_me);
6 days ago
1161
Coroutine_NS(RunNext)();
3 months ago
1162
MyAssert(false);
2 months ago
1163
break;
8 months ago
1164
case Chunk_Create:
3 months ago
1165
MyAssert(me == g_c->tip);
5 months ago
1166
ReserveStackSpace(me->coroutines, me, me->stack_top - me->limit, NULL);
3 months ago
1167
MyAssert(false);
2 months ago
1168
break;
8 months ago
1169
case Chunk_Enter:
1170
// arrive here with mutex locked
8 months ago
1171
cors->active = me;
6 days ago
1172
MyAssert(!Coroutine_NS(StackHasOverrun)());
8 months ago
1173
// when we return here - we are running again
6 days ago
1174
MyAssert(me->state == Coroutine_NS(Running));
8 months ago
1175
void *res = me->entry_param;
7 months ago
1176
_Cor_Mutex_Unlock(&cors->mutex);
8 months ago
1177
return res;
8 months ago
4
1178
}
2 months ago
1179
MyAssert(false);
8 months ago
1180
return NULL;
8 months ago
4
1181
}
1182
8 months ago
1183
6 days ago
1184
void *
1185
Coroutine_NS(GetValue)(
8 months ago
1186
Coroutine *cor
1187
){
8 months ago
4
1188
return cor->value;
1189
}
1190
8 months ago
1191
6 days ago
1192
Coroutine *
1193
Coroutine_NS(GetActive)(
1194
void
1195
){
5 months ago
1196
return g_c ? g_c->active : NULL;
8 months ago
4
1197
}
1198
8 months ago
1199
6 days ago
1200
intptr_t
1201
Coroutine_NS(GetStackHeadroom)(
1202
void
1203
){
3 months ago
1204
Coroutine *me = g_c ? g_c->active : NULL;
6 months ago
1205
if (!me){
1206
// no active coroutine
3 months ago
1207
if (g_stack_limit){
1208
return StackTopNow() - g_stack_limit;
6 months ago
1209
} else {
1210
// no information where the stack ends - return something
5 months ago
1211
return COROUTINE_MINIMUM_STACK_SIZE;
6 months ago
1212
}
1213
}
5 months ago
1214
return StackTopNow() - me->limit;
8 months ago
1215
}
1216
1217
6 months ago
1218
// This is used to avoid compiler warnings about returning the address of a local
6 days ago
1219
static inline void *
1220
StopAddressWarnings(
1221
void *p
1222
){
6 months ago
1223
return p;
8 months ago
1224
}
1225
1226
6 days ago
1227
void *
1228
Coroutine_NS(GetStackHWM)(
1229
void
1230
){
3 months ago
1231
MyAssert(g_c);
1232
MyAssert(g_c->state == Coroutines_Active);
6 days ago
1233
MyAssert(!Coroutine_NS(StackHasOverrun)());
6 months ago
1234
// Find where the guards end
1235
unsigned char *guard;
2 months ago
1236
for (guard = g_c->active->limit; Guard_Pattern_OK(guard); guard += 4){
6 months ago
1237
// do nothing
1238
}
1239
return guard;
1240
}
7 months ago
1241
1242
6 days ago
1243
void
1244
Coroutine_NS(ClearStackForHWM)(
1245
void
1246
){
3 months ago
1247
MyAssert(g_c);
1248
MyAssert(g_c->state == Coroutines_Active);
6 days ago
1249
MyAssert(!Coroutine_NS(StackHasOverrun)());
6 months ago
1250
unsigned char *end = StackTopNow() - GUARD_PATTERN_SIZE;
2 months ago
1251
for (unsigned char *guard = g_c->active->limit; guard <= end; guard += GUARD_PATTERN_SIZE){
6 months ago
1252
Apply_Guard(guard);
6 months ago
1253
}
1254
}
1255
1256
6 days ago
1257
static bool
1258
Coroutine_NS(CanStartCoroutine_Lock_Assumed)(
5 months ago
1259
size_t size
1260
){
1261
if (!g_c->stack_limit){
6 months ago
1262
return true;
1263
}
6 months ago
1264
5 months ago
1265
if (!g_c->tip){
1266
return true;
1267
}
1268
6 days ago
1269
if (g_c->tip->state == Coroutine_NS(Free)){
5 months ago
1270
// last block is free
1271
if ((unsigned char *)g_c->tip - g_c->stack_limit >= (ptrdiff_t)(g_c->gap_after + size)){
1272
// enough room in free block, which is the last block
1273
return true;
1274
}
1275
} else {
1276
// last block is allocated
1277
if (g_c->tip->limit - g_c->stack_limit >= (ptrdiff_t)(g_c->gap_before + g_c->gap_after + size)){
1278
// enough room after the last block, which is allocated
1279
return true;
1280
}
1281
}
1282
1283
// not enough room between allocated blocks and stack limit, so check free list
1284
List_Link *link;
1285
for (link = List_Begin(&g_c->free); Link_NextIsLink(link); link = Link_Next(link)){
1286
Coroutine *cor = List_Link_Container(Coroutine, link, link);
1287
if (cor->size >= size){
1288
return true;
1289
}
1290
}
1291
1292
return false;
6 months ago
1293
}
1294
1295
6 days ago
1296
bool
1297
Coroutine_NS(CanStartCoroutine)(
5 months ago
1298
size_t size
1299
){
3 months ago
1300
MyAssert(g_c);
1301
MyAssert(g_c->state == Coroutines_Started || g_c->state == Coroutines_Active);
6 days ago
1302
MyAssert(!Coroutine_NS(StackHasOverrun)());
3 months ago
1303
5 months ago
1304
_Cor_Mutex_Lock(&g_c->mutex);
1305
6 days ago
1306
bool result = Coroutine_NS(CanStartCoroutine_Lock_Assumed)(size);
5 months ago
1307
1308
_Cor_Mutex_Unlock(&g_c->mutex);
1309
1310
return result;
1311
}
1312
6 days ago
1313
void *
1314
Coroutine_NS(GetCStackTop)(
1315
void
1316
){
1317
MyAssert(!Coroutine_NS(StackHasOverrun)());
5 months ago
1318
if ((g_c->state == Coroutines_Started || g_c->state == Coroutines_Active) && g_c->tip != g_c->active) {
1319
return g_c->tip->stack_top;
6 months ago
1320
} else {
1321
return StackTopNow();
1322
}
8 months ago
1323
}
1324
1325
6 days ago
1326
static unsigned char *
1327
StackTopNow(
1328
void
1329
){
6 months ago
1330
unsigned char here[4];
1331
return StopAddressWarnings(here);
1332
}
1333
1334
6 days ago
1335
struct Coroutine_NS(ChainParam) {
8 months ago
1336
Coroutine_Start start;
1337
void *value;
1338
Coroutine *ret;
1339
};
1340
1341
6 days ago
1342
static void *
1343
Coroutine_NS(ChainFn)(
8 months ago
1344
void *param
1345
){
6 days ago
1346
struct Coroutine_NS(ChainParam) *params = (struct Coroutine_NS(ChainParam) *)param;
1347
return (void *)(uintptr_t)Coroutine_NS(Continue)(params->ret, params->start(params->value), true);
8 months ago
1348
}
1349
1350
6 days ago
1351
static void
1352
Coroutine_NS(ChainYield)(
8 months ago
1353
void *unused
1354
){
1355
(void)unused;
1356
}
1357
1358
6 days ago
1359
Coroutine_Err
1360
Coroutine_NS(Chain)(
5 months ago
1361
size_t size,
8 months ago
1362
Coroutine_Start start,
5 months ago
1363
void *value,
1364
void **result
8 months ago
1365
){
6 days ago
1366
MyAssert(!Coroutine_NS(StackHasOverrun)());
1367
Coroutine *cor = Coroutine_NS(New)(size, Coroutine_NS(ChainFn));
5 months ago
1368
if (!cor){
1369
// failed
3 months ago
1370
return Coroutine_Err_NoStack;
5 months ago
1371
}
6 days ago
1372
struct Coroutine_NS(ChainParam) params = {
8 months ago
1373
start,
1374
value,
6 days ago
1375
Coroutine_NS(GetActive)()
8 months ago
1376
};
6 days ago
1377
Coroutine_Err err = Coroutine_NS(Continue)(cor, &params, true);
3 months ago
1378
if (err){
1379
return err;
1380
}
6 days ago
1381
void *res = Coroutine_NS(Yield)(NULL, Coroutine_NS(ChainYield), NULL);
1382
err = (Coroutine_Err)(uintptr_t)Coroutine_NS(GetValue)(cor);
1383
Coroutine_NS(Delete)(cor);
3 months ago
1384
if (!err && result){
5 months ago
1385
*result = res;
1386
}
3 months ago
1387
// success! ...probably
1388
return err;
8 months ago
1389
}
1390
1391
6 days ago
1392
bool
1393
Coroutine_NS(IsRunning)(
8 months ago
1394
Coroutine *cor
6 days ago
1395
){
8 months ago
1396
int state = cor->state;
6 days ago
1397
return state == Coroutine_NS(Running) || state == Coroutine_NS(Waiting);
8 months ago
4
1398
}
8 months ago
1399
1400
6 days ago
1401
bool Coroutine_NS(IsComplete)(
6 months ago
1402
Coroutine *cor
6 days ago
1403
){
6 months ago
1404
int state = cor->state;
6 days ago
1405
return state == Coroutine_NS(Complete);
6 months ago
1406
}
1407
1408
6 days ago
1409
bool
1410
Coroutine_NS(IsStarted)(
1411
void
1412
){
5 months ago
1413
return g_c && (g_c->state == Coroutines_Active || g_c->state == Coroutines_Started);
8 months ago
1414
}
5 months ago
1415
6 days ago
1416
void
1417
Coroutine_NS(Dump_)(
1418
void
1419
){
5 months ago
1420
char *state_to_text[] = {
1421
"Free",
1422
"Idle",
1423
"Running",
1424
"Waiting",
1425
"Complete"
1426
};
1427
unsigned idx = 0;
1428
List_Link *link;
1429
for (link = List_Begin(&g_c->all); Link_NextIsLink(link); link = Link_Next(link)){
1430
Coroutine *cor = List_Link_Container(Coroutine, all_link, link);
1431
printf("%d) %p (%s) %ld%s\n", idx++, cor, state_to_text[cor->state], cor->size, cor == g_c->tip ? " (TIP)" : "");
1432
}
1433
}
1434