1use crate::client::{Client, ClientConfig};
35use crate::host_identity::HostExpectation;
36use crate::private::PrivateChild;
37use rightkit_child_process::{
38 AdoptedIdentity, Generation, HealthOutcome, HealthPolicy, HealthProbe, HealthTracker,
39 RestartOutcome, RestartPermit, RestartPolicy, RestartTracker,
40};
41use serde_json::json;
42use std::io;
43use std::sync::mpsc::{self, RecvTimeoutError};
44use std::sync::{Arc, Mutex};
45use std::time::{Duration, Instant};
46
47pub const DEFAULT_POLL_INTERVAL: Duration = Duration::from_secs(1);
49pub const DEFAULT_STOP_GRACE: Duration = Duration::from_secs(5);
51
52const MIN_POLL: Duration = Duration::from_millis(10);
53
54#[derive(Debug, Clone, Copy)]
56pub struct SupervisorConfig {
57 restart: RestartPolicy,
58 health: HealthPolicy,
59 poll_interval: Duration,
60 stop_grace: Duration,
61}
62
63impl SupervisorConfig {
64 pub fn new(restart: RestartPolicy, health: HealthPolicy) -> Self {
67 Self {
68 restart,
69 health,
70 poll_interval: DEFAULT_POLL_INTERVAL,
71 stop_grace: DEFAULT_STOP_GRACE,
72 }
73 }
74
75 #[must_use]
78 pub fn poll_interval(mut self, interval: Duration) -> Self {
79 self.poll_interval = interval.max(MIN_POLL);
80 self
81 }
82
83 #[must_use]
86 pub fn stop_grace(mut self, grace: Duration) -> Self {
87 self.stop_grace = grace;
88 self
89 }
90}
91
92#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub enum SupervisorState {
95 Starting,
97 Ready,
99 WaitingRestart,
101 AwaitingDemand,
103 Exhausted,
105 Stopped,
107}
108
109#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub struct SupervisorStatus {
112 pub state: SupervisorState,
113 pub launches: u64,
115 pub pid: Option<u32>,
117}
118
119#[derive(Debug, Clone, PartialEq, Eq)]
121pub enum SupervisorEvent {
122 Started {
123 launch: u64,
124 pid: u32,
125 },
126 Ready {
128 launch: u64,
129 },
130 Exited {
132 launch: u64,
133 code: Option<i32>,
134 success: bool,
135 },
136 Unhealthy {
138 launch: u64,
139 consecutive_failures: u32,
140 },
141 StartupTimedOut {
143 launch: u64,
144 },
145 LaunchFailed {
147 error: String,
148 },
149 RestartScheduled {
150 delay: Duration,
151 },
152 AwaitingDemand,
153 Exhausted,
155 Stopped,
157 StopUnconfirmed {
160 launch: u64,
161 pid: u32,
162 },
163 Adopted {
166 launch: u64,
167 pid: u32,
168 },
169 ForeignExited {
171 launch: u64,
172 pid: u32,
173 },
174}
175
176pub type SupervisorEventFn = Arc<dyn Fn(&SupervisorEvent) + Send + Sync>;
178
179#[derive(Debug, Clone, Copy, PartialEq, Eq)]
185pub enum LockState {
186 Locked,
187 Unlocked,
188 Unknown,
189}
190
191impl LockState {
192 pub fn starts_locked(self) -> bool {
195 !matches!(self, LockState::Unlocked)
196 }
197}
198
199pub type LockStateFn = Arc<dyn Fn() -> LockState + Send + Sync>;
201
202#[derive(Debug, Clone, Copy, PartialEq, Eq)]
205pub struct ExitInfo {
206 pub launch: u64,
208 pub code: Option<i32>,
210 pub success: bool,
211}
212
213#[derive(Debug)]
215#[non_exhaustive]
216pub enum ExitAction {
217 Default,
219 Final,
224 AdoptForeign(AdoptedIdentity),
229}
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq)]
233pub struct StartGateInfo {
234 pub next_launch: u64,
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq)]
240pub enum StartGate {
241 Proceed,
243 Wait(Duration),
246 Stop,
248}
249
250type ExitActionHook = Box<dyn FnMut(&ExitInfo) -> ExitAction + Send>;
251type StartGateHook = Box<dyn FnMut(&StartGateInfo) -> StartGate + Send>;
252
253#[derive(Default)]
256pub struct SupervisorHooks {
257 pub exit_action: Option<ExitActionHook>,
259 pub start_gate: Option<StartGateHook>,
261}
262
263enum Cmd {
264 Stop,
265 RestartNow(Option<LockState>),
266 ReturnChild(mpsc::Sender<Option<PrivateChild>>),
267}
268
269pub struct SupervisorHandle {
271 tx: mpsc::Sender<Cmd>,
272 status: Arc<Mutex<SupervisorStatus>>,
273 exits: Arc<Mutex<ExitRecord>>,
274 thread: Option<std::thread::JoinHandle<()>>,
275}
276
277#[derive(Default)]
278struct ExitRecord {
279 last: Option<ExitInfo>,
280 final_exit: Option<ExitInfo>,
281}
282
283impl SupervisorHandle {
284 pub fn last_exit(&self) -> Option<ExitInfo> {
286 self.exits.lock().unwrap_or_else(|e| e.into_inner()).last
287 }
288
289 pub fn final_exit(&self) -> Option<ExitInfo> {
292 self.exits
293 .lock()
294 .unwrap_or_else(|e| e.into_inner())
295 .final_exit
296 }
297
298 pub fn status(&self) -> SupervisorStatus {
300 *self.status.lock().unwrap_or_else(|e| e.into_inner())
301 }
302
303 pub fn restart_now(&self) {
308 let _ = self.tx.send(Cmd::RestartNow(None));
309 }
310
311 pub fn restart_now_with_lock(&self, state: LockState) {
315 let _ = self.tx.send(Cmd::RestartNow(Some(state)));
316 }
317
318 pub fn stop(mut self) {
321 self.stop_inner();
322 }
323
324 pub fn stop_returning_child(mut self) -> Option<PrivateChild> {
329 let (tx, rx) = mpsc::channel();
330 if self.tx.send(Cmd::ReturnChild(tx)).is_err() {
331 return None;
332 }
333 if let Some(thread) = self.thread.take() {
334 let _ = thread.join();
335 }
336 rx.recv().ok().flatten()
337 }
338
339 fn stop_inner(&mut self) {
340 let _ = self.tx.send(Cmd::Stop);
341 if let Some(thread) = self.thread.take() {
342 let _ = thread.join();
343 }
344 }
345}
346
347impl Drop for SupervisorHandle {
348 fn drop(&mut self) {
349 self.stop_inner();
350 }
351}
352
353pub fn supervise<L, P>(
358 config: SupervisorConfig,
359 mut launch: L,
360 probe: P,
361 on_event: Option<SupervisorEventFn>,
362) -> io::Result<SupervisorHandle>
363where
364 L: FnMut() -> io::Result<PrivateChild> + Send + 'static,
365 P: FnMut(&PrivateChild) -> bool + Send + 'static,
366{
367 spawn_core(
368 config,
369 move |_locked| launch(),
370 probe,
371 None,
372 None,
373 SupervisorHooks::default(),
374 on_event,
375 )
376}
377
378pub fn supervise_with_hooks<L, P>(
382 config: SupervisorConfig,
383 launch: L,
384 probe: P,
385 lock_state: Option<LockStateFn>,
386 hooks: SupervisorHooks,
387 on_event: Option<SupervisorEventFn>,
388) -> io::Result<SupervisorHandle>
389where
390 L: FnMut(bool) -> io::Result<PrivateChild> + Send + 'static,
391 P: FnMut(&PrivateChild) -> bool + Send + 'static,
392{
393 spawn_core(config, launch, probe, None, lock_state, hooks, on_event)
394}
395
396pub fn supervise_with_lock_state<L, P>(
403 config: SupervisorConfig,
404 launch: L,
405 probe: P,
406 lock_state: Option<LockStateFn>,
407 on_event: Option<SupervisorEventFn>,
408) -> io::Result<SupervisorHandle>
409where
410 L: FnMut(bool) -> io::Result<PrivateChild> + Send + 'static,
411 P: FnMut(&PrivateChild) -> bool + Send + 'static,
412{
413 spawn_core(
414 config,
415 launch,
416 probe,
417 None,
418 lock_state,
419 SupervisorHooks::default(),
420 on_event,
421 )
422}
423
424pub fn supervise_adopted<L, P>(
430 config: SupervisorConfig,
431 child: PrivateChild,
432 launch: L,
433 probe: P,
434 lock_state: Option<LockStateFn>,
435 on_event: Option<SupervisorEventFn>,
436) -> io::Result<SupervisorHandle>
437where
438 L: FnMut(bool) -> io::Result<PrivateChild> + Send + 'static,
439 P: FnMut(&PrivateChild) -> bool + Send + 'static,
440{
441 spawn_core(
442 config,
443 launch,
444 probe,
445 Some(child),
446 lock_state,
447 SupervisorHooks::default(),
448 on_event,
449 )
450}
451
452fn spawn_core<L, P>(
453 config: SupervisorConfig,
454 launch: L,
455 probe: P,
456 adopted: Option<PrivateChild>,
457 lock_state: Option<LockStateFn>,
458 hooks: SupervisorHooks,
459 on_event: Option<SupervisorEventFn>,
460) -> io::Result<SupervisorHandle>
461where
462 L: FnMut(bool) -> io::Result<PrivateChild> + Send + 'static,
463 P: FnMut(&PrivateChild) -> bool + Send + 'static,
464{
465 let (tx, rx) = mpsc::channel();
466 let exits = Arc::new(Mutex::new(ExitRecord::default()));
467 let status = Arc::new(Mutex::new(SupervisorStatus {
468 state: SupervisorState::Starting,
469 launches: 0,
470 pid: None,
471 }));
472 let core = Core {
473 cfg: config,
474 launch,
475 probe,
476 tracker: RestartTracker::new(config.restart),
477 origin: Instant::now(),
478 launches: 0,
479 status: status.clone(),
480 on_event,
481 phase: Phase::Idle(SupervisorState::Stopped),
482 adopted,
483 lock_state,
484 pending_locked: None,
485 hooks,
486 exits: exits.clone(),
487 };
488 let thread = std::thread::Builder::new()
489 .name("rk-supervisor".into())
490 .spawn(move || core.run(rx))?;
491 Ok(SupervisorHandle {
492 tx,
493 status,
494 exits,
495 thread: Some(thread),
496 })
497}
498
499pub fn ping_probe(
504 config: ClientConfig,
505 expect: Option<HostExpectation>,
506 timeout: Duration,
507) -> impl FnMut(&PrivateChild) -> bool + Send + 'static {
508 move |child| {
509 let config = config
510 .clone()
511 .secret(child.secret().clone())
512 .deadline(Some(timeout));
513 let client = match &expect {
514 Some(expect) => Client::connect_expecting(&config, expect).ok(),
515 None => Client::connect_config(&config).ok(),
516 };
517 match client {
518 Some(client) => matches!(
519 client.call("ping", json!({})),
520 Ok(value) if value["pong"] == true
521 ),
522 None => false,
523 }
524 }
525}
526
527struct Running {
528 child: PrivateChild,
529 generation: Generation,
530 health: HealthTracker,
531 ready: bool,
532 launch: u64,
533}
534
535enum Phase {
536 Running(Box<Running>),
537 Waiting(RestartPermit, Instant),
539 Idle(SupervisorState),
541 Gated(Instant),
543 Unconfirmed(Box<Running>, bool),
546 Foreign(Box<ForeignRun>),
548}
549
550struct ForeignRun {
551 identity: AdoptedIdentity,
552 generation: Generation,
553 launch: u64,
554}
555
556struct Core<L, P> {
557 cfg: SupervisorConfig,
558 launch: L,
559 probe: P,
560 tracker: RestartTracker,
561 origin: Instant,
562 launches: u64,
563 status: Arc<Mutex<SupervisorStatus>>,
564 on_event: Option<SupervisorEventFn>,
565 phase: Phase,
566 adopted: Option<PrivateChild>,
567 lock_state: Option<LockStateFn>,
568 pending_locked: Option<bool>,
570 hooks: SupervisorHooks,
571 exits: Arc<Mutex<ExitRecord>>,
572}
573
574impl<L, P> Core<L, P>
575where
576 L: FnMut(bool) -> io::Result<PrivateChild>,
577 P: FnMut(&PrivateChild) -> bool,
578{
579 fn at(&self) -> Duration {
580 self.origin.elapsed()
581 }
582
583 fn emit(&self, event: SupervisorEvent) {
584 if let Some(f) = &self.on_event {
585 f(&event);
586 }
587 }
588
589 fn set_status(&self, state: SupervisorState, pid: Option<u32>) {
590 let mut s = self.status.lock().unwrap_or_else(|e| e.into_inner());
591 s.state = state;
592 s.launches = self.launches;
593 s.pid = pid;
594 }
595
596 fn run(mut self, rx: mpsc::Receiver<Cmd>) {
597 match self.adopted.take() {
598 Some(child) => self.adopt(child),
599 None => self.start(),
600 }
601 loop {
602 let wait = match &self.phase {
603 Phase::Waiting(_, due) | Phase::Gated(due) => due
604 .saturating_duration_since(Instant::now())
605 .min(self.cfg.poll_interval),
606 _ => self.cfg.poll_interval,
607 };
608 match rx.recv_timeout(wait) {
609 Ok(Cmd::RestartNow(state)) => self.restart_now(state),
610 Ok(Cmd::ReturnChild(reply)) => {
611 self.return_child(reply);
612 return;
613 }
614 Ok(Cmd::Stop) | Err(RecvTimeoutError::Disconnected) => {
615 self.shutdown();
616 return;
617 }
618 Err(RecvTimeoutError::Timeout) => self.tick(),
619 }
620 }
621 }
622
623 fn adopt(&mut self, child: PrivateChild) {
626 let at = self.at();
627 let generation = self.tracker.started(at);
628 self.tracker.ready(generation);
629 self.launches += 1;
630 let pid = child.child().id();
631 let mut health = HealthTracker::new(self.cfg.health, at);
632 let _ = health.observe(at, HealthProbe::Success);
633 self.set_status(SupervisorState::Ready, Some(pid));
634 self.emit(SupervisorEvent::Started {
635 launch: self.launches,
636 pid,
637 });
638 self.emit(SupervisorEvent::Ready {
639 launch: self.launches,
640 });
641 self.phase = Phase::Running(Box::new(Running {
642 child,
643 generation,
644 health,
645 ready: true,
646 launch: self.launches,
647 }));
648 }
649
650 fn next_locked(&mut self) -> bool {
653 if let Some(locked) = self.pending_locked.take() {
654 return locked;
655 }
656 match &self.lock_state {
657 Some(f) => f().starts_locked(),
658 None => false,
659 }
660 }
661
662 fn start(&mut self) {
664 if let Some(gate) = self.hooks.start_gate.as_mut() {
665 match gate(&StartGateInfo {
666 next_launch: self.launches + 1,
667 }) {
668 StartGate::Proceed => {}
669 StartGate::Wait(wait) => {
670 self.set_status(SupervisorState::WaitingRestart, None);
671 self.phase = Phase::Gated(Instant::now() + wait.max(MIN_POLL));
672 return;
673 }
674 StartGate::Stop => {
675 self.tracker.shutdown();
676 self.emit(SupervisorEvent::Stopped);
677 self.set_status(SupervisorState::Stopped, None);
678 self.phase = Phase::Idle(SupervisorState::Stopped);
679 return;
680 }
681 }
682 }
683 let locked = self.next_locked();
684 match (self.launch)(locked) {
685 Ok(child) => {
686 let at = self.at();
687 let generation = self.tracker.started(at);
688 self.launches += 1;
689 let pid = child.child().id();
690 self.set_status(SupervisorState::Starting, Some(pid));
691 self.emit(SupervisorEvent::Started {
692 launch: self.launches,
693 pid,
694 });
695 self.phase = Phase::Running(Box::new(Running {
696 child,
697 generation,
698 health: HealthTracker::new(self.cfg.health, at),
699 ready: false,
700 launch: self.launches,
701 }));
702 }
703 Err(error) => {
704 self.emit(SupervisorEvent::LaunchFailed {
705 error: error.to_string(),
706 });
707 let outcome = self.tracker.request_restart(self.at());
708 self.apply(outcome);
709 }
710 }
711 }
712
713 fn apply(&mut self, outcome: RestartOutcome) {
714 match outcome {
715 RestartOutcome::Restart(permit) => {
716 let delay = permit.delay();
717 self.emit(SupervisorEvent::RestartScheduled { delay });
718 self.set_status(SupervisorState::WaitingRestart, None);
719 self.phase = Phase::Waiting(permit, Instant::now() + delay);
720 }
721 RestartOutcome::AwaitingDemand => {
722 self.emit(SupervisorEvent::AwaitingDemand);
723 self.set_status(SupervisorState::AwaitingDemand, None);
724 self.phase = Phase::Idle(SupervisorState::AwaitingDemand);
725 }
726 RestartOutcome::Exhausted => {
727 self.emit(SupervisorEvent::Exhausted);
728 self.set_status(SupervisorState::Exhausted, None);
729 self.phase = Phase::Idle(SupervisorState::Exhausted);
730 }
731 RestartOutcome::Stopped | RestartOutcome::IgnoredStale => {
734 self.emit(SupervisorEvent::Stopped);
735 self.set_status(SupervisorState::Stopped, None);
736 self.phase = Phase::Idle(SupervisorState::Stopped);
737 }
738 }
739 }
740
741 fn tick(&mut self) {
742 let placeholder = Phase::Idle(SupervisorState::Stopped);
743 match std::mem::replace(&mut self.phase, placeholder) {
744 Phase::Running(running) => self.tick_running(*running),
745 Phase::Waiting(permit, due) => {
746 if Instant::now() >= due && self.tracker.permit_valid(&permit, self.at()) {
747 self.start();
748 } else {
749 self.phase = Phase::Waiting(permit, due);
750 }
751 }
752 Phase::Gated(due) => {
753 if Instant::now() >= due {
754 self.start();
755 } else {
756 self.phase = Phase::Gated(due);
757 }
758 }
759 Phase::Unconfirmed(running, demand) => self.tick_unconfirmed(*running, demand),
760 Phase::Foreign(foreign) => self.tick_foreign(*foreign),
761 idle @ Phase::Idle(_) => self.phase = idle,
762 }
763 }
764
765 fn tick_unconfirmed(&mut self, mut running: Running, demand: bool) {
767 let gone = matches!(running.child.child_mut().try_wait(), Ok(Some(_)))
768 || self.stop_child(&mut running.child);
769 if gone {
770 self.settle(running, demand);
771 } else {
772 self.phase = Phase::Unconfirmed(Box::new(running), demand);
773 }
774 }
775
776 fn tick_foreign(&mut self, foreign: ForeignRun) {
779 if matches!(foreign.identity.is_running(), Ok(true) | Err(_)) {
780 self.phase = Phase::Foreign(Box::new(foreign));
781 return;
782 }
783 self.emit(SupervisorEvent::ForeignExited {
784 launch: foreign.launch,
785 pid: foreign.identity.pid(),
786 });
787 let outcome = self.tracker.exited(foreign.generation, self.at());
788 self.apply(outcome);
789 }
790
791 fn settle(&mut self, running: Running, demand: bool) {
794 drop(running.child);
795 let at = self.at();
796 let mut outcome = self.tracker.exited(running.generation, at);
797 if demand && outcome == RestartOutcome::AwaitingDemand {
798 outcome = self.tracker.request_restart(at);
799 }
800 self.apply(outcome);
801 }
802
803 fn stop_and_settle(&mut self, mut running: Running, demand: bool) {
805 if self.stop_child(&mut running.child) {
806 self.settle(running, demand);
807 } else {
808 let pid = running.child.child().id();
809 self.emit(SupervisorEvent::StopUnconfirmed {
810 launch: running.launch,
811 pid,
812 });
813 self.set_status(SupervisorState::WaitingRestart, Some(pid));
814 self.phase = Phase::Unconfirmed(Box::new(running), demand);
815 }
816 }
817
818 fn tick_running(&mut self, mut running: Running) {
819 match running.child.child_mut().try_wait() {
820 Ok(Some(status)) => {
821 self.emit(SupervisorEvent::Exited {
822 launch: running.launch,
823 code: status.code(),
824 success: status.success(),
825 });
826 let info = ExitInfo {
827 launch: running.launch,
828 code: status.code(),
829 success: status.success(),
830 };
831 self.exits.lock().unwrap_or_else(|e| e.into_inner()).last = Some(info);
832 let action = match self.hooks.exit_action.as_mut() {
833 Some(decide) => decide(&info),
834 None => ExitAction::Default,
835 };
836 drop(running.child);
838 match action {
839 ExitAction::Final => {
840 self.exits
841 .lock()
842 .unwrap_or_else(|e| e.into_inner())
843 .final_exit = Some(info);
844 self.tracker.shutdown();
845 self.emit(SupervisorEvent::Stopped);
846 self.set_status(SupervisorState::Stopped, None);
847 self.phase = Phase::Idle(SupervisorState::Stopped);
848 }
849 ExitAction::AdoptForeign(identity) => {
850 let pid = identity.pid();
851 self.emit(SupervisorEvent::Adopted {
852 launch: running.launch,
853 pid,
854 });
855 self.set_status(SupervisorState::Ready, Some(pid));
856 self.phase = Phase::Foreign(Box::new(ForeignRun {
857 identity,
858 generation: running.generation,
859 launch: running.launch,
860 }));
861 }
862 _ => {
863 let outcome = self.tracker.exited(running.generation, self.at());
864 self.apply(outcome);
865 }
866 }
867 return;
868 }
869 Ok(None) => {}
870 Err(_) => {
871 self.stop_and_settle(running, false);
873 return;
874 }
875 }
876 let answered = (self.probe)(&running.child);
877 let at = self.at();
878 let outcome = running.health.observe(
879 at,
880 if answered {
881 HealthProbe::Success
882 } else {
883 HealthProbe::Failure
884 },
885 );
886 if answered && !running.ready {
887 running.ready = true;
888 self.tracker.ready(running.generation);
889 self.set_status(SupervisorState::Ready, Some(running.child.child().id()));
890 self.emit(SupervisorEvent::Ready {
891 launch: running.launch,
892 });
893 }
894 let failure = match outcome {
895 HealthOutcome::StartupTimedOut => Some(SupervisorEvent::StartupTimedOut {
896 launch: running.launch,
897 }),
898 HealthOutcome::Unhealthy {
900 consecutive_failures,
901 } if running.ready => Some(SupervisorEvent::Unhealthy {
902 launch: running.launch,
903 consecutive_failures,
904 }),
905 _ => None,
906 };
907 match failure {
908 None => self.phase = Phase::Running(Box::new(running)),
909 Some(event) => {
910 self.emit(event);
911 self.stop_and_settle(running, false);
912 }
913 }
914 }
915
916 fn stop_child(&self, child: &mut PrivateChild) -> bool {
919 child.close_stdin();
920 match child.child_mut().wait_then_kill(self.cfg.stop_grace) {
921 Ok(_) => true,
922 Err(_) => matches!(child.child_mut().try_wait(), Ok(Some(_))),
923 }
924 }
925
926 fn restart_now(&mut self, state: Option<LockState>) {
927 if let Some(state) = state {
928 self.pending_locked = Some(state.starts_locked());
929 }
930 let placeholder = Phase::Idle(SupervisorState::Stopped);
931 match std::mem::replace(&mut self.phase, placeholder) {
932 Phase::Running(running) => self.stop_and_settle(*running, true),
934 Phase::Unconfirmed(running, _) => self.phase = Phase::Unconfirmed(running, true),
935 held @ (Phase::Waiting(..) | Phase::Gated(_) | Phase::Foreign(_)) => self.phase = held,
937 Phase::Idle(SupervisorState::AwaitingDemand) => {
938 match self.tracker.request_restart(self.at()) {
939 RestartOutcome::AwaitingDemand => self.start(),
941 other => self.apply(other),
942 }
943 }
944 Phase::Idle(_) => {
945 self.tracker.reset_budget();
946 self.start();
947 }
948 }
949 }
950
951 fn return_child(&mut self, reply: mpsc::Sender<Option<PrivateChild>>) {
954 self.tracker.shutdown();
955 let child = match std::mem::replace(&mut self.phase, Phase::Idle(SupervisorState::Stopped))
956 {
957 Phase::Running(running) => Some(running.child),
958 _ => None,
959 };
960 self.set_status(SupervisorState::Stopped, None);
961 self.emit(SupervisorEvent::Stopped);
962 let _ = reply.send(child);
963 }
964
965 fn shutdown(&mut self) {
966 self.tracker.shutdown();
967 match std::mem::replace(&mut self.phase, Phase::Idle(SupervisorState::Stopped)) {
968 Phase::Running(mut running) | Phase::Unconfirmed(mut running, _) => {
969 self.stop_child(&mut running.child);
970 }
971 _ => {}
972 }
973 self.set_status(SupervisorState::Stopped, None);
974 self.emit(SupervisorEvent::Stopped);
975 }
976}