Skip to content
Draft
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions examples/external_storage_embedded_reused.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#[cfg(feature = "std")]
fn main() {
use core::ptr::NonNull;

use oneshot::ChannelStorage;

struct Container {
generation: usize,

// NB! We are not allowed to create `&mut` exclusive references to this or the parent type
// when the `ChannelStorage` is in use because that would violate the Rust aliasing rules
// as the channel essentially takes a shared reference to the storage object.
storage: ChannelStorage<usize>,
}

let mut container = Container {
generation: 0,
storage: ChannelStorage::new(),
};

// SAFETY: We promise that no other channel is using this storage and that it stays alive
// at least as long as the channel itself - guaranteed because it is a local variable and
// the channel endpoints are local variables with lesser scope.
let (sender, receiver) =
unsafe { oneshot::channel_with_storage(NonNull::from(&container.storage), |_| {}) };

sender.send(1234).unwrap();
assert_eq!(receiver.recv().unwrap(), 1234);

// The storage is not in use anymore, so we can now mutate the container.
container.generation += 1;

// SAFETY: We promise that no other channel is using this storage and that it stays alive
// at least as long as the channel itself - guaranteed because it is a local variable and
// the channel endpoints are local variables with lesser scope.
let (sender, receiver) =
unsafe { oneshot::channel_with_storage(NonNull::from(&container.storage), |_| {}) };

sender.send(5678).unwrap();
assert_eq!(receiver.recv().unwrap(), 5678);
}

#[cfg(not(feature = "std"))]
fn main() {
panic!("This example is only for when the \"std\" feature is used");
}
26 changes: 26 additions & 0 deletions examples/external_storage_on_heap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#[cfg(feature = "std")]
fn main() {
use core::ptr::NonNull;

use oneshot::ChannelStorage;

// This is functionally identical to just using `oneshot::channel()`.
let storage = NonNull::from(Box::leak(Box::new(ChannelStorage::<usize>::new())));

// SAFETY: We promise that no other channel is using this storage and that it stays alive
// at least as long as the channel itself - guaranteed because it is a leaked box, so the
// only thing that will destroy it is the release fn we provide here.
let (sender, receiver) = unsafe {
oneshot::channel_with_storage(storage, |storage| {
drop(Box::from_raw(storage.as_ptr()));
})
};

sender.send(1234).unwrap();
assert_eq!(receiver.recv().unwrap(), 1234);
}

#[cfg(not(feature = "std"))]
fn main() {
panic!("This example is only for when the \"std\" feature is used");
}
22 changes: 22 additions & 0 deletions examples/external_storage_on_stack.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#[cfg(feature = "std")]
fn main() {
use core::ptr::NonNull;

use oneshot::ChannelStorage;

let storage = ChannelStorage::<usize>::new();

// SAFETY: We promise that no other channel is using this storage and that it stays alive
// at least as long as the channel itself - guaranteed because it is a local variable and
// the channel endpoints are local variables with lesser scope.
let (sender, receiver) =
unsafe { oneshot::channel_with_storage(NonNull::from(&storage), |_| {}) };

sender.send(1234).unwrap();
assert_eq!(receiver.recv().unwrap(), 1234);
}

#[cfg(not(feature = "std"))]
fn main() {
panic!("This example is only for when the \"std\" feature is used");
}
46 changes: 27 additions & 19 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,84 +1,92 @@
use super::{dealloc, Channel};
use crate::Channel;
use crate::Global;
use crate::Storage;

use core::fmt;
use core::marker::PhantomData;
use core::mem;
use core::ptr::NonNull;

/// An error returned when trying to send on a closed channel. Returned from
/// [`Sender::send`](crate::Sender::send) if the corresponding [`Receiver`](crate::Receiver)
/// has already been dropped.
///
/// The message that could not be sent can be retreived again with [`SendError::into_inner`].
pub struct SendError<T> {
channel_ptr: NonNull<Channel<T>>,
pub struct SendError<T, S: Storage<T> = Global<T>> {
storage: S,

_t: PhantomData<T>,
}

unsafe impl<T: Send> Send for SendError<T> {}
unsafe impl<T: Sync> Sync for SendError<T> {}
unsafe impl<T: Send, S: Storage<T>> Send for SendError<T, S> {}
unsafe impl<T: Sync, S: Storage<T>> Sync for SendError<T, S> {}

impl<T> SendError<T> {
impl<T, S: Storage<T>> SendError<T, S> {
/// # Safety
///
/// By calling this function, the caller semantically transfers ownership of the
/// channel's resources to the created `SendError`. Thus the caller must ensure that the
/// pointer is not used in a way which would violate this ownership transfer. Moreover,
/// the caller must assert that the channel contains a valid, initialized message.
pub(crate) const unsafe fn new(channel_ptr: NonNull<Channel<T>>) -> Self {
Self { channel_ptr }
pub(crate) const unsafe fn new(storage: S) -> Self {
Self {
storage,
_t: PhantomData,
}
}

/// Consumes the error and returns the message that failed to be sent.
#[inline]
pub fn into_inner(self) -> T {
let channel_ptr = self.channel_ptr;
let mut storage = self.storage.clone();

// Don't run destructor if we consumed ourselves. Freeing happens here.
mem::forget(self);

// SAFETY: we have ownership of the channel
let channel: &Channel<T> = unsafe { channel_ptr.as_ref() };
let channel: &Channel<T> = unsafe { storage.as_ref() };

// SAFETY: we know that the message is initialized according to the safety requirements of
// `new`
let message = unsafe { channel.take_message() };

// SAFETY: we own the channel
unsafe { dealloc(channel_ptr) };
unsafe { storage.release() };

message
}

/// Get a reference to the message that failed to be sent.
#[inline]
pub fn as_inner(&self) -> &T {
unsafe { self.channel_ptr.as_ref().message().assume_init_ref() }
unsafe { self.storage.as_ref().message().assume_init_ref() }
}
}

impl<T> Drop for SendError<T> {
impl<T, S: Storage<T>> Drop for SendError<T, S> {
fn drop(&mut self) {
// SAFETY: we have ownership of the channel and require that the message is initialized
// upon construction
unsafe {
self.channel_ptr.as_ref().drop_message();
dealloc(self.channel_ptr);
self.storage.as_ref().drop_message();
self.storage.release();
}
}
}

impl<T> fmt::Display for SendError<T> {
impl<T, S: Storage<T>> fmt::Display for SendError<T, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
"sending on a closed channel".fmt(f)
}
}

impl<T> fmt::Debug for SendError<T> {
impl<T, S: Storage<T>> fmt::Debug for SendError<T, S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SendError<{}>(_)", stringify!(T))
}
}

#[cfg(feature = "std")]
impl<T> std::error::Error for SendError<T> {}
impl<T, S: Storage<T>> std::error::Error for SendError<T, S> {}

/// An error returned from receiving methods that block/wait until a message is available.
///
Expand Down
Loading
Loading