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