Skip to main content

behavior_actors/
termination.rs

1//! Typed lifecycle outcomes used by reusable actor compositions.
2
3use behavior::{InterpretationProgress, ItemSettlement, finish_item, prepare_item};
4
5use behavior::Address;
6
7/// The authoritative terminal fact for one exact actor incarnation.
8///
9/// Successful lifecycle classification and execution failure are disjoint
10/// outcomes.  Keeping the complete sum intact prevents compositions from
11/// reconstructing provenance from a stop verdict, address reuse, or an
12/// adjacent diagnostic.
13pub type TerminalOutcome<A> = Result<Exit<A>, Crash>;
14
15/// A successfully observed actor termination.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum Exit<A: Address> {
18    /// The behavior explicitly designated termination.
19    Normal,
20    /// The Bombay runtime collected an actor after its sources were exhausted.
21    Collected,
22    /// A watch composition propagated a linked peer's death.
23    LinkDied(A),
24    /// A supervisor could no longer preserve its child topology.
25    SupervisionFailed(SupervisionFailureReason),
26}
27
28/// Why a supervisor could no longer preserve its child topology.
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum SupervisionFailureReason {
31    RestartDenied(RestartDenial),
32    StableChildStopped,
33    StableChildCreationRejected(behavior::CreationRejection),
34    WorkerFactoryRejected,
35    WorkerCreationRejected(behavior::CreationRejection),
36}
37
38/// Why an otherwise eligible replacement set was denied.
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum RestartDenial {
41    BudgetExceeded {
42        restarts_in_window: usize,
43        replacements_requested: usize,
44        maximum_restarts: u32,
45    },
46    /// The configured release policy rejected the next delay.
47    ReleaseRejected(crate::RestartReleaseFailure),
48    /// The per-trigger restart-attempt sequence could not advance.
49    AttemptSequenceExhausted,
50    /// The per-trigger timer generation could not advance.
51    TimerGenerationExhausted,
52    /// No fresh local restart-timer identity remained.
53    TimerIdentityExhausted,
54}
55
56/// Why execution terminated without a behavior-requested stop.
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub enum Crash {
59    Failed,
60    EnvironmentFailed,
61    /// Live actor execution selected a capability task failure as its stop cause.
62    /// Full failure and recoverable actor-state custody belongs to the runtime.
63    CapabilityFailed,
64    Panicked,
65    Cancelled,
66}
67
68/// Ask the interpreter to publish one exact terminal outcome for the
69/// emitting incarnation before interpreting the same action's terminal
70/// verdict.
71///
72/// This is a Bombay lifecycle-publication policy, not an actor-model
73/// primitive.  It is an explicit effect so a pure composition can propagate
74/// an authoritative child or peer fact without placing lifecycle provenance
75/// in [`behavior::Step`] or using an ambient runtime side channel.
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub struct ReportTerminalOutcome<A: Address> {
78    pub outcome: TerminalOutcome<A>,
79}
80
81impl<A: Address> ReportTerminalOutcome<A> {
82    #[must_use]
83    pub const fn new(outcome: TerminalOutcome<A>) -> Self {
84        Self { outcome }
85    }
86}
87
88impl<A: Address> behavior::InterpreterRequest for ReportTerminalOutcome<A> {
89    type ReturnToEmitter = behavior::NoReturnToEmitter;
90    type LogicalProtocols = behavior::NoBirthProtocols;
91}
92
93impl<A> behavior::ActionItem for ReportTerminalOutcome<A>
94where
95    A: Address + Send,
96{
97    type Custody = (Option<Self>, Option<Self::Reply>);
98    type Input<'a>
99        = &'a mut Option<Self>
100    where
101        Self: 'a;
102    type Reply = ItemSettlement<Self, Self::Accepted, Self::Rejection, Self::Prerequisite>;
103
104    fn prepare_interpretation(
105        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
106    ) {
107        prepare_item::<Self>(progress);
108    }
109
110    fn interpretation_input<'a>(
111        custody: &'a mut Self::Custody,
112    ) -> Option<(Self::Input<'a>, &'a mut Option<Self::Reply>)>
113    where
114        Self: 'a,
115    {
116        match custody {
117            (input @ Some(_), received @ None) => Some((input, received)),
118            _ => None,
119        }
120    }
121
122    fn finish_interpretation(
123        progress: &mut Option<InterpretationProgress<Self, Self::Custody, Self::Reply>>,
124    ) {
125        finish_item::<Self>(progress);
126    }
127
128    type Accepted = ();
129    type Rejection = behavior::Never;
130    type Prerequisite = behavior::Never;
131}