From 4d58d2bcd5e08f0123d2f3ac39c6a090dfee23d6 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Fri, 14 Mar 2025 22:34:42 +0100 Subject: [PATCH] rtic-sync: add test validating that free queue slots are not lost on drop --- rtic-sync/Cargo.toml | 1 + rtic-sync/src/channel.rs | 41 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/rtic-sync/Cargo.toml b/rtic-sync/Cargo.toml index a522c16d243..220f831316a 100644 --- a/rtic-sync/Cargo.toml +++ b/rtic-sync/Cargo.toml @@ -29,6 +29,7 @@ embedded-hal-bus = { version = "0.2.0", features = ["async"] } defmt-03 = { package = "defmt", version = "0.3", optional = true } [dev-dependencies] +cassette = "0.3.0" static_cell = "2.1.0" tokio = { version = "1", features = ["rt", "macros", "time"] } diff --git a/rtic-sync/src/channel.rs b/rtic-sync/src/channel.rs index e4dc835b051..17fd50a8fa3 100644 --- a/rtic-sync/src/channel.rs +++ b/rtic-sync/src/channel.rs @@ -559,6 +559,8 @@ impl Drop for Receiver<'_, T, N> { #[cfg(test)] mod tests { + use cassette::Cassette; + use super::*; #[test] @@ -684,4 +686,43 @@ mod tests { fn tuple_channel() { let _ = make_channel!((i32, u32), 10); } + + fn freeq(channel: &Channel, f: F) -> R + where + F: FnOnce(&mut Deque) -> R, + { + critical_section::with(|cs| f(channel.access(cs).freeq)) + } + + #[test] + fn dropping_waked_send_returns_freeq_item() { + let (mut tx, mut rx) = make_channel!(u8, 1); + + tx.try_send(0).unwrap(); + assert!(freeq(&rx.0, |q| q.is_empty())); + + // Running this in a separate thread scope to ensure that `pinned_future` is dropped fully. + // + // Calling drop explicitly gets hairy because dropping things behind a `Pin` is not easy. + std::thread::scope(|scope| { + scope.spawn(|| { + let pinned_future = core::pin::pin!(tx.send(1)); + let mut future = Cassette::new(pinned_future); + + future.poll_on(); + + assert!(freeq(&rx.0, |q| q.is_empty())); + assert!(!rx.0.wait_queue.is_empty()); + + assert_eq!(rx.try_recv(), Ok(0)); + + assert!(freeq(&rx.0, |q| q.is_empty())); + }); + }); + + assert!(!freeq(&rx.0, |q| q.is_empty())); + + // Make sure that rx & tx are alive until here for good measure. + drop((tx, rx)); + } }