use std::{
io::Result,
sync::{Arc, OnceLock},
task::Waker,
time::{Duration, Instant},
};
use dashmap::DashMap;
use mio::{event, Interest, Token};
use crate::timewheel::Timewheel;
pub(crate) struct Reactor {
mio_registry: mio::Registry,
read_op_wakers: DashMap<Token, Waker>,
write_op_wakers: DashMap<Token, Waker>,
timewheel: Timewheel,
}
pub type ArcReactor = Arc<Reactor>;
impl Reactor {
fn new(tick_interval: Duration) -> Result<ArcReactor> {
let mio_poll = mio::Poll::new()?;
let mio_registry = mio_poll.registry().try_clone()?;
let reactor = Arc::new(Reactor {
mio_registry,
read_op_wakers: Default::default(),
write_op_wakers: Default::default(),
timewheel: Timewheel::new(tick_interval),
});
let background = ReactorBackground::new(tick_interval, mio_poll, reactor.clone());
background.start();
Ok(reactor)
}
pub fn register<S>(&self, source: &mut S, token: Token, interests: Interest) -> Result<()>
where
S: event::Source + ?Sized,
{
self.mio_registry.register(source, token, interests)
}
pub fn deregister<S>(&self, source: &mut S) -> Result<()>
where
S: event::Source + ?Sized,
{
self.mio_registry.deregister(source)
}
pub fn deadline(&self, token: Token, waker: Waker, deadline: Instant) -> Option<u64> {
self.write_op_wakers.insert(token, waker);
if let Some(id) = self.timewheel.new_timer(token, deadline) {
Some(id)
} else {
self.write_op_wakers.remove(&token);
None
}
}
pub fn once(&self, token: Token, interests: Interest, waker: Waker) {
if interests.is_readable() {
self.read_op_wakers.insert(token, waker.clone());
}
if interests.is_writable() {
self.write_op_wakers.insert(token, waker);
}
}
pub fn notify(&self, token: Token, interests: Interest) {
if interests.is_readable() {
if let Some(waker) = self.read_op_wakers.remove(&token).map(|(_, v)| v) {
waker.wake();
}
}
if interests.is_writable() {
if let Some(waker) = self.write_op_wakers.remove(&token).map(|(_, v)| v) {
waker.wake();
}
}
}
pub fn remove_listeners(&self, token: Token, interests: Interest) {
if interests.is_readable() {
self.read_op_wakers.remove(&token);
}
if interests.is_writable() {
self.write_op_wakers.remove(&token);
}
}
}
struct ReactorBackground {
mio_poll: mio::Poll,
reactor: ArcReactor,
tick_interval: Duration,
}
impl ReactorBackground {
fn new(tick_interval: Duration, mio_poll: mio::Poll, reactor: ArcReactor) -> Self {
Self {
mio_poll,
reactor,
tick_interval,
}
}
fn start(mut self) {
std::thread::spawn(move || {
self.dispatch_loop();
});
}
fn dispatch_loop(&mut self) {
let mut events = mio::event::Events::with_capacity(1024);
loop {
self.mio_poll
.poll(&mut events, Some(self.tick_interval))
.expect("Mio poll panic");
for event in &events {
if event.is_readable() {
self.notify(event.token(), Interest::READABLE);
}
if event.is_writable() {
self.notify(event.token(), Interest::WRITABLE);
}
}
let timeout_timers = self.reactor.timewheel.next_tick();
if let Some(timeout_timers) = timeout_timers {
for token in timeout_timers {
self.notify(token, Interest::WRITABLE);
}
}
}
}
fn notify(&self, token: Token, interests: Interest) {
self.reactor.notify(token, interests);
}
}
static GLOBAL_REACTOR: OnceLock<ArcReactor> = OnceLock::new();
pub fn start_reactor_with(tick_interval: Duration) {
if GLOBAL_REACTOR
.set(Reactor::new(tick_interval).unwrap())
.is_err()
{
panic!("Call start_reactor_with twice.");
}
}
pub(crate) fn global_reactor() -> ArcReactor {
GLOBAL_REACTOR
.get_or_init(|| Reactor::new(Duration::from_millis(10)).unwrap())
.clone()
}