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::notifycounts the listeners that are already notified.notify(n)notifies listeners until at leastnare notified, so callingnotify(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_additionalignores that count.notify_additional(n)notifiesnmore listeners. Use it whennmore waiters can act on the change, such as a queue that gainednitems.
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
impl Event
Sourcepub const fn new() -> Self
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.
Sourcepub fn listen(&self) -> EventListener ⓘ
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.
Sourcepub fn notify(&self, n: usize) -> usize
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());Sourcepub fn notify_additional(&self, n: usize) -> usize
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());