diff --git a/CHANGELOG.md b/CHANGELOG.md index dab9d20..cfde4f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,8 +15,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] - -- Handle the UNPARKING state correctly in `Receiver::drop()`. +### Fixed +- Handle the `UNPARKING` state correctly in `Receiver::drop()`. Fixes a panic that could + occur if a `Receiver` had been first polled as a future and then was being dropped + in parallel with the `Sender` sending a message. ## [0.1.10] - 2025-02-04 diff --git a/src/lib.rs b/src/lib.rs index 4e1828f..b54fcd6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,9 +142,9 @@ use loom::{ sync::atomic::{fence, AtomicU8, Ordering::*}, }; -#[cfg(all(feature = "async", not(oneshot_loom)))] +#[cfg(all(any(feature = "std", feature = "async"), not(oneshot_loom)))] use core::hint; -#[cfg(all(feature = "async", oneshot_loom))] +#[cfg(all(any(feature = "std", feature = "async"), oneshot_loom))] use loom::hint; #[cfg(feature = "async")] @@ -1067,31 +1067,29 @@ impl Drop for Receiver { // SAFETY: see safety comment at top of function unsafe { dealloc(self.channel_ptr) }; } - // The sender has observed the RECEIVING state and is currently reading the waker from - // a poll. We need to loop here until we observe the MESSAGE or DISCONNECTED state. + // This receiver was previously polled, so the channel was in the RECEIVING state. + // But the sender has observed the RECEIVING state and is currently reading the waker + // to wake us up. We need to loop here until we observe the MESSAGE or DISCONNECTED state. // We busy loop here since we know the sender is done very soon. #[cfg(any(feature = "std", feature = "async"))] - UNPARKING => loop { - hint::spin_loop(); - // ORDERING: The load above has already synchronized with the write of the message. - match channel.state.load(Relaxed) { - MESSAGE => { - // SAFETY: we are in the message state so the message is initialized - unsafe { channel.drop_message() }; - - // SAFETY: see safety comment at top of function - unsafe { dealloc(self.channel_ptr) }; - break; - } - DISCONNECTED => { - // SAFETY: see safety comment at top of function - unsafe { dealloc(self.channel_ptr) }; - break; + UNPARKING => { + loop { + hint::spin_loop(); + // ORDERING: The swap above has already synchronized with the write of the message. + match channel.state.load(Relaxed) { + MESSAGE => { + // SAFETY: we are in the message state so the message is initialized + unsafe { channel.drop_message() }; + break; + } + DISCONNECTED => break, + UNPARKING => (), + _ => unreachable!(), } - UNPARKING => (), - _ => unreachable!(), } - }, + // SAFETY: see safety comment at top of function + unsafe { dealloc(self.channel_ptr) }; + } _ => unreachable!(), } }