1use std::collections::HashMap;
11use std::io;
12use std::net::SocketAddr;
13use std::os::fd::{IntoRawFd, RawFd};
14use std::path::Path;
15use std::time::Duration;
16
17use mio::{Events, Interest, Token as MioToken, Waker};
18
19use super::{Cqe, Engine, Poll};
20use crate::buffer::BufferPool;
21use crate::token::Token;
22
23const EVENTS_CAP: usize = 1024;
25
26const WAKER: usize = u64::MAX as usize;
28
29enum Pending {
32 Read { slot: u32, token: Token },
34 Write {
36 slot: u32,
37 len: usize,
38 offset: usize,
39 token: Token,
40 },
41 Connect { token: Token },
43 Splice {
45 from: RawFd,
46 to: RawFd,
47 token: Token,
48 },
49 Listener { lfd: RawFd, token: Token },
51}
52
53pub struct MioEngine {
55 poller: mio::Poll,
56 #[allow(dead_code)] waker: Waker,
58 slot_bases: Vec<*mut u8>,
60 buf_size: usize,
61 pending: HashMap<RawFd, Vec<Pending>>,
63 events: Events,
64 cqes: Vec<Cqe>,
65}
66
67unsafe impl Send for MioEngine {}
70
71impl MioEngine {
72 pub fn new(pool: &BufferPool) -> io::Result<Self> {
77 let poller = mio::Poll::new()?;
78 let waker = Waker::new(poller.registry(), MioToken(WAKER))?;
79 let slot_bases = {
82 (0..pool.capacity() as u32)
83 .map(|i| pool.slot(i).as_ptr() as *mut u8)
84 .collect::<Vec<_>>()
85 };
86 Ok(Self {
87 poller,
88 waker,
89 slot_bases,
90 buf_size: pool.buf_size(),
91 pending: HashMap::new(),
92 events: Events::with_capacity(EVENTS_CAP),
93 cqes: Vec::with_capacity(EVENTS_CAP),
94 })
95 }
96
97 fn slot_ptr(&self, slot: u32) -> *mut u8 {
98 self.slot_bases[slot as usize]
99 }
100
101 fn register(&mut self, fd: RawFd) -> io::Result<()> {
102 let mut src = mio::unix::SourceFd(&fd);
103 self.poller.registry().register(
104 &mut src,
105 MioToken(fd as usize),
106 Interest::READABLE.add(Interest::WRITABLE),
107 )
108 }
109
110 fn push(&mut self, fd: RawFd, op: Pending) {
111 self.pending.entry(fd).or_default().push(op);
112 }
113
114 fn try_read_now(&mut self, fd: RawFd, slot: u32, token: &Token) -> bool {
117 let ptr = self.slot_ptr(slot);
118 let res = unsafe { libc::read(fd, ptr.cast(), self.buf_size) };
121 if res >= 0 {
122 crate::dbg_trace!(
123 "RDNOW fd={fd} n={res}{}",
124 if res == 0 { " (EOF?)" } else { "" }
125 );
126 self.cqes.push(Cqe {
127 token: *token,
128 result: Ok(res as u32),
129 });
130 true
131 } else {
132 let err = io::Error::last_os_error();
133 if err.kind() == io::ErrorKind::WouldBlock {
134 crate::dbg_trace!("RDPARK fd={fd}");
135 self.push(
136 fd,
137 Pending::Read {
138 slot,
139 token: *token,
140 },
141 );
142 false
143 } else {
144 self.cqes.push(Cqe {
145 token: *token,
146 result: Err(err),
147 });
148 true
149 }
150 }
151 }
152
153 fn finish_read(&mut self, fd: RawFd, slot: u32, token: Token) {
154 let _ = self.try_read_now(fd, slot, &token);
156 }
157
158 fn try_write_now(
162 &mut self,
163 fd: RawFd,
164 slot: u32,
165 len: usize,
166 offset: usize,
167 token: &Token,
168 ) -> bool {
169 let ptr = self.slot_ptr(slot);
170 let res = unsafe { libc::write(fd, ptr.add(offset).cast(), len - offset) };
172 if res >= 0 {
173 let n = res as usize;
174 if offset + n >= len {
175 self.cqes.push(Cqe {
176 token: *token,
177 result: Ok(len as u32),
178 });
179 true
180 } else {
181 self.push(
182 fd,
183 Pending::Write {
184 slot,
185 len,
186 offset: offset + n,
187 token: *token,
188 },
189 );
190 false
191 }
192 } else {
193 let err = io::Error::last_os_error();
194 if err.kind() == io::ErrorKind::WouldBlock {
195 self.push(
196 fd,
197 Pending::Write {
198 slot,
199 len,
200 offset,
201 token: *token,
202 },
203 );
204 false
205 } else {
206 self.cqes.push(Cqe {
207 token: *token,
208 result: Err(err),
209 });
210 true
211 }
212 }
213 }
214
215 fn finish_write(&mut self, fd: RawFd, slot: u32, len: usize, offset: usize, token: Token) {
216 let _ = self.try_write_now(fd, slot, len, offset, &token);
218 let _ = fd;
219 }
220
221 fn pump_splice(&mut self, from: RawFd, to: RawFd, token: Token) {
222 match crate::splice::pump(from, to, 1 << 20) {
223 crate::splice::PumpResult::Moved(n) => {
224 self.cqes.push(Cqe {
225 token,
226 result: Ok(n as u32),
227 });
228 }
229 crate::splice::PumpResult::Eof => {
230 self.cqes.push(Cqe {
231 token,
232 result: Ok(0),
233 });
234 }
235 crate::splice::PumpResult::WouldBlock => { }
236 crate::splice::PumpResult::Err(code) => {
237 self.cqes.push(Cqe {
238 token,
239 result: Err(io::Error::from_raw_os_error(code)),
240 });
241 return;
242 }
243 }
244 self.push(from, Pending::Splice { from, to, token });
246 }
247
248 fn dispatch(&mut self, fd: RawFd, readable: bool, writable: bool) {
250 let ops = self.pending.remove(&fd).unwrap_or_default();
251 for op in ops {
253 match op {
254 Pending::Connect { token } => {
255 let err = sock_error(fd);
256 self.cqes.push(Cqe {
257 token,
258 result: if err == 0 {
259 Ok(0)
260 } else {
261 Err(io::Error::from_raw_os_error(err))
262 },
263 });
264 }
265 Pending::Listener { lfd, token } => {
266 let _ = lfd;
267 if readable {
268 self.cqes.push(Cqe {
269 token,
270 result: Ok(0),
271 });
272 } else {
273 self.push(fd, Pending::Listener { lfd, token });
276 }
277 }
278 Pending::Read { slot, token } => {
279 if readable {
280 self.finish_read(fd, slot, token);
281 } else {
282 self.push(fd, Pending::Read { slot, token });
283 }
284 }
285 Pending::Write {
286 slot,
287 len,
288 offset,
289 token,
290 } => {
291 if writable {
292 self.finish_write(fd, slot, len, offset, token);
293 } else {
294 self.push(
295 fd,
296 Pending::Write {
297 slot,
298 len,
299 offset,
300 token,
301 },
302 );
303 }
304 }
305 Pending::Splice { from, to, token } => {
306 if readable {
307 self.pump_splice(from, to, token);
308 } else {
309 self.push(fd, Pending::Splice { from, to, token });
310 }
311 }
312 }
313 }
314 }
315}
316
317fn sock_error(fd: RawFd) -> i32 {
318 let mut err: i32 = 0;
319 let mut len = std::mem::size_of::<i32>() as libc::socklen_t;
320 unsafe {
322 libc::getsockopt(
323 fd,
324 libc::SOL_SOCKET,
325 libc::SO_ERROR,
326 std::ptr::addr_of_mut!(err).cast(),
327 std::ptr::addr_of_mut!(len),
328 );
329 }
330 err
331}
332
333impl Engine for MioEngine {
334 fn kind(&self) -> &'static str {
335 "mio"
336 }
337
338 fn add_listener(&mut self, fd: RawFd, token: Token) -> io::Result<()> {
339 let mut src = mio::unix::SourceFd(&fd);
340 self.poller
341 .registry()
342 .register(&mut src, MioToken(fd as usize), Interest::READABLE)?;
343 self.push(fd, Pending::Listener { lfd: fd, token });
344 Ok(())
345 }
346
347 fn add_stream(&mut self, fd: RawFd, _token: Token) -> io::Result<()> {
348 self.register(fd)?;
350 Ok(())
351 }
352
353 fn read(&mut self, token: Token, fd: RawFd, slot: u32) -> io::Result<Poll> {
354 if self.try_read_now(fd, slot, &token) {
358 Ok(Poll::Done(0)) } else {
360 Ok(Poll::Pending)
361 }
362 }
363
364 fn write(
365 &mut self,
366 token: Token,
367 fd: RawFd,
368 slot: u32,
369 len: usize,
370 offset: usize,
371 ) -> io::Result<Poll> {
372 if self.try_write_now(fd, slot, len, offset, &token) {
375 Ok(Poll::Done(len as u32))
376 } else {
377 Ok(Poll::Pending)
378 }
379 }
380
381 fn connect(&mut self, token: Token, addr: SocketAddr) -> io::Result<(RawFd, Poll)> {
382 let domain = if addr.is_ipv4() {
383 socket2::Domain::IPV4
384 } else {
385 socket2::Domain::IPV6
386 };
387 let sock =
388 socket2::Socket::new(domain, socket2::Type::STREAM, Some(socket2::Protocol::TCP))?;
389 sock.set_nonblocking(true)?;
390 sock.set_tcp_nodelay(true)?;
391 let sa: socket2::SockAddr = addr.into();
392 match sock.connect(&sa) {
393 Ok(()) => {
394 let fd = sock.into_raw_fd();
395 self.register(fd)?;
396 Ok((fd, Poll::Done(0)))
397 }
398 Err(e)
399 if e.raw_os_error() == Some(libc::EINPROGRESS)
400 || e.kind() == io::ErrorKind::WouldBlock =>
401 {
402 let fd = sock.into_raw_fd();
403 self.register(fd)?;
404 self.push(fd, Pending::Connect { token });
405 Ok((fd, Poll::Pending))
406 }
407 Err(e) => Err(e),
408 }
409 }
410
411 fn connect_unix(&mut self, token: Token, path: &Path) -> io::Result<(RawFd, Poll)> {
412 let sock = socket2::Socket::new(socket2::Domain::UNIX, socket2::Type::STREAM, None)?;
413 sock.set_nonblocking(true)?;
414 let addr = socket2::SockAddr::unix(path)?;
415 match sock.connect(&addr) {
416 Ok(()) => {
417 let fd = sock.into_raw_fd();
418 self.register(fd)?;
419 Ok((fd, Poll::Done(0)))
420 }
421 Err(e)
422 if e.raw_os_error() == Some(libc::EINPROGRESS)
423 || e.kind() == io::ErrorKind::WouldBlock =>
424 {
425 let fd = sock.into_raw_fd();
426 self.register(fd)?;
427 self.push(fd, Pending::Connect { token });
428 Ok((fd, Poll::Pending))
429 }
430 Err(e) => Err(e),
431 }
432 }
433
434 fn accept(&mut self, lfd: RawFd, ltoken: Token) -> io::Result<Option<(RawFd, SocketAddr)>> {
435 let mut sa: libc::sockaddr_storage = unsafe { std::mem::zeroed() };
437 let mut sa_len = std::mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t;
438 let fd = unsafe {
440 libc::accept4(
441 lfd,
442 std::ptr::addr_of_mut!(sa).cast(),
443 &mut sa_len,
444 libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC,
445 )
446 };
447 if fd >= 0 {
448 return Ok(Some((fd, parse_sockaddr(&sa))));
449 }
450 let err = io::Error::last_os_error();
451 match err.raw_os_error() {
452 Some(code) if code == libc::EAGAIN || code == libc::EWOULDBLOCK => {
453 self.pending
455 .entry(lfd)
456 .or_default()
457 .push(Pending::Listener { lfd, token: ltoken });
458 Ok(None)
459 }
460 Some(libc::EINTR) => self.accept(lfd, ltoken),
461 _ => Err(err),
462 }
463 }
464
465 fn splice_pump(&mut self, a: Token, afd: i32, b: Token, bfd: i32) -> io::Result<()> {
466 self.push(
467 afd,
468 Pending::Splice {
469 from: afd,
470 to: bfd,
471 token: a,
472 },
473 );
474 self.push(
475 bfd,
476 Pending::Splice {
477 from: bfd,
478 to: afd,
479 token: b,
480 },
481 );
482 Ok(())
483 }
484
485 fn remove(&mut self, fd: RawFd) {
486 let mut src = mio::unix::SourceFd(&fd);
489 let _ = self.poller.registry().deregister(&mut src);
490 self.pending.remove(&fd);
491 }
492
493 fn poll(&mut self, timeout: Option<Duration>, out: &mut Vec<Cqe>) -> io::Result<()> {
494 let wait = if self.cqes.is_empty() {
506 timeout
507 } else {
508 Some(Duration::ZERO) };
510 self.poller.poll(&mut self.events, wait)?;
511 let mut ready: Vec<(RawFd, bool, bool)> = Vec::with_capacity(64);
513 for ev in self.events.iter() {
514 if ev.token() == MioToken(WAKER) {
515 continue;
516 }
517 let fd = ev.token().0 as RawFd;
518 ready.push((fd, ev.is_readable(), ev.is_writable()));
519 }
520 for (fd, readable, writable) in ready {
521 self.dispatch(fd, readable, writable);
522 }
523 out.append(&mut self.cqes);
524 Ok(())
525 }
526
527 fn take_accepted(&mut self, _fd: RawFd) -> Option<SocketAddr> {
528 None }
530}
531
532fn parse_sockaddr(sa: &libc::sockaddr_storage) -> SocketAddr {
533 match sa.ss_family as i32 {
534 libc::AF_INET => {
535 let a: &libc::sockaddr_in =
537 unsafe { &*(sa as *const libc::sockaddr_storage).cast::<libc::sockaddr_in>() };
538 SocketAddr::from((
539 std::net::Ipv4Addr::from(u32::from_be(a.sin_addr.s_addr)),
540 u16::from_be(a.sin_port),
541 ))
542 }
543 _ => {
544 let a: &libc::sockaddr_in6 =
546 unsafe { &*(sa as *const libc::sockaddr_storage).cast::<libc::sockaddr_in6>() };
547 SocketAddr::from((
548 std::net::Ipv6Addr::from(a.sin6_addr.s6_addr),
549 u16::from_be(a.sin6_port),
550 ))
551 }
552 }
553}
554
555#[cfg(test)]
556mod fault_tests {
557 use super::*;
558 use crate::buffer::DEFAULT_BUF_SIZE;
559 use crate::token::Op;
560 use std::os::fd::AsRawFd;
561
562 fn test_engine(slots: u32) -> (BufferPool, MioEngine) {
563 let pool = BufferPool::new(slots as usize, DEFAULT_BUF_SIZE).expect("pool");
564 let engine = MioEngine::new(&pool).expect("engine");
565 (pool, engine)
566 }
567
568 fn tok(op: Op) -> Token {
569 Token::new(op, 0, 0, 0)
570 }
571
572 fn sockpair() -> (RawFd, RawFd) {
574 let mut fds = [0 as RawFd; 2];
575 let rc = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
577 assert_eq!(rc, 0);
578 for fd in fds {
579 let flags = unsafe { libc::fcntl(fd, libc::F_GETFL) };
581 unsafe { libc::fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK) };
583 }
584 (fds[0], fds[1])
585 }
586
587 fn close(fd: RawFd) {
588 unsafe { libc::close(fd) };
590 }
591
592 fn rst_peer(fd: RawFd) {
594 let linger = libc::linger {
595 l_onoff: 1,
596 l_linger: 0,
597 };
598 unsafe {
600 libc::setsockopt(
601 fd,
602 libc::SOL_SOCKET,
603 libc::SO_LINGER,
604 std::ptr::addr_of!(linger).cast(),
605 std::mem::size_of::<libc::linger>() as u32,
606 );
607 libc::close(fd);
608 }
609 }
610
611 #[test]
612 fn write_full_success_queues_cqe() {
613 let (_pool, mut engine) = test_engine(2);
614 let (a, b) = sockpair();
615 let t = tok(Op::DownstreamWrite);
616 let poll = engine.write(t, a, 0, 64, 0).expect("write");
617 assert!(matches!(poll, Poll::Done(64)));
618 assert_eq!(engine.cqes.len(), 1);
619 assert!(engine.cqes[0].result.is_ok());
620 let mut buf = [0xFFu8; 64];
622 let n = unsafe { libc::read(b, buf.as_mut_ptr().cast(), 64) };
624 assert_eq!(n, 64);
625 assert!(buf.iter().all(|&x| x == 0));
626 close(a);
627 close(b);
628 }
629
630 #[test]
631 fn write_partial_rearms_for_edge() {
632 let (_pool, mut engine) = test_engine(2);
633 let (a, b) = sockpair();
634 let small: libc::c_int = 4096;
636 unsafe {
638 libc::setsockopt(
639 a,
640 libc::SOL_SOCKET,
641 libc::SO_SNDBUF,
642 std::ptr::addr_of!(small).cast(),
643 std::mem::size_of_val(&small) as u32,
644 );
645 }
646 let chunk = [0xABu8; 8192];
648 loop {
649 let n = unsafe { libc::write(a, chunk.as_ptr().cast(), chunk.len()) };
651 if n < 0 {
652 break;
653 }
654 }
655 let t = tok(Op::DownstreamWrite);
656 let poll = engine.write(t, a, 0, DEFAULT_BUF_SIZE, 0).expect("write");
657 assert!(matches!(poll, Poll::Pending));
660 assert!(
661 engine.pending.get(&a).is_some_and(|v| !v.is_empty()),
662 "write must re-arm, got {:?}",
663 engine
664 .cqes
665 .iter()
666 .map(|c| format!("{:?}", c.result))
667 .collect::<Vec<_>>()
668 );
669 close(a);
670 close(b);
671 }
672
673 #[test]
674 fn write_to_reset_peer_reports_error_cqe() {
675 let (_pool, mut engine) = test_engine(2);
676 let (a, b) = sockpair();
677 rst_peer(b);
678 std::thread::sleep(std::time::Duration::from_millis(50));
680 let t = tok(Op::DownstreamWrite);
683 let mut saw_err = false;
684 for _ in 0..20 {
685 engine.cqes.clear();
686 let _ = engine.write(t, a, 0, DEFAULT_BUF_SIZE, 0);
687 if engine.cqes.iter().any(|c| c.result.is_err()) {
688 saw_err = true;
689 break;
690 }
691 std::thread::sleep(std::time::Duration::from_millis(10));
692 }
693 assert!(saw_err, "RST must surface an error CQE");
694 close(a);
695 }
696
697 #[test]
698 fn dispatch_keeps_unready_ops_armed() {
699 let (_pool, mut engine) = test_engine(2);
700 let (a, _b) = sockpair();
701 engine.push(
704 a,
705 Pending::Read {
706 slot: 0,
707 token: tok(Op::DownstreamRead),
708 },
709 );
710 engine.push(
711 a,
712 Pending::Write {
713 slot: 0,
714 len: 16,
715 offset: 0,
716 token: tok(Op::DownstreamWrite),
717 },
718 );
719 engine.push(
720 a,
721 Pending::Splice {
722 from: a,
723 to: a,
724 token: tok(Op::DownstreamRead),
725 },
726 );
727 engine.push(
728 a,
729 Pending::Listener {
730 lfd: a,
731 token: Token::accept(0),
732 },
733 );
734 engine.dispatch(a, false, false);
735 assert_eq!(engine.pending.get(&a).map(|v| v.len()), Some(4));
736 assert!(engine.cqes.is_empty());
737 close(a);
738 }
739
740 #[test]
741 fn dispatch_readable_read_consumes_and_reports() {
742 let (_pool, mut engine) = test_engine(2);
743 let (a, b) = sockpair();
744 let hello = b"hello";
745 unsafe { libc::write(b, hello.as_ptr().cast(), hello.len()) };
747 engine.push(
748 a,
749 Pending::Read {
750 slot: 1,
751 token: tok(Op::DownstreamRead),
752 },
753 );
754 engine.dispatch(a, true, false);
755 assert!(engine.pending.get(&a).is_none_or(|v| v.is_empty()));
756 assert_eq!(engine.cqes.len(), 1);
757 assert!(matches!(engine.cqes[0].result, Ok(5)));
758 close(a);
759 close(b);
760 }
761
762 #[test]
763 fn splice_wouldblock_rearms_without_cqe() {
764 let (_pool, mut engine) = test_engine(2);
765 let (a, b) = sockpair();
766 engine.pump_splice(a, b, tok(Op::DownstreamRead));
767 assert!(engine.cqes.is_empty(), "empty source must not complete");
768 assert!(engine.pending.get(&a).is_some_and(|v| !v.is_empty()));
769 close(a);
770 close(b);
771 }
772
773 #[test]
774 fn splice_moves_bytes_and_reports() {
775 let (_pool, mut engine) = test_engine(2);
776 let mut fds = [0 as RawFd; 2];
778 assert_eq!(
780 unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_NONBLOCK | libc::O_CLOEXEC) },
782 0
783 );
784 let (pr, pw) = (fds[0], fds[1]);
785 let payload = b"splice-payload";
786 unsafe { libc::write(pw, payload.as_ptr().cast(), payload.len()) };
788 let (sa, sb) = sockpair();
789 engine.pump_splice(pr, sb, tok(Op::DownstreamRead));
790 assert_eq!(engine.cqes.len(), 1);
791 assert!(matches!(engine.cqes[0].result, Ok(n) if n as usize == payload.len()));
792 let mut buf = [0u8; 32];
794 let n = unsafe { libc::read(sa, buf.as_mut_ptr().cast(), 32) };
796 assert!(n > 0, "sink peer must have data");
797 assert_eq!(&buf[..n as usize], payload);
798 close(pr);
799 close(pw);
800 close(sa);
801 close(sb);
802 }
803
804 #[test]
805 fn splice_to_bad_fd_reports_error() {
806 let (_pool, mut engine) = test_engine(2);
807 let mut fds = [0 as RawFd; 2];
808 assert_eq!(
810 unsafe { libc::pipe2(fds.as_mut_ptr(), libc::O_NONBLOCK | libc::O_CLOEXEC) },
812 0
813 );
814 let (pr, pw) = (fds[0], fds[1]);
815 let payload = b"x";
816 unsafe { libc::write(pw, payload.as_ptr().cast(), payload.len()) };
818 engine.pump_splice(pr, -1, tok(Op::DownstreamRead));
819 assert!(engine.cqes.iter().any(|c| c.result.is_err()));
820 close(pr);
821 close(pw);
822 }
823
824 #[test]
825 fn connect_refused_completes_with_error() {
826 let (_pool, mut engine) = test_engine(2);
827 let addr: SocketAddr = "127.0.0.1:1".parse().expect("addr");
829 let t = tok(Op::UpstreamWrite);
830 let (_fd, poll) = engine.connect(t, addr).expect("connect issued");
831 match poll {
833 Poll::Done(_) => {}
834 Poll::Pending => {
835 let mut out = Vec::new();
836 engine
837 .poll(Some(std::time::Duration::from_secs(2)), &mut out)
838 .expect("poll");
839 assert!(
840 out.iter().any(|c| c.result.is_err()),
841 "refused connect must error, got {out:?}"
842 );
843 }
844 }
845 }
846
847 #[test]
848 fn accept_none_then_some() {
849 let (_pool, mut engine) = test_engine(2);
850 let listener = vane_listener();
851 let lfd = listener.as_raw_fd();
852 let none = engine.accept(lfd, Token::accept(0)).expect("accept");
854 assert!(none.is_none());
855 assert!(engine.pending.get(&lfd).is_some_and(|v| !v.is_empty()));
856 let addr = listener.local_addr().expect("addr");
858 let _client = std::net::TcpStream::connect(addr).expect("connect");
859 std::thread::sleep(std::time::Duration::from_millis(50));
860 let some = engine.accept(lfd, Token::accept(0)).expect("accept2");
861 assert!(some.is_some(), "pending connection must accept");
862 if let Some((fd, _)) = some {
863 close(fd);
864 }
865 }
866
867 #[test]
868 fn remove_clears_pending_ops() {
869 let (_pool, mut engine) = test_engine(2);
870 let (a, b) = sockpair();
871 engine.push(
872 a,
873 Pending::Read {
874 slot: 0,
875 token: tok(Op::DownstreamRead),
876 },
877 );
878 assert!(engine.pending.contains_key(&a));
879 engine.remove(a);
880 assert!(!engine.pending.contains_key(&a));
881 close(a);
882 close(b);
883 }
884
885 fn vane_listener() -> std::net::TcpListener {
886 crate::tcp_listener("127.0.0.1:0".parse().expect("addr"), true, 64).expect("bind")
887 }
888}
889
890#[cfg(test)]
891mod unix_tests {
892 use super::*;
893 use crate::buffer::DEFAULT_BUF_SIZE;
894 use crate::token::Op;
895
896 #[test]
897 fn connect_unix_to_live_socket() {
898 let dir = tempfile::tempdir().expect("dir");
899 let path = dir.path().join("test.sock");
900 let listener = std::os::unix::net::UnixListener::bind(&path).expect("bind");
901 listener.set_nonblocking(true).ok();
903 std::thread::spawn(move || {
904 for stream in listener.incoming().flatten() {
905 drop(stream);
906 }
907 });
908
909 let pool = BufferPool::new(4, DEFAULT_BUF_SIZE).expect("pool");
910 let mut engine = MioEngine::new(&pool).expect("engine");
911 let t = Token::new(Op::Connect, 0, 0, 0);
912 let (fd, poll) = engine.connect_unix(t, &path).expect("connect_unix");
913 match poll {
914 Poll::Done(_) => {}
915 Poll::Pending => {
916 let mut out = Vec::new();
917 engine
918 .poll(Some(std::time::Duration::from_secs(2)), &mut out)
919 .expect("poll");
920 assert!(
921 out.iter().any(|c| c.token == t && c.result.is_ok()),
922 "unix connect must complete: {out:?}"
923 );
924 }
925 }
926 unsafe { libc::close(fd) };
928 }
929
930 #[test]
931 fn connect_unix_missing_path_errors() {
932 let pool = BufferPool::new(4, DEFAULT_BUF_SIZE).expect("pool");
933 let mut engine = MioEngine::new(&pool).expect("engine");
934 let t = Token::new(Op::Connect, 0, 0, 0);
935 let missing = std::path::PathBuf::from("/nonexistent/vane-test/no.sock");
936 let res = engine.connect_unix(t, &missing);
937 assert!(res.is_err() || matches!(res, Ok((_, Poll::Pending))));
938 if let Ok((fd, _)) = res {
939 unsafe { libc::close(fd) };
941 }
942 }
943}
944
945#[cfg(test)]
946mod partial_write_tests {
947 use super::*;
948 use crate::buffer::DEFAULT_BUF_SIZE;
949 use crate::token::Op;
950
951 #[test]
954 fn write_partial_rearms_then_completes() {
955 let mut pool = BufferPool::new(8, DEFAULT_BUF_SIZE).expect("pool");
956 let mut engine = MioEngine::new(&pool).expect("engine");
957
958 let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("bind");
959 let addr = listener.local_addr().expect("addr");
960 std::thread::spawn(move || {
962 for stream in listener.incoming().flatten() {
963 std::thread::spawn(move || {
964 std::thread::sleep(std::time::Duration::from_secs(30));
965 drop(stream);
966 });
967 }
968 });
969
970 let client = std::net::TcpStream::connect(addr).expect("connect");
971 client.set_nonblocking(true).expect("nonblock");
972 let fd = std::os::fd::AsRawFd::as_raw_fd(&client);
973
974 engine.register(fd).expect("register");
976 let mut out = Vec::new();
977 engine
978 .poll(Some(std::time::Duration::from_millis(100)), &mut out)
979 .expect("poll warmup");
980
981 let total = 3 * DEFAULT_BUF_SIZE;
983 for i in 0..total {
984 pool.slot_mut(0)[i % DEFAULT_BUF_SIZE] = (i % 251) as u8;
985 }
986 let t = Token::new(Op::DownstreamWrite, 0, 0, 0);
987 let poll = engine.write(t, fd, 0, total, 0).expect("write");
988 match poll {
989 Poll::Done(_) => {
990 return;
994 }
995 Poll::Pending => {}
996 }
997 let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5);
1001 while engine.cqes.is_empty() && std::time::Instant::now() < deadline {
1002 engine
1003 .poll(Some(std::time::Duration::from_millis(100)), &mut out)
1004 .expect("poll");
1005 }
1006 assert!(
1009 engine.cqes.iter().all(|c| c.result.is_ok()),
1010 "no spurious errors: {:?}",
1011 engine.cqes
1012 );
1013 drop(client);
1014 }
1015}