diff options
| author | Emil Fresk <emil.fresk@gmail.com> | 2023-01-28 13:21:44 +0100 |
|---|---|---|
| committer | Henrik Tjäder <henrik@tjaders.com> | 2023-03-01 00:33:36 +0100 |
| commit | 3050fc0591f087a4fbe08840c69633e89d3f58a7 (patch) | |
| tree | 63c26184993ce42c63102e174d781c5d52da6623 /rtic-channel | |
| parent | 5908d5bdbc3a3daf741d58b925fa280a3a81bf6a (diff) | |
Use `Pin` in the linked lists
Diffstat (limited to 'rtic-channel')
| -rw-r--r-- | rtic-channel/src/lib.rs | 18 | ||||
| -rw-r--r-- | rtic-channel/src/wait_queue.rs | 16 |
2 files changed, 23 insertions, 11 deletions
diff --git a/rtic-channel/src/lib.rs b/rtic-channel/src/lib.rs index 5ee2c71..20086ac 100644 --- a/rtic-channel/src/lib.rs +++ b/rtic-channel/src/lib.rs @@ -8,6 +8,7 @@ use core::{ cell::UnsafeCell, future::poll_fn, mem::MaybeUninit, + pin::Pin, ptr, task::{Poll, Waker}, }; @@ -177,10 +178,11 @@ impl<'a, T, const N: usize> Sender<'a, T, N> { pub async fn send(&mut self, val: T) -> Result<(), NoReceiver<T>> { if self.is_closed() {} - let mut __hidden_link: Option<wait_queue::Link<Waker>> = None; + let mut link_ptr: Option<wait_queue::Link<Waker>> = None; + + // Make this future `Drop`-safe, also shadow the original definition so we can't abuse it. + let link_ptr = &mut link_ptr as *mut Option<wait_queue::Link<Waker>>; - // Make this future `Drop`-safe - let link_ptr = &mut __hidden_link as *mut Option<wait_queue::Link<Waker>>; let dropper = OnDrop::new(|| { // SAFETY: We only run this closure and dereference the pointer if we have // exited the `poll_fn` below in the `drop(dropper)` call. The other dereference @@ -198,12 +200,18 @@ impl<'a, T, const N: usize> Sender<'a, T, N> { // Do all this in one critical section, else there can be race conditions let queue_idx = critical_section::with(|cs| { if !self.0.wait_queue.is_empty() || self.0.access(cs).freeq.is_empty() { - // SAFETY: This pointer is only dereferenced here and on drop of the future. + // SAFETY: This pointer is only dereferenced here and on drop of the future + // which happens outside this `poll_fn`'s stack frame. let link = unsafe { &mut *link_ptr }; if link.is_none() { // Place the link in the wait queue on first run. let link_ref = link.insert(wait_queue::Link::new(cx.waker().clone())); - self.0.wait_queue.push(link_ref); + + // SAFETY: The address to the link is stable as it is hidden behind + // `link_ptr`, and `link_ptr` shadows the original making it unmovable. + self.0 + .wait_queue + .push(unsafe { Pin::new_unchecked(link_ref) }); } return None; diff --git a/rtic-channel/src/wait_queue.rs b/rtic-channel/src/wait_queue.rs index 5b59983..ba05e6b 100644 --- a/rtic-channel/src/wait_queue.rs +++ b/rtic-channel/src/wait_queue.rs @@ -1,6 +1,7 @@ //! ... use core::marker::PhantomPinned; +use core::pin::Pin; use core::ptr::null_mut; use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::Waker; @@ -65,13 +66,16 @@ impl<T: Clone> LinkedList<T> { } /// Put an element at the back of the queue. - pub fn push(&self, link: &mut Link<T>) { + pub fn push(&self, link: Pin<&mut Link<T>>) { cs::with(|_| { // Make sure all previous writes are visible core::sync::atomic::fence(Ordering::SeqCst); let tail = self.tail.load(Self::R); + // SAFETY: This datastructure does not move the underlying value. + let link = unsafe { link.get_unchecked_mut() }; + if let Some(tail_ref) = unsafe { tail.as_ref() } { // Queue is not empty link.prev.store(tail, Self::R); @@ -221,11 +225,11 @@ mod tests { let mut i4 = Link::new(13); let mut i5 = Link::new(14); - wq.push(&mut i1); - wq.push(&mut i2); - wq.push(&mut i3); - wq.push(&mut i4); - wq.push(&mut i5); + wq.push(unsafe { Pin::new_unchecked(&mut i1) }); + wq.push(unsafe { Pin::new_unchecked(&mut i2) }); + wq.push(unsafe { Pin::new_unchecked(&mut i3) }); + wq.push(unsafe { Pin::new_unchecked(&mut i4) }); + wq.push(unsafe { Pin::new_unchecked(&mut i5) }); wq.print(); |
