Skip to content

Commit

Permalink
Replace boxed closure with a vec of tags
Browse files Browse the repository at this point in the history
This should hopefully reduce the amount of allocation on the cold no_std
path
  • Loading branch information
notgull committed May 10, 2023
1 parent a8c3aa7 commit 9275054
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 23 deletions.
18 changes: 2 additions & 16 deletions src/no_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ use core::num::NonZeroUsize;
use core::ops;
use core::pin::Pin;

use alloc::boxed::Box;
use alloc::vec::Vec;

impl<T> crate::Inner<T> {
Expand Down Expand Up @@ -125,7 +124,7 @@ impl<T> crate::Inner<T> {
notify.is_additional(Internal::new()),
{
// Collect every tag we need.
let mut tags = {
let tags = {
let count = notify.count(Internal::new());
let mut tags = Vec::with_capacity(count);
for _ in 0..count {
Expand All @@ -136,20 +135,7 @@ impl<T> crate::Inner<T> {
tags.into_iter()
};

// Function that iterates over the tags.
let tags = Box::new(move || tags.next().unwrap());

// SAFETY: The generic `GenericNotify` expects a `Box` that is `Send` and
// `Sync`. The `tags` function is `Send` if `T` is `Send`, and `Sync` if `T`
// is `Sync`. However, the end result (the `Event`) is `Send` and `Sync` if
// `T` is `Send` and `Sync`, so we can safely assume that the `Box` is
// going to be handled safely.
//
// This also works out lifetime wise, since the Box<dyn FnMut()> does not
// outlive the `Event`.
unsafe {
mem::transmute::<Box<dyn FnMut() -> T>, node::GenericTags<T>>(tags)
}
node::VecProducer(tags)
},
));

Expand Down
15 changes: 12 additions & 3 deletions src/no_std/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@

//! The node that makes up queues.

use crate::notify::GenericNotify;
use crate::notify::{GenericNotify, TagProducer};
use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering};
use crate::sync::Arc;
use crate::sys::ListenerSlab;
use crate::{State, Task};

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

use core::num::NonZeroUsize;
use core::ptr;

pub(super) type GenericTags<T> = Box<dyn FnMut() -> T + Send + Sync + 'static>;
pub(crate) struct VecProducer<T>(pub(crate) VecIter<T>);

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

fn next_tag(&mut self) -> Self::Tag {
self.0.next().unwrap()
}
}

/// A node in the backup queue.
pub(crate) enum Node<T> {
Expand All @@ -26,7 +35,7 @@ pub(crate) enum Node<T> {
},

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

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

use super::*;

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

fn node_to_num(node: Node<()>) -> usize {
Expand Down
22 changes: 19 additions & 3 deletions src/notify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ pub(crate) struct GenericNotify<F> {
tags: F,
}

impl<T, F: FnMut() -> T> GenericNotify<F> {
impl<T, F: TagProducer<Tag = T>> GenericNotify<F> {
pub(crate) fn new(count: usize, additional: bool, tags: F) -> Self {
Self {
count,
Expand All @@ -273,7 +273,7 @@ impl<T, F: FnMut() -> T> GenericNotify<F> {
}
}

impl<T, F: FnMut() -> T> NotificationPrivate for GenericNotify<F> {
impl<T, F: TagProducer<Tag = T>> NotificationPrivate for GenericNotify<F> {
type Tag = T;

fn is_additional(&self, _: Internal) -> bool {
Expand All @@ -289,7 +289,23 @@ impl<T, F: FnMut() -> T> NotificationPrivate for GenericNotify<F> {
}

fn next_tag(&mut self, _: Internal) -> Self::Tag {
(self.tags)()
self.tags.next_tag()
}
}

/// The producer for a generic notification.
pub(crate) trait TagProducer {
type Tag;

/// Get the next tag.
fn next_tag(&mut self) -> Self::Tag;
}

impl<T, F: FnMut() -> T> TagProducer for F {
type Tag = T;

fn next_tag(&mut self) -> T {
(self)()
}
}

Expand Down

0 comments on commit 9275054

Please sign in to comment.