0 branches 0 tags
51 52
55
56 63
Allow stack limit to be set; add Coroutine to stack on demand
on 12:31 PM Nov 18 2025
cor_platform.h
54
55
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
#ifndef COR_PLATFORM_H
#define COR_PLATFORM_H
// platform specific parts collected together
#include <stdbool.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
// Non-reentrant Mutexes
typedef struct _Cor_Mutex {
pthread_mutex_t mut;
} _Cor_Mutex;
extern void _Cor_Mutex_ctor(_Cor_Mutex *);
extern void _Cor_Mutex_dtor(_Cor_Mutex *);
extern void _Cor_Mutex_Lock(_Cor_Mutex *);
extern void _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
#ifndef COR_PLATFORM_H
#define COR_PLATFORM_H
// platform specific parts collected together
#include <stdbool.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 void _Cor_Mutex_ctor(_Cor_Mutex *);
extern void _Cor_Mutex_dtor(_Cor_Mutex *);
extern void _Cor_Mutex_Lock(_Cor_Mutex *);
extern void _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
coroutine.h
54
55
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
#ifndef COROUTINE_H
#define COROUTINE_H
#include <stdbool.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 = Coroutine_Run(co, initial_value);
// 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:
#ifndef COROUTINE_STACK_SIZE
#define COROUTINE_STACK_SIZE 65536
#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
#define COROUTINE_STARTUP_STACK_SIZE 4096
#endif
// Returned by Coroutine_StopSystem(), this summarises the coroutine session
typedef struct Coroutine_Report {
unsigned coroutines_created;
unsigned coroutines_pool_size;
unsigned lowest_headroom;
uintptr_t stack_per_coroutine;
} Coroutine_Report;
typedef struct Coroutine Coroutine;
typedef void (*Coroutine_YieldCallback)(void *me);
typedef void *(*Coroutine_Start)(void *);
extern void Coroutine_StartSystem(void);
extern Coroutine_Report Coroutine_StopSystem(void);
extern Coroutine *Coroutine_New(Coroutine_Start start);
extern void Coroutine_Run_Coroutine(Coroutine *cor, void *value);
extern void *Coroutine_Run(Coroutine_Start start, void *value);
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(void *);
extern void *Coroutine_GetCStackTop(void);
extern void *Coroutine_Chain(Coroutine_Start start, void *value);
extern bool Coroutine_IsStarted(void);
extern bool Coroutine_IsRunning(Coroutine *cor);
#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
#ifndef COROUTINE_H
#define COROUTINE_H
#include <stdbool.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 = Coroutine_Run(co, initial_value);
// 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)
// Each coroutine has this much stack:
#ifndef COROUTINE_STACK_SIZE
#define COROUTINE_STACK_SIZE 65536
#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
#define COROUTINE_STARTUP_STACK_SIZE 4096
#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;
unsigned lowest_headroom;
uintptr_t stack_per_coroutine;
} 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(Coroutine_Start start);
extern void Coroutine_Run_Coroutine(Coroutine *cor, void *value);
extern void *Coroutine_Run(Coroutine_Start start, void *value);
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();
extern void *Coroutine_GetCStackTop(void);
extern void *Coroutine_Chain(Coroutine_Start start, void *value);
extern bool Coroutine_IsStarted(void);
extern bool Coroutine_IsRunning(Coroutine *cor);
#endif