use std::io;
use std::marker::PhantomData;
use std::ops::DerefMut;
use std::time::Duration;
use crate::driver::Installer;
use crate::handler::Handler;
use crate::world::{ResourceId, World, WorldBuilder};
const DEFAULT_EVENT_CAPACITY: usize = 1024;
const DEFAULT_HANDLER_CAPACITY: usize = 64;
pub trait MioConfig: Send + 'static {
type Storage: DerefMut<Target = dyn Handler<::mio::event::Event>> + Send + 'static;
fn wrap(handler: impl Handler<::mio::event::Event> + 'static) -> Self::Storage;
}
pub struct BoxedMio;
impl MioConfig for BoxedMio {
type Storage = Box<dyn Handler<::mio::event::Event>>;
fn wrap(handler: impl Handler<::mio::event::Event> + 'static) -> Self::Storage {
Box::new(handler)
}
}
#[cfg(feature = "smartptr")]
pub struct InlineMio;
#[cfg(feature = "smartptr")]
impl MioConfig for InlineMio {
type Storage = crate::FlatVirtual<::mio::event::Event, nexus_smartptr::B256>;
fn wrap(handler: impl Handler<::mio::event::Event> + 'static) -> Self::Storage {
let ptr: *const dyn Handler<::mio::event::Event> = &handler;
unsafe { nexus_smartptr::Flat::new_raw(handler, ptr) }
}
}
#[cfg(feature = "smartptr")]
pub struct FlexMio;
#[cfg(feature = "smartptr")]
impl MioConfig for FlexMio {
type Storage = crate::FlexVirtual<::mio::event::Event, nexus_smartptr::B256>;
fn wrap(handler: impl Handler<::mio::event::Event> + 'static) -> Self::Storage {
let ptr: *const dyn Handler<::mio::event::Event> = &handler;
unsafe { nexus_smartptr::Flex::new_raw(handler, ptr) }
}
}
pub struct MioDriver<S = Box<dyn Handler<::mio::event::Event>>> {
poll: ::mio::Poll,
handlers: ::slab::Slab<S>,
}
impl<S: Send + 'static> crate::world::Resource for MioDriver<S> {}
impl<S> MioDriver<S> {
pub fn registry(&self) -> &::mio::Registry {
self.poll.registry()
}
pub fn insert(&mut self, handler: S) -> ::mio::Token {
::mio::Token(self.handlers.insert(handler))
}
pub fn remove(&mut self, token: ::mio::Token) -> S {
self.handlers.remove(token.0)
}
pub fn contains(&self, token: ::mio::Token) -> bool {
self.handlers.contains(token.0)
}
pub fn len(&self) -> usize {
self.handlers.len()
}
pub fn is_empty(&self) -> bool {
self.handlers.is_empty()
}
}
pub struct MioInstaller<S = Box<dyn Handler<::mio::event::Event>>> {
event_capacity: usize,
handler_capacity: usize,
_marker: PhantomData<S>,
}
impl<S> MioInstaller<S> {
pub fn new() -> Self {
MioInstaller {
event_capacity: DEFAULT_EVENT_CAPACITY,
handler_capacity: DEFAULT_HANDLER_CAPACITY,
_marker: PhantomData,
}
}
pub fn event_capacity(mut self, cap: usize) -> Self {
self.event_capacity = cap;
self
}
pub fn handler_capacity(mut self, cap: usize) -> Self {
self.handler_capacity = cap;
self
}
}
impl<S> Default for MioInstaller<S> {
fn default() -> Self {
Self::new()
}
}
impl<S: Send + 'static> Installer for MioInstaller<S> {
type Poller = MioPoller<S>;
fn install(self, world: &mut WorldBuilder) -> MioPoller<S> {
let poll = ::mio::Poll::new().expect("failed to create mio Poll");
let handlers = ::slab::Slab::<S>::with_capacity(self.handler_capacity);
let driver_id = world.register(MioDriver { poll, handlers });
MioPoller {
driver_id,
events: ::mio::Events::with_capacity(self.event_capacity),
buf: Vec::with_capacity(self.event_capacity),
}
}
}
pub struct MioPoller<S = Box<dyn Handler<::mio::event::Event>>> {
driver_id: ResourceId,
events: ::mio::Events,
buf: Vec<(::mio::event::Event, S)>,
}
impl<S> std::fmt::Debug for MioPoller<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MioPoller")
.field("driver_id", &self.driver_id)
.field("buf_len", &self.buf.len())
.finish()
}
}
impl<S: DerefMut + Send + 'static> MioPoller<S>
where
S::Target: Handler<::mio::event::Event>,
{
pub fn poll(&mut self, world: &mut World, timeout: Option<Duration>) -> io::Result<usize> {
let driver = unsafe { world.get_mut::<MioDriver<S>>(self.driver_id) };
driver.poll.poll(&mut self.events, timeout)?;
for event in &self.events {
let key = event.token().0;
if driver.handlers.contains(key) {
let handler = driver.handlers.remove(key);
self.buf.push((event.clone(), handler));
}
}
let fired = self.buf.len();
for (event, mut handler) in self.buf.drain(..) {
world.next_sequence();
handler.deref_mut().run(world, event);
}
Ok(fired)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{IntoHandler, ResMut, WorldBuilder};
#[test]
fn install_registers_driver() {
let mut builder = WorldBuilder::new();
let _poller: MioPoller = builder.install_driver(MioInstaller::new());
let world = builder.build();
assert!(world.contains::<MioDriver>());
}
#[test]
fn poll_empty_returns_zero() {
let mut builder = WorldBuilder::new();
let mut poller: MioPoller = builder.install_driver(MioInstaller::new());
let mut world = builder.build();
let fired = poller
.poll(&mut world, Some(Duration::from_millis(0)))
.unwrap();
assert_eq!(fired, 0);
}
#[test]
fn waker_fires_handler() {
let mut builder = WorldBuilder::new();
builder.register::<bool>(false);
let mut poller: MioPoller = builder.install_driver(MioInstaller::new());
let mut world = builder.build();
fn on_wake(mut flag: ResMut<bool>, _event: ::mio::event::Event) {
*flag = true;
}
let handler = on_wake.into_handler(world.registry());
let driver = world.resource_mut::<MioDriver>();
let token = driver.insert(Box::new(handler));
let waker = ::mio::Waker::new(driver.registry(), token).unwrap();
waker.wake().unwrap();
let fired = poller
.poll(&mut world, Some(Duration::from_millis(100)))
.unwrap();
assert_eq!(fired, 1);
assert!(*world.resource::<bool>());
}
#[test]
fn handler_fires_twice_with_waker() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut poller: MioPoller = builder.install_driver(MioInstaller::new());
let mut world = builder.build();
fn on_wake(mut counter: ResMut<u64>, _event: ::mio::event::Event) {
*counter += 1;
}
let handler = on_wake.into_handler(world.registry());
let driver = world.resource_mut::<MioDriver>();
let token = driver.insert(Box::new(handler));
let waker = ::mio::Waker::new(driver.registry(), token).unwrap();
waker.wake().unwrap();
let fired = poller
.poll(&mut world, Some(Duration::from_millis(100)))
.unwrap();
assert_eq!(fired, 1);
assert_eq!(*world.resource::<u64>(), 1);
let handler2 = on_wake.into_handler(world.registry());
let driver = world.resource_mut::<MioDriver>();
let token2 = driver.insert(Box::new(handler2));
assert_eq!(token, token2, "slab must reuse freed slot");
waker.wake().unwrap();
let fired = poller
.poll(&mut world, Some(Duration::from_millis(100)))
.unwrap();
assert_eq!(fired, 1);
assert_eq!(*world.resource::<u64>(), 2);
}
#[test]
fn cancel_before_fire() {
let mut builder = WorldBuilder::new();
builder.register::<bool>(false);
let mut poller: MioPoller = builder.install_driver(MioInstaller::new());
let mut world = builder.build();
fn on_wake(mut flag: ResMut<bool>, _event: ::mio::event::Event) {
*flag = true;
}
let handler = on_wake.into_handler(world.registry());
let driver = world.resource_mut::<MioDriver>();
let token = driver.insert(Box::new(handler));
let waker = ::mio::Waker::new(driver.registry(), token).unwrap();
let driver = world.resource_mut::<MioDriver>();
let _removed = driver.remove(token);
waker.wake().unwrap();
let fired = poller
.poll(&mut world, Some(Duration::from_millis(100)))
.unwrap();
assert_eq!(fired, 0);
assert!(!*world.resource::<bool>());
}
#[test]
fn poll_advances_sequence() {
let mut builder = WorldBuilder::new();
builder.register::<u64>(0);
let mut poller: MioPoller = builder.install_driver(MioInstaller::new());
let mut world = builder.build();
fn on_wake(mut counter: ResMut<u64>, _event: ::mio::event::Event) {
*counter += 1;
}
let handler = on_wake.into_handler(world.registry());
let driver = world.resource_mut::<MioDriver>();
let token = driver.insert(Box::new(handler));
let waker = ::mio::Waker::new(driver.registry(), token).unwrap();
waker.wake().unwrap();
let seq_before = world.current_sequence();
poller
.poll(&mut world, Some(Duration::from_millis(100)))
.unwrap();
assert_eq!(world.current_sequence().0, seq_before.0 + 1);
}
#[test]
fn stale_token_skipped() {
let mut builder = WorldBuilder::new();
let mut poller: MioPoller = builder.install_driver(MioInstaller::new());
let mut world = builder.build();
let fired = poller
.poll(&mut world, Some(Duration::from_millis(0)))
.unwrap();
assert_eq!(fired, 0);
}
#[test]
fn custom_capacities() {
let mut builder = WorldBuilder::new();
let _poller: MioPoller =
builder.install_driver(MioInstaller::new().event_capacity(256).handler_capacity(32));
let world = builder.build();
assert!(world.contains::<MioDriver>());
}
}