Skip to main content

sim_lib_control/
coroutine.rs

1use sim_kernel::Ref;
2
3/// Identifies which of a coroutine's two cooperating lanes yielded a value.
4#[derive(Clone, Copy, Debug, PartialEq, Eq)]
5pub enum CoroutineLane {
6    /// The first lane.
7    First,
8    /// The second lane.
9    Second,
10}
11
12/// Outcome of resuming a [`Coroutine`]: a yielded value, or exhaustion.
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub enum CoroutineStep {
15    /// A lane yielded a value and control returned to the driver.
16    Yielded {
17        /// The lane that produced this value.
18        lane: CoroutineLane,
19        /// The yielded value.
20        value: Ref,
21    },
22    /// Both lanes are drained; the coroutine has nothing left to yield.
23    Exhausted,
24}
25
26/// Two cooperating value streams that yield by alternating between lanes.
27///
28/// Models symmetric coroutine control: each [`Coroutine::resume`] hands control
29/// to the next lane, falling through to the other when one is drained.
30///
31/// # Examples
32///
33/// ```
34/// use sim_kernel::{Ref, Symbol};
35/// use sim_lib_control::{Coroutine, CoroutineLane, CoroutineStep};
36///
37/// let a = Ref::Symbol(Symbol::new("a"));
38/// let b = Ref::Symbol(Symbol::new("b"));
39/// let mut co = Coroutine::alternating(vec![a.clone()], vec![b.clone()]);
40/// assert_eq!(
41///     co.resume(),
42///     CoroutineStep::Yielded { lane: CoroutineLane::First, value: a }
43/// );
44/// assert_eq!(
45///     co.resume(),
46///     CoroutineStep::Yielded { lane: CoroutineLane::Second, value: b }
47/// );
48/// assert_eq!(co.resume(), CoroutineStep::Exhausted);
49/// ```
50#[derive(Clone, Debug, PartialEq, Eq)]
51pub struct Coroutine {
52    first: Vec<Ref>,
53    second: Vec<Ref>,
54    first_index: usize,
55    second_index: usize,
56    next_lane: CoroutineLane,
57}
58
59impl Coroutine {
60    /// Builds a coroutine that alternates yields between the `first` and
61    /// `second` lanes, starting with the first.
62    pub fn alternating(first: Vec<Ref>, second: Vec<Ref>) -> Self {
63        Self {
64            first,
65            second,
66            first_index: 0,
67            second_index: 0,
68            next_lane: CoroutineLane::First,
69        }
70    }
71
72    /// Resumes the coroutine, yielding the next value from the active lane (or
73    /// the other lane if the active one is drained), or
74    /// [`CoroutineStep::Exhausted`] when both are empty.
75    pub fn resume(&mut self) -> CoroutineStep {
76        let step = match self.next_lane {
77            CoroutineLane::First => self.resume_first().or_else(|| self.resume_second()),
78            CoroutineLane::Second => self.resume_second().or_else(|| self.resume_first()),
79        };
80        step.unwrap_or(CoroutineStep::Exhausted)
81    }
82
83    fn resume_first(&mut self) -> Option<CoroutineStep> {
84        let value = self.first.get(self.first_index).cloned()?;
85        self.first_index += 1;
86        self.next_lane = CoroutineLane::Second;
87        Some(CoroutineStep::Yielded {
88            lane: CoroutineLane::First,
89            value,
90        })
91    }
92
93    fn resume_second(&mut self) -> Option<CoroutineStep> {
94        let value = self.second.get(self.second_index).cloned()?;
95        self.second_index += 1;
96        self.next_lane = CoroutineLane::First;
97        Some(CoroutineStep::Yielded {
98            lane: CoroutineLane::Second,
99            value,
100        })
101    }
102}