This repository was archived by the owner on Jul 15, 2020. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsupervisor.gleam
More file actions
138 lines (115 loc) · 3.33 KB
/
Copy pathsupervisor.gleam
File metadata and controls
138 lines (115 loc) · 3.33 KB
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
import gleam/list
// Test helpers
pub external type Pid(a)
pub external type Fail
pub external type OpaquePid
pub type Option(a) =
Result(a, Nil)
pub type Start(a) =
Result(Pid(a), Fail)
pub external fn erase(Pid(a)) -> OpaquePid = "" ""
pub external fn kill(Pid(a)) -> Nil = "" ""
// API
pub type Children(state) {
Children(
pids: List(OpaquePid),
state: state,
private_initialiser: Initialiser(Nil, state),
)
}
pub type StartChildren(state) =
Result(Children(state), Fail)
fn problem_flag(problem_pid: Option(OpaquePid), current_pid: OpaquePid) -> Option(OpaquePid) {
case problem_pid {
Ok(pid) if current_pid != pid -> problem_pid
_ -> Error(Nil)
}
}
pub type Initialiser(state_in, state_out) =
fn(
state_in,
Option(OpaquePid),
) -> Result(tuple(state_out, List(OpaquePid)), Fail)
fn add_child(
init: Initialiser(state_0, state_1),
start: fn(state_1) -> Start(msg),
merge: fn(state_1, Pid(msg)) -> state_2,
existing_pid: Pid(msg),
existing_state: state_2,
) -> Initialiser(state_0, state_2) {
fn(state, problem_pid) {
case init(state, problem_pid) {
// Older siblings failed, exit early
Error(fail) -> Error(fail)
// Older siblings ok, initialise the new process
Ok(tuple(state, pids)) -> {
let opaque_pid = erase(existing_pid)
case problem_pid {
// This Pid is still alive, keep going.
Ok(problem_pid) if problem_pid != opaque_pid -> {
Ok(tuple(existing_state, [opaque_pid, ..pids]))
}
// This pid either is the cause of the problem, or we don't have problem
// pid to compare with. In either case it must be restarted.
_ -> {
kill(existing_pid)
case start(state) {
Error(fail) -> Error(fail)
Ok(new_pid) -> {
let problem_pid = problem_flag(problem_pid, opaque_pid)
let new_state = merge(state, new_pid)
Ok(tuple(new_state, [erase(new_pid), ..pids]))
}
}
}
}
}
}
}
}
pub fn empty() -> StartChildren(Nil) {
Ok(Children(
state: Nil,
pids: [],
private_initialiser: fn(state, _) { Ok(tuple(state, [])) },
))
}
pub fn worker(
children: StartChildren(state),
start start_child: fn(state) -> Start(msg),
returning merge: fn(state, Pid(msg)) -> new_state
) -> StartChildren(new_state) {
case children {
Ok(Children(pids: pids, state: state, private_initialiser: init)) -> case start_child(state) {
Ok(pid) -> {
let state = merge(state, pid)
let pids = [erase(pid), ..pids]
let init = add_child(init, start_child, merge, pid, state)
Ok(Children(pids, state, init))
}
Error(fail) -> Error(fail)
}
}
}
pub fn unreferenced_worker(
children: StartChildren(state),
start start_child: fn(state) -> Start(msg),
) -> StartChildren(state) {
worker(children, start_child, fn(x, _) { x })
}
// Testing
pub fn start_child1(x: Nil) -> Start(Int) {
todo
}
pub fn start_child2(older: Pid(Int)) -> Start(String) {
todo
}
pub fn start_child3(older: Pid(Int)) -> Start(Float) {
todo
}
pub fn supervisor_init() {
empty()
|> worker(start: start_child1, returning: fn(_state, pid) { pid })
|> unreferenced_worker(start: start_child2)
|> unreferenced_worker(start: start_child3)
}