umsh_ulcp_runtime/
reply_completion.rs1use core::cell::RefCell;
7use embassy_sync::{
8 blocking_mutex::{Mutex, raw::CriticalSectionRawMutex},
9 signal::Signal,
10};
11use embassy_time::{Instant, with_deadline};
12
13#[derive(Default)]
14struct State {
15 next: u32,
16 active: Option<(u32, Option<bool>)>,
17}
18
19impl State {
20 fn begin(&mut self) -> u32 {
21 self.next = self.next.wrapping_add(1);
22 self.active = Some((self.next, None));
23 self.next
24 }
25
26 fn complete(&mut self, id: u32, sent: bool) -> bool {
27 if let Some((active, result)) = &mut self.active {
28 if *active == id && result.is_none() {
29 *result = Some(sent);
30 return sent;
31 }
32 }
33 false
34 }
35}
36
37static STATE: Mutex<CriticalSectionRawMutex, RefCell<State>> = Mutex::new(RefCell::new(State {
38 next: 0,
39 active: None,
40}));
41static CHANGED: Signal<CriticalSectionRawMutex, ()> = Signal::new();
42
43#[derive(Clone, Copy, Debug)]
44pub struct ReplyCompletion {
45 id: u32,
46 deadline: Instant,
47}
48
49impl ReplyCompletion {
50 pub(crate) fn begin(deadline: Instant) -> Self {
51 let id = STATE.lock(|state| state.borrow_mut().begin());
52 Self { id, deadline }
53 }
54
55 pub fn deadline(self) -> Instant {
56 self.deadline
57 }
58
59 pub fn is_pending(self) -> bool {
61 Instant::now() < self.deadline
62 && STATE.lock(|state| state.borrow().active == Some((self.id, None)))
63 }
64
65 pub fn complete(self, sent: bool) -> bool {
69 let accepted = STATE.lock(|state| {
70 state
71 .borrow_mut()
72 .complete(self.id, sent && Instant::now() < self.deadline)
73 });
74 CHANGED.signal(());
75 accepted
76 }
77
78 pub(crate) async fn wait(self) -> bool {
79 loop {
80 let result = STATE.lock(|state| {
81 let state = state.borrow();
82 match state.active {
83 Some((id, result)) if id == self.id => result,
84 _ => Some(false),
85 }
86 });
87 if let Some(sent) = result {
88 STATE.lock(|state| {
89 let mut state = state.borrow_mut();
90 if state.active.is_some_and(|(id, _)| id == self.id) {
91 state.active = None;
92 }
93 });
94 return sent;
95 }
96 if with_deadline(self.deadline, CHANGED.wait()).await.is_err() {
97 self.complete(false);
98 }
99 }
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn only_the_current_response_can_complete_once() {
109 let mut state = State::default();
110 let first = state.begin();
111 assert_eq!(state.active, Some((first, None)));
112 state.complete(first, false);
113 state.complete(first, true);
114 assert_eq!(state.active, Some((first, Some(false))));
115 let second = state.begin();
116 state.complete(first, true);
117 assert_eq!(state.active, Some((second, None)));
118 state.complete(second, true);
119 state.complete(second, false);
120 assert_eq!(state.active, Some((second, Some(true))));
121 }
122}