Skip to main content

rustdv_methodology/
shared.rs

1//! `RustdvShared<T>`: state two components can both see (D88).
2//!
3//! Analysis delivery has to be synchronous — the publisher calls `write` and
4//! every subscriber's handler runs before control comes back, with no
5//! `await` and no simulation time passing. But a subscriber's handler needs
6//! `&mut subscriber` while the publisher's `run` holds `&mut publisher`, and
7//! siblings in the tree cannot reach each other.
8//!
9//! The way out is to share the **state**, not the component. A subscriber
10//! keeps its tally in a `RustdvShared<T>`, hands a second handle to its port,
11//! and both see the same data. Delivery then mutates the tally without ever
12//! touching the component.
13//!
14//! The name is deliberate. This is not a general Rust facility — someone who
15//! goes looking for `Shared<T>` in the standard library will not find it — so
16//! it wears the framework's name and says where it comes from.
17
18use std::cell::{Ref, RefCell, RefMut};
19use std::fmt;
20use std::rc::Rc;
21
22/// A handle to state shared between a component and its analysis port.
23///
24/// Cloning gives another handle to the *same* state, the way `Rc` does — it
25/// does not copy the data.
26pub struct RustdvShared<T> {
27    inner: Rc<RefCell<T>>,
28}
29
30impl<T> RustdvShared<T> {
31    pub fn new(value: T) -> RustdvShared<T> {
32        RustdvShared {
33            inner: Rc::new(RefCell::new(value)),
34        }
35    }
36
37    /// Read the shared state.
38    ///
39    /// The guard borrows at run time, so holding one across a call that also
40    /// reads is fine and holding one across a call that *writes* panics. Keep
41    /// the guard short — usually one line, as in
42    /// `let seen = self.tally.get();`.
43    pub fn get(&self) -> Ref<'_, T> {
44        self.inner.borrow()
45    }
46
47    /// Modify the shared state.
48    pub fn get_mut(&self) -> RefMut<'_, T> {
49        self.inner.borrow_mut()
50    }
51
52    /// How many handles point at this state — the component's, the port's,
53    /// and any others.
54    pub fn handle_count(&self) -> usize {
55        Rc::strong_count(&self.inner)
56    }
57}
58
59impl<T> Clone for RustdvShared<T> {
60    fn clone(&self) -> Self {
61        RustdvShared {
62            inner: self.inner.clone(),
63        }
64    }
65}
66
67impl<T: Default> Default for RustdvShared<T> {
68    fn default() -> Self {
69        RustdvShared::new(T::default())
70    }
71}
72
73impl<T: fmt::Debug> fmt::Debug for RustdvShared<T> {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        write!(f, "RustdvShared({:?})", self.inner.borrow())
76    }
77}