1use crate::{CreationKind, CreationRejection, CreationResolved};
4
5enum IncarnationState<N, C> {
7 Dormant {
8 initial: C,
9 },
10 Installing {
11 attempt: N,
12 kind: CreationKind<N>,
13 },
14 Running {
15 incarnation: N,
16 queued_replacement: Option<C>,
17 },
18 Vacant {
19 last_installed: Option<N>,
20 },
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum IncarnationPhase<N> {
26 Dormant,
27 Installing { attempt: N, kind: CreationKind<N> },
28 Running { incarnation: N },
29 AwaitingStop { incarnation: N },
30 Vacant { last_installed: Option<N> },
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
34pub(crate) enum IncarnationError {
35 #[error("the incarnation lifecycle was already initialized")]
36 AlreadyInitialized,
37 #[error("the incarnation creation-attempt sequence is exhausted")]
38 AttemptSequenceExhausted,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
43pub(crate) struct IncarnationCreation<N, C> {
44 pub attempt: N,
45 pub kind: CreationKind<N>,
46 pub child: C,
47}
48
49impl<N, C> IncarnationCreation<N, C> {
50 #[must_use]
51 pub const fn new(attempt: N, kind: CreationKind<N>, child: C) -> Self {
52 Self {
53 attempt,
54 kind,
55 child,
56 }
57 }
58}
59
60#[derive(Debug, Clone, PartialEq, Eq)]
65pub(crate) struct IncarnationEffects<N, C, M> {
66 pub creation: Option<IncarnationCreation<N, C>>,
67 pub delivery: Option<(N, M)>,
68 pub creation_report: Option<CreationResolved<N>>,
69}
70
71#[derive(Debug, Clone, PartialEq, Eq)]
76pub(crate) struct IncarnationStopEffects<N, C> {
77 pub creation: Option<IncarnationCreation<N, C>>,
78 pub stopped: Option<N>,
79}
80
81impl<N, C, M> IncarnationEffects<N, C, M> {
82 #[must_use]
83 pub fn new(
84 creation: Option<IncarnationCreation<N, C>>,
85 delivery: Option<(N, M)>,
86 creation_report: Option<CreationResolved<N>>,
87 ) -> Self {
88 Self {
89 creation,
90 delivery,
91 creation_report,
92 }
93 }
94
95 #[must_use]
96 pub fn none() -> Self {
97 Self {
98 creation: None,
99 delivery: None,
100 creation_report: None,
101 }
102 }
103}
104
105pub(crate) struct Incarnation<N, C> {
107 state: IncarnationState<N, C>,
108 next_attempt: u64,
109}
110
111impl<N, C> Incarnation<N, C> {
112 #[must_use]
113 pub const fn new(initial: C) -> Self {
114 Self {
115 state: IncarnationState::Dormant { initial },
116 next_attempt: 0,
117 }
118 }
119}
120
121impl<N: Copy, C> Incarnation<N, C> {
122 #[must_use]
123 pub const fn phase(&self) -> IncarnationPhase<N> {
124 match &self.state {
125 IncarnationState::Dormant { .. } => IncarnationPhase::Dormant,
126 IncarnationState::Installing { attempt, kind } => IncarnationPhase::Installing {
127 attempt: *attempt,
128 kind: *kind,
129 },
130 IncarnationState::Running {
131 incarnation,
132 queued_replacement: Some(_),
133 } => IncarnationPhase::AwaitingStop {
134 incarnation: *incarnation,
135 },
136 IncarnationState::Running {
137 incarnation,
138 queued_replacement: None,
139 } => IncarnationPhase::Running {
140 incarnation: *incarnation,
141 },
142 IncarnationState::Vacant { last_installed } => IncarnationPhase::Vacant {
143 last_installed: *last_installed,
144 },
145 }
146 }
147}
148
149impl<N: Copy + From<u64> + PartialEq, C> Incarnation<N, C> {
150 pub(crate) fn initialize<M>(
156 &mut self,
157 ) -> Result<IncarnationEffects<N, C, M>, IncarnationError> {
158 let previous = core::mem::replace(
159 &mut self.state,
160 IncarnationState::Vacant {
161 last_installed: None,
162 },
163 );
164 match previous {
165 IncarnationState::Dormant { initial } => self.begin(initial, CreationKind::Birth),
166 state => {
167 self.state = state;
168 Err(IncarnationError::AlreadyInitialized)
169 }
170 }
171 }
172
173 fn begin<M>(
174 &mut self,
175 child: C,
176 kind: CreationKind<N>,
177 ) -> Result<IncarnationEffects<N, C, M>, IncarnationError> {
178 let attempt = N::from(self.next_attempt);
179 self.next_attempt = self
180 .next_attempt
181 .checked_add(1)
182 .ok_or(IncarnationError::AttemptSequenceExhausted)?;
183 self.state = IncarnationState::Installing { attempt, kind };
184 Ok(IncarnationEffects::new(
185 Some(IncarnationCreation::new(attempt, kind, child)),
186 None,
187 None,
188 ))
189 }
190
191 pub(crate) fn creation_resolved<M>(
192 &mut self,
193 attempt: N,
194 kind: CreationKind<N>,
195 result: Result<(), CreationRejection>,
196 ) -> IncarnationEffects<N, C, M> {
197 let IncarnationState::Installing {
198 attempt: pending,
199 kind: pending_kind,
200 } = self.state
201 else {
202 return IncarnationEffects::none();
203 };
204 if attempt != pending || kind != pending_kind {
205 return IncarnationEffects::none();
206 }
207 self.state = match result {
208 Ok(()) => IncarnationState::Running {
209 incarnation: attempt,
210 queued_replacement: None,
211 },
212 Err(_) => IncarnationState::Vacant {
213 last_installed: match kind {
214 CreationKind::Birth => None,
215 CreationKind::ReplacementIncarnation { replaces } => Some(replaces),
216 },
217 },
218 };
219 IncarnationEffects::new(
220 None,
221 None,
222 Some(CreationResolved::new(attempt, kind, result)),
223 )
224 }
225
226 pub(crate) fn child_stopped(
227 &mut self,
228 stopped: N,
229 ) -> Result<IncarnationStopEffects<N, C>, IncarnationError> {
230 let IncarnationState::Running { incarnation, .. } = self.state else {
231 return Ok(IncarnationStopEffects {
232 creation: None,
233 stopped: None,
234 });
235 };
236 if stopped != incarnation {
237 return Ok(IncarnationStopEffects {
238 creation: None,
239 stopped: None,
240 });
241 }
242 let previous = core::mem::replace(
243 &mut self.state,
244 IncarnationState::Vacant {
245 last_installed: Some(incarnation),
246 },
247 );
248 let IncarnationState::Running {
249 incarnation,
250 queued_replacement,
251 } = previous
252 else {
253 self.state = previous;
254 return Ok(IncarnationStopEffects {
255 creation: None,
256 stopped: None,
257 });
258 };
259 let creation = match queued_replacement {
260 Some(child) => {
261 self.begin::<()>(
262 child,
263 CreationKind::ReplacementIncarnation {
264 replaces: incarnation,
265 },
266 )?
267 .creation
268 }
269 None => None,
270 };
271 Ok(IncarnationStopEffects {
272 creation,
273 stopped: Some(incarnation),
274 })
275 }
276
277 pub(crate) fn forward<M>(&self, message: M) -> IncarnationEffects<N, C, M> {
278 let delivery = match self.state {
279 IncarnationState::Running { incarnation, .. } => Some((incarnation, message)),
280 IncarnationState::Dormant { .. }
281 | IncarnationState::Installing { .. }
282 | IncarnationState::Vacant { .. } => None,
283 };
284 IncarnationEffects::new(None, delivery, None)
285 }
286
287 pub(crate) fn replace<M>(
288 &mut self,
289 child: C,
290 ) -> Result<IncarnationEffects<N, C, M>, IncarnationError> {
291 Ok(match &mut self.state {
292 IncarnationState::Running {
293 queued_replacement: queued_replacement @ None,
294 ..
295 } => {
296 *queued_replacement = Some(child);
297 IncarnationEffects::none()
298 }
299 IncarnationState::Vacant {
300 last_installed: Some(last),
301 } => {
302 let replaces = *last;
303 return self.begin(child, CreationKind::ReplacementIncarnation { replaces });
304 }
305 IncarnationState::Dormant { .. }
306 | IncarnationState::Installing { .. }
307 | IncarnationState::Running { .. }
308 | IncarnationState::Vacant {
309 last_installed: None,
310 } => IncarnationEffects::none(),
311 })
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn rejected_attempt_preserves_successful_provenance() {
321 let mut machine = Incarnation::<u64, &'static str>::new("first");
322 machine.initialize::<()>().unwrap();
323 machine.creation_resolved::<()>(0, CreationKind::Birth, Ok(()));
324 machine.replace::<()>("second").unwrap();
325 machine.child_stopped(0).unwrap();
326 machine.creation_resolved::<()>(
327 1,
328 CreationKind::ReplacementIncarnation { replaces: 0 },
329 Err(CreationRejection::EnvironmentFailed),
330 );
331
332 assert_eq!(
333 machine.phase(),
334 IncarnationPhase::Vacant {
335 last_installed: Some(0)
336 }
337 );
338 let effects = machine.replace::<()>("third").unwrap();
339 let creation = effects.creation.expect("replacement begins");
340 assert_eq!(creation.attempt, 2);
341 assert_eq!(
342 creation.kind,
343 CreationKind::ReplacementIncarnation { replaces: 0 }
344 );
345 }
346
347 #[test]
348 fn stale_inputs_are_inert() {
349 let mut machine = Incarnation::<u64, ()>::new(());
350 machine.initialize::<()>().unwrap();
351 let effects = machine.creation_resolved::<()>(9, CreationKind::Birth, Ok(()));
352 assert!(effects.creation.is_none());
353 assert!(effects.delivery.is_none());
354 assert!(effects.creation_report.is_none());
355 assert_eq!(
356 machine.phase(),
357 IncarnationPhase::Installing {
358 attempt: 0,
359 kind: CreationKind::Birth
360 }
361 );
362 }
363}