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

    /// Create a new mutex of this type.
    fn create(v: Self::Port) -> Self;

    /// Lock the mutex and give a closure access to the port-expander inside.
    fn lock<R, F: FnOnce(&mut Self::Port) -> R>(&self, f: F) -> R;
}

impl<T> PortMutex for core::cell::RefCell<T> {
    type Port = T;

    fn create(v: Self::Port) -> Self {
        core::cell::RefCell::new(v)
    }

    fn lock<R, F: FnOnce(&mut Self::Port) -> R>(&self, f: F) -> R {
        let mut v = self.borrow_mut();
        f(&mut v)
    }
}

#[cfg(any(test, feature = "std"))]
impl<T> PortMutex for std::sync::Mutex<T> {
    type Port = T;

    fn create(v: Self::Port) -> Self {
        std::sync::Mutex::new(v)
    }

    fn lock<R, F: FnOnce(&mut Self::Port) -> R>(&self, f: F) -> R {
        let mut v = self.lock().unwrap();
        f(&mut v)
    }
}

#[cfg(feature = "critical-section")]
impl<T> PortMutex for critical_section::Mutex<core::cell::RefCell<T>> {
    type Port = T;

    fn create(v: Self::Port) -> Self {
        critical_section::Mutex::new(core::cell::RefCell::new(v))
    }

    fn lock<R, F: FnOnce(&mut Self::Port) -> R>(&self, f: F) -> R {
        critical_section::with(|cs| {
            let mut v = self.borrow_ref_mut(cs);
            f(&mut v)
        })
    }
}