subetha_cxc/
net_bridge.rs1use std::io::{Read, Write};
33use std::net::{Shutdown, SocketAddr, TcpListener, TcpStream};
34use std::sync::Arc;
35
36use crate::cross_process_waker::CrossProcessWaker;
37use crate::spsc_ring::{SpscRingCore, SPSC_PAYLOAD_BYTES};
38
39const SLOT: usize = SPSC_PAYLOAD_BYTES;
41
42pub const EGRESS_BATCH_SLOTS: usize = 256;
45
46const INGRESS_BUF_BYTES: usize = 64 * 1024;
48
49pub fn ship(
54 addr: SocketAddr,
55 producer_ring: Arc<SpscRingCore>,
56 n_items: u64,
57) -> std::io::Result<()> {
58 let mut stream = TcpStream::connect(addr)?;
59 stream.set_nodelay(true)?;
60 stream.write_all(&n_items.to_be_bytes())?;
62
63 let mut batch = vec![0u8; EGRESS_BATCH_SLOTS * SLOT];
64 let mut slot = [0u8; SLOT];
65 let mut shipped: u64 = 0;
66 while shipped < n_items {
67 let budget = EGRESS_BATCH_SLOTS.min((n_items - shipped) as usize);
68 let mut filled = 0usize;
69 while filled < budget {
70 match producer_ring.try_pop(&mut slot) {
71 Ok(_) => {
72 batch[filled * SLOT..(filled + 1) * SLOT]
73 .copy_from_slice(&slot);
74 filled += 1;
75 }
76 Err(_) => break,
77 }
78 }
79 if filled == 0 {
80 std::thread::yield_now();
82 continue;
83 }
84 stream.write_all(&batch[..filled * SLOT])?;
85 shipped += filled as u64;
86 }
87 stream.shutdown(Shutdown::Write)?;
88 Ok(())
89}
90
91pub fn serve_one(
101 listener: &TcpListener,
102 consumer_ring: &Arc<SpscRingCore>,
103 xwaker: &Arc<CrossProcessWaker>,
104) -> std::io::Result<u64> {
105 let (mut stream, _) = listener.accept()?;
106 stream.set_nodelay(true)?;
107 let mut header = [0u8; 8];
108 stream.read_exact(&mut header)?;
109 let total: u64 = u64::from_be_bytes(header);
110
111 let mut buf = vec![0u8; INGRESS_BUF_BYTES];
112 let mut carry: Vec<u8> = Vec::with_capacity(SLOT);
113 let mut received: u64 = 0;
114 let trace = crate::reactor::wake_trace();
115 let (mut reads, mut eor_wakes) = (0u64, 0u64);
116 let mut last_snap = std::time::Instant::now();
117 while received < total {
118 if trace && last_snap.elapsed() >= std::time::Duration::from_secs(1) {
119 last_snap = std::time::Instant::now();
120 eprintln!(
121 "subetha: serve_one ring={:p} reads={reads} received={received}/{total} \
122 eor_wakes={eor_wakes} full_wakes={} head={}",
123 Arc::as_ptr(consumer_ring),
124 FULL_WAKES.load(std::sync::atomic::Ordering::Relaxed),
125 consumer_ring.head(),
126 );
127 }
128 let n = stream.read(&mut buf)?;
129 reads += 1;
130 if n == 0 {
131 return Err(std::io::Error::new(
132 std::io::ErrorKind::UnexpectedEof,
133 "peer closed before sending all framed items",
134 ));
135 }
136 let mut pushed_any = false;
137 let mut data: &[u8] = &buf[..n];
138
139 if !carry.is_empty() {
141 let need = SLOT - carry.len();
142 let take = need.min(data.len());
143 carry.extend_from_slice(&data[..take]);
144 data = &data[take..];
145 if carry.len() == SLOT {
146 push_spin(consumer_ring, xwaker, &carry);
147 carry.clear();
148 received += 1;
149 pushed_any = true;
150 }
151 }
152 while data.len() >= SLOT && received < total {
154 push_spin(consumer_ring, xwaker, &data[..SLOT]);
155 data = &data[SLOT..];
156 received += 1;
157 pushed_any = true;
158 }
159 if !data.is_empty() {
161 carry.extend_from_slice(data);
162 }
163
164 if pushed_any {
168 eor_wakes += 1;
169 xwaker.wake_up_to(consumer_ring.head());
170 }
171 }
172 Ok(total)
173}
174
175static FULL_WAKES: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
177
178fn push_spin(ring: &SpscRingCore, xwaker: &CrossProcessWaker, slot: &[u8]) {
179 while ring.try_push(slot).is_err() {
180 FULL_WAKES.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
184 xwaker.wake_up_to(ring.head());
185 std::hint::spin_loop();
186 }
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192 use crate::cross_process_waker::MAX_WAITERS_DEFAULT;
193 use crate::reactor::{block_on, receiver_cross};
194
195 #[test]
196 fn network_packet_wakes_parked_recv() {
197 const N: u64 = 5_000;
201 const CAP: usize = 256;
202
203 let consumer_ring = Arc::new(SpscRingCore::create_anon(CAP).unwrap());
204 let xwaker = Arc::new(
205 CrossProcessWaker::create_anon(MAX_WAITERS_DEFAULT).unwrap(),
206 );
207 let rx = receiver_cross(Arc::clone(&consumer_ring), Arc::clone(&xwaker));
208
209 let producer_ring = Arc::new(SpscRingCore::create_anon(CAP).unwrap());
210
211 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
212 let addr = listener.local_addr().unwrap();
213
214 let server = {
216 let consumer_ring = Arc::clone(&consumer_ring);
217 let xwaker = Arc::clone(&xwaker);
218 std::thread::spawn(move || {
219 serve_one(&listener, &consumer_ring, &xwaker).unwrap()
220 })
221 };
222
223 let feeder = {
226 let producer_ring = Arc::clone(&producer_ring);
227 std::thread::spawn(move || {
228 let mut buf = [0u8; SLOT];
229 for i in 0..N {
230 if i > 0 && i % (N / 3) == 0 {
231 std::thread::sleep(std::time::Duration::from_millis(10));
232 }
233 buf[..8].copy_from_slice(&i.to_le_bytes());
234 while producer_ring.try_push(&buf).is_err() {
235 std::hint::spin_loop();
236 }
237 }
238 })
239 };
240
241 let client = std::thread::spawn(move || {
243 ship(addr, producer_ring, N).unwrap();
244 });
245
246 let sum = block_on(async move {
248 let mut s = 0u64;
249 for expected in 0..N {
250 let item = rx.recv().await;
251 let seq = u64::from_le_bytes(item[..8].try_into().unwrap());
252 assert_eq!(seq, expected, "network FIFO order violated");
253 s = s.wrapping_add(seq);
254 }
255 s
256 });
257
258 feeder.join().unwrap();
259 client.join().unwrap();
260 assert_eq!(server.join().unwrap(), N);
261 assert_eq!(sum, (0..N).sum());
262 }
263
264 #[test]
265 fn tiny_consumer_ring_does_not_deadlock_on_full() {
266 const N: u64 = 4_000;
271 const CAP: usize = 4;
272
273 let consumer_ring = Arc::new(SpscRingCore::create_anon(CAP).unwrap());
274 let xwaker = Arc::new(
275 CrossProcessWaker::create_anon(MAX_WAITERS_DEFAULT).unwrap(),
276 );
277 let rx = receiver_cross(Arc::clone(&consumer_ring), Arc::clone(&xwaker));
278 let producer_ring = Arc::new(SpscRingCore::create_anon(CAP).unwrap());
279
280 let listener = TcpListener::bind("127.0.0.1:0").unwrap();
281 let addr = listener.local_addr().unwrap();
282
283 let server = {
284 let consumer_ring = Arc::clone(&consumer_ring);
285 let xwaker = Arc::clone(&xwaker);
286 std::thread::spawn(move || {
287 serve_one(&listener, &consumer_ring, &xwaker).unwrap()
288 })
289 };
290 let feeder = {
291 let producer_ring = Arc::clone(&producer_ring);
292 std::thread::spawn(move || {
293 let mut buf = [0u8; SLOT];
294 for i in 0..N {
295 buf[..8].copy_from_slice(&i.to_le_bytes());
296 while producer_ring.try_push(&buf).is_err() {
297 std::hint::spin_loop();
298 }
299 }
300 })
301 };
302 let client = std::thread::spawn(move || {
303 ship(addr, producer_ring, N).unwrap();
304 });
305
306 let sum = block_on(async move {
307 let mut s = 0u64;
308 for expected in 0..N {
309 let item = rx.recv().await;
310 let seq = u64::from_le_bytes(item[..8].try_into().unwrap());
311 assert_eq!(seq, expected, "FIFO order violated under Full pressure");
312 s = s.wrapping_add(seq);
313 }
314 s
315 });
316
317 feeder.join().unwrap();
318 client.join().unwrap();
319 assert_eq!(server.join().unwrap(), N);
320 assert_eq!(sum, (0..N).sum());
321 }
322}