1use std::{io, net::SocketAddr, thread};
19
20use compio::{
21 BufResult,
22 io::{AsyncRead, AsyncWriteExt},
23 net::{TcpListener, TcpStream},
24 runtime::Runtime,
25};
26use compio_pool::{LocalPool, ManageConnection, Pool, bind_reuseport, cpu};
27
28struct Buffers;
32
33impl ManageConnection for Buffers {
34 type Connection = Vec<u8>;
35 type Error = io::Error;
36
37 async fn connect(&self) -> io::Result<Vec<u8>> {
38 Ok(Vec::with_capacity(16 * 1024))
39 }
40
41 async fn is_valid(&self, _buf: &mut Vec<u8>) -> io::Result<()> {
42 Ok(())
43 }
44
45 fn has_broken(&self, _buf: &mut Vec<u8>) -> bool {
46 false
47 }
48}
49
50fn main() -> io::Result<()> {
51 let addr: SocketAddr = std::env::args()
52 .nth(1)
53 .unwrap_or_else(|| "0.0.0.0:7000".into())
54 .parse()
55 .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, format!("bad address: {e}")))?;
56 let capacity: u32 = std::env::args()
57 .nth(2)
58 .and_then(|s| s.parse().ok())
59 .unwrap_or(1024);
60
61 let pool = Pool::builder().max_size(capacity).build(Buffers);
63
64 let cores = cpu::cores();
65 if cores.is_empty() {
66 return Err(io::Error::new(io::ErrorKind::Unsupported, "no cores to run on"));
67 }
68 println!(
69 "echo on {addr}: {} workers, capacity {capacity}/worker",
70 cores.len()
71 );
72
73 let mut handles = Vec::new();
74 for (index, core) in cores.into_iter().enumerate() {
75 let pool = pool.clone();
76 let handle = thread::Builder::new()
77 .name(format!("worker/{index}"))
78 .spawn(move || worker(core, addr, pool))?;
79 handles.push(handle);
80 }
81 for handle in handles {
82 let _ = handle.join();
83 }
84 Ok(())
85}
86
87fn worker(core: cpu::CoreId, addr: SocketAddr, pool: Pool<Buffers>) {
90 cpu::pin_current(core);
91
92 let runtime = Runtime::builder().build().expect("build compio runtime");
93 runtime.block_on(async move {
94 let local = pool.local();
97
98 let std_listener = bind_reuseport(addr, 1024, None).expect("bind SO_REUSEPORT");
99 let listener = TcpListener::from_std(std_listener).expect("wrap listener in ring");
100
101 loop {
102 let Ok((stream, _peer)) = listener.accept().await else {
103 continue;
104 };
105 compio::runtime::spawn(serve(local.clone(), stream)).detach();
106 }
107 });
108}
109
110async fn serve(local: LocalPool<Buffers>, mut stream: TcpStream) {
113 let mut lease = match local.get().await {
114 Ok(lease) => lease,
115 Err(_) => return,
116 };
117 loop {
118 let mut buf = std::mem::take(&mut *lease);
119 buf.clear();
120 let BufResult(read, buf) = stream.read(buf).await;
121 match read {
122 Ok(0) | Err(_) => {
123 *lease = buf;
124 return;
125 }
126 Ok(_) => {}
127 }
128 let BufResult(written, buf) = stream.write_all(buf).await;
129 *lease = buf;
130 if written.is_err() {
131 return;
132 }
133 }
134}