use crate::{
backend::Backend,
config::BackendConfig,
error::CrossioError,
event::Events,
registration::{Registration, Source},
};
use std::time::Duration;
pub struct Reactor<B: Backend> {
backend: B,
events: Events,
}
impl<B: Backend> Reactor<B> {
pub fn new() -> Result<Self, CrossioError> {
Self::with_config(BackendConfig::default())
}
pub fn with_config(config: BackendConfig) -> Result<Self, CrossioError> {
let capacity = config.event_batch_size();
Ok(Self {
backend: B::new(config)?,
events: Events::with_capacity(capacity),
})
}
pub fn register<S: Source<B>>(
&self,
source: &S,
registration: Registration,
) -> Result<(), CrossioError> {
if registration.interest().is_empty() {
return Err(CrossioError::InvalidConfig("interest mask cannot be empty"));
}
self.backend.register(
source.raw_source(),
registration.token(),
registration.interest(),
)
}
pub fn reregister<S: Source<B>>(
&self,
source: &S,
registration: Registration,
) -> Result<(), CrossioError> {
self.backend.reregister(
source.raw_source(),
registration.token(),
registration.interest(),
)
}
pub fn deregister<S: Source<B>>(&self, source: &S) -> Result<(), CrossioError> {
self.backend.deregister(source.raw_source())
}
pub fn poll(&mut self, timeout: Option<Duration>) -> Result<&Events, CrossioError> {
self.events.clear();
self.backend.poll(&mut self.events, timeout)?;
Ok(&self.events)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Event, Events, Interest, Token};
#[derive(Clone, Default)]
struct DummyBackend;
impl Backend for DummyBackend {
type RawSource = usize;
fn new(_: BackendConfig) -> Result<Self, CrossioError> {
Ok(Self)
}
fn register(
&self,
_: Self::RawSource,
_: Token,
_: Interest,
) -> Result<(), CrossioError> {
Ok(())
}
fn reregister(
&self,
_: Self::RawSource,
_: Token,
_: Interest,
) -> Result<(), CrossioError> {
Ok(())
}
fn deregister(&self, _: Self::RawSource) -> Result<(), CrossioError> {
Ok(())
}
fn poll(
&self,
events: &mut Events,
_: Option<Duration>,
) -> Result<usize, CrossioError> {
events.push(Event::new(Token::from(0usize), Interest::READABLE));
Ok(events.len())
}
}
struct DummySource(usize);
impl Source<DummyBackend> for DummySource {
fn raw_source(&self) -> <DummyBackend as Backend>::RawSource {
self.0
}
}
#[test]
fn register_rejects_empty_interest() {
let reactor = Reactor::<DummyBackend>::new().expect("dummy backend should initialize");
let source = DummySource(1);
let registration = Registration::new(Token::from_usize(1), Interest::empty());
match reactor.register(&source, registration) {
Err(CrossioError::InvalidConfig(_)) => {}
other => panic!("expected invalid config error, got {other:?}"),
}
}
#[test]
fn poll_returns_events_from_backend() {
let mut reactor = Reactor::<DummyBackend>::new().unwrap();
let events = reactor.poll(Some(Duration::from_secs(0))).unwrap();
assert_eq!(events.len(), 1);
let event = events.iter().next().unwrap();
assert_eq!(event.token(), Token::from_usize(0));
assert!(event.readiness().contains(Interest::READABLE));
}
}