use super::gen::Epoch;
use std::io::{self, Write};
use std::net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs, UdpSocket};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex, PoisonError};
use std::time::{Duration, Instant};
pub trait Sink {
fn send(&mut self, epoch: &Epoch) -> io::Result<()>;
fn finish(&mut self) -> io::Result<()> {
Ok(())
}
}
pub struct WriterSink<W: Write>(pub W);
impl<W: Write> Sink for WriterSink<W> {
fn send(&mut self, epoch: &Epoch) -> io::Result<()> {
for l in &epoch.lines {
self.0.write_all(l.as_bytes())?;
self.0.write_all(b"\r\n")?;
}
Ok(())
}
fn finish(&mut self) -> io::Result<()> {
self.0.flush()
}
}
pub struct TcpServer {
listener_addr: SocketAddr,
clients: Arc<Mutex<Vec<TcpStream>>>,
stop: Arc<AtomicBool>,
write_timeout: Arc<Mutex<Duration>>,
}
pub const DEFAULT_WRITE_TIMEOUT: Duration = Duration::from_secs(2);
impl TcpServer {
pub fn bind(addr: impl ToSocketAddrs) -> io::Result<Self> {
let listener = TcpListener::bind(addr)?;
listener.set_nonblocking(true)?;
let listener_addr = listener.local_addr()?;
let clients: Arc<Mutex<Vec<TcpStream>>> = Arc::default();
let stop = Arc::new(AtomicBool::new(false));
let write_timeout = Arc::new(Mutex::new(DEFAULT_WRITE_TIMEOUT));
let (c2, s2, w2) = (clients.clone(), stop.clone(), write_timeout.clone());
std::thread::spawn(move || {
while !s2.load(Ordering::Relaxed) {
match listener.accept() {
Ok((s, _)) => {
let _ = s.set_nodelay(true);
let _ = s.set_nonblocking(false);
let to = *w2.lock().unwrap_or_else(PoisonError::into_inner);
let _ = s.set_write_timeout(Some(to));
c2.lock().unwrap_or_else(PoisonError::into_inner).push(s);
}
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
std::thread::sleep(Duration::from_millis(10))
}
Err(_) => break,
}
}
});
Ok(Self {
listener_addr,
clients,
stop,
write_timeout,
})
}
pub fn set_write_timeout(&self, timeout: Duration) {
*self
.write_timeout
.lock()
.unwrap_or_else(PoisonError::into_inner) = timeout;
}
pub fn local_addr(&self) -> SocketAddr {
self.listener_addr
}
pub fn wait_for_clients(&self, n: usize, timeout: Option<Duration>) -> bool {
let t0 = Instant::now();
loop {
if self
.clients
.lock()
.unwrap_or_else(PoisonError::into_inner)
.len()
>= n
{
return true;
}
if timeout.is_some_and(|t| t0.elapsed() > t) {
return false;
}
std::thread::sleep(Duration::from_millis(20));
}
}
}
impl Drop for TcpServer {
fn drop(&mut self) {
self.stop.store(true, Ordering::Relaxed);
}
}
impl Sink for TcpServer {
fn send(&mut self, epoch: &Epoch) -> io::Result<()> {
let mut block = String::new();
for l in &epoch.lines {
block.push_str(l);
block.push_str("\r\n");
}
let mut current =
std::mem::take(&mut *self.clients.lock().unwrap_or_else(PoisonError::into_inner));
current.retain_mut(|c| c.write_all(block.as_bytes()).is_ok());
let mut shared = self.clients.lock().unwrap_or_else(PoisonError::into_inner);
current.append(&mut shared);
*shared = current;
Ok(())
}
fn finish(&mut self) -> io::Result<()> {
for c in self
.clients
.lock()
.unwrap_or_else(PoisonError::into_inner)
.iter_mut()
{
let _ = c.flush();
}
Ok(())
}
}
pub struct UdpSink {
socket: UdpSocket,
dest: SocketAddr,
}
impl UdpSink {
pub fn new(dest: impl ToSocketAddrs, broadcast: bool) -> io::Result<Self> {
let dest = dest
.to_socket_addrs()?
.next()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "no UDP address"))?;
let socket = UdpSocket::bind(if dest.is_ipv4() {
"0.0.0.0:0"
} else {
"[::]:0"
})?;
if broadcast {
socket.set_broadcast(true)?;
}
Ok(Self { socket, dest })
}
}
impl Sink for UdpSink {
fn send(&mut self, epoch: &Epoch) -> io::Result<()> {
for l in &epoch.lines {
let mut d = l.clone().into_bytes();
d.extend_from_slice(b"\r\n");
self.socket.send_to(&d, self.dest)?;
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Pace {
Max,
Speed(f64),
}
pub fn run<'a>(epochs: &[Epoch], sinks: &mut [Box<dyn Sink + 'a>], pace: Pace) -> io::Result<()> {
let t0 = Instant::now();
for e in epochs {
if let Pace::Speed(x) = pace {
let due = Duration::from_secs_f64(e.t_s / x);
if let Some(wait) = due.checked_sub(t0.elapsed()) {
std::thread::sleep(wait);
}
}
for s in sinks.iter_mut() {
s.send(e)?;
}
}
for s in sinks.iter_mut() {
s.finish()?;
}
Ok(())
}