diff options
| author | Liam Murphy <[email protected]> | 2021-07-29 15:11:26 +1000 |
|---|---|---|
| committer | Liam Murphy <[email protected]> | 2021-07-29 15:11:26 +1000 |
| commit | d5ba35424d7eef2cc0c501758d214ce3a6febfc1 (patch) | |
| tree | 8d3b07073b7f5f2ea67bb061a990b019c79aeeb5 /embassy-extras/src | |
| parent | 4d9514cbcb97343c3a75bfa565d753c44c2a0e27 (diff) | |
Replace `PeripheralStateUnchecked` with `register_interrupt_unchecked`
Diffstat (limited to 'embassy-extras/src')
| -rw-r--r-- | embassy-extras/src/peripheral.rs | 66 | ||||
| -rw-r--r-- | embassy-extras/src/peripheral_shared.rs | 52 | ||||
| -rw-r--r-- | embassy-extras/src/usb/mod.rs | 9 |
3 files changed, 74 insertions, 53 deletions
diff --git a/embassy-extras/src/peripheral.rs b/embassy-extras/src/peripheral.rs index 725a58a46..1868edd7d 100644 --- a/embassy-extras/src/peripheral.rs +++ b/embassy-extras/src/peripheral.rs | |||
| @@ -6,42 +6,20 @@ use cortex_m::peripheral::scb::{Exception, SystemHandler, VectActive}; | |||
| 6 | use cortex_m::peripheral::{NVIC, SCB}; | 6 | use cortex_m::peripheral::{NVIC, SCB}; |
| 7 | use embassy::interrupt::{Interrupt, InterruptExt}; | 7 | use embassy::interrupt::{Interrupt, InterruptExt}; |
| 8 | 8 | ||
| 9 | /// A version of `PeripheralState` without the `'static` bound, | ||
| 10 | /// for cases where the compiler can't statically make sure | ||
| 11 | /// that `on_interrupt` doesn't reference anything which might be invalidated. | ||
| 12 | /// | ||
| 13 | /// # Safety | ||
| 14 | /// When types implementing this trait are used with `PeripheralMutex`, | ||
| 15 | /// no fields referenced by `on_interrupt`'s lifetimes must end without first calling `Drop` on the `PeripheralMutex`. | ||
| 16 | pub unsafe trait PeripheralStateUnchecked: Send { | ||
| 17 | type Interrupt: Interrupt; | ||
| 18 | fn on_interrupt(&mut self); | ||
| 19 | } | ||
| 20 | |||
| 21 | /// A type which can be used as state with `PeripheralMutex`. | 9 | /// A type which can be used as state with `PeripheralMutex`. |
| 22 | /// | 10 | /// |
| 23 | /// It needs to be `Send` because `&mut` references are sent back and forth between the 'thread' which owns the `PeripheralMutex` and the interrupt, | 11 | /// It needs to be `Send` because `&mut` references are sent back and forth between the 'thread' which owns the `PeripheralMutex` and the interrupt, |
| 24 | /// and `&mut T` is only `Send` where `T: Send`. | 12 | /// and `&mut T` is only `Send` where `T: Send`. |
| 25 | /// | 13 | /// |
| 26 | /// It also requires `'static`, because although `Pin` guarantees that the memory of the state won't be invalidated, | 14 | /// It also requires `'static` to be used safely with `PeripheralMutex::register_interrupt`, |
| 15 | /// because although `Pin` guarantees that the memory of the state won't be invalidated, | ||
| 27 | /// it doesn't guarantee that the lifetime will last. | 16 | /// it doesn't guarantee that the lifetime will last. |
| 28 | pub trait PeripheralState: Send + 'static { | 17 | pub trait PeripheralState: Send { |
| 29 | type Interrupt: Interrupt; | 18 | type Interrupt: Interrupt; |
| 30 | fn on_interrupt(&mut self); | 19 | fn on_interrupt(&mut self); |
| 31 | } | 20 | } |
| 32 | 21 | ||
| 33 | // SAFETY: `T` has to live for `'static` to implement `PeripheralState`, thus its lifetime cannot end. | 22 | pub struct PeripheralMutex<S: PeripheralState> { |
| 34 | unsafe impl<T> PeripheralStateUnchecked for T | ||
| 35 | where | ||
| 36 | T: PeripheralState, | ||
| 37 | { | ||
| 38 | type Interrupt = T::Interrupt; | ||
| 39 | fn on_interrupt(&mut self) { | ||
| 40 | self.on_interrupt() | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | pub struct PeripheralMutex<S: PeripheralStateUnchecked> { | ||
| 45 | state: UnsafeCell<S>, | 23 | state: UnsafeCell<S>, |
| 46 | 24 | ||
| 47 | irq_setup_done: bool, | 25 | irq_setup_done: bool, |
| @@ -98,7 +76,25 @@ pub(crate) fn can_be_preempted(irq: &impl Interrupt) -> bool { | |||
| 98 | } | 76 | } |
| 99 | } | 77 | } |
| 100 | 78 | ||
| 101 | impl<S: PeripheralStateUnchecked> PeripheralMutex<S> { | 79 | impl<S: PeripheralState + 'static> PeripheralMutex<S> { |
| 80 | /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it. | ||
| 81 | /// | ||
| 82 | /// This requires this `PeripheralMutex`'s `PeripheralState` to live for `'static`, | ||
| 83 | /// because `Pin` only guarantees that it's memory won't be repurposed, | ||
| 84 | /// not that it's lifetime will last. | ||
| 85 | /// | ||
| 86 | /// To use non-`'static` `PeripheralState`, use the unsafe `register_interrupt_unchecked`. | ||
| 87 | /// | ||
| 88 | /// Note: `'static` doesn't mean it _has_ to live for the entire program, like an `&'static T`; | ||
| 89 | /// it just means it _can_ live for the entire program - for example, `u8` lives for `'static`. | ||
| 90 | pub fn register_interrupt(self: Pin<&mut Self>) { | ||
| 91 | // SAFETY: `S: 'static`, so there's no way it's lifetime can expire. | ||
| 92 | unsafe { self.register_interrupt_unchecked() } | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | impl<S: PeripheralState> PeripheralMutex<S> { | ||
| 97 | /// Create a new `PeripheralMutex` wrapping `irq`, with the initial state `state`. | ||
| 102 | pub fn new(state: S, irq: S::Interrupt) -> Self { | 98 | pub fn new(state: S, irq: S::Interrupt) -> Self { |
| 103 | if can_be_preempted(&irq) { | 99 | if can_be_preempted(&irq) { |
| 104 | panic!("`PeripheralMutex` cannot be created in an interrupt with higher priority than the interrupt it wraps"); | 100 | panic!("`PeripheralMutex` cannot be created in an interrupt with higher priority than the interrupt it wraps"); |
| @@ -114,8 +110,18 @@ impl<S: PeripheralStateUnchecked> PeripheralMutex<S> { | |||
| 114 | } | 110 | } |
| 115 | } | 111 | } |
| 116 | 112 | ||
| 117 | pub fn register_interrupt(self: Pin<&mut Self>) { | 113 | /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it. |
| 118 | let this = unsafe { self.get_unchecked_mut() }; | 114 | /// |
| 115 | /// # Safety | ||
| 116 | /// The lifetime of any data in `PeripheralState` that is accessed by the interrupt handler | ||
| 117 | /// must not end without `Drop` being called on this `PeripheralMutex`. | ||
| 118 | /// | ||
| 119 | /// This can be accomplished by either not accessing any data with a lifetime in `on_interrupt`, | ||
| 120 | /// or making sure that nothing like `mem::forget` is used on the `PeripheralMutex`. | ||
| 121 | |||
| 122 | // TODO: this name isn't the best. | ||
| 123 | pub unsafe fn register_interrupt_unchecked(self: Pin<&mut Self>) { | ||
| 124 | let this = self.get_unchecked_mut(); | ||
| 119 | if this.irq_setup_done { | 125 | if this.irq_setup_done { |
| 120 | return; | 126 | return; |
| 121 | } | 127 | } |
| @@ -172,7 +178,7 @@ impl<S: PeripheralStateUnchecked> PeripheralMutex<S> { | |||
| 172 | } | 178 | } |
| 173 | } | 179 | } |
| 174 | 180 | ||
| 175 | impl<S: PeripheralStateUnchecked> Drop for PeripheralMutex<S> { | 181 | impl<S: PeripheralState> Drop for PeripheralMutex<S> { |
| 176 | fn drop(&mut self) { | 182 | fn drop(&mut self) { |
| 177 | self.irq.disable(); | 183 | self.irq.disable(); |
| 178 | self.irq.remove_handler(); | 184 | self.irq.remove_handler(); |
diff --git a/embassy-extras/src/peripheral_shared.rs b/embassy-extras/src/peripheral_shared.rs index 788ac3f96..71d746341 100644 --- a/embassy-extras/src/peripheral_shared.rs +++ b/embassy-extras/src/peripheral_shared.rs | |||
| @@ -5,30 +5,19 @@ use embassy::interrupt::{Interrupt, InterruptExt}; | |||
| 5 | 5 | ||
| 6 | use crate::peripheral::can_be_preempted; | 6 | use crate::peripheral::can_be_preempted; |
| 7 | 7 | ||
| 8 | /// A version of `PeripheralState` without the `'static` bound, | ||
| 9 | /// for cases where the compiler can't statically make sure | ||
| 10 | /// that `on_interrupt` doesn't reference anything which might be invalidated. | ||
| 11 | /// | ||
| 12 | /// # Safety | ||
| 13 | /// When types implementing this trait are used with `Peripheral`, | ||
| 14 | /// no fields referenced by `on_interrupt`'s lifetimes must end without first calling `Drop` on the `Peripheral`. | ||
| 15 | pub unsafe trait PeripheralStateUnchecked: Sync { | ||
| 16 | type Interrupt: Interrupt; | ||
| 17 | fn on_interrupt(&self); | ||
| 18 | } | ||
| 19 | |||
| 20 | /// A type which can be used as state with `Peripheral`. | 8 | /// A type which can be used as state with `Peripheral`. |
| 21 | /// | 9 | /// |
| 22 | /// It needs to be `Sync` because references are shared between the 'thread' which owns the `Peripheral` and the interrupt. | 10 | /// It needs to be `Sync` because references are shared between the 'thread' which owns the `Peripheral` and the interrupt. |
| 23 | /// | 11 | /// |
| 24 | /// It also requires `'static`, because although `Pin` guarantees that the memory of the state won't be invalidated, | 12 | /// It also requires `'static` to be used safely with `Peripheral::register_interrupt`, |
| 13 | /// because although `Pin` guarantees that the memory of the state won't be invalidated, | ||
| 25 | /// it doesn't guarantee that the lifetime will last. | 14 | /// it doesn't guarantee that the lifetime will last. |
| 26 | pub trait PeripheralState: Sync + 'static { | 15 | pub trait PeripheralState: Sync { |
| 27 | type Interrupt: Interrupt; | 16 | type Interrupt: Interrupt; |
| 28 | fn on_interrupt(&self); | 17 | fn on_interrupt(&self); |
| 29 | } | 18 | } |
| 30 | 19 | ||
| 31 | pub struct Peripheral<S: PeripheralStateUnchecked> { | 20 | pub struct Peripheral<S: PeripheralState> { |
| 32 | state: S, | 21 | state: S, |
| 33 | 22 | ||
| 34 | irq_setup_done: bool, | 23 | irq_setup_done: bool, |
| @@ -38,7 +27,24 @@ pub struct Peripheral<S: PeripheralStateUnchecked> { | |||
| 38 | _pinned: PhantomPinned, | 27 | _pinned: PhantomPinned, |
| 39 | } | 28 | } |
| 40 | 29 | ||
| 41 | impl<S: PeripheralStateUnchecked> Peripheral<S> { | 30 | impl<S: PeripheralState + 'static> Peripheral<S> { |
| 31 | /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it. | ||
| 32 | /// | ||
| 33 | /// This requires this `Peripheral`'s `PeripheralState` to live for `'static`, | ||
| 34 | /// because `Pin` only guarantees that it's memory won't be repurposed, | ||
| 35 | /// not that it's lifetime will last. | ||
| 36 | /// | ||
| 37 | /// To use non-`'static` `PeripheralState`, use the unsafe `register_interrupt_unchecked`. | ||
| 38 | /// | ||
| 39 | /// Note: `'static` doesn't mean it _has_ to live for the entire program, like an `&'static T`; | ||
| 40 | /// it just means it _can_ live for the entire program - for example, `u8` lives for `'static`. | ||
| 41 | pub fn register_interrupt(self: Pin<&mut Self>) { | ||
| 42 | // SAFETY: `S: 'static`, so there's no way it's lifetime can expire. | ||
| 43 | unsafe { self.register_interrupt_unchecked() } | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | impl<S: PeripheralState> Peripheral<S> { | ||
| 42 | pub fn new(irq: S::Interrupt, state: S) -> Self { | 48 | pub fn new(irq: S::Interrupt, state: S) -> Self { |
| 43 | if can_be_preempted(&irq) { | 49 | if can_be_preempted(&irq) { |
| 44 | panic!("`Peripheral` cannot be created in an interrupt with higher priority than the interrupt it wraps"); | 50 | panic!("`Peripheral` cannot be created in an interrupt with higher priority than the interrupt it wraps"); |
| @@ -54,8 +60,16 @@ impl<S: PeripheralStateUnchecked> Peripheral<S> { | |||
| 54 | } | 60 | } |
| 55 | } | 61 | } |
| 56 | 62 | ||
| 57 | pub fn register_interrupt(self: Pin<&mut Self>) { | 63 | /// Registers `on_interrupt` as the wrapped interrupt's interrupt handler and enables it. |
| 58 | let this = unsafe { self.get_unchecked_mut() }; | 64 | /// |
| 65 | /// # Safety | ||
| 66 | /// The lifetime of any data in `PeripheralState` that is accessed by the interrupt handler | ||
| 67 | /// must not end without `Drop` being called on this `Peripheral`. | ||
| 68 | /// | ||
| 69 | /// This can be accomplished by either not accessing any data with a lifetime in `on_interrupt`, | ||
| 70 | /// or making sure that nothing like `mem::forget` is used on the `Peripheral`. | ||
| 71 | pub unsafe fn register_interrupt_unchecked(self: Pin<&mut Self>) { | ||
| 72 | let this = self.get_unchecked_mut(); | ||
| 59 | if this.irq_setup_done { | 73 | if this.irq_setup_done { |
| 60 | return; | 74 | return; |
| 61 | } | 75 | } |
| @@ -100,7 +114,7 @@ impl<S: PeripheralStateUnchecked> Peripheral<S> { | |||
| 100 | } | 114 | } |
| 101 | } | 115 | } |
| 102 | 116 | ||
| 103 | impl<S: PeripheralStateUnchecked> Drop for Peripheral<S> { | 117 | impl<S: PeripheralState> Drop for Peripheral<S> { |
| 104 | fn drop(&mut self) { | 118 | fn drop(&mut self) { |
| 105 | self.irq.disable(); | 119 | self.irq.disable(); |
| 106 | self.irq.remove_handler(); | 120 | self.irq.remove_handler(); |
diff --git a/embassy-extras/src/usb/mod.rs b/embassy-extras/src/usb/mod.rs index 481987a6c..1fb501d7f 100644 --- a/embassy-extras/src/usb/mod.rs +++ b/embassy-extras/src/usb/mod.rs | |||
| @@ -9,7 +9,7 @@ use usb_device::device::UsbDevice; | |||
| 9 | mod cdc_acm; | 9 | mod cdc_acm; |
| 10 | pub mod usb_serial; | 10 | pub mod usb_serial; |
| 11 | 11 | ||
| 12 | use crate::peripheral::{PeripheralMutex, PeripheralStateUnchecked}; | 12 | use crate::peripheral::{PeripheralMutex, PeripheralState}; |
| 13 | use embassy::interrupt::Interrupt; | 13 | use embassy::interrupt::Interrupt; |
| 14 | use usb_serial::{ReadInterface, UsbSerial, WriteInterface}; | 14 | use usb_serial::{ReadInterface, UsbSerial, WriteInterface}; |
| 15 | 15 | ||
| @@ -63,7 +63,9 @@ where | |||
| 63 | let mutex = Pin::new_unchecked(&mut *mutex); | 63 | let mutex = Pin::new_unchecked(&mut *mutex); |
| 64 | 64 | ||
| 65 | // Use inner to register the irq | 65 | // Use inner to register the irq |
| 66 | mutex.register_interrupt(); | 66 | // SAFETY: the safety contract of this function makes sure the `UsbDevice` won't be invalidated |
| 67 | // without the `PeripheralMutex` being dropped. | ||
| 68 | mutex.register_interrupt_unchecked(); | ||
| 67 | } | 69 | } |
| 68 | } | 70 | } |
| 69 | 71 | ||
| @@ -127,8 +129,7 @@ where | |||
| 127 | } | 129 | } |
| 128 | } | 130 | } |
| 129 | 131 | ||
| 130 | // SAFETY: The safety contract of `PeripheralStateUnchecked` is forwarded to `Usb::start`. | 132 | impl<'bus, B, T, I> PeripheralState for State<'bus, B, T, I> |
| 131 | unsafe impl<'bus, B, T, I> PeripheralStateUnchecked for State<'bus, B, T, I> | ||
| 132 | where | 133 | where |
| 133 | B: UsbBus, | 134 | B: UsbBus, |
| 134 | T: ClassSet<B>, | 135 | T: ClassSet<B>, |
