use std::{
cell::RefCell,
collections::BTreeMap,
io,
sync::Arc,
task::Waker,
time::{Duration, Instant},
};
use polling::{Event, Events, Poller};
use slab::Slab;
#[cfg(unix)]
pub(crate) type RawSource = std::os::fd::RawFd;
#[cfg(windows)]
pub(crate) type RawSource = std::os::windows::io::RawSocket;
#[derive(Default)]
struct Source {
raw: Option<RawSource>,
read: Option<Waker>,
write: Option<Waker>,
}
pub(crate) type TimerHandle = (Instant, u64);
pub struct Reactor {
poller: Arc<Poller>,
mailbox: Arc<crate::blocking::Mailbox>,
events: Events,
wake_scratch: Vec<Waker>,
rearm_scratch: Vec<usize>,
sources: Slab<Source>,
timers: BTreeMap<TimerHandle, Waker>,
next_timer: u64,
}
#[cfg(not(nexio_runtime_fallback))]
thread_local! {
pub static REACTOR: RefCell<Reactor> =
RefCell::new(Reactor::new().expect("failed to create the readiness poller"));
}
#[cfg(not(nexio_runtime_fallback))]
#[inline]
pub(crate) fn with_poll_mut<R>(f: impl FnOnce(&mut Reactor) -> R) -> R {
REACTOR.with(|r| f(&mut r.borrow_mut()))
}
#[cfg(not(nexio_runtime_fallback))]
#[inline]
pub(crate) fn try_with_poll_mut<R>(f: impl FnOnce(&mut Reactor) -> R) -> Option<R> {
REACTOR
.try_with(|r| r.try_borrow_mut().ok().map(|mut r| f(&mut r)))
.ok()
.flatten()
}
#[cfg(nexio_runtime_fallback)]
#[inline]
pub(crate) fn with_poll_mut<R>(f: impl FnOnce(&mut Reactor) -> R) -> R {
crate::reactor::with_poll_mut_impl(f)
}
#[cfg(nexio_runtime_fallback)]
#[inline]
pub(crate) fn try_with_poll_mut<R>(f: impl FnOnce(&mut Reactor) -> R) -> Option<R> {
crate::reactor::try_with_poll_mut_impl(f)
}
#[derive(Debug, Clone, Default)]
pub struct ReactorBuilder {
entries: Option<u32>,
}
impl ReactorBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn entries(mut self, entries: u32) -> Self {
self.entries = Some(entries);
self
}
pub fn coop_taskrun(self, _enabled: bool) -> Self {
self
}
pub fn sqpoll(self, _idle: Option<u32>) -> Self {
self
}
pub fn build(&self) -> io::Result<Reactor> {
Reactor::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct ReactorStats {
pub in_flight_ops: usize,
pub pending_timers: usize,
pub sq_pending: usize,
}
#[cfg(not(nexio_runtime_fallback))]
pub fn current_stats() -> Option<ReactorStats> {
REACTOR
.try_with(|r| r.try_borrow().ok().map(|r| r.stats()))
.ok()
.flatten()
}
impl Reactor {
pub fn builder() -> ReactorBuilder {
ReactorBuilder::new()
}
pub fn with_entries(_entries: u32) -> io::Result<Self> {
Self::new()
}
pub fn new() -> io::Result<Self> {
let poller = Arc::new(Poller::new()?);
let waker_poller = poller.clone();
let mailbox = Arc::new(crate::blocking::Mailbox::new(Box::new(move || {
let _ = waker_poller.notify();
})));
Ok(Self {
poller,
mailbox,
events: Events::new(),
wake_scratch: Vec::new(),
rearm_scratch: Vec::new(),
sources: Slab::new(),
timers: BTreeMap::new(),
next_timer: 0,
})
}
pub fn is_supported() -> bool {
true
}
pub const fn backend_name() -> &'static str {
"poll"
}
pub fn stats(&self) -> ReactorStats {
ReactorStats {
in_flight_ops: self.sources.len(),
pending_timers: self.timers.len(),
sq_pending: 0,
}
}
pub(crate) fn mailbox(&self) -> Arc<crate::blocking::Mailbox> {
self.mailbox.clone()
}
pub(crate) fn register(&mut self, raw: RawSource) -> io::Result<usize> {
let key = self.sources.insert(Source {
raw: Some(raw),
..Source::default()
});
if let Err(e) = unsafe { self.poller.add(raw, Event::none(key)) } {
self.sources.remove(key);
return Err(e);
}
Ok(key)
}
pub(crate) fn deregister(&mut self, key: usize, source: impl polling::AsSource) {
if self.sources.try_remove(key).is_some() {
let _ = self.poller.delete(source);
}
}
pub(crate) fn arm(
&mut self,
key: usize,
source: impl polling::AsSource,
write: bool,
waker: &Waker,
) -> io::Result<()> {
let Some(src) = self.sources.get_mut(key) else {
return Err(io::Error::new(
io::ErrorKind::NotConnected,
"socket is deregistered",
));
};
let slot = if write { &mut src.write } else { &mut src.read };
if !matches!(slot, Some(existing) if existing.will_wake(waker)) {
*slot = Some(waker.clone());
}
let ev = Event::new(key, src.read.is_some(), src.write.is_some());
self.poller.modify(source, ev)
}
pub(crate) fn add_timer(&mut self, deadline: Instant, waker: Waker) -> TimerHandle {
self.next_timer += 1;
let handle = (deadline, self.next_timer);
self.timers.insert(handle, waker);
handle
}
pub(crate) fn update_timer(&mut self, handle: TimerHandle, waker: &Waker) {
if let Some(slot) = self.timers.get_mut(&handle) {
if !slot.will_wake(waker) {
*slot = waker.clone();
}
}
}
pub(crate) fn cancel_timer(&mut self, handle: TimerHandle) {
self.timers.remove(&handle);
}
pub fn submit_and_poll(&mut self) -> io::Result<usize> {
let mut wakers = std::mem::take(&mut self.wake_scratch);
let res = self.submit_and_poll_into(&mut wakers);
for w in wakers.drain(..) {
w.wake();
}
self.wake_scratch = wakers;
res
}
pub(crate) fn submit_and_poll_into(&mut self, wakers: &mut Vec<Waker>) -> io::Result<usize> {
self.wait_into(Some(Duration::ZERO), wakers)
}
pub fn park(&mut self) -> io::Result<usize> {
let mut wakers = std::mem::take(&mut self.wake_scratch);
let res = self.park_into(&mut wakers);
for w in wakers.drain(..) {
w.wake();
}
self.wake_scratch = wakers;
res
}
pub(crate) fn park_into(&mut self, wakers: &mut Vec<Waker>) -> io::Result<usize> {
let timeout = self
.timers
.keys()
.next()
.map(|(deadline, _)| deadline.saturating_duration_since(Instant::now()));
self.wait_into(timeout, wakers)
}
fn wait_into(
&mut self,
timeout: Option<Duration>,
to_wake: &mut Vec<Waker>,
) -> io::Result<usize> {
self.events.clear();
match self.poller.wait(&mut self.events, timeout) {
Ok(_) => {}
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
let before = to_wake.len();
let mut rearm = std::mem::take(&mut self.rearm_scratch);
for ev in self.events.iter() {
let Some(src) = self.sources.get_mut(ev.key) else {
continue;
};
if ev.readable {
to_wake.extend(src.read.take());
}
if ev.writable {
to_wake.extend(src.write.take());
}
if src.read.is_some() || src.write.is_some() {
rearm.push(ev.key);
}
}
for key in rearm.drain(..) {
if let Some(src) = self.sources.get(key) {
if let Some(raw) = src.raw {
let ev = Event::new(key, src.read.is_some(), src.write.is_some());
let _ = self.poller.modify(unsafe { borrow(raw) }, ev);
}
}
}
let now = Instant::now();
while let Some(entry) = self.timers.first_entry() {
if entry.key().0 > now {
break;
}
to_wake.push(entry.remove());
}
let woken = to_wake.len() - before;
self.rearm_scratch = rearm;
let finished = self.mailbox.drain();
let blocking_woken = if finished.is_empty() {
0
} else {
crate::blocking::deliver(finished)
};
let remote = self.mailbox.drain_wakes();
let remote_woken = remote.len();
crate::executor::deliver_remote_wakes(remote);
Ok(woken + blocking_woken + remote_woken)
}
}
#[cfg(unix)]
unsafe fn borrow<'a>(raw: RawSource) -> std::os::fd::BorrowedFd<'a> {
unsafe { std::os::fd::BorrowedFd::borrow_raw(raw) }
}
#[cfg(windows)]
unsafe fn borrow<'a>(raw: RawSource) -> std::os::windows::io::BorrowedSocket<'a> {
unsafe { std::os::windows::io::BorrowedSocket::borrow_raw(raw) }
}