From b4356cb01cddcb4155faea85849baac250c9e239 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Mon, 17 Mar 2025 19:29:20 +0100 Subject: [PATCH] rtic-sync: require channel-users to deal with non-empty channels --- rtic-sync/src/channel.rs | 58 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/rtic-sync/src/channel.rs b/rtic-sync/src/channel.rs index 3c0a27a3d4a..3706e584c07 100644 --- a/rtic-sync/src/channel.rs +++ b/rtic-sync/src/channel.rs @@ -102,10 +102,62 @@ impl Channel { } } + /// Clear any remaining items from this `Channel`. + pub fn clear(&mut self) { + for _ in self.queued_items() {} + } + + /// Return an iterator over the still-queued items, removing them + /// from this channel. + pub fn queued_items(&mut self) -> impl Iterator + '_ { + struct Iter<'a, T, const N: usize> { + inner: &'a mut Channel, + } + + impl Iterator for Iter<'_, T, N> { + type Item = T; + + fn next(&mut self) -> Option { + let slot = self.inner.readyq.as_mut().pop_back()?; + + let value = unsafe { + // SAFETY: `ready` is a valid slot. + let first_element = self.inner.slots.get_unchecked(slot as usize).get_mut(); + let ptr = first_element.deref().as_ptr(); + // SAFETY: `ptr` points to an initialized `T`. + core::ptr::read(ptr) + }; + + assert!(!self.inner.freeq.as_mut().is_full()); + unsafe { + // SAFETY: `freeq` is not ful. + self.inner.freeq.as_mut().push_back_unchecked(slot); + } + + Some(value) + } + } + + Iter { inner: self } + } + /// Split the queue into a `Sender`/`Receiver` pair. + /// + /// # Panics + /// This function panics if there are items in this channel while splitting. + /// + /// Call [`Channel::clear`] to clear all items from it, or [`Channel::queued_items`] to retrieve + /// an iterator that yields the values. pub fn split(&mut self) -> (Sender<'_, T, N>, Receiver<'_, T, N>) { + assert!( + self.readyq.as_mut().is_empty(), + "Cannot re-split non-empty queue. Call `Channel::clear()`." + ); + let freeq = self.freeq.as_mut(); + freeq.clear(); + // Fill free queue for idx in 0..N as u8 { // NOTE(assert): `split`-ing does not put `freeq` into a known-empty @@ -162,6 +214,12 @@ impl Channel { } } +impl Drop for Channel { + fn drop(&mut self) { + self.clear(); + } +} + /// Creates a split channel with `'static` lifetime. #[macro_export] #[cfg(not(loom))]