use std::sync::Arc;
use std::thread::JoinHandle;
use slab::Slab;
use crate::{MIO_EVENTS_CAPACITY, SEND_TIMEOUT};
pub(crate) enum MultiplexerTask {
Attach(Box<dyn SonicConnectionTrait>),
}
pub struct SonicMultiplexer {
_event_loop_handle: JoinHandle<std::io::Result<()>>,
tx: crossbeam_channel::Sender<MultiplexerTask>,
pub(crate) poll_waker: Arc<mio::Waker>,
}
impl SonicMultiplexer {
pub fn new() -> std::io::Result<Self> {
let mut poll = mio::Poll::new()?;
let (tx, rx) = crossbeam_channel::bounded(8);
let poll_waker = Arc::new(mio::Waker::new(poll.registry(), mio::Token(usize::MAX))?);
let event_loop_handle = std::thread::spawn(move || run_event_loop(&mut poll, rx));
Ok(Self {
_event_loop_handle: event_loop_handle,
tx,
poll_waker,
})
}
pub(crate) fn attach<C: SonicConnectionTrait + 'static>(&self, conn: C) -> std::io::Result<()> {
if let Err(error) =
(self.tx).send_timeout(MultiplexerTask::Attach(Box::new(conn)), SEND_TIMEOUT)
{
return Err(std::io::Error::new(
std::io::ErrorKind::BrokenPipe,
error.to_string(),
));
};
self.poll_waker.wake()
}
}
pub(crate) trait SonicConnectionTrait: AsMut<mio::net::TcpStream> + Send {
fn wants_to_write(&self) -> bool;
fn wants_to_read(&self) -> bool;
fn interest(&self) -> Option<mio::Interest> {
match (self.wants_to_write(), self.wants_to_read()) {
(false, false) => None,
(true, false) => Some(mio::Interest::WRITABLE),
(false, true) => Some(mio::Interest::READABLE),
(true, true) => Some(mio::Interest::READABLE | mio::Interest::WRITABLE),
}
}
fn drain_reads(&mut self) -> std::io::Result<()>;
fn flush_writes(&mut self) -> std::io::Result<usize>;
}
fn run_event_loop(
poll: &mut mio::Poll,
rx: crossbeam_channel::Receiver<MultiplexerTask>,
) -> std::io::Result<()> {
let mut connections: Slab<ConnectionState> = Slab::with_capacity(1);
let mut events = mio::Events::with_capacity(MIO_EVENTS_CAPACITY);
loop {
if let Err(e) = poll.poll(&mut events, None) {
if e.kind() == std::io::ErrorKind::Interrupted {
continue;
}
return Err(e);
}
for task in rx.try_iter().take(rx.len()) {
match task {
MultiplexerTask::Attach(conn) => {
connections.insert(ConnectionState::new(conn));
}
}
}
for event in &events {
log_trace!("Got `mio` event: {event:?}");
let token = event.token();
if let Some(conn) = connections.get_mut(token.0) {
if event.is_readable() {
log_trace!("Draining read");
conn.drain_reads()?;
}
if event.is_writable() {
flush_writes(conn.as_mut())?;
}
if event.is_error() || event.is_read_closed() {
log_debug!("Detaching connection {}", token.0);
connections.remove(token.0);
}
}
}
for (key, conn) in connections.iter_mut() {
conn.update_interest(poll.registry(), mio::Token(key))?;
}
}
}
fn flush_writes(conn: &mut dyn SonicConnectionTrait) -> std::io::Result<()> {
match conn.flush_writes() {
Ok(n) => {
log_debug!("Wrote {n} bytes (flush total)");
Ok(())
}
Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => Ok(()),
Err(error) => Err(error),
}
}
struct ConnectionState {
inner: Box<dyn SonicConnectionTrait>,
interest: Option<mio::Interest>,
}
impl ConnectionState {
fn new(inner: Box<dyn SonicConnectionTrait>) -> Self {
Self {
inner,
interest: None,
}
}
fn update_interest(
&mut self,
registry: &mio::Registry,
token: mio::Token,
) -> std::io::Result<()> {
let mut new_interest: Option<mio::Interest> = self.inner.interest();
if new_interest != self.interest {
let key = token.0;
match (self.interest, new_interest) {
(None, Some(new_interest)) => {
log_debug!("Registering #{key} for {new_interest:?}");
registry.register((*self.inner).as_mut(), token, new_interest)
}
(Some(_), Some(new_interest)) => {
log_debug!("Re-registering #{key} for {new_interest:?}");
registry.reregister((*self.inner).as_mut(), token, new_interest)
}
(Some(_), None) => {
log_debug!("De-registering #{key}");
registry.deregister((*self.inner).as_mut())
}
(None, None) => unreachable!("new_interest != self.interest"),
}?;
std::mem::swap(&mut self.interest, &mut new_interest);
}
Ok(())
}
}
impl std::ops::Deref for ConnectionState {
type Target = Box<dyn SonicConnectionTrait>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl std::ops::DerefMut for ConnectionState {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}