0 branches 0 tags
68 69
78
79 80
Allow failures to be returned
on 10:24 AM Feb 11 2026
coroutine.h
77
78
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#ifndef COROUTINE_H
#define COROUTINE_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
///////////////////////////////////////////////////////////////////////////////
// Coroutine
//
// Coroutines for C, based on setjmp/longjmp.
// Thread safe - each thread has its own coroutine system
// Coroutines are cooperatively scheduled
// Coroutines have their own stack (currently 16K each)
// A coroutine can be continued, queried, or deleted on a different thread.
//
// Usage:
// Coroutine_StartSystem(); // call once per thread before using coroutines
// Coroutine *co = Coroutine_New(start_function);
// void *result;
// if (Coroutine_Run(co, initial_value, &result)){}
// // Handle the failure
// |
// Coroutine_Delete(co);
// Coroutine_StopSystem(); // call once per thread when done with coroutines
//
// Inside the coroutine function:
// void *value = Coroutine_Yield(yield_value, on_yield, this);
// ...
// return return_value;
//
// To create a coroutine:
// Coroutine *co = Coroutine_New(start_function);
// To start or continue a coroutine:
// void *result = Coroutine_Continue(co, value, early);
// // early=true puts the coroutine at the head of the run queue
// // early=false puts the coroutine at the tail of the run queue
// To yield from inside a coroutine:
// void *value = Coroutine_Yield(yield_value, on_yield, this);
// // on_yield is called before the next coroutine is run
// // 'this' is passed to on_yield as its parameter
// // value is the value passed to Coroutine_Continue
// To delete a coroutine:
// Coroutine_Delete(co);
// To get the value yielded from, or returned by a corotuine:
// void *value = Coroutine_GetValue(co);
// To get the currently running coroutine (NULL if none):
// Coroutine *co = Coroutine_GetActive();
// To check if a coroutine is currently running:
// bool running = Coroutine_IsRunning(co);
//
// Notes:
// Coroutine is not expected to be used directly, but as a foundation for
// higher level constructs such as Generators, Async, etc.
//
///////////////////////////////////////////////////////////////////////////////
// The stack is used as follows:
//
// Note: the stack is assumed to grow downwards through memory
//
// (low memory)
//
// <- limit. When set this and lower memory addresses are assumed to be unavailable for stack use
// (- lowest address usable by the stack -)
// . .
// . .
// | coroutine stack |
// +------------------+
// | coroutine header | <- 'tip' coroutine (latest allocated Coroutine)
// +------------------+
// | coroutine stack |
// +------------------+
// | coroutine header | <- 'active' Coroutine (the one currently running - could be 'tip')
// +------------------+
// | coroutine stack |
// +------------------+
// | coroutine header |
// +------------------+
// | coroutine stack |
// +------------------+
// | coroutine header |
// +------------------+
// | startup space | <- set aside by Coroutine_StartSystem
// +------------------+
// | caller | <- This calls Coroutine_StartSystem etc
// +------------------+
// | used stack |
// +------------------+ <- stack 'bottom'; highest address used by the stack
// (high memory)
// No coroutine will ask for less stack than this
#ifndef COROUTINE_MINIMUM_STACK_SIZE
#define COROUTINE_MINIMUM_STACK_SIZE (4096 * sizeof(void *))
#endif
// Before Coroutine has New()'ed a Coroutine, this amount of stack
// is set aside for workings between the New() and Run():
#ifndef COROUTINE_STARTUP_STACK_SIZE
#define COROUTINE_STARTUP_STACK_SIZE (128 * sizeof(void *))
#endif
// When allocating space for a coroutine stack, fill it with guard pattern
// so that lowest_headroom in the Coroutine_Report can be worked out
#ifndef COROUTINE_RECORD_LOWEST_HEADROOM
#define COROUTINE_RECORD_LOWEST_HEADROOM 1
#endif
// Returned by Coroutine_StopSystem(), this summarises the coroutine session
typedef struct Coroutine_Report {
unsigned coroutines_created;
unsigned coroutines_pool_size;
size_t lowest_headroom;
size_t largest_stack;
} Coroutine_Report;
typedef struct Coroutine Coroutine;
typedef void (*Coroutine_YieldCallback)(void *me);
typedef void *(*Coroutine_Start)(void *);
extern void Coroutine_StartSystem(void);
extern void Coroutine_SetStackLimit(void *);
extern Coroutine_Report Coroutine_StopSystem(void);
extern Coroutine *Coroutine_New(size_t size, Coroutine_Start start);
extern void Coroutine_Run_Coroutine(Coroutine *cor, void *value);
extern bool Coroutine_Run(size_t size, Coroutine_Start start, void *value, void **result);
extern void Coroutine_Delete(Coroutine *cor);
extern void Coroutine_Continue(Coroutine *cor, void *value, bool early);
extern void *Coroutine_Yield(void *value, Coroutine_YieldCallback on_yield, void *me);
extern void *Coroutine_GetValue(Coroutine *cor);
extern Coroutine *Coroutine_GetActive(void);
extern intptr_t Coroutine_GetStackHeadroom(void);
extern void *Coroutine_GetStackHWM(void);
extern void Coroutine_ClearStackForHWM(void);
extern bool Coroutine_CanStartCoroutine(size_t size);
extern void *Coroutine_GetCStackTop(void);
extern bool Coroutine_Chain(size_t size, Coroutine_Start start, void *value, void **result);
extern bool Coroutine_IsStarted(void);
extern bool Coroutine_IsRunning(Coroutine *cor);
extern bool Coroutine_IsComplete(Coroutine *cor);
extern void _Coroutine_Dump(void);
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#ifndef COROUTINE_H
#define COROUTINE_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
///////////////////////////////////////////////////////////////////////////////
// Coroutine
//
// Coroutines for C, based on setjmp/longjmp.
// Thread safe - each thread has its own coroutine system
// Coroutines are cooperatively scheduled
// Coroutines have their own stack (currently 16K each)
// A coroutine can be continued, queried, or deleted on a different thread.
//
// Usage:
// Coroutine_StartSystem(); // call once per thread before using coroutines
// Coroutine *co = Coroutine_New(start_function);
// void *result;
// if (Coroutine_Run(co, initial_value, &result)){}
// // Handle the failure
// |
// Coroutine_Delete(co);
// Coroutine_StopSystem(); // call once per thread when done with coroutines
//
// Inside the coroutine function:
// void *value = Coroutine_Yield(yield_value, on_yield, this);
// ...
// return return_value;
//
// To create a coroutine:
// Coroutine *co = Coroutine_New(start_function);
// To start or continue a coroutine:
// void *result = Coroutine_Continue(co, value, early);
// // early=true puts the coroutine at the head of the run queue
// // early=false puts the coroutine at the tail of the run queue
// To yield from inside a coroutine:
// void *value = Coroutine_Yield(yield_value, on_yield, this);
// // on_yield is called before the next coroutine is run
// // 'this' is passed to on_yield as its parameter
// // value is the value passed to Coroutine_Continue
// To delete a coroutine:
// Coroutine_Delete(co);
// To get the value yielded from, or returned by a corotuine:
// void *value = Coroutine_GetValue(co);
// To get the currently running coroutine (NULL if none):
// Coroutine *co = Coroutine_GetActive();
// To check if a coroutine is currently running:
// bool running = Coroutine_IsRunning(co);
//
// Notes:
// Coroutine is not expected to be used directly, but as a foundation for
// higher level constructs such as Generators, Async, etc.
//
///////////////////////////////////////////////////////////////////////////////
// The stack is used as follows:
//
// Note: the stack is assumed to grow downwards through memory
//
// (low memory)
//
// <- limit. When set this and lower memory addresses are assumed to be unavailable for stack use
// (- lowest address usable by the stack -)
// . .
// . .
// | coroutine stack |
// +------------------+
// | coroutine header | <- 'tip' coroutine (latest allocated Coroutine)
// +------------------+
// | coroutine stack |
// +------------------+
// | coroutine header | <- 'active' Coroutine (the one currently running - could be 'tip')
// +------------------+
// | coroutine stack |
// +------------------+
// | coroutine header |
// +------------------+
// | coroutine stack |
// +------------------+
// | coroutine header |
// +------------------+
// | startup space | <- set aside by Coroutine_StartSystem
// +------------------+
// | caller | <- This calls Coroutine_StartSystem etc
// +------------------+
// | used stack |
// +------------------+ <- stack 'bottom'; highest address used by the stack
// (high memory)
// No coroutine will ask for less stack than this
#ifndef COROUTINE_MINIMUM_STACK_SIZE
#define COROUTINE_MINIMUM_STACK_SIZE (4096 * sizeof(void *))
#endif
// Before Coroutine has New()'ed a Coroutine, this amount of stack
// is set aside for workings between the New() and Run():
#ifndef COROUTINE_STARTUP_STACK_SIZE
#define COROUTINE_STARTUP_STACK_SIZE (128 * sizeof(void *))
#endif
// When allocating space for a coroutine stack, fill it with guard pattern
// so that lowest_headroom in the Coroutine_Report can be worked out
#ifndef COROUTINE_RECORD_LOWEST_HEADROOM
#define COROUTINE_RECORD_LOWEST_HEADROOM 1
#endif
// Returned by Coroutine_StopSystem(), this summarises the coroutine session
typedef struct Coroutine_Report {
unsigned coroutines_created;
unsigned coroutines_pool_size;
size_t lowest_headroom;
size_t largest_stack;
} Coroutine_Report;
typedef struct Coroutine Coroutine;
typedef void (*Coroutine_YieldCallback)(void *me);
typedef void *(*Coroutine_Start)(void *);
extern void Coroutine_StartSystem(void);
extern void Coroutine_SetStackLimit(void *);
extern Coroutine_Report Coroutine_StopSystem(void);
extern Coroutine *Coroutine_New(size_t size, Coroutine_Start start);
extern void Coroutine_Run_Coroutine(Coroutine *cor, void *value);
extern bool Coroutine_Run(size_t size, Coroutine_Start start, void *value, void **result);
extern void Coroutine_Delete(Coroutine *cor);
extern bool Coroutine_Continue(Coroutine *cor, void *value, bool early);
extern void *Coroutine_Yield(void *value, Coroutine_YieldCallback on_yield, void *me);
extern void *Coroutine_GetValue(Coroutine *cor);
extern Coroutine *Coroutine_GetActive(void);
extern intptr_t Coroutine_GetStackHeadroom(void);
extern void *Coroutine_GetStackHWM(void);
extern void Coroutine_ClearStackForHWM(void);
extern bool Coroutine_CanStartCoroutine(size_t size);
extern void *Coroutine_GetCStackTop(void);
extern bool Coroutine_Chain(size_t size, Coroutine_Start start, void *value, void **result);
extern bool Coroutine_IsStarted(void);
extern bool Coroutine_IsRunning(Coroutine *cor);
extern bool Coroutine_IsComplete(Coroutine *cor);
extern void _Coroutine_Dump(void);
#endif