0 branches 0 tags
89 90
91
92 93
WIP re-engineer for more robust operation, less chaining, and biggest-stack-possible chains
on 4:02 PM Jun 5 2026
cor_platform.h
90
91
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
#ifndef COR_PLATFORM_H
#define COR_PLATFORM_H
// platform specific parts collected together
#include <stdbool.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
// inspired by CPython to achieve platform indenpendence for thread local variables
#ifdef thread_local
#define _Cor_thread_local thread_local
#elif __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
#define _Cor_thread_local _Thread_local
#elif defined(_MSC_VER) /* AKA NT_THREADS */
#define _Cor_thread_local __declspec(thread)
#elif defined(__GNUC__) /* includes clang */
#define _Cor_thread_local __thread
#else
#define _Cor_thread_local
#endif
// see CPython again, this time in ctypes.h
#define COROUTINE_HAVE_ALLOCA_H 1
// Non-reentrant Mutexes
typedef struct _Cor_Mutex {
pthread_mutex_t mut;
} _Cor_Mutex;
extern int _Cor_Mutex_ctor(_Cor_Mutex *);
extern int _Cor_Mutex_dtor(_Cor_Mutex *);
extern int _Cor_Mutex_Lock(_Cor_Mutex *);
extern int _Cor_Mutex_Unlock(_Cor_Mutex *);
// The 'now' to use for _Cor_Semaphore_Wait, in ns.
extern int64_t _Cor_Realtime_Now();
typedef struct _Cor_Semaphore {
pthread_mutex_t mut;
pthread_cond_t cond;
unsigned count;
} _Cor_Semaphore;
extern void _Cor_Semaphore_ctor(_Cor_Semaphore *sem);
extern void _Cor_Sempahore_dtor(_Cor_Semaphore *sem);
// timeout_when < 0 means 'wait forever'
// Returns true for success, false for timeout
extern bool _Cor_Semaphore_Wait(_Cor_Semaphore *sem, int64_t timeout_when);
extern void _Cor_Semaphore_Signal(_Cor_Semaphore *sem);
typedef struct _Cor_Thread {
pthread_t th;
bool joined;
} _Cor_Thread;
extern void _Cor_Thread_ctor(_Cor_Thread *, void *(*)(void *), void *);
extern void _Cor_Thread_dtor(_Cor_Thread *);
extern void *_Cor_Thread_Join(_Cor_Thread *);
#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
#ifndef COR_PLATFORM_H
#define COR_PLATFORM_H
// platform specific parts collected together
#include <stdbool.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
// inspired by CPython to achieve platform indenpendence for thread local variables
#ifdef thread_local
#define _Cor_thread_local thread_local
#elif __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
#define _Cor_thread_local _Thread_local
#elif defined(_MSC_VER) /* AKA NT_THREADS */
#define _Cor_thread_local __declspec(thread)
#elif defined(__GNUC__) /* includes clang */
#define _Cor_thread_local __thread
#else
#define _Cor_thread_local
#endif
// see CPython again, this time in ctypes.h
#define COROUTINE_HAVE_ALLOCA_H 1
// Non-reentrant Mutexes
typedef struct _Cor_Mutex {
pthread_mutex_t mut;
} _Cor_Mutex;
extern int _Cor_Mutex_ctor(_Cor_Mutex *);
extern int _Cor_Mutex_dtor(_Cor_Mutex *);
extern int _Cor_Mutex_Lock(_Cor_Mutex *);
extern int _Cor_Mutex_Unlock(_Cor_Mutex *);
// The 'now' to use for _Cor_Semaphore_Wait, in ns.
extern int64_t _Cor_Realtime_Now();
typedef struct _Cor_Semaphore {
pthread_mutex_t mut;
pthread_cond_t cond;
unsigned count;
} _Cor_Semaphore;
extern void _Cor_Semaphore_ctor(_Cor_Semaphore *sem);
extern void _Cor_Sempahore_dtor(_Cor_Semaphore *sem);
// timeout_when < 0 means 'wait forever'
// Returns true for success, false for timeout
extern bool _Cor_Semaphore_Wait(_Cor_Semaphore *sem, int64_t timeout_when);
extern void _Cor_Semaphore_Signal(_Cor_Semaphore *sem);
typedef struct _Cor_Thread {
pthread_t th;
bool joined;
} _Cor_Thread;
extern void _Cor_Thread_ctor(_Cor_Thread *, void *(*)(void *), void *);
extern void _Cor_Thread_dtor(_Cor_Thread *);
extern void *_Cor_Thread_Join(_Cor_Thread *);
#define COROUTINE_STACK_GROWS_UP 0
#endif
coroutine.h
90
91
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#ifndef COROUTINE_H
#define COROUTINE_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "cor_platform_inc.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:
// +------------------+ <- stack top
// | coroutine header | <- more claimed as needed in Coroutine_New
// +------------------+ <-
// | coroutine stack | <-
// +------------------+ <-
// | coroutine header |
// +------------------+
// | coroutine stack |
// +------------------+
// | coroutine header |
// +------------------+
// | 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
// Each coroutine has this much stack:
// For Python, we set it to 17 * (enough for a PyEval_EvalDefault), so we get at least 7
// calls deep before we need a new chunk, ie maximum multi-chunk wastage is under 6% address space.
//
// There's a trade-off between smaller chunk sizes, which allow more async tasks to co-exist
// on a thread, and larger chunk sizes which waste less memory in part-used chunks.
//
// ... which means 10000 async tasks need a 2.6 GB stack, which fits comfortably in the address map.
//
// Note, when developing the use of Coroutine in Python, the author found the following used
// excessive amounts of stack space:
// Tk_Init: on an Intel 64 bit Mac it used 72k.
// _decimal multplies of big decimal numbers: 256k+640 (2 x 128k buffers in squaretrans_pow2() + workings)
//
// 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.
// This allows you to rename all Coroutine things with your own namespace.
#ifndef Coroutine_NS
#define Coroutine_NS(N) Coroutine_##N
#endif
#ifndef Coroutine_API_FUNC
#define Coroutine_API_FUNC(T) extern T
#endif
// No coroutine will ask for less stack than this
#ifndef COROUTINE_MINIMUM_STACK_SIZE
#define COROUTINE_MINIMUM_STACK_SIZE (4096 * sizeof(void *))
#endif
// When Coroutine is started, an amount of stack is set aside to give
// the caller of Coroutine_StartSystem a bit of room to work before calling
// Coroutine_Run(), that is this amount:
#ifndef COROUTINE_STARTUP_STACK_SIZE
#ifndef _NDEBUG
#define COROUTINE_STARTUP_STACK_SIZE (1024 * sizeof(void *))
#else
#define COROUTINE_STARTUP_STACK_SIZE (128 * sizeof(void *))
#endif
#endif
// This is *expensive* to turn on, especially if you have lots of stack pieces (eg when there's lots of Tasks)
#ifndef COROUTINE_CHECK_INTEGRITY_ON_STACK_CHECK
#define COROUTINE_CHECK_INTEGRITY_ON_STACK_CHECK 0
#endif
#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 enum Coroutine_Err {
Coroutine_OK = 0,
Coroutine_Err_SystemNotRunning,
Coroutine_Err_SystemRunning,
Coroutine_Err_NoStack,
Coroutine_Err_CoroutineFromWrongThread,
Coroutine_Err_ACoroutineIsAlreadyRunning,
Coroutine_Err_ExitWithRunningCoroutines,
Coroutine_Err_StackOverrun,
Coroutine_Err_InternalInsistency,
Coroutine_Err_CouldNotInitialiseSystem,
Coroutine_Err_WrongState,
Coroutine_Err_Canceled
} Coroutine_Err;
typedef struct Coroutine Coroutine;
typedef void (*Coroutine_YieldCallback)(void *me);
typedef Coroutine_Err (*Coroutine_SystemStart)(void *);
typedef void *(*Coroutine_Start)(void *);
Coroutine_API_FUNC(void) Coroutine_NS(SetStackLimit)(void *);
Coroutine_API_FUNC(Coroutine_Report) Coroutine_NS(GetReport)(void);
#ifndef NDEBUG
Coroutine_API_FUNC(Coroutine_Err) Coroutine_NS(CheckIntegrity)(void);
#else
static inline Coroutine_Err Coroutine_NS(CheckIntegrity)(void){return Coroutine_OK;}
#endif
Coroutine_API_FUNC(Coroutine *) Coroutine_NS(New)(size_t size, Coroutine_Start start);
Coroutine_API_FUNC(Coroutine_Err) Coroutine_NS(Run_Coroutine)(Coroutine *cor, void *value);
Coroutine_API_FUNC(Coroutine_Err) Coroutine_NS(RunSystem)(Coroutine_SystemStart start, void *value);
Coroutine_API_FUNC(Coroutine_Err) Coroutine_NS(Run)(size_t size, Coroutine_Start start, void *value, void **result);
Coroutine_API_FUNC(void) Coroutine_NS(Delete)(Coroutine *cor);
Coroutine_API_FUNC(Coroutine_Err) Coroutine_NS(Continue)(Coroutine *cor, void *value, bool early);
Coroutine_API_FUNC(void *) Coroutine_NS(Yield)(void *value, Coroutine_YieldCallback on_yield, void *me);
Coroutine_API_FUNC(void *) Coroutine_NS(GetValue)(Coroutine *cor);
Coroutine_API_FUNC(Coroutine *) Coroutine_NS(GetActive)(void);
Coroutine_API_FUNC(intptr_t) Coroutine_NS(GetStackHeadroom)(void);
Coroutine_API_FUNC(void *) Coroutine_NS(GetStackHWM)(void);
Coroutine_API_FUNC(void) Coroutine_NS(ClearStackForHWM)(void);
Coroutine_API_FUNC(bool) Coroutine_NS(CanStartCoroutine)(size_t size);
Coroutine_API_FUNC(void *) Coroutine_NS(GetCStackTop)(void);
Coroutine_API_FUNC(Coroutine_Err) Coroutine_NS(Chain)(size_t size, Coroutine_Start start, void *value, void **result);
Coroutine_API_FUNC(bool) Coroutine_NS(IsStarted)(void);
Coroutine_API_FUNC(bool) Coroutine_NS(IsRunning)(Coroutine *cor);
Coroutine_API_FUNC(bool) Coroutine_NS(IsComplete)(Coroutine *cor);
Coroutine_API_FUNC(void) Coroutine_NS(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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#ifndef COROUTINE_H
#define COROUTINE_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include "cor_platform_inc.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:
// +------------------+ <- stack top
// | coroutine header | <- more claimed as needed in Coroutine_New
// +------------------+ <-
// | coroutine stack | <-
// +------------------+ <-
// | coroutine header |
// +------------------+
// | coroutine stack |
// +------------------+
// | coroutine header |
// +------------------+
// | 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
// Each coroutine has this much stack:
// For Python, we set it to 17 * (enough for a PyEval_EvalDefault), so we get at least 7
// calls deep before we need a new chunk, ie maximum multi-chunk wastage is under 6% address space.
//
// There's a trade-off between smaller chunk sizes, which allow more async tasks to co-exist
// on a thread, and larger chunk sizes which waste less memory in part-used chunks.
//
// ... which means 10000 async tasks need a 2.6 GB stack, which fits comfortably in the address map.
//
// Note, when developing the use of Coroutine in Python, the author found the following used
// excessive amounts of stack space:
// Tk_Init: on an Intel 64 bit Mac it used 72k.
// _decimal multplies of big decimal numbers: 256k+640 (2 x 128k buffers in squaretrans_pow2() + workings)
//
// 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.
// This allows you to rename all Coroutine things with your own namespace.
#ifndef Coroutine_NS
#define Coroutine_NS(N) Coroutine_##N
#endif
#ifndef Coroutine_API_FUNC
#define Coroutine_API_FUNC(T) extern T
#endif
// No coroutine will ask for less stack than this
#ifndef COROUTINE_MINIMUM_STACK_SIZE
#define COROUTINE_MINIMUM_STACK_SIZE (4096 * sizeof(void *))
#endif
// When Coroutine is started, an amount of stack is set aside to give
// the caller of Coroutine_StartSystem a bit of room to work before calling
// Coroutine_Run(), that is this amount:
#ifndef COROUTINE_STARTUP_STACK_SIZE
#ifndef _NDEBUG
#define COROUTINE_STARTUP_STACK_SIZE (1024 * sizeof(void *))
#else
#define COROUTINE_STARTUP_STACK_SIZE (128 * sizeof(void *))
#endif
#endif
// This is *expensive* to turn on, especially if you have lots of stack pieces (eg when there's lots of Tasks)
#ifndef COROUTINE_CHECK_INTEGRITY_ON_STACK_CHECK
#define COROUTINE_CHECK_INTEGRITY_ON_STACK_CHECK 0
#endif
#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 enum Coroutine_Err {
Coroutine_OK = 0,
Coroutine_Err_SystemNotRunning,
Coroutine_Err_SystemRunning,
Coroutine_Err_NoStack,
Coroutine_Err_CoroutineFromWrongThread,
Coroutine_Err_ACoroutineIsAlreadyRunning,
Coroutine_Err_ExitWithRunningCoroutines,
Coroutine_Err_StackOverrun,
Coroutine_Err_InternalInsistency,
Coroutine_Err_CouldNotInitialiseSystem,
Coroutine_Err_WrongState,
Coroutine_Err_Canceled
} Coroutine_Err;
typedef struct Coroutine Coroutine;
typedef void (*Coroutine_YieldCallback)(void *me);
typedef Coroutine_Err (*Coroutine_SystemStart)(void *, Coroutine *);
typedef void *(*Coroutine_Start)(void *);
Coroutine_API_FUNC(void) Coroutine_NS(SetStackLimit)(void *);
Coroutine_API_FUNC(Coroutine_Report) Coroutine_NS(GetReport)(void);
#ifndef NDEBUG
Coroutine_API_FUNC(Coroutine_Err) Coroutine_NS(CheckIntegrity)(void);
#else
static inline Coroutine_Err Coroutine_NS(CheckIntegrity)(void){return Coroutine_OK;}
#endif
Coroutine_API_FUNC(Coroutine *) Coroutine_NS(New)(size_t min_size, size_t min_headroom, Coroutine_Start start);
Coroutine_API_FUNC(Coroutine_Err) Coroutine_NS(Run_Coroutine)(Coroutine *cor, void *value);
Coroutine_API_FUNC(Coroutine_Err) Coroutine_NS(RunSystem)(size_t min_size, size_t min_headroom, Coroutine_SystemStart start, void *value);
Coroutine_API_FUNC(Coroutine_Err) Coroutine_NS(Run)(size_t min_size, size_t min_headroom, Coroutine_Start start, void *value, void **result);
Coroutine_API_FUNC(void) Coroutine_NS(Delete)(Coroutine *cor);
Coroutine_API_FUNC(Coroutine_Err) Coroutine_NS(Continue)(Coroutine *cor, void *value, bool early);
Coroutine_API_FUNC(void *) Coroutine_NS(Yield)(void *value, Coroutine_YieldCallback on_yield, void *me);
Coroutine_API_FUNC(void *) Coroutine_NS(GetValue)(Coroutine *cor);
Coroutine_API_FUNC(Coroutine *) Coroutine_NS(GetActive)(void);
Coroutine_API_FUNC(intptr_t) Coroutine_NS(GetStackHeadroom)(void);
Coroutine_API_FUNC(void *) Coroutine_NS(GetStackHWM)(void);
Coroutine_API_FUNC(void) Coroutine_NS(ClearStackForHWM)(void);
Coroutine_API_FUNC(bool) Coroutine_NS(CanStartCoroutine)(size_t size);
Coroutine_API_FUNC(void *) Coroutine_NS(GetCStackTop)(void);
Coroutine_API_FUNC(Coroutine_Err) Coroutine_NS(Chain)(size_t min_size, size_t min_headroom, Coroutine_Start start, void *value, void **result);
Coroutine_API_FUNC(bool) Coroutine_NS(IsStarted)(void);
Coroutine_API_FUNC(bool) Coroutine_NS(IsRunning)(Coroutine *cor);
Coroutine_API_FUNC(bool) Coroutine_NS(IsComplete)(Coroutine *cor);
Coroutine_API_FUNC(void) Coroutine_NS(Dump_)(void);
#endif
generator.h
90
91
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
#ifndef GENERATOR_H
#define GENERATOR_H
#include "coroutine.h"
#include <stdbool.h>
typedef enum Generator_State {
Generator_Running,
Generator_Deleting,
Generator_Complete
} Generator_State;
typedef struct Generator {
Coroutine *coroutine;
Coroutine *caller;
void *(*start)(void *);
void *param;
Generator_State state;
} Generator;
extern void Generator_ctor(Generator *gen, size_t stack_size, void *(*start)(void *), void *param);
extern Generator *Generator_New(size_t stack_size, void *(*)(void *), void *);
extern void Generator_dtor(Generator *gen);
extern void Generator_Delete(Generator *);
// Returns true if generator yielded a value, false if generator is complete
// *value is set to generator's value (yield / return value from exit)
extern bool Generator_Next(Generator *, void **value);
// Yield a value from the generator
// Returns true if generator should continue, false for generator to exit PDQ
extern bool Generator_Yield(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
#ifndef GENERATOR_H
#define GENERATOR_H
#include "coroutine.h"
#include <stdbool.h>
typedef enum Generator_State {
Generator_Running,
Generator_Deleting,
Generator_Complete
} Generator_State;
typedef struct Generator {
Coroutine *coroutine;
Coroutine *caller;
void *(*start)(void *);
void *param;
Generator_State state;
} Generator;
extern void Generator_ctor(Generator *gen, size_t min_stack, size_t min_stack_headroom, void *(*start)(void *), void *param);
extern Generator *Generator_New(size_t min_stack, size_t min_stack_headroom, void *(*)(void *), void *);
extern void Generator_dtor(Generator *gen);
extern void Generator_Delete(Generator *);
// Returns true if generator yielded a value, false if generator is complete
// *value is set to generator's value (yield / return value from exit)
extern bool Generator_Next(Generator *, void **value);
// Yield a value from the generator
// Returns true if generator should continue, false for generator to exit PDQ
extern bool Generator_Yield(void *);
#endif
task.def.h
90
91
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
#include "coroutine.h"
struct Task {
Future base;
Coroutine *cor;
Task_Entry entry;
void *param;
Future *awaiting_future;
bool canceled;
void *cancel_value;
};
static inline bool Task_IsCanceled(
Task *tsk
){
return tsk->canceled;
}
static inline bool Task_Await(
Task *tsk,
void **res
){
return Future_Await(&tsk->base, res);
}
static inline Future *Task_GetAwaitedFuture(
Task *tsk
){
return tsk->awaiting_future;
}
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
#include "coroutine.h"
struct Task {
Future base;
Coroutine *cor;
Task_Entry entry;
void *param;
Future *awaiting_future;
void *cancel_value;
bool canceled;
bool cor_owned;
};
static inline bool Task_IsCanceled(
Task *tsk
){
return tsk->canceled;
}
static inline Coroutine_Err Task_Await(
Task *tsk,
void **res
){
return Future_Await(&tsk->base, res);
}
static inline Future *Task_GetAwaitedFuture(
Task *tsk
){
return tsk->awaiting_future;
}
task.h
90
91
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
#ifndef TASK_H
#define TASK_H
#include <stdbool.h>
#include "future.h"
#include "cor_platform.h"
typedef struct Task Task;
extern _Cor_thread_local Task *current_task;
typedef bool (*Task_Entry)(void *param, void **res);
extern void Task_ctor(Task *tsk, size_t stack_size, Task_Entry entry, void *param);
extern Task *Task_New(size_t stack_size, Task_Entry entry, void *param);
extern void Task_dtor(Task *tsk);
extern void Task_Delete(Task *tsk);
static inline bool Task_Await(Task *tsk, void **res);
extern void Task_Cancel(Task *tsk, void *cancel_value);
static inline bool Task_IsCanceled(Task *tsk);
static inline Future *Task_GetAwaitedFuture(Task *tsk);
extern bool Task_Run(size_t stack_size, Task_Entry start, void *value, void **res);
#include "task.def.h"
#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
#ifndef TASK_H
#define TASK_H
#include <stdbool.h>
#include "future.h"
#include "cor_platform.h"
typedef struct Task Task;
extern _Cor_thread_local Task *current_task;
typedef bool (*Task_Entry)(void *param, void **res);
extern void Task_ctor(Task *tsk, size_t min_stack, size_t min_stack_headroom, Task_Entry entry, void *param);
extern Task *Task_New(size_t min_stack, size_t min_stack_headroom, Task_Entry entry, void *param);
extern void Task_dtor(Task *tsk);
extern void Task_Delete(Task *tsk);
static inline Coroutine_Err Task_Await(Task *tsk, void **res);
extern void Task_Cancel(Task *tsk, void *cancel_value);
static inline bool Task_IsCanceled(Task *tsk);
static inline Future *Task_GetAwaitedFuture(Task *tsk);
extern bool Task_Run(size_t min_stack, size_t min_stack_headroom, Task_Entry start, void *value, void **res);
#include "task.def.h"
#endif