port_expander/
mutex.rs

1/// Common interface for mutex implementations.
2///
3/// `port-expander` needs a mutex to ensure only a single pin object can access the port-expander at the same time,
4/// in concurrent situations.  `port-expander` already implements this trait for a number of existing
5/// mutex types.  Most of them are guarded by a feature that needs to be enabled.  Here is an
6/// overview:
7///
8/// | Mutex | Feature Name | Notes |
9/// | --- | --- | --- |
10/// | [`core::cell::RefCell`] | _always available_ | For sharing within a single execution context. |
11/// | [`std::sync::Mutex`][mutex-std] | `std` | For platforms where `std` is available. |
12/// | [`critical_section::Mutex`][mutex-cs] | `critical-section` | Use critical sections to ensure synchronized access, via the [`critical-section`][crate-critical-section] crate. |
13///
14/// [mutex-std]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
15/// [mutex-cs]: https://docs.rs/critical-section/latest/critical_section/struct.Mutex.html
16/// [crate-critical-section]: https://crates.io/crates/critical-section
17///
18/// For other mutex types, a custom implementation is needed.  Due to the orphan rule, it might be
19/// necessary to wrap it in a newtype.  As an example, this is what such a custom implementation
20/// might look like:
21///
22/// ```
23/// struct MyMutex<T>(std::sync::Mutex<T>);
24///
25/// impl<T> port_expander::PortMutex for MyMutex<T> {
26///     type Port = T;
27///
28///     fn create(v: T) -> Self {
29///         Self(std::sync::Mutex::new(v))
30///     }
31///
32///     fn lock<R, F: FnOnce(&mut Self::Port) -> R>(&self, f: F) -> R {
33///         let mut v = self.0.lock().unwrap();
34///         f(&mut v)
35///     }
36/// }
37/// ```
38pub trait PortMutex {
39    /// The actual port-expander that is wrapped inside this mutex.
40    type Port;
41
42    /// Create a new mutex of this type.
43    fn create(v: Self::Port) -> Self;
44
45    /// Lock the mutex and give a closure access to the port-expander inside.
46    fn lock<R, F: FnOnce(&mut Self::Port) -> R>(&self, f: F) -> R;
47}
48
49impl<T> PortMutex for core::cell::RefCell<T> {
50    type Port = T;
51
52    fn create(v: Self::Port) -> Self {
53        core::cell::RefCell::new(v)
54    }
55
56    fn lock<R, F: FnOnce(&mut Self::Port) -> R>(&self, f: F) -> R {
57        let mut v = self.borrow_mut();
58        f(&mut v)
59    }
60}
61
62#[cfg(any(test, feature = "std"))]
63impl<T> PortMutex for std::sync::Mutex<T> {
64    type Port = T;
65
66    fn create(v: Self::Port) -> Self {
67        std::sync::Mutex::new(v)
68    }
69
70    fn lock<R, F: FnOnce(&mut Self::Port) -> R>(&self, f: F) -> R {
71        let mut v = self.lock().unwrap();
72        f(&mut v)
73    }
74}
75
76#[cfg(feature = "critical-section")]
77impl<T> PortMutex for critical_section::Mutex<core::cell::RefCell<T>> {
78    type Port = T;
79
80    fn create(v: Self::Port) -> Self {
81        critical_section::Mutex::new(core::cell::RefCell::new(v))
82    }
83
84    fn lock<R, F: FnOnce(&mut Self::Port) -> R>(&self, f: F) -> R {
85        critical_section::with(|cs| {
86            let mut v = self.borrow_ref_mut(cs);
87            f(&mut v)
88        })
89    }
90}