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::ShutdownRequested;
10use crate::protocol::forward::forward_event_lane;
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    Behavior(E),
17    ShutdownRequested(ShutdownRequested),
18}
19
20impl<E: UserEvent> crate::RouteInput<ShutdownRequested> for ShutdownProtocol<E> {
21    fn route(event: ShutdownRequested) -> Result<Self, ShutdownRequested> {
22        Ok(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::Behavior(E::user(from, message))
38    }
39
40    fn into_user(self) -> Result<User<Self::Addr, Self::Message>, Self> {
41        match self {
42            Self::Behavior(event) => event.into_user().map_err(Self::Behavior),
43            shutdown @ Self::ShutdownRequested(_) => Err(shutdown),
44        }
45    }
46}
47
48forward_event_lane!(ShutdownProtocol, crate::TimerElapsed);
49forward_event_lane!(ShutdownProtocol, crate::PeerStopped<E::Addr>);
50forward_event_lane!(ShutdownProtocol, crate::ChildStopped<E::Addr>);
51forward_event_lane!(ShutdownProtocol, crate::WorkerStopped<E::Addr>);
52forward_event_lane!(
53    ShutdownProtocol,
54    crate::CreationResolved<<E::Addr as crate::Address>::Nonce>
55);
56forward_event_lane!(
57    ShutdownProtocol,
58    crate::WorkerCreationResolved<<E::Addr as crate::Address>::Nonce>
59);
60
61/// Stop normally when the shutdown lane is received.
62pub struct StopOnShutdown<B> {
63    inner: B,
64}
65
66impl<B> StopOnShutdown<B> {
67    #[must_use]
68    pub(crate) fn new(inner: B) -> Self {
69        Self { inner }
70    }
71}
72
73impl<B: Behavior + crate::BehaviorBase> crate::BehaviorBase for StopOnShutdown<B> {
74    type Base = B::Base;
75
76    fn base(&self) -> &Self::Base {
77        self.inner.base()
78    }
79}
80
81impl<B: crate::StashStatus> crate::StashStatus for StopOnShutdown<B> {
82    fn stashed_messages(&self) -> usize {
83        self.inner.stashed_messages()
84    }
85}
86
87/// A final shutdown fold. Its sends and fresh creations are retained, while
88/// its become verdict is replaced with `Stop(Normal)`.
89pub type ShutdownReaction<B> = fn(
90    &mut B,
91    ShutdownRequested,
92) -> Result<
93    Actions<
94        <B as Behavior>::Addr,
95        <B as Behavior>::Ph,
96        <B as Behavior>::Sends,
97        <B as Behavior>::Birth,
98    >,
99    <B as Behavior>::Error,
100>;
101
102/// Run one explicit final fold and then stop normally.
103pub struct FinalizeOnShutdown<B: Behavior> {
104    inner: B,
105    finalize: ShutdownReaction<B>,
106}
107
108impl<B: Behavior> FinalizeOnShutdown<B> {
109    #[must_use]
110    pub(crate) fn new(inner: B, finalize: ShutdownReaction<B>) -> Self {
111        Self { inner, finalize }
112    }
113}
114
115impl<B: Behavior + crate::BehaviorBase> crate::BehaviorBase for FinalizeOnShutdown<B> {
116    type Base = B::Base;
117
118    fn base(&self) -> &Self::Base {
119        self.inner.base()
120    }
121}
122
123impl<B> crate::StashStatus for FinalizeOnShutdown<B>
124where
125    B: Behavior + crate::StashStatus,
126{
127    fn stashed_messages(&self) -> usize {
128        self.inner.stashed_messages()
129    }
130}
131
132macro_rules! impl_shutdown_behavior {
133    ($wrapper:ident, $shutdown:expr) => {
134        impl<B, A, Ph, Sends, Br> Behavior for $wrapper<B>
135        where
136            A: Address,
137            Sends: SendAlgebra,
138            Br: BirthMode,
139            B: Behavior<Addr = A, Ph = Ph, Sends = Sends, Birth = Br>,
140        {
141            type Addr = A;
142            type Msg = B::Msg;
143            type Event = ShutdownProtocol<B::Event>;
144            type Sends = Sends;
145            type Ph = Ph;
146            type Error = B::Error;
147            type Birth = Br;
148
149            fn init(
150                &mut self,
151                _: crate::InitializationTurn,
152            ) -> Result<Actions<A, Ph, Sends, Br>, B::Error> {
153                crate::calculus::initialize(&mut self.inner)
154            }
155
156            fn transition(
157                &mut self,
158                _: crate::ActiveTurn,
159                event: Self::Event,
160            ) -> Result<Actions<A, Ph, Sends, Br>, B::Error> {
161                match event {
162                    ShutdownProtocol::Behavior(event) => {
163                        crate::calculus::delegate_transition(&mut self.inner, event)
164                    }
165                    ShutdownProtocol::ShutdownRequested(request) => $shutdown(self, request),
166                }
167            }
168        }
169    };
170}
171
172impl_shutdown_behavior!(StopOnShutdown, |_this: &mut StopOnShutdown<B>, _request| {
173    Ok(Actions::stop(Exit::Normal))
174});
175
176impl_shutdown_behavior!(
177    FinalizeOnShutdown,
178    |this: &mut FinalizeOnShutdown<B>, request| {
179        let actions = (this.finalize)(&mut this.inner, request)?;
180        Ok(actions.map_become(|_| Step::Stop(Exit::Normal)))
181    }
182);