Skip to content

Commit

Permalink
m: Remove tag support from no_std
Browse files Browse the repository at this point in the history
For tagging to work under no-std mode, the tags would need to be stored
in the backup queue. For notifications for std::usize::MAX listeners,
this is not possible. Therefore the only strategy left to use that doesn't
massively constrain the use of tagging is to remove tags from no_std
mode.

Closes #73

Signed-off-by: John Nunley <dev@notgull.net>
  • Loading branch information
notgull committed Aug 17, 2023
1 parent 1c95cd2 commit 564b84b
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 31 deletions.
12 changes: 9 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,23 +176,27 @@ impl<T> fmt::Debug for Event<T> {
}
}

impl<T> Default for Event<T> {
impl Default for Event {
#[inline]
fn default() -> Self {
Self::with_tag()
Self::new()
}
}

impl<T> Event<T> {
/// Creates a new `Event` with a tag type.
///
/// Tagging cannot be implemented efficiently on `no_std`, so this is only available when the
/// `std` feature is enabled.
///
/// # Examples
///
/// ```
/// use event_listener::Event;
///
/// let event = Event::<usize>::with_tag();
/// ```
#[cfg(feature = "std")]
#[inline]
pub const fn with_tag() -> Self {
Self {
Expand Down Expand Up @@ -449,7 +453,9 @@ impl Event<()> {
/// ```
#[inline]
pub const fn new() -> Self {
Self::with_tag()
Self {
inner: AtomicPtr::new(ptr::null_mut()),
}
}

/// Notifies a number of active listeners without emitting a `SeqCst` fence.
Expand Down
20 changes: 3 additions & 17 deletions src/no_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ mod node;
#[path = "no_std/queue.rs"]
mod queue;

use node::{Node, TaskWaiting};
use node::{Node, NothingProducer, TaskWaiting};
use queue::Queue;

use crate::notify::{GenericNotify, Internal, Notification};
Expand Down Expand Up @@ -110,7 +110,7 @@ impl<T> crate::Inner<T> {

/// Notifies a number of entries.
#[cold]
pub(crate) fn notify(&self, mut notify: impl Notification<Tag = T>) -> usize {
pub(crate) fn notify(&self, notify: impl Notification<Tag = T>) -> usize {
match self.try_lock() {
Some(mut guard) => {
// Notify the listeners.
Expand All @@ -122,21 +122,7 @@ impl<T> crate::Inner<T> {
let node = Node::Notify(GenericNotify::new(
notify.count(Internal::new()),
notify.is_additional(Internal::new()),
{
// Collect every tag we need.
let tags = {
let count = notify.count(Internal::new());
let mut tags = Vec::with_capacity(count);
for _ in 0..count {
tags.push(notify.next_tag(Internal::new()));
}

// Convert into an iterator.
tags.into_iter()
};

node::VecProducer(tags)
},
NothingProducer::default(),
));

self.list.queue.push(node);
Expand Down
22 changes: 17 additions & 5 deletions src/no_std/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,30 @@ use crate::sys::ListenerSlab;
use crate::{State, Task};

use alloc::boxed::Box;
use alloc::vec::IntoIter as VecIter;

use core::marker::PhantomData;
use core::mem;
use core::num::NonZeroUsize;
use core::ptr;

pub(crate) struct VecProducer<T>(pub(crate) VecIter<T>);
pub(crate) struct NothingProducer<T>(PhantomData<T>);

impl<T> TagProducer for VecProducer<T> {
impl<T> Default for NothingProducer<T> {
fn default() -> Self {
Self(PhantomData)
}
}

impl<T> TagProducer for NothingProducer<T> {
type Tag = T;

fn next_tag(&mut self) -> Self::Tag {
self.0.next().unwrap()
// This has to be a zero-sized type with no drop handler.
assert_eq!(mem::size_of::<Self::Tag>(), 0);
assert!(!mem::needs_drop::<Self::Tag>());

// SAFETY: As this is a ZST without a drop handler, zero is valid.
unsafe { mem::zeroed() }
}
}

Expand All @@ -35,7 +47,7 @@ pub(crate) enum Node<T> {
},

/// This node is notifying a listener.
Notify(GenericNotify<VecProducer<T>>),
Notify(GenericNotify<NothingProducer<T>>),

/// This node is removing a listener.
RemoveListener {
Expand Down
8 changes: 2 additions & 6 deletions src/no_std/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,16 +131,12 @@ impl<T> Drop for Queue<T> {
#[cfg(test)]
mod tests {
use crate::notify::{GenericNotify, Internal, NotificationPrivate};
use crate::sys::node::VecProducer;
use crate::sys::node::NothingProducer;

use super::*;

fn node_from_num(num: usize) -> Node<()> {
Node::Notify(GenericNotify::new(
num,
true,
VecProducer(vec![(); num].into_iter()),
))
Node::Notify(GenericNotify::new(num, true, NothingProducer::default()))
}

fn node_to_num(node: Node<()>) -> usize {
Expand Down
5 changes: 5 additions & 0 deletions src/notify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ impl<N: fmt::Debug, F> fmt::Debug for TagWith<N, F> {

impl<N, F> TagWith<N, F> {
/// Create a new `TagFn` with the given tag function and notification.
#[cfg(feature = "std")]
fn new(tag: F, inner: N) -> Self {
Self { tag, inner }
}
Expand Down Expand Up @@ -494,6 +495,9 @@ pub trait IntoNotification: __private::Sealed {
/// it is possible to optimize a `Mutex` implementation by locking directly on the next listener, without
/// needing to ever unlock the mutex at all.
///
/// Tagging functions cannot be implemented efficiently for `no_std`, so this is only available
/// when the `std` feature is enabled.
///
/// # Examples
///
/// ```
Expand All @@ -511,6 +515,7 @@ pub trait IntoNotification: __private::Sealed {
/// assert_eq!(listener1.as_mut().wait(), true);
/// assert_eq!(listener2.as_mut().wait(), false);
/// ```
#[cfg(feature = "std")]
fn tag_with<T, F>(self, tag: F) -> TagWith<Self::Notify, F>
where
Self: Sized + IntoNotification<Tag = ()>,
Expand Down

0 comments on commit 564b84b

Please sign in to comment.