supervisor_machines 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: bcf2434ae701ae257e80d7203631c5a771fe2f918fb30ccc654e7cfb8a4bdedd
4
+ data.tar.gz: e49e4fb0b7b99f34a0e81ac06e68bc3b22244253a34877410130568a4303bd9d
5
+ SHA512:
6
+ metadata.gz: aba61a49c4e301984d91c9bd9e6c85da75672038ded38bb7a9d3ed26b661a1d16bf5a2befe4b5d58fdb47c1f5119e8050672feb1b2c1ba6504d800198a226260
7
+ data.tar.gz: 2cfeba21ac509d76ae75018c8c8811f0d52a3f9462c234b26b1d451f7afcd35b45dcdc7a99bd21afe8f6e8b3880753f655929fc3cab8462dde9789fd7d97a679
data/CHANGELOG.md ADDED
@@ -0,0 +1,28 @@
1
+ # Changelog
2
+
3
+ ## [0.1.1](https://github.com/seuros/supervisor_machines/compare/supervisor_machines-v0.1.0...supervisor_machines/v0.1.1) (2026-11-01)
4
+
5
+
6
+ ### Features
7
+
8
+ * OTP-style supervision decisions ([013b054](https://github.com/seuros/supervisor_machines/commit/013b0543abb42eff100032c286642e9c26f5a907))
9
+ * pass a rubygems OTP through the environment ([d6b00e6](https://github.com/seuros/supervisor_machines/commit/d6b00e6f10606b4d8584d4f58999a184d2f749df))
10
+ * supervise threads over the decision core ([c423f63](https://github.com/seuros/supervisor_machines/commit/c423f63ceef340edfaef7eb2774eb353046b7556))
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * a source install can build, and a failed gem push fails the release ([5907461](https://github.com/seuros/supervisor_machines/commit/5907461674b3fc53bcbc6c077b39d6b61d959eaf))
16
+ * a started tree takes on no more children, and an unstarted one has no restarts ([49baae8](https://github.com/seuros/supervisor_machines/commit/49baae849adedfea8fc96ab18c8989e77e57edf7))
17
+ * a stop no longer waits out the restart backoff ([5de0f95](https://github.com/seuros/supervisor_machines/commit/5de0f95db1f15ceb2a4eb8fb8114d7ee48a5da8b))
18
+ * a stopped tree does not keep announcing its shutdown ([44f9e78](https://github.com/seuros/supervisor_machines/commit/44f9e78f8c5279be42fb29b10ef40ef8778b099b))
19
+ * an in-family floor, not a pessimistic pin ([7113e08](https://github.com/seuros/supervisor_machines/commit/7113e087b649320fe1d0b1dba1c2dabb268be175))
20
+ * bound the child index ([3761aab](https://github.com/seuros/supervisor_machines/commit/3761aab987111b4570aeaefc171175552d0c7948))
21
+ * call starters and the backoff outside the supervisor's lock ([b656111](https://github.com/seuros/supervisor_machines/commit/b656111a32fe5623a44b1dd7f665d708839b17b5))
22
+ * close six supervision lifecycle holes ([50d330f](https://github.com/seuros/supervisor_machines/commit/50d330f8bac893b75b8616b284bd0e1e1b006e99))
23
+ * collect the built gems whatever depth the download puts them at ([961776d](https://github.com/seuros/supervisor_machines/commit/961776db5edbee33b2fbe87494e6c6ac7ee0d3f7))
24
+ * give the tree lifecycle states so a failed start leaves nothing running ([dc7deba](https://github.com/seuros/supervisor_machines/commit/dc7debaff42f2440c80c6f43f057660c0a5c79a0))
25
+ * refuse a fractional restart intensity instead of tightening it quietly ([ea34650](https://github.com/seuros/supervisor_machines/commit/ea34650c0480f12e824ec488daa911daeda30ccf))
26
+ * ship the core's test files so the packaged crate builds ([4af208e](https://github.com/seuros/supervisor_machines/commit/4af208efd80a9e48275aedfe8e2137155acd59a0))
27
+ * the release tag carries the gem name, and the gate uses the aggregate output ([49982d6](https://github.com/seuros/supervisor_machines/commit/49982d60eed580789d6d52502c0f275576139df3))
28
+ * the tree reads the clock before taking its own lock ([6dd61ef](https://github.com/seuros/supervisor_machines/commit/6dd61efced111ae04b01ffdd7dfb4b559b0fd26e))
data/Cargo.lock ADDED
@@ -0,0 +1,286 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "aho-corasick"
7
+ version = "1.1.5"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
10
+ dependencies = [
11
+ "memchr",
12
+ ]
13
+
14
+ [[package]]
15
+ name = "bindgen"
16
+ version = "0.72.1"
17
+ source = "registry+https://github.com/rust-lang/crates.io-index"
18
+ checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
19
+ dependencies = [
20
+ "bitflags",
21
+ "cexpr",
22
+ "clang-sys",
23
+ "itertools",
24
+ "proc-macro2",
25
+ "quote",
26
+ "regex",
27
+ "rustc-hash",
28
+ "shlex",
29
+ "syn",
30
+ ]
31
+
32
+ [[package]]
33
+ name = "bitflags"
34
+ version = "2.13.2"
35
+ source = "registry+https://github.com/rust-lang/crates.io-index"
36
+ checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
37
+
38
+ [[package]]
39
+ name = "cexpr"
40
+ version = "0.6.0"
41
+ source = "registry+https://github.com/rust-lang/crates.io-index"
42
+ checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
43
+ dependencies = [
44
+ "nom",
45
+ ]
46
+
47
+ [[package]]
48
+ name = "cfg-if"
49
+ version = "1.0.5"
50
+ source = "registry+https://github.com/rust-lang/crates.io-index"
51
+ checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600"
52
+
53
+ [[package]]
54
+ name = "clang-sys"
55
+ version = "1.9.1"
56
+ source = "registry+https://github.com/rust-lang/crates.io-index"
57
+ checksum = "157a8ba7b480713b56f4c09fd13fc3e0a22a5dfab8097ba61cbc5feef950788a"
58
+ dependencies = [
59
+ "glob",
60
+ "libc",
61
+ "libloading",
62
+ ]
63
+
64
+ [[package]]
65
+ name = "either"
66
+ version = "1.19.0"
67
+ source = "registry+https://github.com/rust-lang/crates.io-index"
68
+ checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be"
69
+
70
+ [[package]]
71
+ name = "glob"
72
+ version = "0.3.4"
73
+ source = "registry+https://github.com/rust-lang/crates.io-index"
74
+ checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
75
+
76
+ [[package]]
77
+ name = "itertools"
78
+ version = "0.13.0"
79
+ source = "registry+https://github.com/rust-lang/crates.io-index"
80
+ checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
81
+ dependencies = [
82
+ "either",
83
+ ]
84
+
85
+ [[package]]
86
+ name = "lazy_static"
87
+ version = "1.5.1"
88
+ source = "registry+https://github.com/rust-lang/crates.io-index"
89
+ checksum = "20870f649af7073d53e38067b2a84312175d56ea15217e1b15bc83506ec50afb"
90
+
91
+ [[package]]
92
+ name = "libc"
93
+ version = "0.2.191"
94
+ source = "registry+https://github.com/rust-lang/crates.io-index"
95
+ checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a"
96
+
97
+ [[package]]
98
+ name = "libloading"
99
+ version = "0.8.9"
100
+ source = "registry+https://github.com/rust-lang/crates.io-index"
101
+ checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
102
+ dependencies = [
103
+ "cfg-if",
104
+ "windows-link",
105
+ ]
106
+
107
+ [[package]]
108
+ name = "magnus"
109
+ version = "0.9.3"
110
+ source = "registry+https://github.com/rust-lang/crates.io-index"
111
+ checksum = "40ef424404d166cbb71c8b8d19c9c589c748c4547c1afa1928c5504299f6c713"
112
+ dependencies = [
113
+ "magnus-macros",
114
+ "rb-sys",
115
+ "rb-sys-env",
116
+ "seq-macro",
117
+ ]
118
+
119
+ [[package]]
120
+ name = "magnus-macros"
121
+ version = "0.9.1"
122
+ source = "registry+https://github.com/rust-lang/crates.io-index"
123
+ checksum = "9dafde2eeb66f090e0f326112323eb8324b12cb32b5d1210e541d3df6b0ce77e"
124
+ dependencies = [
125
+ "proc-macro2",
126
+ "quote",
127
+ "syn",
128
+ ]
129
+
130
+ [[package]]
131
+ name = "memchr"
132
+ version = "2.8.3"
133
+ source = "registry+https://github.com/rust-lang/crates.io-index"
134
+ checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
135
+
136
+ [[package]]
137
+ name = "minimal-lexical"
138
+ version = "0.2.1"
139
+ source = "registry+https://github.com/rust-lang/crates.io-index"
140
+ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
141
+
142
+ [[package]]
143
+ name = "nom"
144
+ version = "7.1.3"
145
+ source = "registry+https://github.com/rust-lang/crates.io-index"
146
+ checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
147
+ dependencies = [
148
+ "memchr",
149
+ "minimal-lexical",
150
+ ]
151
+
152
+ [[package]]
153
+ name = "proc-macro2"
154
+ version = "1.0.107"
155
+ source = "registry+https://github.com/rust-lang/crates.io-index"
156
+ checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
157
+ dependencies = [
158
+ "unicode-ident",
159
+ ]
160
+
161
+ [[package]]
162
+ name = "quote"
163
+ version = "1.0.47"
164
+ source = "registry+https://github.com/rust-lang/crates.io-index"
165
+ checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
166
+ dependencies = [
167
+ "proc-macro2",
168
+ ]
169
+
170
+ [[package]]
171
+ name = "rb-sys"
172
+ version = "0.9.130"
173
+ source = "registry+https://github.com/rust-lang/crates.io-index"
174
+ checksum = "02faf625bb10ba893e3ae620f19c9fb1b5f8fcae0fe4eb86bb3f2230fad75edb"
175
+ dependencies = [
176
+ "rb-sys-build",
177
+ ]
178
+
179
+ [[package]]
180
+ name = "rb-sys-build"
181
+ version = "0.9.130"
182
+ source = "registry+https://github.com/rust-lang/crates.io-index"
183
+ checksum = "05be6f9c86fe5482808162826f6c047d093b3670582296c770de1d94f8066694"
184
+ dependencies = [
185
+ "bindgen",
186
+ "lazy_static",
187
+ "proc-macro2",
188
+ "quote",
189
+ "regex",
190
+ "shell-words",
191
+ "syn",
192
+ ]
193
+
194
+ [[package]]
195
+ name = "rb-sys-env"
196
+ version = "0.2.3"
197
+ source = "registry+https://github.com/rust-lang/crates.io-index"
198
+ checksum = "cca7ad6a7e21e72151d56fe2495a259b5670e204c3adac41ee7ef676ea08117a"
199
+
200
+ [[package]]
201
+ name = "regex"
202
+ version = "1.13.1"
203
+ source = "registry+https://github.com/rust-lang/crates.io-index"
204
+ checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d"
205
+ dependencies = [
206
+ "aho-corasick",
207
+ "memchr",
208
+ "regex-automata",
209
+ "regex-syntax",
210
+ ]
211
+
212
+ [[package]]
213
+ name = "regex-automata"
214
+ version = "0.4.18"
215
+ source = "registry+https://github.com/rust-lang/crates.io-index"
216
+ checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
217
+ dependencies = [
218
+ "aho-corasick",
219
+ "memchr",
220
+ "regex-syntax",
221
+ ]
222
+
223
+ [[package]]
224
+ name = "regex-syntax"
225
+ version = "0.8.11"
226
+ source = "registry+https://github.com/rust-lang/crates.io-index"
227
+ checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
228
+
229
+ [[package]]
230
+ name = "rustc-hash"
231
+ version = "2.1.3"
232
+ source = "registry+https://github.com/rust-lang/crates.io-index"
233
+ checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d"
234
+
235
+ [[package]]
236
+ name = "seq-macro"
237
+ version = "0.3.6"
238
+ source = "registry+https://github.com/rust-lang/crates.io-index"
239
+ checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
240
+
241
+ [[package]]
242
+ name = "shell-words"
243
+ version = "1.1.1"
244
+ source = "registry+https://github.com/rust-lang/crates.io-index"
245
+ checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
246
+
247
+ [[package]]
248
+ name = "shlex"
249
+ version = "1.3.0"
250
+ source = "registry+https://github.com/rust-lang/crates.io-index"
251
+ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
252
+
253
+ [[package]]
254
+ name = "supervisor-machines"
255
+ version = "0.2.0"
256
+
257
+ [[package]]
258
+ name = "supervisor_machines_native"
259
+ version = "0.1.1"
260
+ dependencies = [
261
+ "magnus",
262
+ "supervisor-machines",
263
+ ]
264
+
265
+ [[package]]
266
+ name = "syn"
267
+ version = "2.0.119"
268
+ source = "registry+https://github.com/rust-lang/crates.io-index"
269
+ checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
270
+ dependencies = [
271
+ "proc-macro2",
272
+ "quote",
273
+ "unicode-ident",
274
+ ]
275
+
276
+ [[package]]
277
+ name = "unicode-ident"
278
+ version = "1.0.27"
279
+ source = "registry+https://github.com/rust-lang/crates.io-index"
280
+ checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
281
+
282
+ [[package]]
283
+ name = "windows-link"
284
+ version = "0.2.1"
285
+ source = "registry+https://github.com/rust-lang/crates.io-index"
286
+ checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
data/Cargo.toml ADDED
@@ -0,0 +1,24 @@
1
+ [workspace]
2
+ members = ["ext/supervisor_machines_native/core", "ext/supervisor_machines_native/ffi"]
3
+ resolver = "3"
4
+
5
+ [workspace.package]
6
+ edition = "2024"
7
+ rust-version = "1.99"
8
+
9
+ [workspace.dependencies]
10
+ supervisor-machines = { path = "ext/supervisor_machines_native/core" }
11
+
12
+ [workspace.lints.rust]
13
+ unsafe_code = "forbid"
14
+ unused_must_use = "warn"
15
+
16
+ [workspace.lints.clippy]
17
+ all = { level = "warn", priority = -1 }
18
+ pedantic = { level = "warn", priority = -1 }
19
+ nursery = { level = "warn", priority = -1 }
20
+ expect_used = "deny"
21
+ unwrap_used = "deny"
22
+ uninlined_format_args = "deny"
23
+ needless_borrow = "deny"
24
+ redundant_clone = "deny"
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Abdelkader Boudih
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # SupervisorMachines 🔁
2
+
3
+ > **OTP-style supervision trees over Ruby threads.** The command vessel that
4
+ > notices a station has gone quiet and decides who returns to duty.
5
+
6
+ A child died. Which children restart, and has that happened too often to keep
7
+ trying? Those answers come from the `no_std` [supervisor-machines][core] crate.
8
+ Everything the core refuses to touch, spawning, joining, killing, ordered
9
+ shutdown, is this gem. **AMS Armstrong** is the bridge around that decision.
10
+
11
+ ---
12
+
13
+ ## 🚀 Launch Sequence
14
+
15
+ ```bash
16
+ # Add to your ship's Gemfile
17
+ gem 'supervisor_machines'
18
+ ```
19
+
20
+ Requires **Ruby 4.0+** and a Rust toolchain, or a platform gem: the core is a
21
+ native extension, not an option.
22
+
23
+ ---
24
+
25
+ ## 🎯 Mission Brief
26
+
27
+ ```ruby
28
+ SM = SupervisorMachines
29
+
30
+ tree = SM::Supervisor.new(strategy: :one_for_all, restarts: 3, window: 60.0)
31
+ tree.supervise(:cache) { Thread.new { cache.run } }
32
+ tree.supervise(:warmup, policy: :transient) { Thread.new { warm_the_cache } }
33
+ tree.start
34
+
35
+ tree.children # => [:cache, :warmup]
36
+ tree.restarts # => 0
37
+ tree.terminated? # => false
38
+
39
+ tree.stop(grace: 5.0)
40
+ ```
41
+
42
+ A starter block returns the `Thread` that runs the child, and is called again
43
+ for every restart. A child that stops is noticed, handed to the core, and
44
+ whatever the core decides is carried out.
45
+
46
+ ---
47
+
48
+ ## 🔄 Lifecycle
49
+
50
+ A tree registers children, runs them, and ends either stopped or failed. Both
51
+ endings are final: `start` raises `SupervisorMachines::Error` for a tree that
52
+ is already running, has been stopped, or gave up. There is one generation of
53
+ children per tree, and starting a second one is a bug, not a refresh.
54
+ `supervise` is refused the same way once `start` has fixed the shape of the
55
+ tree: a tree past registering cannot take on a child.
56
+
57
+ A `start` that cannot finish, because a starter raises, takes the children it
58
+ already brought up back down in reverse start order, leaves the tree failed,
59
+ and raises what the starter raised. Nothing is left running and nothing is left
60
+ unsupervised. `stop` then raises `Escalated`, like any other tree that gave up,
61
+ every time it is called, and a tree that was never started has nothing to take
62
+ down.
63
+
64
+ ---
65
+
66
+ ## 🎛️ Strategies
67
+
68
+ `:one_for_one` restarts only what died. `:one_for_all` restarts every child,
69
+ for a set that only works together. `:rest_for_one` restarts the child and
70
+ everything started after it, for a chain where later children depend on earlier
71
+ ones.
72
+
73
+ One restart is recorded per failure handled, not per child brought back, so the
74
+ strategy does not change how fast the restart intensity is spent. That is OTP's
75
+ accounting.
76
+
77
+ ---
78
+
79
+ ## 📋 Standing Orders
80
+
81
+ A child's policy is its standing order. `:permanent` always comes back, even
82
+ from a clean exit. `:transient` comes back
83
+ only from a bad one, because a clean exit means it finished. `:temporary` never
84
+ comes back, and never spends the restart budget.
85
+
86
+ ```ruby
87
+ tree.supervise(:migration, policy: :temporary) { Thread.new { migrate } }
88
+ ```
89
+
90
+ ---
91
+
92
+ ## 🔥 Restart Intensity
93
+
94
+ `restarts` within `window` seconds is all the thrashing a tree will do. Past
95
+ it, the tree stops its remaining children in reverse start order and raises
96
+ `SupervisorMachines::Escalated`, and the supervisor's own supervisor now owns
97
+ the problem. That decision is sticky: `terminated?` stays true and every later
98
+ `stop` raises again.
99
+
100
+ ---
101
+
102
+ ## 🚫 Rules of Engagement
103
+
104
+ - The delay between a restart decision and the respawn comes from
105
+ [chrono_machines][chrono]. Pass your own `backoff:` to change the curve.
106
+ - The shutdown budget comes from [deadline_machines][deadline]: `stop(grace:)`
107
+ bounds the whole ordered teardown, and a child that outstays it is left
108
+ behind rather than waited on forever.
109
+ - Restart intensity, strategy spans and policy decisions come from the core
110
+ crate. None of it is reimplemented here.
111
+
112
+ ---
113
+
114
+ ## 📐 Shape
115
+
116
+ The core carries its child count in its type, so a tree is dispatched to one of
117
+ `SupervisorMachines::Decider::SUPPORTED_CHILDREN` monomorphised shapes: 1
118
+ through 16, and 32. Any other count raises `ArgumentError` from `#start`.
119
+
120
+ Threads are the only supervised unit.
121
+
122
+ ---
123
+
124
+ ## 📡 Credits
125
+
126
+ **AMS Armstrong**, NCC-3186, *Armstrong*-class command vessel.
127
+
128
+ Named for Joe Armstrong's OTP supervision trees, which settled that the way to
129
+ keep a system up is to let a part of it die and have something above it decide
130
+ what comes back.
131
+
132
+ ---
133
+
134
+ ## License
135
+
136
+ MIT.
137
+
138
+ [core]: ext/supervisor_machines_native/core/README.md
139
+ [chrono]: https://github.com/seuros/chrono_machines
140
+ [deadline]: https://github.com/seuros/deadline_machines
@@ -0,0 +1,24 @@
1
+ [package]
2
+ name = "supervisor-machines"
3
+ version = "0.2.0"
4
+ edition.workspace = true
5
+ rust-version.workspace = true
6
+ authors = ["Abdelkader Boudih <oss@seuros.com>"]
7
+ license = "MIT"
8
+ description = "OTP-style supervision decisions: restart strategies, restart intensity and escalation - no_std with zero dependencies"
9
+ repository = "https://github.com/seuros/supervisor_machines"
10
+ keywords = ["supervisor", "supervision-tree", "otp", "restart", "no-std"]
11
+ categories = ["no-std", "concurrency"]
12
+ readme = "README.md"
13
+
14
+ [lib]
15
+ name = "supervisor_machines"
16
+ crate-type = ["lib"]
17
+
18
+ [features]
19
+ default = ["std"]
20
+ std = ["alloc"]
21
+ alloc = []
22
+
23
+ [lints]
24
+ workspace = true
@@ -0,0 +1,128 @@
1
+ #![cfg_attr(not(feature = "std"), no_std)]
2
+
3
+ //! Supervision decisions, in the OTP sense.
4
+ //!
5
+ //! Which children restart after one dies, and whether that has happened too
6
+ //! often to keep trying. The caller owns the threads, fibers or processes.
7
+
8
+ /// Compiles and runs the README's examples as doctests.
9
+ #[cfg(doctest)]
10
+ #[doc = include_str!("../README.md")]
11
+ struct ReadmeDoctests;
12
+
13
+ pub mod supervisor;
14
+
15
+ pub use supervisor::Supervisor;
16
+
17
+ /// Which siblings a failure takes down with it.
18
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
19
+ pub enum Strategy {
20
+ /// Restart only the child that died.
21
+ OneForOne,
22
+ /// Restart every child.
23
+ OneForAll,
24
+ /// Restart the child that died and everything started after it.
25
+ RestForOne,
26
+ }
27
+
28
+ /// When a child is worth restarting.
29
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
30
+ pub enum Policy {
31
+ /// Always bring it back, even after a clean exit.
32
+ Permanent,
33
+ /// Bring it back only if it died badly. A clean exit means it was done.
34
+ Transient,
35
+ /// Never bring it back.
36
+ Temporary,
37
+ }
38
+
39
+ /// How a child's run ended.
40
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
41
+ pub enum Exit {
42
+ /// It finished its work.
43
+ Normal,
44
+ /// It raised, was killed, or vanished.
45
+ Abnormal,
46
+ }
47
+
48
+ /// What the supervisor should do about a child that stopped.
49
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
50
+ pub enum Decision {
51
+ /// Leave it stopped. Nothing restarts.
52
+ Ignore,
53
+ /// Restart this inclusive range of children, in index order.
54
+ Restart(Span),
55
+ /// Restarting is not working. The supervisor must terminate its remaining
56
+ /// children and let its own supervisor deal with it.
57
+ Escalate,
58
+ }
59
+
60
+ /// An inclusive range of child indices.
61
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
62
+ pub struct Span {
63
+ /// Lowest index, restarted first.
64
+ pub first: usize,
65
+ /// Highest index, restarted last.
66
+ pub last: usize,
67
+ }
68
+
69
+ impl Span {
70
+ #[must_use]
71
+ pub const fn single(index: usize) -> Self {
72
+ Self {
73
+ first: index,
74
+ last: index,
75
+ }
76
+ }
77
+
78
+ /// Children in the span. Always at least one.
79
+ #[must_use]
80
+ pub const fn len(&self) -> usize {
81
+ self.last - self.first + 1
82
+ }
83
+
84
+ /// Never true. A span always covers the child that died.
85
+ #[must_use]
86
+ pub const fn is_empty(&self) -> bool {
87
+ false
88
+ }
89
+
90
+ #[must_use]
91
+ pub const fn contains(&self, index: usize) -> bool {
92
+ self.first <= index && index <= self.last
93
+ }
94
+ }
95
+
96
+ /// How many restarts within how long before a supervisor gives up.
97
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
98
+ pub struct Intensity {
99
+ /// Restarts tolerated inside the window.
100
+ pub restarts: u32,
101
+ /// Length of the window, in nanoseconds.
102
+ pub window_ns: u64,
103
+ }
104
+
105
+ /// Restarts OTP tolerates by default before escalating.
106
+ pub const DEFAULT_RESTARTS: u32 = 1;
107
+
108
+ /// The window OTP measures those restarts over, five seconds.
109
+ pub const DEFAULT_WINDOW_NS: u64 = 5_000_000_000;
110
+
111
+ impl Intensity {
112
+ #[must_use]
113
+ pub const fn new(restarts: u32, window_ns: u64) -> Self {
114
+ Self {
115
+ restarts,
116
+ window_ns,
117
+ }
118
+ }
119
+ }
120
+
121
+ impl Default for Intensity {
122
+ fn default() -> Self {
123
+ Self::new(DEFAULT_RESTARTS, DEFAULT_WINDOW_NS)
124
+ }
125
+ }
126
+
127
+ #[cfg(test)]
128
+ mod tests;