event-listener/src/list.rs

238 lines
6.3 KiB
Rust

//! The inner list of listeners.
use crate::sync::cell::Cell;
use crate::Task;
use core::mem;
use core::num::NonZeroUsize;
use slab::Slab;
/// The state of a listener.
pub(crate) enum State {
/// It has just been created.
Created,
/// It has received a notification.
///
/// The `bool` is `true` if this was an "additional" notification.
Notified(bool),
/// A task is polling it.
Task(Task),
}
impl State {
/// Returns `true` if this is the `Notified` state.
#[inline]
pub(crate) fn is_notified(&self) -> bool {
match self {
State::Notified(_) => true,
_ => false,
}
}
}
/// An entry representing a registered listener.
pub(crate) struct Entry {
/// The state of this listener.
state: Cell<State>,
/// Previous entry in the linked list.
prev: Cell<Option<NonZeroUsize>>,
/// Next entry in the linked list.
next: Cell<Option<NonZeroUsize>>,
}
/// A linked list of entries.
pub(crate) struct List {
/// The raw list of entries.
entries: Slab<Entry>,
/// First entry in the list.
head: Option<NonZeroUsize>,
/// Last entry in the list.
tail: Option<NonZeroUsize>,
/// The first unnotified entry in the list.
start: Option<NonZeroUsize>,
/// The number of notified entries in the list.
pub(crate) notified: usize,
}
impl List {
/// Create a new, empty list.
pub(crate) fn new() -> Self {
// Create a Slab with a permanent entry occupying index 0, so that
// it is never used (and we can therefore use 0 as a sentinel value).
let mut entries = Slab::new();
entries.insert(Entry::new());
Self {
entries,
head: None,
tail: None,
start: None,
notified: 0,
}
}
/// Get the number of entries in the list.
pub(crate) fn len(&self) -> usize {
self.entries.len() - 1
}
/// Get the state of the entry at the given index.
pub(crate) fn state(&self, index: NonZeroUsize) -> &Cell<State> {
&self.entries[index.get()].state
}
/// Inserts a new entry into the list.
pub(crate) fn insert(&mut self, entry: Entry) -> NonZeroUsize {
// Replace the tail with the new entry.
let key = NonZeroUsize::new(self.entries.vacant_key()).unwrap();
match mem::replace(&mut self.tail, Some(key)) {
None => self.head = Some(key),
Some(t) => {
self.entries[t.get()].next.set(Some(key));
entry.prev.set(Some(t));
}
}
// If there were no unnotified entries, this one is the first now.
if self.start.is_none() {
self.start = self.tail;
}
// Insert the entry into the slab.
self.entries.insert(entry);
// Return the key.
key
}
/// Removes an entry from the list and returns its state.
pub(crate) fn remove(&mut self, key: NonZeroUsize) -> State {
let entry = self.entries.remove(key.get());
let prev = entry.prev.get();
let next = entry.next.get();
// Unlink from the previous entry.
match prev {
None => self.head = next,
Some(p) => self.entries[p.get()].next.set(next),
}
// Unlink from the next entry.
match next {
None => self.tail = prev,
Some(n) => self.entries[n.get()].prev.set(prev),
}
// If this was the first unnotified entry, move the pointer to the next one.
if self.start == Some(key) {
self.start = next;
}
// Extract the state.
let state = entry.state.replace(State::Created);
// Update the counters.
if state.is_notified() {
self.notified = self.notified.saturating_sub(1);
}
state
}
/// Notifies a number of entries, either normally or as an additional notification.
#[cold]
pub(crate) fn notify(&mut self, count: usize, additional: bool) {
if additional {
self.notify_additional(count);
} else {
self.notify_unnotified(count);
}
}
/// Notifies a number of entries.
#[cold]
pub(crate) fn notify_unnotified(&mut self, mut n: usize) {
if n <= self.notified {
return;
}
n -= self.notified;
while n > 0 {
n -= 1;
// Notify the first unnotified entry.
match self.start {
None => break,
Some(e) => {
// Get the entry and move the pointer forward.
let e = &self.entries[e.get()];
self.start = e.next.get();
// Set the state of this entry to `Notified` and notify.
let was_notified = e.notify(false);
// Update the counter.
self.notified += was_notified as usize;
}
}
}
}
/// Notifies a number of additional entries.
#[cold]
pub(crate) fn notify_additional(&mut self, mut n: usize) {
while n > 0 {
n -= 1;
// Notify the first unnotified entry.
match self.start {
None => break,
Some(e) => {
// Get the entry and move the pointer forward.
let e = &self.entries[e.get()];
self.start = e.next.get();
// Set the state of this entry to `Notified` and notify.
let was_notified = e.notify(true);
// Update the counter.
self.notified += was_notified as usize;
}
}
}
}
}
impl Entry {
/// Create a new, empty entry.
pub(crate) fn new() -> Self {
Self {
state: Cell::new(State::Created),
prev: Cell::new(None),
next: Cell::new(None),
}
}
/// Indicate that this entry has been notified.
#[cold]
pub(crate) fn notify(&self, additional: bool) -> bool {
match self.state.replace(State::Notified(additional)) {
State::Notified(_) => {}
State::Created => {}
State::Task(w) => w.wake(),
}
// Return whether the notification would have had any effect.
true
}
}