1use std::{io, net::SocketAddr, thread};
23
24use compio::{BufResult, net::UdpSocket, runtime::Runtime};
25use compio_pool::{ManageConnection, Pool, cpu};
26use socket2::{Domain, Protocol, Socket, Type};
27
28struct Buffers;
31
32impl ManageConnection for Buffers {
33 type Connection = Vec<u8>;
34 type Error = io::Error;
35
36 async fn connect(&self) -> io::Result<Vec<u8>> {
37 Ok(Vec::with_capacity(64 * 1024))
38 }
39
40 async fn is_valid(&self, _buf: &mut Vec<u8>) -> io::Result<()> {
41 Ok(())
42 }
43
44 fn has_broken(&self, _buf: &mut Vec<u8>) -> bool {
45 false
46 }
47}
48
49fn bind_reuseport_udp(addr: SocketAddr) -> io::Result<std::net::UdpSocket> {
54 let socket = Socket::new(Domain::for_address(addr), Type::DGRAM, Some(Protocol::UDP))?;
55 socket.set_reuse_address(true)?;
56 socket.set_reuse_port(true)?;
57 socket.bind(&addr.into())?;
58 Ok(socket.into())
59}
60
61fn main() -> io::Result<()> {
62 let addr: SocketAddr = std::env::args()
63 .nth(1)
64 .unwrap_or_else(|| "0.0.0.0:7000".into())
65 .parse()
66 .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, format!("bad address: {e}")))?;
67 let capacity: u32 = std::env::args()
68 .nth(2)
69 .and_then(|s| s.parse().ok())
70 .unwrap_or(1024);
71
72 let pool = Pool::builder().max_size(capacity).build(Buffers);
73
74 let cores = cpu::cores();
75 if cores.is_empty() {
76 return Err(io::Error::new(io::ErrorKind::Unsupported, "no cores to run on"));
77 }
78 println!(
79 "udp_echo on {addr}: {} workers, capacity {capacity}/worker",
80 cores.len()
81 );
82
83 let mut handles = Vec::new();
84 for (index, core) in cores.into_iter().enumerate() {
85 let pool = pool.clone();
86 let handle = thread::Builder::new()
87 .name(format!("worker/{index}"))
88 .spawn(move || worker(core, addr, pool))?;
89 handles.push(handle);
90 }
91 for handle in handles {
92 let _ = handle.join();
93 }
94 Ok(())
95}
96
97fn worker(core: cpu::CoreId, addr: SocketAddr, pool: Pool<Buffers>) {
100 cpu::pin_current(core);
101
102 let runtime = Runtime::builder().build().expect("build compio runtime");
103 runtime.block_on(async move {
104 let local = pool.local();
105
106 let std_socket = bind_reuseport_udp(addr).expect("bind SO_REUSEPORT UDP");
107 let socket = UdpSocket::from_std(std_socket).expect("wrap socket in ring");
108
109 loop {
113 let mut lease = match local.get().await {
114 Ok(lease) => lease,
115 Err(_) => continue,
116 };
117
118 let mut buf = std::mem::take(&mut *lease);
119 buf.clear();
120 let BufResult(res, buf) = socket.recv_from(buf).await;
121 let (buf, peer) = match res {
122 Ok((_n, peer)) => (buf, peer),
125 Err(_) => {
126 *lease = buf;
127 continue;
128 }
129 };
130
131 let BufResult(_sent, buf) = socket.send_to(buf, peer).await;
132 *lease = buf; }
134 });
135}