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
|
// License: MPL-2.0
// (c) 2022 citrons <citrons@mondecitronne.com>
use rt;
use fmt;
def STACK_SIZE: size = 8192;
// A coroutine.
export type coroutine = struct {
// The function comprising the body of the coroutine.
func: *func,
// The current execution status of the coroutine.
status: status,
stack: *void,
state: arch_state,
};
// Represents the execution status of a coroutine.
export type status = enum {
// The coroutine has been suspended or has not been executed yet.
suspended,
// The coroutine is currently executing.
running,
// The coroutine has completed.
dead,
dying,
};
// A function that can be run as a coroutine.
export type func = fn(arg: nullable *void) nullable *void;
// Create a coroutine which will execute the function f. When done using the
// coroutine, use [[destroy]].
export fn new(f: *func) *coroutine = {
let stack = rt::malloc(STACK_SIZE) as *void;
let co = alloc(coroutine {func = f, stack = stack, ...});
arch_init(co.func, &co.state, stack, STACK_SIZE);
return co;
};
// Stop a coroutine.
export fn terminate(co: *coroutine) void = {
if (co.status == status::dead) return;
assert(co.status != status::running);
rt::free_(co.stack);
co.status = status::dead;
};
// Free a coroutine.
export fn destroy(co: *coroutine) void = {
terminate(co);
free(co);
};
let running_: nullable *coroutine = null;
// Returns the currently running coroutine.
export fn running() nullable *coroutine = running_;
// Start or resume a coroutine. When called for the first time on a coroutine,
// arg is passed as the argument to the body. If the coroutine has suspended,
// [[resume]] restarts it, and arg is returned as the result of [[suspend]].
// When the body completes, [[resume]] returns its return value.
export fn resume(co: *coroutine, arg: nullable *void) nullable *void = {
if (co.status == status::dead) return null;
assert(co.status == status::suspended,
"cannot resume non-suspended coroutine");
let prev = running_;
defer running_ = prev;
running_ = co;
defer if (co.status == status::dying) terminate(co);
co.status = status::running;
return arch_resume(&co.state);
};
// Suspend the execution of the present coroutine. arg is returned by
// [[resume]].
export fn suspend(arg: nullable *void) nullable *void = {
let co = running_ as *coroutine;
if (co.status == status::running)
co.status = status::suspended;
return arch_suspend(arg);
};
export @noreturn fn _start(f: *func) void = {
let arg = arch_suspend(null);
let retval = f(arg);
let co = running_ as *coroutine;
co.status = status::dying;
suspend(retval);
assert(false);
};
fn arch_suspend(arg: nullable *void) nullable *void;
fn arch_init(f: *func, s: *arch_state, stack: *void, stack_size: size) void;
fn arch_resume(s: *arch_state) nullable *void;
|