#![no_std]
#![deny(missing_docs)]
extern crate alloc;
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use core::{
sync::atomic::{AtomicBool, Ordering},
task::Waker,
};
use ax_lazyinit::OnceLock;
use ax_sync::SpinLock;
use axpoll::{IoEvents, PollRegistration, PollSource, RegistrationMode};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct RegistrationId(u64);
struct Entry {
id: RegistrationId,
waker: Waker,
notified: Arc<AtomicBool>,
interests: IoEvents,
mode: RegistrationMode,
}
struct Inner {
entries: Vec<Entry>,
next_id: u64,
closed: bool,
}
impl Inner {
const fn new() -> Self {
Self {
entries: Vec::new(),
next_id: 0,
closed: false,
}
}
fn register(
&mut self,
waker: &Waker,
interests: IoEvents,
mode: RegistrationMode,
) -> Option<(RegistrationId, Arc<AtomicBool>)> {
if self.closed || interests.is_empty() {
return None;
}
let id = RegistrationId(self.next_id);
let notified = Arc::new(AtomicBool::new(false));
self.next_id = self
.next_id
.checked_add(1)
.expect("poll registration ID space exhausted");
self.entries.push(Entry {
id,
waker: waker.clone(),
notified: notified.clone(),
interests,
mode,
});
Some((id, notified))
}
fn unregister(&mut self, id: RegistrationId) {
if let Some(index) = self.entries.iter().position(|entry| entry.id == id) {
self.entries.remove(index);
}
}
fn wake_boundary(&self) -> u64 {
self.next_id
}
fn take_next_matching(
&mut self,
ready: IoEvents,
boundary: u64,
exclusive_available: bool,
) -> Option<Entry> {
let index = self.entries.iter().position(|entry| {
entry.id.0 < boundary
&& entry.interests.intersects(ready)
&& (entry.mode == RegistrationMode::Shared || exclusive_available)
})?;
self.entries[index].notified.store(true, Ordering::Release);
Some(self.entries.remove(index))
}
fn take_next_before(&mut self, boundary: u64) -> Option<Entry> {
let index = self
.entries
.iter()
.position(|entry| entry.id.0 < boundary)?;
self.entries[index].notified.store(true, Ordering::Release);
Some(self.entries.remove(index))
}
}
struct PollState(SpinLock<Inner>);
impl PollState {
const fn new() -> Self {
Self(SpinLock::new(Inner::new()))
}
fn register(
&self,
waker: &Waker,
interests: IoEvents,
mode: RegistrationMode,
) -> Option<(RegistrationId, Arc<AtomicBool>)> {
self.0.lock().register(waker, interests, mode)
}
fn unregister(&self, id: RegistrationId) {
self.0.lock().unregister(id);
}
fn wake_with(
&self,
ready: IoEvents,
mut exclusive_budget: usize,
wake: &mut impl FnMut(Waker),
) -> usize {
let boundary = self.0.lock().wake_boundary();
let mut woke = 0;
loop {
let entry = self
.0
.lock()
.take_next_matching(ready, boundary, exclusive_budget != 0);
let Some(entry) = entry else {
return woke;
};
if entry.mode == RegistrationMode::Exclusive {
exclusive_budget -= 1;
}
wake(entry.waker);
woke += 1;
}
}
fn close(&self) {
let boundary = {
let mut inner = self.0.lock();
inner.closed = true;
inner.wake_boundary()
};
loop {
let entry = self.0.lock().take_next_before(boundary);
let Some(entry) = entry else {
return;
};
entry.waker.wake();
}
}
}
struct Registration {
state: Arc<PollState>,
id: RegistrationId,
notified: Arc<AtomicBool>,
}
impl PollRegistration for Registration {
fn was_notified(&self) -> bool {
self.notified.load(Ordering::Acquire)
}
}
impl Drop for Registration {
fn drop(&mut self) {
self.state.unregister(self.id);
}
}
pub struct PollSet(OnceLock<Arc<PollState>>);
impl Default for PollSet {
fn default() -> Self {
Self::new()
}
}
impl PollSet {
pub const fn new() -> Self {
Self(OnceLock::new())
}
fn state(&self) -> Arc<PollState> {
Arc::clone(self.0.call_once(|| Arc::new(PollState::new())))
}
pub unsafe fn wake(&self, ready: IoEvents) -> usize {
let Some(state) = self.0.get() else {
return 0;
};
state.wake_with(ready, 1, &mut Waker::wake)
}
pub unsafe fn wake_with(&self, ready: IoEvents, mut wake: impl FnMut(Waker)) -> usize {
let Some(state) = self.0.get() else {
return 0;
};
state.wake_with(ready, 1, &mut wake)
}
pub unsafe fn wake_all(&self, ready: IoEvents) -> usize {
let Some(state) = self.0.get() else {
return 0;
};
state.wake_with(ready, usize::MAX, &mut Waker::wake)
}
}
impl PollSource for PollSet {
unsafe fn register(
&self,
waker: &Waker,
interests: IoEvents,
mode: RegistrationMode,
) -> Option<Box<dyn PollRegistration>> {
let state = self.state();
state
.register(waker, interests, mode)
.map(|(id, notified)| {
let registration: Box<dyn PollRegistration> = Box::new(Registration {
state,
id,
notified,
});
registration
})
}
}
impl Drop for PollSet {
fn drop(&mut self) {
if let Some(state) = self.0.get() {
state.close();
}
}
}