Skip to content

Commit ac3978e

Browse files
committed
docs: Document that EventListeners must be listen'd on
In retrospect it is sometimes unclear in the documentation that the new event listener needs to be pinned and inserted into the list before it can receive events. This PR adds documentation that should clarify this issue. Closes #89 Signed-off-by: John Nunley <[email protected]>
1 parent ccd2dfe commit ac3978e

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed

src/lib.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,25 @@ impl<T> Event<T> {
237237
/// let event = Event::new();
238238
/// let listener = event.listen();
239239
/// ```
240+
///
241+
/// # Caveats
242+
///
243+
/// The above example is equivalent to this code:
244+
///
245+
/// ```
246+
/// use event_listener::{Event, EventListener};
247+
///
248+
/// let event = Event::new();
249+
/// let mut listener = Box::pin(EventListener::new(&event));
250+
/// listener.as_mut().listen();
251+
/// ```
252+
///
253+
/// It creates a new listener, pins it to the heap, and inserts it into the linked list
254+
/// of listeners. While this type of usage is simple, it may be desired to eliminate this
255+
/// heap allocation. In this case, consider using the [`EventListener::new`] constructor
256+
/// directly, which allows for greater control over where the [`EventListener`] is
257+
/// allocated. However, users of this `new` method must be careful to ensure that the
258+
/// [`EventListener`] is `listen`ing before waiting on it; panics may occur otherwise.
240259
#[cold]
241260
pub fn listen(&self) -> Pin<Box<EventListener<T>>> {
242261
let mut listener = Box::pin(EventListener::new(self));
@@ -638,6 +657,77 @@ pin_project_lite::pin_project! {
638657
/// If a notified listener is dropped without receiving a notification, dropping will notify
639658
/// another active listener. Whether one *additional* listener will be notified depends on what
640659
/// kind of notification was delivered.
660+
///
661+
/// The listener is not registered into the linked list inside of the [`Event`] by default. It
662+
/// needs to be pinned first before being inserted using the `listen()` method. After the
663+
/// listener has begun `listen`ing, the user can `await` it like a future or call `wait()`
664+
/// to block the current thread until it is notified.
665+
///
666+
/// ## Examples
667+
///
668+
/// ```
669+
/// use event_listener::{Event, EventListener};
670+
/// use std::sync::{Arc, atomic::{AtomicBool, Ordering}};
671+
/// use std::thread;
672+
/// use std::time::Duration;
673+
///
674+
/// // Some flag to wait on.
675+
/// let flag = Arc::new(AtomicBool::new(false));
676+
///
677+
/// // Create an event to wait on.
678+
/// let event = Event::new();
679+
///
680+
/// thread::spawn({
681+
/// let flag = flag.clone();
682+
/// move || {
683+
/// thread::sleep(Duration::from_secs(2));
684+
/// flag.store(true, Ordering::SeqCst);
685+
///
686+
/// // Wake up the listener.
687+
/// event.notify_additional(std::usize::MAX);
688+
/// }
689+
/// });
690+
///
691+
/// let listener = EventListener::new(&event);
692+
///
693+
/// // Make sure that the event listener is pinned before doing anything else.
694+
/// //
695+
/// // We pin the listener to the stack here, as it lets us avoid a heap allocation.
696+
/// futures_lite::pin!(listener);
697+
///
698+
/// // Wait for the flag to become ready.
699+
/// loop {
700+
/// if flag.load(Ordering::Acquire) {
701+
/// // We are done.
702+
/// break;
703+
/// }
704+
///
705+
/// if listener.is_listening() {
706+
/// // We are inserted into the linked list and we can now wait.
707+
/// listener.as_mut().wait();
708+
/// } else {
709+
/// // We need to insert ourselves into the list. Since this insertion is an atomic
710+
/// // operation, we should check the flag again before waiting.
711+
/// listener.as_mut().listen();
712+
/// }
713+
/// }
714+
/// ```
715+
///
716+
/// The above example is equivalent to the one provided in the crate level example. However,
717+
/// it has some advantages. By directly creating the listener with `EventListener::new()`,
718+
/// we have control over how the listener is handled in memory. We take advantage of this by
719+
/// pinning the `listener` variable to the stack using the [`futures_lite::pin`] macro. In
720+
/// contrast, `Event::listen` binds the listener to the heap.
721+
///
722+
/// However, this additional power comes with additional responsibility. By default, the
723+
/// event listener is created in an "uninserted" state. This property means that any
724+
/// notifications delivered to the [`Event`] by default will not wake up this listener.
725+
/// Before any notifications can be received, the `listen()` method must be called on
726+
/// `EventListener` to insert it into the list of listeners. After a `.await` or a `wait()`
727+
/// call has completed, `listen()` must be called again if the user is still interested in
728+
/// any events.
729+
///
730+
/// [`futures_lite::pin`]: https://docs.rs/futures-lite/latest/futures_lite/macro.pin.html
641731
#[project(!Unpin)] // implied by Listener, but can generate better docs
642732
pub struct EventListener<T = ()> {
643733
#[pin]
@@ -656,6 +746,23 @@ impl<T> fmt::Debug for EventListener<T> {
656746

657747
impl<T> EventListener<T> {
658748
/// Create a new `EventListener` that will wait for a notification from the given [`Event`].
749+
///
750+
/// This function does not register the `EventListener` into the linked list of listeners
751+
/// contained within the [`Event`]. Make sure to call `listen` before `await`ing on
752+
/// this future or calling `wait()`.
753+
///
754+
/// ## Examples
755+
///
756+
/// ```
757+
/// use event_listener::{Event, EventListener};
758+
///
759+
/// let event = Event::new();
760+
/// let listener = EventListener::new(&event);
761+
///
762+
/// // Make sure that the listener is pinned and listening before doing anything else.
763+
/// let mut listener = Box::pin(listener);
764+
/// listener.as_mut().listen();
765+
/// ```
659766
pub fn new(event: &Event<T>) -> Self {
660767
let inner = event.inner();
661768

src/notify.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ pub(crate) use __private::Internal;
99
/// The type of notification to use with an [`Event`].
1010
///
1111
/// This is hidden and sealed to prevent changes to this trait from being breaking.
12+
///
13+
/// [`Event`]: crate::Event
1214
#[doc(hidden)]
1315
pub trait NotificationPrivate {
1416
/// The tag data associated with a notification.
@@ -52,6 +54,8 @@ pub trait NotificationPrivate {
5254
///
5355
/// notify(&Event::new(), 1.additional());
5456
/// ```
57+
///
58+
/// [`Event`]: crate::Event
5559
pub trait Notification: NotificationPrivate {}
5660
impl<N: NotificationPrivate + ?Sized> Notification for N {}
5761

0 commit comments

Comments
 (0)