Skip to content

Commit

Permalink
Review comments
Browse files Browse the repository at this point in the history
Signed-off-by: John Nunley <dev@notgull.net>
  • Loading branch information
notgull committed Dec 21, 2023
1 parent 2759547 commit f658705
Showing 1 changed file with 90 additions and 6 deletions.
96 changes: 90 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,7 @@ impl<T> Drop for Event<T> {
///
/// This trait represents a type waiting for a notification from an [`Event`]. See the
/// [`EventListener`] type for more documentation on this trait's usage.
pub trait Listener<T>: Future<Output = T> + __sealed::Sealed {
pub trait Listener<T = ()>: Future<Output = T> + __sealed::Sealed {
/// Blocks until a notification is received.
///
/// # Examples
Expand Down Expand Up @@ -851,11 +851,6 @@ macro_rules! forward_impl_to_listener {
///
/// See the [`Listener`] trait for the functionality exposed by this type.
///
/// The listener is not registered into the linked list inside of the [`Event`] by default if
/// it is created via the `new()` method. It needs to be pinned first before being inserted
/// using the `listen()` method. After the listener has begun `listen`ing, the user can
/// `await` it like a future or call `wait()` to block the current thread until it is notified.
///
/// This structure allocates the listener on the heap.
pub struct EventListener<T = ()> {
listener: Pin<Box<InnerListener<T, Arc<Inner<T>>>>>,
Expand Down Expand Up @@ -889,6 +884,95 @@ impl<T> EventListener<T> {
forward_impl_to_listener! { T => EventListener<T> }

/// Create a stack-based event listener for an [`Event`].
///
/// [`EventListener`] allocates the listener on the heap. While this works for most use cases, in
/// practice this heap allocation can be expensive for repeated uses. This method allows for
/// allocating the listener on the stack instead.
///
/// There are limitations to using this macro instead of the [`EventListener`] type, however.
/// Firstly, it is significantly less flexible. The listener is locked to the current stack
/// frame, meaning that it can't be returned or put into a place where it would go out of
/// scope. For instance, this will not work:
///
/// ```compile_fail
/// use event_listener::{Event, Listener, listener};
///
/// fn get_listener(event: &Event) -> impl Listener {
/// listener!(event => cant_return_this);
/// cant_return_this
/// }
/// ```
///
/// In addition, the types involved in creating this listener are not able to be named. Therefore
/// it cannot be used in hand-rolled futures or similar structures.
///
/// The type created by this macro implements [`Listener`], allowing it to be used in cases where
/// [`EventListener`] would normally be used.
///
/// ## Example
///
/// To use this macro, replace cases where you would normally use this...
///
/// ```no_compile
/// let listener = event.listen();
/// ```
///
/// ...with this:
///
/// ```no_compile
/// listener!(event => listener);
/// ```
///
/// Here is the top level example from this crate's documentation, but using [`listener`] instead
/// of [`EventListener`].
///
/// ```
/// use std::sync::atomic::{AtomicBool, Ordering};
/// use std::sync::Arc;
/// use std::thread;
/// use std::time::Duration;
/// use std::usize;
/// use event_listener::{Event, listener, prelude::*};
///
/// let flag = Arc::new(AtomicBool::new(false));
/// let event = Arc::new(Event::new());
///
/// // Spawn a thread that will set the flag after 1 second.
/// thread::spawn({
/// let flag = flag.clone();
/// let event = event.clone();
/// move || {
/// // Wait for a second.
/// thread::sleep(Duration::from_secs(1));
///
/// // Set the flag.
/// flag.store(true, Ordering::SeqCst);
///
/// // Notify all listeners that the flag has been set.
/// event.notify(usize::MAX);
/// }
/// });
///
/// // Wait until the flag is set.
/// loop {
/// // Check the flag.
/// if flag.load(Ordering::SeqCst) {
/// break;
/// }
///
/// // Start listening for events.
/// // NEW: Changed to a stack-based listener.
/// listener!(event => listener);
///
/// // Check the flag again after creating the listener.
/// if flag.load(Ordering::SeqCst) {
/// break;
/// }
///
/// // Wait for a notification and continue the loop.
/// listener.wait();
/// }
/// ```
#[macro_export]
macro_rules! listener {
($event:expr => $listener:ident) => {
Expand Down

0 comments on commit f658705

Please sign in to comment.