1 contributor
147 lines5.4 KB
1#ifndef COROUTINE_H
2#define COROUTINE_H
3
4#include <stdbool.h>
5#include <stddef.h>
6#include <stdint.h>
7
8///////////////////////////////////////////////////////////////////////////////
9// Coroutine
10//
11// Coroutines for C, based on setjmp/longjmp.
12// Thread safe - each thread has its own coroutine system
13// Coroutines are cooperatively scheduled
14// Coroutines have their own stack (currently 16K each)
15// A coroutine can be continued, queried, or deleted on a different thread.
16//
17// Usage:
18// Coroutine_StartSystem(); // call once per thread before using coroutines
19// Coroutine *co = Coroutine_New(start_function);
20// void *result;
21// if (Coroutine_Run(co, initial_value, &result)){}
22// // Handle the failure
23// |
24// Coroutine_Delete(co);
25// Coroutine_StopSystem(); // call once per thread when done with coroutines
26//
27// Inside the coroutine function:
28// void *value = Coroutine_Yield(yield_value, on_yield, this);
29// ...
30// return return_value;
31//
32// To create a coroutine:
33// Coroutine *co = Coroutine_New(start_function);
34// To start or continue a coroutine:
35// void *result = Coroutine_Continue(co, value, early);
36// // early=true puts the coroutine at the head of the run queue
37// // early=false puts the coroutine at the tail of the run queue
38// To yield from inside a coroutine:
39// void *value = Coroutine_Yield(yield_value, on_yield, this);
40// // on_yield is called before the next coroutine is run
41// // 'this' is passed to on_yield as its parameter
42// // value is the value passed to Coroutine_Continue
43// To delete a coroutine:
44// Coroutine_Delete(co);
45// To get the value yielded from, or returned by a corotuine:
46// void *value = Coroutine_GetValue(co);
47// To get the currently running coroutine (NULL if none):
48// Coroutine *co = Coroutine_GetActive();
49// To check if a coroutine is currently running:
50// bool running = Coroutine_IsRunning(co);
51//
52// Notes:
53// Coroutine is not expected to be used directly, but as a foundation for
54// higher level constructs such as Generators, Async, etc.
55//
56///////////////////////////////////////////////////////////////////////////////
57
58
59// The stack is used as follows:
60//
61// Note: the stack is assumed to grow downwards through memory
62//
63// (low memory)
64//
65// <- limit. When set this and lower memory addresses are assumed to be unavailable for stack use
66// (- lowest address usable by the stack -)
67// . .
68// . .
69// | coroutine stack |
70// +------------------+
71// | coroutine header | <- 'tip' coroutine (latest allocated Coroutine)
72// +------------------+
73// | coroutine stack |
74// +------------------+
75// | coroutine header | <- 'active' Coroutine (the one currently running - could be 'tip')
76// +------------------+
77// | coroutine stack |
78// +------------------+
79// | coroutine header |
80// +------------------+
81// | coroutine stack |
82// +------------------+
83// | coroutine header |
84// +------------------+
85// | startup space | <- set aside by Coroutine_StartSystem
86// +------------------+
87// | caller | <- This calls Coroutine_StartSystem etc
88// +------------------+
89// | used stack |
90// +------------------+ <- stack 'bottom'; highest address used by the stack
91// (high memory)
92
93// No coroutine will ask for less stack than this
94#ifndef COROUTINE_MINIMUM_STACK_SIZE
95 #define COROUTINE_MINIMUM_STACK_SIZE (4096 * sizeof(void *))
96#endif
97
98// Before Coroutine has New()'ed a Coroutine, this amount of stack
99// is set aside for workings between the New() and Run():
100#ifndef COROUTINE_STARTUP_STACK_SIZE
101 #define COROUTINE_STARTUP_STACK_SIZE (128 * sizeof(void *))
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 size_t lowest_headroom;
115 size_t largest_stack;
116} Coroutine_Report;
117
118typedef struct Coroutine Coroutine;
119
120typedef void (*Coroutine_YieldCallback)(void *me);
121typedef void *(*Coroutine_Start)(void *);
122
123extern void Coroutine_StartSystem(void);
124extern void Coroutine_SetStackLimit(void *);
125extern Coroutine_Report Coroutine_StopSystem(void);
126extern Coroutine *Coroutine_New(size_t size, Coroutine_Start start);
127extern void Coroutine_Run_Coroutine(Coroutine *cor, void *value);
128extern bool Coroutine_Run(size_t size, Coroutine_Start start, void *value, void **result);
129extern void Coroutine_Delete(Coroutine *cor);
130extern void Coroutine_Continue(Coroutine *cor, void *value, bool early);
131extern void *Coroutine_Yield(void *value, Coroutine_YieldCallback on_yield, void *me);
132extern void *Coroutine_GetValue(Coroutine *cor);
133extern Coroutine *Coroutine_GetActive(void);
134extern intptr_t Coroutine_GetStackHeadroom(void);
135extern void *Coroutine_GetStackHWM(void);
136extern void Coroutine_ClearStackForHWM(void);
137extern bool Coroutine_CanStartCoroutine(size_t size);
138extern void *Coroutine_GetCStackTop(void);
139extern bool Coroutine_Chain(size_t size, Coroutine_Start start, void *value, void **result);
140extern bool Coroutine_IsStarted(void);
141extern bool Coroutine_IsRunning(Coroutine *cor);
142extern bool Coroutine_IsComplete(Coroutine *cor);
143
144extern void _Coroutine_Dump(void);
145
146#endif
147