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