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}