#[cfg(not(any(
target_os = "linux",
target_os = "android",
target_os = "illumos",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly",
target_os = "windows",
)))]
compile_error!("reactor does not support this target OS");
use std::collections::BTreeMap;
use std::fmt::Debug;
use std::io;
use std::mem;
#[cfg(unix)]
use std::os::unix::io::RawFd;
#[cfg(windows)]
use std::os::windows::io::{FromRawSocket, RawSocket};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::task::{Poll, Waker};
use std::time::{Duration, Instant};
use crossbeam_queue::ArrayQueue;
use futures_util::future;
use once_cell::sync::Lazy;
use slab::Slab;
#[cfg(windows)]
use socket2::Socket;
#[cfg(unix)]
use crate::sys::fcntl::{fcntl, FcntlArg};
use crate::io_event::IoEvent;
pub(crate) struct Reactor {
sys: sys::Reactor,
sources: piper::Mutex<Slab<Arc<Source>>>,
events: piper::Lock<sys::Events>,
timers: piper::Mutex<BTreeMap<(Instant, usize), Waker>>,
timer_ops: ArrayQueue<TimerOp>,
timer_event: Lazy<IoEvent>,
}
impl Reactor {
pub fn get() -> &'static Reactor {
static REACTOR: Lazy<Reactor> = Lazy::new(|| Reactor {
sys: sys::Reactor::new().expect("cannot initialize I/O event notification"),
sources: piper::Mutex::new(Slab::new()),
events: piper::Lock::new(sys::Events::new()),
timers: piper::Mutex::new(BTreeMap::new()),
timer_ops: ArrayQueue::new(1000),
timer_event: Lazy::new(|| IoEvent::new().expect("cannot create an `IoEvent`")),
});
&REACTOR
}
pub fn insert_io(
&self,
#[cfg(unix)] raw: RawFd,
#[cfg(windows)] raw: RawSocket,
) -> io::Result<Arc<Source>> {
let mut sources = self.sources.lock();
let vacant = sources.vacant_entry();
#[cfg(unix)]
{
let flags = fcntl(raw, FcntlArg::F_GETFL)?;
let flags = flags | libc::O_NONBLOCK;
fcntl(raw, FcntlArg::F_SETFL(flags))?;
}
#[cfg(windows)]
{
let socket = unsafe { Socket::from_raw_socket(raw) };
mem::ManuallyDrop::new(socket).set_nonblocking(true)?;
}
let key = vacant.key();
self.sys.register(raw, key)?;
let source = Arc::new(Source {
raw,
key,
wakers: piper::Mutex::new(Wakers {
readers: Vec::new(),
writers: Vec::new(),
}),
});
Ok(vacant.insert(source).clone())
}
pub fn remove_io(&self, source: &Source) -> io::Result<()> {
let mut sources = self.sources.lock();
sources.remove(source.key);
self.sys.deregister(source.raw)
}
pub fn insert_timer(&self, when: Instant, waker: &Waker) -> usize {
static ID_GENERATOR: AtomicUsize = AtomicUsize::new(1);
let id = ID_GENERATOR.fetch_add(1, Ordering::Relaxed);
while self
.timer_ops
.push(TimerOp::Insert(when, id, waker.clone()))
.is_err()
{
self.fire_timers();
}
self.timer_event.notify();
id
}
pub fn remove_timer(&self, when: Instant, id: usize) {
while self.timer_ops.push(TimerOp::Remove(when, id)).is_err() {
self.fire_timers();
}
}
pub fn try_lock(&self) -> Option<ReactorLock<'_>> {
self.events.try_lock().map(|events| {
let reactor = self;
ReactorLock { reactor, events }
})
}
pub async fn lock(&self) -> ReactorLock<'_> {
let reactor = self;
let events = self.events.lock().await;
ReactorLock { reactor, events }
}
fn fire_timers(&self) -> Option<Duration> {
self.timer_event.clear();
let mut timers = self.timers.lock();
for _ in 0..self.timer_ops.capacity() {
match self.timer_ops.pop() {
Ok(TimerOp::Insert(when, id, waker)) => {
timers.insert((when, id), waker);
}
Ok(TimerOp::Remove(when, id)) => {
timers.remove(&(when, id));
}
Err(_) => break,
}
}
let now = Instant::now();
let pending = timers.split_off(&(now, 0));
let ready = mem::replace(&mut *timers, pending);
let dur = if ready.is_empty() {
timers
.keys()
.next()
.map(|(when, _)| when.saturating_duration_since(now))
} else {
Some(Duration::from_secs(0))
};
drop(timers);
for (_, waker) in ready {
waker.wake();
}
dur
}
}
pub(crate) struct ReactorLock<'a> {
reactor: &'a Reactor,
events: piper::LockGuard<sys::Events>,
}
impl ReactorLock<'_> {
pub fn poll(&mut self) -> io::Result<()> {
self.react(false)
}
pub fn wait(&mut self) -> io::Result<()> {
self.react(true)
}
fn react(&mut self, block: bool) -> io::Result<()> {
let next_timer = self.reactor.fire_timers();
let timeout = if block {
next_timer
} else {
Some(Duration::from_secs(0))
};
loop {
match self.reactor.sys.wait(&mut self.events, timeout) {
Ok(0) => {
self.reactor.fire_timers();
return Ok(());
}
Ok(_) => {
let sources = self.reactor.sources.lock();
let mut ready = Vec::new();
for ev in self.events.iter() {
if let Some(source) = sources.get(ev.key) {
let mut wakers = source.wakers.lock();
if ev.readable {
ready.append(&mut wakers.readers);
}
if ev.writable {
ready.append(&mut wakers.writers);
}
}
}
drop(sources);
for waker in ready {
waker.wake();
}
return Ok(());
}
Err(err) if err.kind() == io::ErrorKind::Interrupted => continue,
Err(err) => return Err(err),
}
}
}
}
enum TimerOp {
Insert(Instant, usize, Waker),
Remove(Instant, usize),
}
#[derive(Debug)]
pub(crate) struct Source {
#[cfg(unix)]
pub(crate) raw: RawFd,
#[cfg(windows)]
pub(crate) raw: RawSocket,
key: usize,
wakers: piper::Mutex<Wakers>,
}
#[derive(Debug)]
struct Wakers {
readers: Vec<Waker>,
writers: Vec<Waker>,
}
impl Source {
pub(crate) fn reregister_io_event(&self) -> io::Result<()> {
let wakers = self.wakers.lock();
Reactor::get()
.sys
.reregister(self.raw, self.key, true, !wakers.writers.is_empty())?;
Ok(())
}
pub(crate) async fn readable(&self) -> io::Result<()> {
let mut polled = false;
future::poll_fn(|cx| {
if polled {
Poll::Ready(Ok(()))
} else {
let mut wakers = self.wakers.lock();
if wakers.readers.is_empty() {
Reactor::get().sys.reregister(
self.raw,
self.key,
true,
!wakers.writers.is_empty(),
)?;
}
if wakers.readers.iter().all(|w| !w.will_wake(cx.waker())) {
wakers.readers.push(cx.waker().clone());
}
polled = true;
Poll::Pending
}
})
.await
}
pub(crate) async fn writable(&self) -> io::Result<()> {
let mut polled = false;
future::poll_fn(|cx| {
if polled {
Poll::Ready(Ok(()))
} else {
let mut wakers = self.wakers.lock();
if wakers.writers.is_empty() {
Reactor::get().sys.reregister(
self.raw,
self.key,
!wakers.readers.is_empty(),
true,
)?;
}
if wakers.writers.iter().all(|w| !w.will_wake(cx.waker())) {
wakers.writers.push(cx.waker().clone());
}
polled = true;
Poll::Pending
}
})
.await
}
}
#[cfg(any(target_os = "linux", target_os = "android", target_os = "illumos"))]
mod sys {
use std::convert::TryInto;
use std::io;
use std::os::unix::io::RawFd;
use std::time::Duration;
use crate::sys::epoll::{
epoll_create1, epoll_ctl, epoll_wait, EpollEvent, EpollFlags, EpollOp,
};
pub struct Reactor(RawFd);
impl Reactor {
pub fn new() -> io::Result<Reactor> {
let epoll_fd = epoll_create1()?;
Ok(Reactor(epoll_fd))
}
pub fn register(&self, fd: RawFd, key: usize) -> io::Result<()> {
let ev = &mut EpollEvent::new(0, key as u64);
epoll_ctl(self.0, EpollOp::EpollCtlAdd, fd, Some(ev))
}
pub fn reregister(&self, fd: RawFd, key: usize, read: bool, write: bool) -> io::Result<()> {
let mut flags = libc::EPOLLONESHOT;
if read {
flags |= read_flags();
}
if write {
flags |= write_flags();
}
let ev = &mut EpollEvent::new(flags, key as u64);
epoll_ctl(self.0, EpollOp::EpollCtlMod, fd, Some(ev))
}
pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
epoll_ctl(self.0, EpollOp::EpollCtlDel, fd, None)
}
pub fn wait(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<usize> {
let timeout_ms = timeout
.map(|t| {
if t == Duration::from_millis(0) {
t
} else {
t.max(Duration::from_millis(1))
}
})
.and_then(|t| t.as_millis().try_into().ok())
.unwrap_or(-1);
events.len = epoll_wait(self.0, &mut events.list, timeout_ms)?;
Ok(events.len)
}
}
fn read_flags() -> EpollFlags {
libc::EPOLLIN | libc::EPOLLRDHUP
}
fn write_flags() -> EpollFlags {
libc::EPOLLOUT
}
pub struct Events {
list: Box<[EpollEvent]>,
len: usize,
}
impl Events {
pub fn new() -> Events {
let list = vec![EpollEvent::empty(); 1000].into_boxed_slice();
let len = 0;
Events { list, len }
}
pub fn iter(&self) -> impl Iterator<Item = Event> + '_ {
self.list[..self.len].iter().map(|ev| Event {
readable: (ev.events() & read_flags()) > 0,
writable: (ev.events() & write_flags()) > 0,
key: ev.data() as usize,
})
}
}
pub struct Event {
pub readable: bool,
pub writable: bool,
pub key: usize,
}
}
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly",
))]
mod sys {
use std::io;
use std::os::unix::io::RawFd;
use std::time::Duration;
use crate::sys::event::{kevent_ts, kqueue, KEvent};
use crate::sys::fcntl::{fcntl, FcntlArg};
pub struct Reactor(RawFd);
impl Reactor {
pub fn new() -> io::Result<Reactor> {
let fd = kqueue()?;
fcntl(fd, FcntlArg::F_SETFD(libc::FD_CLOEXEC))?;
Ok(Reactor(fd))
}
pub fn register(&self, _fd: RawFd, _key: usize) -> io::Result<()> {
Ok(())
}
pub fn reregister(&self, fd: RawFd, key: usize, read: bool, write: bool) -> io::Result<()> {
let mut read_flags = libc::EV_ONESHOT | libc::EV_RECEIPT;
let mut write_flags = libc::EV_ONESHOT | libc::EV_RECEIPT;
if read {
read_flags |= libc::EV_ADD;
} else {
read_flags |= libc::EV_DELETE;
}
if write {
write_flags |= libc::EV_ADD;
} else {
write_flags |= libc::EV_DELETE;
}
let udata = key as _;
let changelist = [
KEvent::new(fd as _, libc::EVFILT_READ, read_flags, 0, 0, udata),
KEvent::new(fd as _, libc::EVFILT_WRITE, write_flags, 0, 0, udata),
];
let mut eventlist = changelist;
kevent_ts(self.0, &changelist, &mut eventlist, None)?;
for ev in &eventlist {
let (flags, data) = (ev.flags(), ev.data());
if (flags & libc::EV_ERROR) == 1
&& data != 0
&& data != libc::ENOENT as _
&& data != libc::EPIPE as _
{
return Err(io::Error::from_raw_os_error(data as _));
}
}
Ok(())
}
pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
let flags = libc::EV_RECEIPT | libc::EV_DELETE;
let changelist = [
KEvent::new(fd as _, libc::EVFILT_WRITE, flags, 0, 0, 0),
KEvent::new(fd as _, libc::EVFILT_READ, flags, 0, 0, 0),
];
let mut eventlist = changelist;
kevent_ts(self.0, &changelist, &mut eventlist, None)?;
for ev in &eventlist {
let (flags, data) = (ev.flags(), ev.data());
if (flags & libc::EV_ERROR == 1) && data != 0 && data != libc::ENOENT as _ {
return Err(io::Error::from_raw_os_error(data as _));
}
}
Ok(())
}
pub fn wait(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<usize> {
let timeout = timeout.map(|t| libc::timespec {
tv_sec: t.as_secs() as libc::time_t,
tv_nsec: t.subsec_nanos() as libc::c_long,
});
events.len = kevent_ts(self.0, &[], &mut events.list, timeout)?;
Ok(events.len)
}
}
pub struct Events {
list: Box<[KEvent]>,
len: usize,
}
impl Events {
pub fn new() -> Events {
let flags = 0;
let event = KEvent::new(0, 0, flags, 0, 0, 0);
let list = vec![event; 1000].into_boxed_slice();
let len = 0;
Events { list, len }
}
pub fn iter(&self) -> impl Iterator<Item = Event> + '_ {
self.list[..self.len].iter().map(|ev| Event {
readable: ev.filter() == libc::EVFILT_READ,
writable: ev.filter() == libc::EVFILT_WRITE,
key: ev.udata() as usize,
})
}
}
pub struct Event {
pub readable: bool,
pub writable: bool,
pub key: usize,
}
}
#[cfg(target_os = "windows")]
mod sys {
use std::io;
use std::os::windows::io::{AsRawSocket, RawSocket};
use std::time::Duration;
use wepoll_binding::{Epoll, EventFlag};
pub struct Reactor(Epoll);
impl Reactor {
pub fn new() -> io::Result<Reactor> {
Ok(Reactor(Epoll::new()?))
}
pub fn register(&self, sock: RawSocket, key: usize) -> io::Result<()> {
self.0.register(&As(sock), EventFlag::empty(), key as u64)
}
pub fn reregister(
&self,
sock: RawSocket,
key: usize,
read: bool,
write: bool,
) -> io::Result<()> {
let mut flags = EventFlag::ONESHOT;
if read {
flags |= read_flags();
}
if write {
flags |= write_flags();
}
self.0.reregister(&As(sock), flags, key as u64)
}
pub fn deregister(&self, sock: RawSocket) -> io::Result<()> {
self.0.deregister(&As(sock))
}
pub fn wait(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<usize> {
let timeout = timeout.map(|t| {
if t == Duration::from_millis(0) {
t
} else {
t.max(Duration::from_millis(1))
}
});
events.0.clear();
self.0.poll(&mut events.0, timeout)
}
}
struct As(RawSocket);
impl AsRawSocket for As {
fn as_raw_socket(&self) -> RawSocket {
self.0
}
}
fn read_flags() -> EventFlag {
EventFlag::IN | EventFlag::RDHUP
}
fn write_flags() -> EventFlag {
EventFlag::OUT
}
pub struct Events(wepoll_binding::Events);
impl Events {
pub fn new() -> Events {
Events(wepoll_binding::Events::with_capacity(1000))
}
pub fn iter(&self) -> impl Iterator<Item = Event> + '_ {
self.0.iter().map(|ev| Event {
readable: ev.flags().intersects(read_flags()),
writable: ev.flags().intersects(write_flags()),
key: ev.data() as usize,
})
}
}
pub struct Event {
pub readable: bool,
pub writable: bool,
pub key: usize,
}
}