177 lines6.8 KB
Newer
Older
-
+
commited
{line.log.rev}
on
8 months ago
1
#ifndef COROUTINE_H
2
#define COROUTINE_H
3
9 months ago
2
4
#include <stdbool.h>
5 months ago
5
#include <stddef.h>
7 months ago
6
#include <stdint.h>
9 months ago
2
7
8 months ago
8
///////////////////////////////////////////////////////////////////////////////
9
// Coroutine
10
//
11
// Coroutines for C, based on setjmp/longjmp.
12
// Thread safe - each thread has its own coroutine system
13
// Coroutines are cooperatively scheduled
14
// Coroutines have their own stack (currently 16K each)
15
// A coroutine can be continued, queried, or deleted on a different thread.
16
//
17
// Usage:
18
// Coroutine_StartSystem(); // call once per thread before using coroutines
19
// Coroutine *co = Coroutine_New(start_function);
5 months ago
20
// void *result;
3 months ago
21
// if (Coroutine_Run(co, initial_value, &result)) {
5 months ago
22
// // Handle the failure
3 months ago
23
// }
8 months ago
24
// Coroutine_Delete(co);
25
// Coroutine_StopSystem(); // call once per thread when done with coroutines
26
//
27
// Inside the coroutine function:
28
// void *value = Coroutine_Yield(yield_value, on_yield, this);
29
// ...
30
// return return_value;
31
//
32
// To create a coroutine:
33
// Coroutine *co = Coroutine_New(start_function);
34
// To start or continue a coroutine:
35
// void *result = Coroutine_Continue(co, value, early);
36
// // early=true puts the coroutine at the head of the run queue
37
// // early=false puts the coroutine at the tail of the run queue
38
// To yield from inside a coroutine:
39
// void *value = Coroutine_Yield(yield_value, on_yield, this);
40
// // on_yield is called before the next coroutine is run
41
// // 'this' is passed to on_yield as its parameter
42
// // value is the value passed to Coroutine_Continue
43
// To delete a coroutine:
44
// Coroutine_Delete(co);
45
// To get the value yielded from, or returned by a corotuine:
46
// void *value = Coroutine_GetValue(co);
47
// To get the currently running coroutine (NULL if none):
48
// Coroutine *co = Coroutine_GetActive();
49
// To check if a coroutine is currently running:
50
// bool running = Coroutine_IsRunning(co);
51
//
52
// Notes:
53
// Coroutine is not expected to be used directly, but as a foundation for
54
// higher level constructs such as Generators, Async, etc.
55
//
56
///////////////////////////////////////////////////////////////////////////////
57
58
59
// The stack is used as follows:
3 months ago
60
// +------------------+ <- stack top
61
// | coroutine header | <- more claimed as needed in Coroutine_New
62
// +------------------+ <-
63
// | coroutine stack | <-
64
// +------------------+ <-
65
// | coroutine header |
8 months ago
66
// +------------------+
67
// | coroutine stack |
68
// +------------------+
3 months ago
69
// | coroutine header |
8 months ago
70
// +------------------+
71
// | coroutine stack |
72
// +------------------+
73
// | coroutine header |
74
// +------------------+
75
// | coroutine stack |
76
// +------------------+
77
// | coroutine header |
78
// +------------------+
79
// | startup space | <- set aside by Coroutine_StartSystem
80
// +------------------+
81
// | caller | <- This calls Coroutine_StartSystem etc
82
// +------------------+
83
// | used stack |
3 months ago
84
// +------------------+ <- stack bottom
8 months ago
85
3 months ago
86
// Each coroutine has this much stack:
87
// For Python, we set it to 17 * (enough for a PyEval_EvalDefault), so we get at least 7
88
// calls deep before we need a new chunk, ie maximum multi-chunk wastage is under 6% address space.
89
//
90
// There's a trade-off between smaller chunk sizes, which allow more async tasks to co-exist
91
// on a thread, and larger chunk sizes which waste less memory in part-used chunks.
92
//
93
// ... which means 10000 async tasks need a 2.6 GB stack, which fits comfortably in the address map.
94
//
95
// Note, when developing the use of Coroutine in Python, the author found the following used
96
// excessive amounts of stack space:
97
// Tk_Init: on an Intel 64 bit Mac it used 72k.
98
// _decimal multplies of big decimal numbers: 256k+640 (2 x 128k buffers in squaretrans_pow2() + workings)
99
//
100
// On 64 bit macos, PYOS_STACK_MARGIN_BYTES is 2k * sizeof(void *), ie 16k, or 17 of those, 272k, should give enough slack to operate well.
101
5 months ago
102
// No coroutine will ask for less stack than this
103
#ifndef COROUTINE_MINIMUM_STACK_SIZE
104
#define COROUTINE_MINIMUM_STACK_SIZE (4096 * sizeof(void *))
8 months ago
105
#endif
8 months ago
106
3 months ago
107
// When Coroutine is started, an amount of stack is set aside to give
108
// the caller of Coroutine_StartSystem a bit of room to work before calling
109
// Coroutine_Run(), that is this amount:
8 months ago
110
#ifndef COROUTINE_STARTUP_STACK_SIZE
5 months ago
111
#define COROUTINE_STARTUP_STACK_SIZE (128 * sizeof(void *))
8 months ago
112
#endif
8 months ago
113
3 months ago
114
// This is *expensive* to turn on, especially if you have lots of stack pieces (eg when there's lots of Tasks)
115
#ifndef COROUTINE_CHECK_INTEGRITY_ON_STACK_CHECK
116
#define COROUTINE_CHECK_INTEGRITY_ON_STACK_CHECK 0
6 months ago
117
#endif
118
3 months ago
119
#ifndef COROUTINE_RECORD_LOWEST_HEADROOM
120
#define COROUTINE_RECORD_LOWEST_HEADROOM 1
121
#endif
122
8 months ago
123
// Returned by Coroutine_StopSystem(), this summarises the coroutine session
124
typedef struct Coroutine_Report {
125
unsigned coroutines_created;
126
unsigned coroutines_pool_size;
5 months ago
127
size_t lowest_headroom;
128
size_t largest_stack;
8 months ago
129
} Coroutine_Report;
130
3 months ago
131
typedef enum Coroutine_Err {
132
Coroutine_OK = 0,
133
Coroutine_Err_SystemNotRunning,
134
Coroutine_Err_SystemRunning,
135
Coroutine_Err_NoStack,
136
Coroutine_Err_CoroutineFromWrongThread,
137
Coroutine_Err_ACoroutineIsAlreadyRunning,
138
Coroutine_Err_ExitWithRunningCoroutines,
139
Coroutine_Err_StackOverrun,
140
Coroutine_Err_InternalInsistency,
141
Coroutine_Err_CouldNotInitialiseSystem,
142
Coroutine_Err_WrongState,
143
Coroutine_Err_Canceled
144
} Coroutine_Err;
145
9 months ago
2
146
typedef struct Coroutine Coroutine;
147
8 months ago
148
typedef void (*Coroutine_YieldCallback)(void *me);
3 months ago
149
typedef Coroutine_Err (*Coroutine_SystemStart)(void *);
9 months ago
2
150
typedef void *(*Coroutine_Start)(void *);
151
6 months ago
152
extern void Coroutine_SetStackLimit(void *);
3 months ago
153
extern Coroutine_Report Coroutine_GetReport(void);
3 months ago
154
extern Coroutine_Err Coroutine_CheckIntegrity(void);
5 months ago
155
extern Coroutine *Coroutine_New(size_t size, Coroutine_Start start);
3 months ago
156
extern Coroutine_Err Coroutine_Run_Coroutine(Coroutine *cor, void *value);
157
extern Coroutine_Err Coroutine_RunSystem(Coroutine_SystemStart start, void *value);
158
extern Coroutine_Err Coroutine_Run(size_t size, Coroutine_Start start, void *value, void **result);
7 months ago
159
extern void Coroutine_Delete(Coroutine *cor);
3 months ago
160
extern Coroutine_Err Coroutine_Continue(Coroutine *cor, void *value, bool early);
7 months ago
161
extern void *Coroutine_Yield(void *value, Coroutine_YieldCallback on_yield, void *me);
162
extern void *Coroutine_GetValue(Coroutine *cor);
7 months ago
163
extern Coroutine *Coroutine_GetActive(void);
7 months ago
164
extern intptr_t Coroutine_GetStackHeadroom(void);
6 months ago
165
extern void *Coroutine_GetStackHWM(void);
166
extern void Coroutine_ClearStackForHWM(void);
5 months ago
167
extern bool Coroutine_CanStartCoroutine(size_t size);
7 months ago
168
extern void *Coroutine_GetCStackTop(void);
3 months ago
169
extern Coroutine_Err Coroutine_Chain(size_t size, Coroutine_Start start, void *value, void **result);
7 months ago
170
extern bool Coroutine_IsStarted(void);
7 months ago
171
extern bool Coroutine_IsRunning(Coroutine *cor);
6 months ago
172
extern bool Coroutine_IsComplete(Coroutine *cor);
8 months ago
173
5 months ago
174
extern void _Coroutine_Dump(void);
175
8 months ago
176
#endif
177