Skip to main content

rustdv_sim/
sync.rs

1//! Sim-aware synchronization primitives: `Event` and FIFO-fair `Lock`
2//! (design-doc mapping row 17 โ€” std/tokio primitives don't know sim time).
3
4use std::cell::{Cell, RefCell};
5use std::collections::VecDeque;
6use std::future::Future;
7use std::pin::Pin;
8use std::rc::Rc;
9use std::task::{Context, Poll, Waker};
10
11// ---------------------------------------------------------------------------
12// Event: manual-reset event (cocotb: _base_triggers.py, _Event)
13// ---------------------------------------------------------------------------
14
15struct EventInner {
16    is_set: Cell<bool>,
17    waiters: RefCell<Vec<Waker>>,
18}
19
20/// Manual-reset event; `wait()` completes immediately if already set.
21#[derive(Clone)]
22pub struct Event {
23    inner: Rc<EventInner>,
24}
25
26impl Event {
27    #[allow(clippy::new_without_default)]
28    pub fn new() -> Event {
29        Event {
30            inner: Rc::new(EventInner {
31                is_set: Cell::new(false),
32                waiters: RefCell::new(Vec::new()),
33            }),
34        }
35    }
36
37    pub fn set(&self) {
38        self.inner.is_set.set(true);
39        for w in self.inner.waiters.borrow_mut().drain(..) {
40            w.wake();
41        }
42    }
43
44    pub fn clear(&self) {
45        self.inner.is_set.set(false);
46    }
47
48    pub fn is_set(&self) -> bool {
49        self.inner.is_set.get()
50    }
51
52    pub fn wait(&self) -> EventWait {
53        EventWait {
54            inner: self.inner.clone(),
55        }
56    }
57}
58
59pub struct EventWait {
60    inner: Rc<EventInner>,
61}
62
63impl Future for EventWait {
64    type Output = ();
65    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
66        if self.inner.is_set.get() {
67            Poll::Ready(())
68        } else {
69            self.inner.waiters.borrow_mut().push(cx.waker().clone());
70            Poll::Pending
71        }
72    }
73}
74
75// ---------------------------------------------------------------------------
76// Lock: FIFO-fair mutex (cocotb Lock docstring: "Guarantees fair scheduling")
77// ---------------------------------------------------------------------------
78
79struct LockWaiter {
80    granted: Cell<bool>,
81    waker: RefCell<Option<Waker>>,
82}
83
84struct LockInner {
85    locked: Cell<bool>,
86    waiters: RefCell<VecDeque<Rc<LockWaiter>>>,
87}
88
89impl LockInner {
90    /// Pass ownership to the next waiter, or unlock.
91    fn release(&self) {
92        match self.waiters.borrow_mut().pop_front() {
93            Some(w) => {
94                w.granted.set(true);
95                if let Some(waker) = w.waker.borrow_mut().take() {
96                    waker.wake();
97                }
98                // Ownership transferred (still locked).
99            }
100            None => {
101                self.locked.set(false);
102            }
103        }
104    }
105}
106
107#[derive(Clone)]
108pub struct Lock {
109    inner: Rc<LockInner>,
110}
111
112impl Lock {
113    #[allow(clippy::new_without_default)]
114    pub fn new() -> Lock {
115        Lock {
116            inner: Rc::new(LockInner {
117                locked: Cell::new(false),
118                waiters: RefCell::new(VecDeque::new()),
119            }),
120        }
121    }
122
123    pub fn locked(&self) -> bool {
124        self.inner.locked.get()
125    }
126
127    pub fn acquire(&self) -> Acquire {
128        Acquire {
129            inner: self.inner.clone(),
130            waiter: None,
131            acquired: false,
132        }
133    }
134}
135
136pub struct Acquire {
137    inner: Rc<LockInner>,
138    waiter: Option<Rc<LockWaiter>>,
139    acquired: bool,
140}
141
142impl Future for Acquire {
143    type Output = LockGuard;
144    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<LockGuard> {
145        match &self.waiter {
146            None => {
147                if !self.inner.locked.get() {
148                    self.inner.locked.set(true);
149                    self.acquired = true;
150                    Poll::Ready(LockGuard {
151                        inner: self.inner.clone(),
152                    })
153                } else {
154                    let w = Rc::new(LockWaiter {
155                        granted: Cell::new(false),
156                        waker: RefCell::new(Some(cx.waker().clone())),
157                    });
158                    self.inner.waiters.borrow_mut().push_back(w.clone());
159                    self.waiter = Some(w);
160                    Poll::Pending
161                }
162            }
163            Some(w) => {
164                if w.granted.get() {
165                    self.acquired = true;
166                    Poll::Ready(LockGuard {
167                        inner: self.inner.clone(),
168                    })
169                } else {
170                    *w.waker.borrow_mut() = Some(cx.waker().clone());
171                    Poll::Pending
172                }
173            }
174        }
175    }
176}
177
178impl Drop for Acquire {
179    fn drop(&mut self) {
180        if self.acquired {
181            return; // guard owns the lock now
182        }
183        if let Some(w) = &self.waiter {
184            if w.granted.get() {
185                // Granted but never polled (task cancelled): pass it on.
186                self.inner.release();
187            } else {
188                // Remove ourselves from the queue.
189                self.inner
190                    .waiters
191                    .borrow_mut()
192                    .retain(|x| !Rc::ptr_eq(x, w));
193            }
194        }
195    }
196}
197
198/// RAII lock guard; drop releases (fair handoff to the next waiter).
199pub struct LockGuard {
200    inner: Rc<LockInner>,
201}
202
203impl Drop for LockGuard {
204    fn drop(&mut self) {
205        self.inner.release();
206    }
207}
208
209// ===========================================================================
210// Tests โ€” no simulator.
211// ===========================================================================
212
213#[cfg(test)]
214mod tests {
215    use super::*;
216    use crate::executor;
217    use crate::testing::{assert_pending, block_on};
218    use std::cell::RefCell;
219    use std::rc::Rc;
220
221    #[test]
222    fn wait_before_set_wakes() {
223        block_on(async {
224            let ev = Event::new();
225            let setter = ev.clone();
226            executor::spawn(async move {
227                setter.set();
228            });
229            ev.wait().await; // returns only because the spawned task set it
230        });
231    }
232
233    /// Events are **level**-triggered with an explicit `clear`, not edge:
234    /// `is_set` is sticky, so a `set` that happens before anyone waits is
235    /// still there when they arrive. This is what makes the sequencer
236    /// handshake safe โ€” a driver may grant an item before the sequence gets
237    /// round to waiting for the grant, and the sequence still proceeds.
238    #[test]
239    fn set_latches_until_cleared() {
240        block_on(async {
241            let ev = Event::new();
242            ev.set();
243            ev.wait().await; // returns at once: the set is remembered
244            assert!(ev.is_set());
245        });
246    }
247
248    #[test]
249    fn clear_makes_a_later_wait_block_again() {
250        let ev = Event::new();
251        ev.set();
252        ev.clear();
253        assert!(!ev.is_set());
254        assert_pending(async move { ev.wait().await });
255    }
256
257    #[test]
258    fn one_set_wakes_every_waiter() {
259        block_on(async {
260            let ev = Event::new();
261            let woken = Rc::new(RefCell::new(0));
262            for _ in 0..3 {
263                let (e, w) = (ev.clone(), woken.clone());
264                executor::spawn(async move {
265                    e.wait().await;
266                    *w.borrow_mut() += 1;
267                });
268            }
269            // Let the three waiters register before firing.
270            crate::executor::current().run_until_idle();
271            ev.set();
272            crate::executor::current().run_until_idle();
273            assert_eq!(*woken.borrow(), 3, "all three saw one set");
274        });
275    }
276
277    #[test]
278    fn lock_is_fifo_fair() {
279        block_on(async {
280            let lock = Lock::new();
281            let order = Rc::new(RefCell::new(Vec::new()));
282
283            let held = lock.acquire().await; // the test holds it first
284
285            for who in 1..=3 {
286                let (l, o) = (lock.clone(), order.clone());
287                executor::spawn(async move {
288                    let _g = l.acquire().await;
289                    o.borrow_mut().push(who);
290                });
291                // Each spawn runs far enough to queue for the lock, in order.
292                crate::executor::current().run_until_idle();
293            }
294
295            drop(held);
296            crate::executor::current().run_until_idle();
297            assert_eq!(*order.borrow(), vec![1, 2, 3], "granted in request order");
298        });
299    }
300}