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