use monocoque_core::options::SocketOptions;
use monocoque_core::rt::{TcpListener, TcpStream};
use std::io;
use super::PushSocket;
pub struct PushFanOut {
workers: Vec<PushSocket<TcpStream>>,
next: usize,
}
impl PushFanOut {
pub async fn bind(
addr: impl monocoque_core::rt::ToSocketAddrs,
n_workers: usize,
) -> io::Result<(TcpListener, Self)> {
Self::bind_with_options(addr, n_workers, SocketOptions::default()).await
}
pub async fn bind_with_options(
addr: impl monocoque_core::rt::ToSocketAddrs,
n_workers: usize,
options: SocketOptions,
) -> io::Result<(TcpListener, Self)> {
let listener = TcpListener::bind(addr).await?;
let fanout = Self::accept_workers(&listener, n_workers, options).await?;
Ok((listener, fanout))
}
pub async fn accept_workers(
listener: &TcpListener,
n_workers: usize,
options: SocketOptions,
) -> io::Result<Self> {
let mut workers = Vec::with_capacity(n_workers);
for _ in 0..n_workers {
let (stream, _) = listener.accept().await?;
workers.push(PushSocket::from_tcp_with_options(stream, options.clone()).await?);
}
Ok(Self { workers, next: 0 })
}
pub async fn accept(&mut self, listener: &TcpListener) -> io::Result<()> {
let (stream, _) = listener.accept().await?;
self.workers.push(PushSocket::from_tcp(stream).await?);
Ok(())
}
#[inline]
pub fn len(&self) -> usize {
self.workers.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.workers.is_empty()
}
pub async fn send(&mut self, msg: Vec<bytes::Bytes>) -> io::Result<()> {
while !self.workers.is_empty() {
let idx = self.next % self.workers.len();
if !self.workers[idx].is_connected() {
self.workers.remove(idx);
self.next = idx;
continue;
}
return match self.workers[idx].send(msg).await {
Ok(()) => {
self.next = idx + 1;
Ok(())
}
Err(e) => {
self.workers.remove(idx);
self.next = idx;
Err(e)
}
};
}
Err(io::Error::new(
io::ErrorKind::NotConnected,
"PushFanOut has no live workers",
))
}
pub async fn send_one(&mut self, frame: bytes::Bytes) -> io::Result<()> {
while !self.workers.is_empty() {
let idx = self.next % self.workers.len();
if !self.workers[idx].is_connected() {
self.workers.remove(idx);
self.next = idx;
continue;
}
return match self.workers[idx].send_one(frame).await {
Ok(()) => {
self.next = idx + 1;
Ok(())
}
Err(e) => {
self.workers.remove(idx);
self.next = idx;
Err(e)
}
};
}
Err(io::Error::new(
io::ErrorKind::NotConnected,
"PushFanOut has no live workers",
))
}
pub async fn flush(&mut self) -> io::Result<()> {
for worker in &mut self.workers {
worker.flush().await?;
}
Ok(())
}
}