Skip to main content

Event

Struct Event 

Source
pub struct Event { /* private fields */ }
Expand description

A notification that tasks can wait for.

A task that waits for a condition to change, such as a lock being released or a queue getting room, calls Event::listen and awaits the EventListener it gets. Whoever changes the condition notifies the event, which wakes the tasks that are listening.

An event carries no value and knows nothing about the condition. It only reports that something changed.

An event needs no runtime and works under any executor. It can be notified from any thread, from inside a task or from outside one. An EventListener is a plain Future. A notification wakes the task that polled it last.

§Listen, then check

A notification reaches only the listeners that exist when it is sent. It is not kept for listeners created later. So a task creates its listener first, then checks the condition, and awaits the listener only if the check fails:

  • A change made before the check is seen by the check.
  • A change made after the check is heard by the listener.

If the task checks first and listens afterwards, a notification can arrive between the two and go unheard, and the task waits forever.

§Which listeners are notified

Listeners are notified oldest first. A listener counts as notified from the moment a notification reaches it until it completes or is dropped.

  • Event::notify counts the listeners that are already notified. notify(n) notifies listeners until at least n are notified, so calling notify(1) twice in a row notifies one listener, not two. Use it when only one waiter can act on the change, such as a lock that only one task can take.
  • Event::notify_additional ignores that count. notify_additional(n) notifies n more listeners. Use it when n more waiters can act on the change, such as a queue that gained n items.

notify(usize::MAX) notifies every listener.

A listener that is dropped after it was notified passes its notification on. See EventListener.

§Wakers

The event records a notification before it wakes any task. So a waker can call back into the same event from wake: it can notify the event, listen to it or drop one of its listeners. A listener that another thread polls in the meantime already sees its notification and completes.

§Example

One thread raises a flag and a task waits for it. The example runs the task with block_on from the futures crate, but any executor works:

use std::{
    sync::{
        Arc,
        atomic::{AtomicBool, Ordering},
    },
    thread,
};

use futures::executor::block_on;
use zruntime::Event;

/// A flag that is raised once, and the event that announces it.
struct Flag {
    raised: AtomicBool,
    event: Event,
}

let flag = Arc::new(Flag {
    raised: AtomicBool::new(false),
    event: Event::new(),
});
let raiser = thread::spawn({
    let flag = flag.clone();
    move || {
        flag.raised.store(true, Ordering::Release);
        flag.event.notify(usize::MAX);
    }
});

block_on(async {
    // Listen before the check, so a change that the check misses is heard by the listener.
    let listener = flag.event.listen();
    if !flag.raised.load(Ordering::Acquire) {
        listener.await;
    }
});

assert!(flag.raised.load(Ordering::Acquire));
raiser.join().expect("the other thread did not panic");

Implementations§

Source§

impl Event

Source

pub const fn new() -> Self

Creates an event with no listeners.

This is a const fn and does not allocate, so an event can live in a static.

Source

pub fn listen(&self) -> EventListener ⓘ

Creates a listener that completes when a notification reaches it.

The listener is in the event’s queue when this returns. Every notification sent from then on can reach it, even if it has not been polled yet.

See listen, then check for why to create the listener before the last check of the condition it is for.

Source

pub fn notify(&self, n: usize) -> usize

Notifies the oldest listeners that are not notified yet, until at least n are notified.

Listeners that were notified earlier count towards n until they complete or are dropped. So this notifies nobody if n listeners are notified already, and notify(usize::MAX) notifies every listener. It stops early if there are no more listeners to notify.

Wakes the task that last polled each listener it notifies.

Returns the number of listeners this call notified.

§Panics

If a waker panics, the tasks of the other notified listeners are still woken. The first panic is then raised from this call.

§Example
use std::{
    future::Future,
    pin::Pin,
    task::{Context, Waker},
};

use zruntime::Event;

let event = Event::new();
let mut first = event.listen();
let mut second = event.listen();
// Poll the listeners by hand, with a waker that does nothing.
let mut cx = Context::from_waker(Waker::noop());

assert_eq!(event.notify(1), 1);
// The first listener is still notified, so `notify(1)` notifies nobody.
assert_eq!(event.notify(1), 0);

assert!(Pin::new(&mut first).poll(&mut cx).is_ready());
assert!(Pin::new(&mut second).poll(&mut cx).is_pending());
// The first listener has completed and no longer counts, so this notifies the second.
assert_eq!(event.notify(1), 1);
assert!(Pin::new(&mut second).poll(&mut cx).is_ready());
Source

pub fn notify_additional(&self, n: usize) -> usize

Notifies up to n more listeners, whether or not other listeners are notified already.

These are the n oldest listeners that are not notified yet, or all of them if there are fewer. Wakes the task that last polled each listener it notifies.

Returns the number of listeners this call notified.

§Panics

If a waker panics, the tasks of the other notified listeners are still woken. The first panic is then raised from this call.

§Example
use std::{
    future::Future,
    pin::Pin,
    task::{Context, Waker},
};

use zruntime::Event;

let event = Event::new();
let mut first = event.listen();
let mut second = event.listen();
// Poll the listeners by hand, with a waker that does nothing.
let mut cx = Context::from_waker(Waker::noop());

assert_eq!(event.notify(1), 1);
// There is one more thing to act on, so notify one more listener.
assert_eq!(event.notify_additional(1), 1);

assert!(Pin::new(&mut first).poll(&mut cx).is_ready());
assert!(Pin::new(&mut second).poll(&mut cx).is_ready());

Trait Implementations§

Source§

impl Debug for Event

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Event

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl !Freeze for Event

§

impl RefUnwindSafe for Event

§

impl Send for Event

§

impl Sync for Event

§

impl Unpin for Event

§

impl UnsafeUnpin for Event

§

impl UnwindSafe for Event

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more