Skip to main content

behavior/timing/
deadline.rs

1//! Pure one-shot time composition. Scheduling is a request to the emitting
2//! actor's local clock service.
3
4use std::time::Instant;
5
6use super::domain::OneShotSchedule;
7use super::event::TimedEvent;
8use crate::Step;
9use crate::behavior::{Actions, Address, Become, Behavior, BirthMode, SendAlgebra, ServiceSends};
10use crate::protocol::{ScheduleAt, TimerElapsed, TimerId};
11use crate::{Own, RouteInput, SendInput};
12
13pub type DeadlineEvent<E> = TimedEvent<E>;
14
15pub type DeadlineReaction<B> =
16    fn(&mut B) -> Result<Become<<B as Behavior>::Addr>, <B as Behavior>::Error>;
17
18/// Named effect lanes added by [`Deadline`].
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct DeadlineSends<Sends> {
21    pub behavior: Sends,
22    pub schedules: ServiceSends<ScheduleAt>,
23}
24
25impl<Sends: SendAlgebra> SendAlgebra for DeadlineSends<Sends> {
26    fn empty() -> Self {
27        Self {
28            behavior: Sends::empty(),
29            schedules: ServiceSends::empty(),
30        }
31    }
32
33    fn append(&mut self, other: Self) {
34        self.behavior.append(other.behavior);
35        self.schedules.append(other.schedules);
36    }
37}
38
39impl<Sends> SendInput<ScheduleAt, Own> for DeadlineSends<Sends> {
40    fn emit(&mut self, input: ScheduleAt) {
41        self.schedules.send(input);
42    }
43}
44
45pub(crate) type DeadlineActions<B> = Actions<
46    <B as Behavior>::Addr,
47    <B as Behavior>::Ph,
48    DeadlineSends<<B as Behavior>::Sends>,
49    <B as Behavior>::Birth,
50>;
51
52pub struct Deadline<B: Behavior> {
53    inner: B,
54    schedule: OneShotSchedule,
55    on_reached: DeadlineReaction<B>,
56}
57
58impl<B: Behavior> Deadline<B> {
59    #[must_use]
60    pub(crate) fn new(
61        inner: B,
62        id: TimerId,
63        at: Option<Instant>,
64        on_reached: DeadlineReaction<B>,
65    ) -> Self {
66        Self {
67            inner,
68            schedule: OneShotSchedule::new(id, at),
69            on_reached,
70        }
71    }
72}
73
74impl<B: Behavior + crate::BehaviorBase> crate::BehaviorBase for Deadline<B> {
75    type Base = B::Base;
76
77    fn base(&self) -> &Self::Base {
78        self.inner.base()
79    }
80}
81
82impl<B> crate::StashStatus for Deadline<B>
83where
84    B: Behavior + crate::StashStatus,
85{
86    fn stashed_messages(&self) -> usize {
87        self.inner.stashed_messages()
88    }
89}
90
91impl<B, A, Ph, Sends, Br> Behavior for Deadline<B>
92where
93    A: Address,
94    Sends: SendAlgebra,
95    Br: BirthMode,
96    B: Behavior<Addr = A, Ph = Ph, Sends = Sends, Birth = Br>,
97    B::Event: crate::RouteInput<TimerElapsed>,
98{
99    type Addr = A;
100    type Msg = B::Msg;
101    type Event = DeadlineEvent<B::Event>;
102    type Sends = DeadlineSends<Sends>;
103    type Ph = Ph;
104    type Error = B::Error;
105    type Birth = Br;
106
107    fn init(&mut self, _: crate::InitializationTurn) -> Result<DeadlineActions<B>, B::Error> {
108        let actions = crate::calculus::initialize(&mut self.inner)?;
109        let own = if matches!(actions.become_, Step::Stop(_)) {
110            self.schedule.cancel();
111            ServiceSends::empty()
112        } else {
113            self.schedule
114                .request()
115                .map_or_else(ServiceSends::empty, |(id, generation, at)| {
116                    ServiceSends::one(ScheduleAt::new(id, generation, at))
117                })
118        };
119        Ok(Self::wrap(actions, own))
120    }
121
122    fn transition(
123        &mut self,
124        _: crate::ActiveTurn,
125        event: Self::Event,
126    ) -> Result<DeadlineActions<B>, B::Error> {
127        match event {
128            DeadlineEvent::Elapsed(event) if self.schedule.accept(event.id, event.generation) => {
129                let become_ = match (self.on_reached)(&mut self.inner)? {
130                    Step::Continue => Step::Continue,
131                    Step::Goto(never) => match never {},
132                    Step::Stop(exit) => Step::Stop(exit),
133                };
134                Ok(Actions::just(become_))
135            }
136            DeadlineEvent::Elapsed(event) => match B::Event::route(event) {
137                Ok(inner) => {
138                    let actions = crate::calculus::delegate_transition(&mut self.inner, inner)?;
139                    if matches!(actions.become_, Step::Stop(_)) {
140                        self.schedule.cancel();
141                    }
142                    Ok(Self::wrap(actions, ServiceSends::empty()))
143                }
144                Err(_) => Ok(Actions::cont()),
145            },
146            DeadlineEvent::Behavior(event) => {
147                let actions = crate::calculus::delegate_transition(&mut self.inner, event)?;
148                if matches!(actions.become_, Step::Stop(_)) {
149                    self.schedule.cancel();
150                }
151                Ok(Self::wrap(actions, ServiceSends::empty()))
152            }
153        }
154    }
155}
156
157impl<B: Behavior> Deadline<B> {
158    fn wrap(
159        actions: Actions<B::Addr, B::Ph, B::Sends, B::Birth>,
160        own: ServiceSends<ScheduleAt>,
161    ) -> DeadlineActions<B> {
162        actions.map_sends(|behavior| DeadlineSends {
163            behavior,
164            schedules: own,
165        })
166    }
167}