1 contributor
140 lines5.1 KB
1#ifndef COROUTINE_H
2#define COROUTINE_H
3
4#include <stdbool.h>
5#include <stdint.h>
6
7///////////////////////////////////////////////////////////////////////////////
8// Coroutine
9//
10// Coroutines for C, based on setjmp/longjmp.
11// Thread safe - each thread has its own coroutine system
12// Coroutines are cooperatively scheduled
13// Coroutines have their own stack (currently 16K each)
14// A coroutine can be continued, queried, or deleted on a different thread.
15//
16// Usage:
17// Coroutine_StartSystem(); // call once per thread before using coroutines
18// Coroutine *co = Coroutine_New(start_function);
19// void *result = Coroutine_Run(co, initial_value);
20// Coroutine_Delete(co);
21// Coroutine_StopSystem(); // call once per thread when done with coroutines
22//
23// Inside the coroutine function:
24// void *value = Coroutine_Yield(yield_value, on_yield, this);
25// ...
26// return return_value;
27//
28// To create a coroutine:
29// Coroutine *co = Coroutine_New(start_function);
30// To start or continue a coroutine:
31// void *result = Coroutine_Continue(co, value, early);
32// // early=true puts the coroutine at the head of the run queue
33// // early=false puts the coroutine at the tail of the run queue
34// To yield from inside a coroutine:
35// void *value = Coroutine_Yield(yield_value, on_yield, this);
36// // on_yield is called before the next coroutine is run
37// // 'this' is passed to on_yield as its parameter
38// // value is the value passed to Coroutine_Continue
39// To delete a coroutine:
40// Coroutine_Delete(co);
41// To get the value yielded from, or returned by a corotuine:
42// void *value = Coroutine_GetValue(co);
43// To get the currently running coroutine (NULL if none):
44// Coroutine *co = Coroutine_GetActive();
45// To check if a coroutine is currently running:
46// bool running = Coroutine_IsRunning(co);
47//
48// Notes:
49// Coroutine is not expected to be used directly, but as a foundation for
50// higher level constructs such as Generators, Async, etc.
51//
52///////////////////////////////////////////////////////////////////////////////
53
54
55// The stack is used as follows:
56//
57// Note: the stack is assumed to grow downwards through memory
58//
59// (low memory)
60//
61// <- limit. When set this and lower memory addresses are assumed to be unavailable for stack use
62// (- lowest address usable by the stack -)
63// . .
64// . .
65// | coroutine stack |
66// +------------------+
67// | coroutine header | <- 'tip' coroutine (latest allocated Coroutine)
68// +------------------+
69// | coroutine stack |
70// +------------------+
71// | coroutine header | <- 'active' Coroutine (the one currently running - could be 'tip')
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 |
86// +------------------+ <- stack 'bottom'; highest address used by the stack
87// (high memory)
88
89// Each coroutine has this much stack:
90#ifndef COROUTINE_STACK_SIZE
91 #define COROUTINE_STACK_SIZE 65536
92#endif
93
94// When Coroutine is started, an amount of stack is set aside to give
95// the caller of Coroutine_StartSystem a bit of room to work before calling
96// Coroutine_Run(), that is this amount:
97#ifndef COROUTINE_STARTUP_STACK_SIZE
98 #define COROUTINE_STARTUP_STACK_SIZE 4096
99#endif
100
101// When allocating space for a coroutine stack, fill it with guard pattern
102// so that lowest_headroom in the Coroutine_Report can be worked out
103#ifndef COROUTINE_RECORD_LOWEST_HEADROOM
104 #define COROUTINE_RECORD_LOWEST_HEADROOM 1
105#endif
106
107// Returned by Coroutine_StopSystem(), this summarises the coroutine session
108typedef struct Coroutine_Report {
109 unsigned coroutines_created;
110 unsigned coroutines_pool_size;
111 unsigned lowest_headroom;
112} Coroutine_Report;
113
114typedef struct Coroutine Coroutine;
115
116typedef void (*Coroutine_YieldCallback)(void *me);
117typedef void *(*Coroutine_Start)(void *);
118
119extern void Coroutine_StartSystem(void);
120extern void Coroutine_SetStackLimit(void *);
121extern Coroutine_Report Coroutine_StopSystem(void);
122extern Coroutine *Coroutine_New(Coroutine_Start start);
123extern void Coroutine_Run_Coroutine(Coroutine *cor, void *value);
124extern void *Coroutine_Run(Coroutine_Start start, void *value);
125extern void Coroutine_Delete(Coroutine *cor);
126extern void Coroutine_Continue(Coroutine *cor, void *value, bool early);
127extern void *Coroutine_Yield(void *value, Coroutine_YieldCallback on_yield, void *me);
128extern void *Coroutine_GetValue(Coroutine *cor);
129extern Coroutine *Coroutine_GetActive(void);
130extern intptr_t Coroutine_GetStackHeadroom(void);
131extern void *Coroutine_GetStackHWM(void);
132extern void Coroutine_ClearStackForHWM(void);
133extern bool Coroutine_CanStartCoroutine();
134extern void *Coroutine_GetCStackTop(void);
135extern void *Coroutine_Chain(Coroutine_Start start, void *value);
136extern bool Coroutine_IsStarted(void);
137extern bool Coroutine_IsRunning(Coroutine *cor);
138
139#endif
140