From 079cfffc179a4c9148deccb2cfc3d970cd2fa2fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linus=20F=C3=A4rnstrand?= Date: Sun, 2 Feb 2025 08:56:41 +0100 Subject: [PATCH] Make RecvError opaque by adding dummy field Makes it impossible to create instances from outside the library. Allows future modifications to the error struct without breaking the API --- src/errors.rs | 11 +++++++++-- src/lib.rs | 18 +++++++++--------- tests/miri.rs | 2 +- tests/sync.rs | 6 +++--- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 9f5496e..5da2bb2 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -90,8 +90,15 @@ impl std::error::Error for SendError {} /// The receive operation can only fail if the corresponding [`Sender`](crate::Sender) was dropped /// before sending any message, or if a message has already been received on the channel. #[cfg(any(feature = "std", feature = "async"))] -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct RecvError; +#[derive(Debug, Eq, PartialEq)] +pub struct RecvError(()); + +#[cfg(any(feature = "std", feature = "async"))] +impl RecvError { + pub(crate) const fn new() -> Self { + RecvError(()) + } +} #[cfg(any(feature = "std", feature = "async"))] impl fmt::Display for RecvError { diff --git a/src/lib.rs b/src/lib.rs index 883de7c..f5f65c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -609,7 +609,7 @@ impl Receiver { // sender's final write of the DISCONNECTED state. unsafe { dealloc(channel_ptr) }; - break Err(RecvError); + break Err(RecvError::new()); } // State did not change, spurious wakeup, park again. RECEIVING | UNPARKING => (), @@ -654,7 +654,7 @@ impl Receiver { // write of the channel state. unsafe { dealloc(channel_ptr) }; - Err(RecvError) + Err(RecvError::new()) } _ => unreachable!(), } @@ -684,7 +684,7 @@ impl Receiver { // final write of the channel state. unsafe { dealloc(channel_ptr) }; - Err(RecvError) + Err(RecvError::new()) } // The receiver must have been `Future::poll`ed prior to this call. #[cfg(feature = "async")] @@ -705,7 +705,7 @@ impl Receiver { /// Panics if called after this receiver has been polled asynchronously. #[cfg(feature = "std")] pub fn recv_ref(&self) -> Result { - self.start_recv_ref(RecvError, |channel| { + self.start_recv_ref(RecvError::new(), |channel| { loop { thread::park(); @@ -723,7 +723,7 @@ impl Receiver { break Ok(unsafe { channel.take_message() }); } // The sender was dropped while we were parked. - DISCONNECTED => break Err(RecvError), + DISCONNECTED => break Err(RecvError::new()), // State did not change, spurious wakeup, park again. RECEIVING | UNPARKING => (), _ => unreachable!(), @@ -1077,7 +1077,7 @@ impl core::future::Future for Receiver { } // The sender was dropped before sending anything while we prepared to park. // The sender has taken the waker already. - Err(DISCONNECTED) => Poll::Ready(Err(RecvError)), + Err(DISCONNECTED) => Poll::Ready(Err(RecvError::new())), // The sender is currently waking us up. Err(UNPARKING) => { // We can't trust that the old waker that the sender has access to @@ -1105,7 +1105,7 @@ impl core::future::Future for Receiver { Poll::Ready(Ok(unsafe { channel.take_message() })) } // The sender was dropped before sending anything, or we already received the message. - DISCONNECTED => Poll::Ready(Err(RecvError)), + DISCONNECTED => Poll::Ready(Err(RecvError::new())), // The sender has observed the RECEIVING state and is currently reading the waker from // a previous poll. 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. @@ -1129,7 +1129,7 @@ impl core::future::Future for Receiver { } // The sender was dropped. Our drop impl will synchronize with the sender's // final write of the channel state and deallocate the channel. - DISCONNECTED => break Poll::Ready(Err(RecvError)), + DISCONNECTED => break Poll::Ready(Err(RecvError::new())), // Sender is still unparking us... Spin more UNPARKING => (), _ => unreachable!(), @@ -1403,7 +1403,7 @@ impl Channel { // RECEIVING state, so it has not accessed the waker. We must drop it. self.drop_waker(); - Poll::Ready(Err(RecvError)) + Poll::Ready(Err(RecvError::new())) } _ => unreachable!(), } diff --git a/tests/miri.rs b/tests/miri.rs index ce85e43..cdc5185 100644 --- a/tests/miri.rs +++ b/tests/miri.rs @@ -308,7 +308,7 @@ fn tx_drop_rx_poll_to_completion() { let mut cx = task::Context::from_waker(Waker::noop()); loop { match rx.as_mut().poll(&mut cx) { - Poll::Ready(Err(oneshot::RecvError)) => break, + Poll::Ready(Err(_)) => break, Poll::Ready(result) => panic!("Unexpected result: {:?}", result), Poll::Pending => spin_loop(), } diff --git a/tests/sync.rs b/tests/sync.rs index cfc75df..bddf646 100644 --- a/tests/sync.rs +++ b/tests/sync.rs @@ -2,7 +2,7 @@ use core::mem; use oneshot::TryRecvError; #[cfg(feature = "std")] -use oneshot::{RecvError, RecvTimeoutError}; +use oneshot::RecvTimeoutError; #[cfg(feature = "std")] use std::time::{Duration, Instant}; @@ -35,7 +35,7 @@ fn send_before_try_recv() { assert_eq!(receiver.try_recv(), Err(TryRecvError::Disconnected)); #[cfg(feature = "std")] { - assert_eq!(receiver.recv_ref(), Err(RecvError)); + assert!(receiver.recv_ref().is_err()); assert!(receiver.recv_timeout(Duration::from_secs(1)).is_err()); } }) @@ -78,7 +78,7 @@ fn send_before_recv_ref() { assert!(sender.send(19i128).is_ok()); assert_eq!(receiver.recv_ref(), Ok(19i128)); - assert_eq!(receiver.recv_ref(), Err(RecvError)); + assert!(receiver.recv_ref().is_err()); assert_eq!(receiver.try_recv(), Err(TryRecvError::Disconnected)); assert!(receiver.recv_timeout(Duration::from_secs(1)).is_err()); })