Skip to main content

behavior/
shutdown.rs

1//! Typed graceful-shutdown composition.
2//!
3//! Shutdown is a Bombay policy expressed as an ordinary behavior transition,
4//! not an additional actor-model effect. An interpreter may construct the
5//! shutdown lane, but ingress closure and mailbox ordering remain interpreter
6//! concerns.
7
8use crate::behavior::{Actions, Address, Behavior, BirthMode, SendAlgebra, User, UserEvent};
9use crate::protocol::forward::forward_event_lane;
10use crate::protocol::{ShutdownEvent, ShutdownRequested};
11use crate::{Exit, Step};
12
13/// The complete protocol of a behavior that supports graceful shutdown.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum ShutdownProtocol<E> {
16    Inner(E),
17    ShutdownRequested(ShutdownRequested),
18}
19
20impl<E: UserEvent> ShutdownEvent for ShutdownProtocol<E> {
21    fn shutdown_requested(event: ShutdownRequested) -> Option<Self> {
22        Some(Self::ShutdownRequested(event))
23    }
24}
25
26impl<E: UserEvent> crate::EventInput<ShutdownRequested> for ShutdownProtocol<E> {
27    fn inject(event: ShutdownRequested) -> Self {
28        Self::ShutdownRequested(event)
29    }
30}
31
32impl<E: UserEvent> UserEvent for ShutdownProtocol<E> {
33    type Addr = E::Addr;
34    type Message = E::Message;
35
36    fn user(from: Self::Addr, message: Self::Message) -> Self {
37        Self::Inner(E::user(from, message))
38    }
39
40    fn into_user(self) -> Result<User<Self::Addr, Self::Message>, Self> {
41        match self {
42            Self::Inner(event) => event.into_user().map_err(Self::Inner),
43            shutdown @ Self::ShutdownRequested(_) => Err(shutdown),
44        }
45    }
46}
47
48forward_event_lane!(
49    ShutdownProtocol,
50    TimeEvent,
51    time_reached,
52    crate::TimerElapsed
53);
54forward_event_lane!(
55    ShutdownProtocol,
56    PeerEvent,
57    peer_stopped,
58    crate::PeerStopped<E::Addr>
59);
60forward_event_lane!(
61    ShutdownProtocol,
62    ChildEvent,
63    child_stopped,
64    crate::ChildStopped<E::Addr>
65);
66forward_event_lane!(
67    ShutdownProtocol,
68    WorkerEvent,
69    worker_stopped,
70    crate::WorkerStopped<E::Addr>
71);
72forward_event_lane!(
73    ShutdownProtocol,
74    CreationEvent,
75    creation_resolved,
76    crate::CreationResolved<<E::Addr as crate::Address>::Nonce>
77);
78forward_event_lane!(
79    ShutdownProtocol,
80    WorkerCreationEvent,
81    worker_creation_resolved,
82    crate::WorkerCreationResolved<<E::Addr as crate::Address>::Nonce>
83);
84
85/// Stop normally when the shutdown lane is received.
86pub struct StopOnShutdown<B> {
87    inner: B,
88}
89
90impl<B> StopOnShutdown<B> {
91    #[must_use]
92    pub fn new(inner: B) -> Self {
93        Self { inner }
94    }
95
96    #[must_use]
97    pub fn inner(&self) -> &B {
98        &self.inner
99    }
100}
101
102/// A final shutdown fold. Its sends and fresh creations are retained, while
103/// its become verdict is replaced with `Stop(Normal)`.
104pub type ShutdownReaction<B> = fn(
105    &mut B,
106    ShutdownRequested,
107) -> Result<
108    Actions<
109        <B as Behavior>::Addr,
110        <B as Behavior>::Ph,
111        <B as Behavior>::Sends,
112        <B as Behavior>::Birth,
113    >,
114    <B as Behavior>::Error,
115>;
116
117/// Run one explicit final fold and then stop normally.
118pub struct FinalizeOnShutdown<B: Behavior> {
119    inner: B,
120    finalize: ShutdownReaction<B>,
121}
122
123impl<B: Behavior> FinalizeOnShutdown<B> {
124    #[must_use]
125    pub fn new(inner: B, finalize: ShutdownReaction<B>) -> Self {
126        Self { inner, finalize }
127    }
128
129    #[must_use]
130    pub fn inner(&self) -> &B {
131        &self.inner
132    }
133}
134
135macro_rules! impl_shutdown_behavior {
136    ($wrapper:ident, $shutdown:expr) => {
137        impl<B, A, Ph, Sends, Br> Behavior for $wrapper<B>
138        where
139            A: Address,
140            Sends: SendAlgebra,
141            Br: BirthMode,
142            B: Behavior<Addr = A, Ph = Ph, Sends = Sends, Birth = Br>,
143        {
144            type Addr = A;
145            type Msg = B::Msg;
146            type Event = ShutdownProtocol<B::Event>;
147            type Sends = Sends;
148            type Ph = Ph;
149            type Error = B::Error;
150            type Birth = Br;
151
152            fn init(&mut self) -> Result<Actions<A, Ph, Sends, Br>, B::Error> {
153                self.inner.init()
154            }
155
156            fn transition(
157                &mut self,
158                event: Self::Event,
159            ) -> Result<Actions<A, Ph, Sends, Br>, B::Error> {
160                match event {
161                    ShutdownProtocol::Inner(event) => self.inner.transition(event),
162                    ShutdownProtocol::ShutdownRequested(request) => $shutdown(self, request),
163                }
164            }
165        }
166    };
167}
168
169impl_shutdown_behavior!(StopOnShutdown, |_this: &mut StopOnShutdown<B>, _request| {
170    Ok(Actions::stop(Exit::Normal))
171});
172
173impl_shutdown_behavior!(
174    FinalizeOnShutdown,
175    |this: &mut FinalizeOnShutdown<B>, request| {
176        let actions = (this.finalize)(&mut this.inner, request)?;
177        Ok(actions.map_become(|_| Step::Stop(Exit::Normal)))
178    }
179);