#![no_std]
#![deny(missing_docs)]
extern crate alloc;
use alloc::{boxed::Box, vec::Vec};
use core::{
mem::MaybeUninit,
task::{Context, Waker},
};
use ax_kspin::SpinNoIrq;
use bitflags::bitflags;
use linux_raw_sys::general::*;
use spin::Once;
bitflags! {
#[derive(Debug, Clone, Copy)]
pub struct IoEvents: u32 {
const IN = POLLIN;
const PRI = POLLPRI;
const OUT = POLLOUT;
const ERR = POLLERR;
const HUP = POLLHUP;
const NVAL = POLLNVAL;
const RDNORM = POLLRDNORM;
const RDBAND = POLLRDBAND;
const WRNORM = POLLWRNORM;
const WRBAND = POLLWRBAND;
const MSG = POLLMSG;
const REMOVE = POLLREMOVE;
const RDHUP = POLLRDHUP;
const ALWAYS_POLL = Self::ERR.bits() | Self::HUP.bits();
}
}
pub trait Pollable {
fn poll(&self) -> IoEvents;
fn register(&self, context: &mut Context<'_>, events: IoEvents);
}
const POLL_SET_CAPACITY: usize = 64;
struct Entry {
waker: Waker,
interests: IoEvents,
}
impl Entry {
fn wake(self) {
self.waker.wake();
}
}
struct Inner {
entries: Box<[MaybeUninit<Entry>]>,
cursor: usize,
}
impl Inner {
fn new() -> Self {
Self {
entries: Box::new_uninit_slice(POLL_SET_CAPACITY),
cursor: 0,
}
}
fn len(&self) -> usize {
self.cursor.min(POLL_SET_CAPACITY)
}
fn is_empty(&self) -> bool {
self.cursor == 0
}
fn push_entry(&mut self, entry: Entry) {
debug_assert!(self.cursor < POLL_SET_CAPACITY);
let slot = self.cursor;
self.cursor += 1;
self.entries[slot].write(entry);
}
fn register(&mut self, waker: &Waker, interests: IoEvents) -> Option<Entry> {
let slot = self.cursor % POLL_SET_CAPACITY;
let replaced = if self.cursor >= POLL_SET_CAPACITY {
let old = unsafe { self.entries[slot].assume_init_read() };
let replaced = (!old.waker.will_wake(waker)).then_some(old);
self.cursor = ((slot + 1) % POLL_SET_CAPACITY) + POLL_SET_CAPACITY;
replaced
} else {
self.cursor += 1;
None
};
self.entries[slot].write(Entry {
waker: waker.clone(),
interests,
});
replaced
}
fn drain_ready(&mut self, ready: IoEvents, ready_entries: &mut Vec<Entry>) {
if self.is_empty() {
return;
}
let mut old = Self::new();
core::mem::swap(&mut old, self);
for i in 0..old.len() {
let entry = unsafe { old.entries[i].assume_init_read() };
if entry.interests.intersects(ready) {
ready_entries.push(entry);
} else {
self.push_entry(entry);
}
}
old.cursor = 0;
}
}
impl Drop for Inner {
fn drop(&mut self) {
for i in 0..self.len() {
unsafe { self.entries[i].assume_init_read() }.wake();
}
}
}
pub struct PollSet(Once<SpinNoIrq<Inner>>);
impl Default for PollSet {
fn default() -> Self {
Self::new()
}
}
impl PollSet {
pub const fn new() -> Self {
Self(Once::new())
}
pub unsafe fn register(&self, waker: &Waker, interests: IoEvents) {
let replaced = {
self.0
.call_once(|| SpinNoIrq::new(Inner::new()))
.lock()
.register(waker, interests)
};
if let Some(entry) = replaced {
entry.wake();
}
}
pub unsafe fn wake(&self, ready: IoEvents) -> usize {
let Some(inner) = self.0.get() else {
return 0;
};
let mut ready_entries = Vec::with_capacity(POLL_SET_CAPACITY);
{
inner.lock().drain_ready(ready, &mut ready_entries);
}
let woke = ready_entries.len();
for entry in ready_entries {
entry.wake();
}
woke
}
pub fn wake_from_irq(&self, ready: IoEvents) -> usize {
let Some(inner) = self.0.get() else {
return 0;
};
let mut ready_entries = [const { MaybeUninit::<Entry>::uninit() }; POLL_SET_CAPACITY];
let ready_len = {
let mut inner = inner.lock();
let len = inner.len();
if len == 0 {
return 0;
}
let mut ready_len = 0;
let mut keep_len = 0;
for i in 0..len {
let entry = unsafe { inner.entries[i].assume_init_read() };
if entry.interests.intersects(ready) {
ready_entries[ready_len].write(entry);
ready_len += 1;
} else {
inner.entries[keep_len].write(entry);
keep_len += 1;
}
}
inner.cursor = keep_len;
ready_len
};
for entry in ready_entries.iter_mut().take(ready_len) {
unsafe { entry.assume_init_read() }.wake();
}
ready_len
}
}
impl Drop for PollSet {
fn drop(&mut self) {
unsafe { self.wake(IoEvents::all()) };
}
}