pub struct Supervisor { /* private fields */ }Expand description
Host-side child restarter (design notes §15-16).
A Supervisor is not a bytecode Flow. It is a dedicated OS
thread plus a table of ChildSpecs. When a supervised flow
becomes FlowState::Failed (or completes, under
RestartPolicy::Always), the worker delivers the
FlowOutcome here instead of letting the fault take anything
else down. The supervisor then consults the child’s
RestartPolicy and, if intensity allows, respawns it under a
fresh FlowId — Pids are never reused (see
super::process::FlowId).
Constructed from a RuntimeSpawner so it does not have to own the
runtime’s worker JoinHandles.
Implementations§
Source§impl Supervisor
impl Supervisor
pub fn new(spawner: RuntimeSpawner) -> Result<Self, SpawnError>
Sourcepub fn with_config(
spawner: RuntimeSpawner,
config: SupervisorConfig,
) -> Result<Self, SpawnError>
pub fn with_config( spawner: RuntimeSpawner, config: SupervisorConfig, ) -> Result<Self, SpawnError>
Start the dedicated supervisor OS thread. Thread-spawn failure is
SpawnError::ThreadSpawnFailed — same category-A surface as
super::runtime::Runtime::new, not a panic.
Examples found in repository?
15fn main() {
16 let rt = match Runtime::with_config(
17 samples::boom(),
18 RuntimeConfig {
19 workers: 1,
20 quantum: 1_000,
21 mailbox: byteflow::MailboxConfig::DEFAULT,
22 },
23 ) {
24 Ok(rt) => rt,
25 Err(e) => {
26 eprintln!("runtime: {e}");
27 std::process::exit(1);
28 }
29 };
30 let Some(boom) = rt.function_index("boom") else {
31 eprintln!("missing boom");
32 std::process::exit(1);
33 };
34 let sup = match Supervisor::with_config(
35 rt.spawner(),
36 SupervisorConfig {
37 max_restarts: 2,
38 max_period: Duration::from_secs(5),
39 },
40 ) {
41 Ok(s) => s,
42 Err(e) => {
43 eprintln!("supervisor: {e}");
44 std::process::exit(1);
45 }
46 };
47 if let Err(e) = sup.start_child(ChildSpec::new("boom", boom).restart(RestartPolicy::OnFailure))
48 {
49 eprintln!("start_child: {e}");
50 std::process::exit(1);
51 }
52
53 let start = std::time::Instant::now();
54 while !sup.intensity_exceeded() {
55 if start.elapsed() > Duration::from_secs(2) {
56 eprintln!("supervisor did not hit intensity in time");
57 std::process::exit(1);
58 }
59 thread::sleep(Duration::from_millis(5));
60 }
61
62 let metrics = rt.metrics();
63 println!("intensity exceeded after {} failures", metrics.processes_failed);
64 println!("{metrics}");
65 sup.shutdown();
66 rt.shutdown();
67}Sourcepub fn start_child(&self, spec: ChildSpec) -> Result<FlowHandle, SpawnError>
pub fn start_child(&self, spec: ChildSpec) -> Result<FlowHandle, SpawnError>
Spawn spec and start supervising it. The returned handle is for
this incarnation only — a restart allocates a new Pid and a new
completion channel.
Examples found in repository?
15fn main() {
16 let rt = match Runtime::with_config(
17 samples::boom(),
18 RuntimeConfig {
19 workers: 1,
20 quantum: 1_000,
21 mailbox: byteflow::MailboxConfig::DEFAULT,
22 },
23 ) {
24 Ok(rt) => rt,
25 Err(e) => {
26 eprintln!("runtime: {e}");
27 std::process::exit(1);
28 }
29 };
30 let Some(boom) = rt.function_index("boom") else {
31 eprintln!("missing boom");
32 std::process::exit(1);
33 };
34 let sup = match Supervisor::with_config(
35 rt.spawner(),
36 SupervisorConfig {
37 max_restarts: 2,
38 max_period: Duration::from_secs(5),
39 },
40 ) {
41 Ok(s) => s,
42 Err(e) => {
43 eprintln!("supervisor: {e}");
44 std::process::exit(1);
45 }
46 };
47 if let Err(e) = sup.start_child(ChildSpec::new("boom", boom).restart(RestartPolicy::OnFailure))
48 {
49 eprintln!("start_child: {e}");
50 std::process::exit(1);
51 }
52
53 let start = std::time::Instant::now();
54 while !sup.intensity_exceeded() {
55 if start.elapsed() > Duration::from_secs(2) {
56 eprintln!("supervisor did not hit intensity in time");
57 std::process::exit(1);
58 }
59 thread::sleep(Duration::from_millis(5));
60 }
61
62 let metrics = rt.metrics();
63 println!("intensity exceeded after {} failures", metrics.processes_failed);
64 println!("{metrics}");
65 sup.shutdown();
66 rt.shutdown();
67}pub fn live_children(&self) -> usize
Sourcepub fn intensity_exceeded(&self) -> bool
pub fn intensity_exceeded(&self) -> bool
true once more than SupervisorConfig::max_restarts respawns
landed inside the intensity window. Remaining children keep
running; we just stop bringing them back (no safe abort of a
mid-quantum flow).
Examples found in repository?
15fn main() {
16 let rt = match Runtime::with_config(
17 samples::boom(),
18 RuntimeConfig {
19 workers: 1,
20 quantum: 1_000,
21 mailbox: byteflow::MailboxConfig::DEFAULT,
22 },
23 ) {
24 Ok(rt) => rt,
25 Err(e) => {
26 eprintln!("runtime: {e}");
27 std::process::exit(1);
28 }
29 };
30 let Some(boom) = rt.function_index("boom") else {
31 eprintln!("missing boom");
32 std::process::exit(1);
33 };
34 let sup = match Supervisor::with_config(
35 rt.spawner(),
36 SupervisorConfig {
37 max_restarts: 2,
38 max_period: Duration::from_secs(5),
39 },
40 ) {
41 Ok(s) => s,
42 Err(e) => {
43 eprintln!("supervisor: {e}");
44 std::process::exit(1);
45 }
46 };
47 if let Err(e) = sup.start_child(ChildSpec::new("boom", boom).restart(RestartPolicy::OnFailure))
48 {
49 eprintln!("start_child: {e}");
50 std::process::exit(1);
51 }
52
53 let start = std::time::Instant::now();
54 while !sup.intensity_exceeded() {
55 if start.elapsed() > Duration::from_secs(2) {
56 eprintln!("supervisor did not hit intensity in time");
57 std::process::exit(1);
58 }
59 thread::sleep(Duration::from_millis(5));
60 }
61
62 let metrics = rt.metrics();
63 println!("intensity exceeded after {} failures", metrics.processes_failed);
64 println!("{metrics}");
65 sup.shutdown();
66 rt.shutdown();
67}Sourcepub fn shutdown(self)
pub fn shutdown(self)
Stop the drive thread. Does not terminate live children — they belong to the runtime, not to us.
Examples found in repository?
15fn main() {
16 let rt = match Runtime::with_config(
17 samples::boom(),
18 RuntimeConfig {
19 workers: 1,
20 quantum: 1_000,
21 mailbox: byteflow::MailboxConfig::DEFAULT,
22 },
23 ) {
24 Ok(rt) => rt,
25 Err(e) => {
26 eprintln!("runtime: {e}");
27 std::process::exit(1);
28 }
29 };
30 let Some(boom) = rt.function_index("boom") else {
31 eprintln!("missing boom");
32 std::process::exit(1);
33 };
34 let sup = match Supervisor::with_config(
35 rt.spawner(),
36 SupervisorConfig {
37 max_restarts: 2,
38 max_period: Duration::from_secs(5),
39 },
40 ) {
41 Ok(s) => s,
42 Err(e) => {
43 eprintln!("supervisor: {e}");
44 std::process::exit(1);
45 }
46 };
47 if let Err(e) = sup.start_child(ChildSpec::new("boom", boom).restart(RestartPolicy::OnFailure))
48 {
49 eprintln!("start_child: {e}");
50 std::process::exit(1);
51 }
52
53 let start = std::time::Instant::now();
54 while !sup.intensity_exceeded() {
55 if start.elapsed() > Duration::from_secs(2) {
56 eprintln!("supervisor did not hit intensity in time");
57 std::process::exit(1);
58 }
59 thread::sleep(Duration::from_millis(5));
60 }
61
62 let metrics = rt.metrics();
63 println!("intensity exceeded after {} failures", metrics.processes_failed);
64 println!("{metrics}");
65 sup.shutdown();
66 rt.shutdown();
67}