1use std::io;
28use std::os::fd::AsRawFd;
29use std::os::fd::BorrowedFd;
30
31use smallvec::SmallVec;
32use tokio::io::ReadBuf;
33
34pub const MAX_MMSG: usize = 16;
37
38pub fn recvmmsg(fd: BorrowedFd, bufs: &mut [ReadBuf<'_>]) -> io::Result<usize> {
39 let mut msgvec: SmallVec<[libc::mmsghdr; MAX_MMSG]> = SmallVec::new();
40 let mut iovecs: SmallVec<[libc::iovec; MAX_MMSG]> = SmallVec::new();
41
42 let mut ret = 0;
43
44 for bufs in bufs.chunks_mut(MAX_MMSG) {
45 msgvec.clear();
46 iovecs.clear();
47
48 for buf in bufs.iter_mut() {
49 let unfilled = unsafe { buf.unfilled_mut() };
52
53 iovecs.push(libc::iovec {
54 iov_base: unfilled.as_mut_ptr().cast(),
55 iov_len: unfilled.len(),
56 });
57 }
58
59 for (buf, iov) in bufs.iter().zip(iovecs.iter_mut()) {
62 msgvec.push(libc::mmsghdr {
63 msg_hdr: libc::msghdr {
64 msg_name: std::ptr::null_mut(),
65 msg_namelen: 0,
66 msg_iov: iov,
67 msg_iovlen: 1,
68 msg_control: std::ptr::null_mut(),
69 msg_controllen: 0,
70 msg_flags: 0,
71 },
72 msg_len: buf.capacity().try_into().unwrap(),
73 });
74 }
75
76 let result = unsafe {
83 libc::recvmmsg(
84 fd.as_raw_fd(),
85 msgvec.as_mut_ptr(),
86 msgvec.len() as _,
87 0,
88 std::ptr::null_mut(),
89 )
90 };
91
92 if result == -1 {
93 break;
94 }
95
96 for i in 0..result as usize {
97 let filled = msgvec[i].msg_len as usize;
98 unsafe { bufs[i].assume_init(filled) };
99 bufs[i].advance(filled);
100 ret += 1;
101 }
102
103 if (result as usize) < MAX_MMSG {
104 break;
105 }
106 }
107
108 if ret == 0 {
109 return Err(io::Error::last_os_error());
110 }
111
112 Ok(ret)
113}
114
115pub fn sendmmsg(fd: BorrowedFd, bufs: &[ReadBuf<'_>]) -> io::Result<usize> {
119 sendmmsg_impl(fd, bufs, None)
120}
121
122pub fn sendmmsg_with_suffix(
133 fd: BorrowedFd, bufs: &[ReadBuf<'_>], suffix: &[u8],
134) -> io::Result<usize> {
135 sendmmsg_impl(fd, bufs, Some(suffix))
136}
137
138fn sendmmsg_impl(
139 fd: BorrowedFd, bufs: &[ReadBuf<'_>], suffix: Option<&[u8]>,
140) -> io::Result<usize> {
141 if bufs.is_empty() {
142 return Ok(0);
143 }
144
145 let mut msgvec: SmallVec<[libc::mmsghdr; MAX_MMSG]> = SmallVec::new();
146 let mut iovecs: SmallVec<[libc::iovec; 2 * MAX_MMSG]> = SmallVec::new();
147
148 let mut ret = 0;
149
150 for bufs in bufs.chunks(MAX_MMSG) {
151 msgvec.clear();
152 iovecs.clear();
153
154 for buf in bufs {
155 iovecs.push(iovec(buf.filled()));
156
157 if let Some(suffix) = suffix {
158 iovecs.push(iovec(suffix));
159 }
160 }
161
162 let iovecs_per_message = if suffix.is_some() { 2 } else { 1 };
165 for message_iovecs in iovecs.chunks_exact_mut(iovecs_per_message) {
166 msgvec.push(libc::mmsghdr {
167 msg_hdr: libc::msghdr {
168 msg_name: std::ptr::null_mut(),
169 msg_namelen: 0,
170 msg_iov: message_iovecs.as_mut_ptr(),
171 msg_iovlen: iovecs_per_message as _,
172 msg_control: std::ptr::null_mut(),
173 msg_controllen: 0,
174 msg_flags: 0,
175 },
176 msg_len: 0,
178 });
179 }
180
181 let result = unsafe {
186 libc::sendmmsg(
187 fd.as_raw_fd(),
188 msgvec.as_mut_ptr(),
189 msgvec.len() as _,
190 0,
191 )
192 };
193
194 if result == -1 {
195 let err = io::Error::last_os_error();
196
197 if ret == 0 {
198 return Err(err);
199 }
200
201 break;
202 }
203
204 ret += result as usize;
205
206 if (result as usize) < bufs.len() {
207 break;
208 }
209 }
210
211 Ok(ret)
212}
213
214fn iovec(buf: &[u8]) -> libc::iovec {
215 libc::iovec {
216 iov_base: buf.as_ptr().cast_mut().cast(),
219 iov_len: buf.len(),
220 }
221}
222
223#[macro_export]
224macro_rules! poll_recvmmsg {
225 ($self: expr, $cx: ident, $bufs: ident) => {
226 loop {
227 match $self.poll_recv_ready($cx)? {
228 Poll::Ready(()) => {
229 match $self.try_io(tokio::io::Interest::READABLE, || {
230 $crate::mmsg::recvmmsg($self.as_fd(), $bufs)
231 }) {
232 Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => break Poll::Ready(res),
234 }
235 }
236 Poll::Pending => break Poll::Pending,
237 }
238 }
239 };
240}
241
242#[macro_export]
243macro_rules! poll_sendmmsg {
244 ($self: expr, $cx: ident, $bufs: ident) => {
245 loop {
246 match $self.poll_send_ready($cx)? {
247 Poll::Ready(()) => {
248 match $self.try_io(tokio::io::Interest::WRITABLE, || {
249 $crate::mmsg::sendmmsg($self.as_fd(), $bufs)
250 }) {
251 Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => break Poll::Ready(res),
253 }
254 }
255 Poll::Pending => break Poll::Pending,
256 }
257 }
258 };
259}
260
261#[cfg(test)]
262mod tests {
263 use std::io;
264 use std::os::fd::AsFd;
265
266 use tokio::io::ReadBuf;
267 use tokio::net::UnixDatagram;
268
269 use super::sendmmsg;
270 use super::sendmmsg_with_suffix;
271 use super::MAX_MMSG;
272 use crate::DatagramSocketRecvExt;
273 use crate::DatagramSocketSendExt;
274
275 #[tokio::test]
276 async fn recvmmsg() -> io::Result<()> {
277 let (s, mut r) = UnixDatagram::pair()?;
278 let mut bufs = [[0u8; 128]; 128];
279
280 for i in 0..5 {
281 s.send(&[i; 128]).await?;
282 }
283
284 let mut rbufs: Vec<_> =
285 bufs.iter_mut().map(|s| ReadBuf::new(&mut s[..])).collect();
286 assert_eq!(r.recv_many(&mut rbufs).await?, 5);
287
288 for (i, buf) in rbufs[0..5].iter().enumerate() {
289 assert_eq!(buf.filled(), &[i as u8; 128]);
290 }
291
292 for i in 0..92 {
293 s.send(&[i; 128]).await?;
294 }
295
296 let mut rbufs: Vec<_> =
297 bufs.iter_mut().map(|s| ReadBuf::new(&mut s[..])).collect();
298 assert_eq!(r.recv_many(&mut rbufs).await?, 92);
299
300 for (i, buf) in rbufs[0..92].iter().enumerate() {
301 assert_eq!(buf.filled(), &[i as u8; 128]);
302 }
303
304 Ok(())
305 }
306
307 #[tokio::test]
308 async fn send_many() -> io::Result<()> {
309 let (s, r) = UnixDatagram::pair()?;
310 let mut bufs: [_; 128] = std::array::from_fn(|i| [i as u8; 128]);
311
312 let wbufs: Vec<_> = bufs
313 .iter_mut()
314 .map(|s| {
315 let mut b = ReadBuf::new(&mut s[..]);
316 b.set_filled(128);
317 b
318 })
319 .collect();
320
321 assert_eq!(s.send_many(&wbufs[..5]).await?, 5);
322
323 let mut rbuf = [0u8; 128];
324
325 for i in 0..5 {
326 assert_eq!(r.recv(&mut rbuf).await?, 128);
327 assert_eq!(rbuf, [i as u8; 128]);
328 }
329
330 Ok(())
331 }
332
333 #[tokio::test]
334 async fn sendmmsg_with_suffix_appends_to_every_datagram() -> io::Result<()> {
335 let (s, r) = UnixDatagram::pair()?;
336 let suffix = b"-suffix";
337 let mut payloads: Vec<Vec<u8>> =
338 (0..MAX_MMSG + 4).map(|i| vec![i as u8; i]).collect();
339 let bufs: Vec<_> = payloads
340 .iter_mut()
341 .map(|payload| {
342 let len = payload.len();
343 let mut buf = ReadBuf::new(payload);
344 buf.set_filled(len);
345 buf
346 })
347 .collect();
348
349 assert_eq!(
350 sendmmsg_with_suffix(s.as_fd(), &bufs, suffix)?,
351 payloads.len()
352 );
353
354 let mut received = vec![0; MAX_MMSG + suffix.len() + 4];
355 for expected in &payloads {
356 let received_len = r.recv(&mut received).await?;
357 assert_eq!(
358 &received[..received_len],
359 [expected.as_slice(), suffix].concat()
360 );
361 }
362
363 Ok(())
364 }
365
366 #[test]
367 fn empty_send_batches_are_noops() -> io::Result<()> {
368 let (s, _r) = std::os::unix::net::UnixDatagram::pair()?;
369
370 assert_eq!(sendmmsg(s.as_fd(), &[])?, 0);
371 assert_eq!(sendmmsg_with_suffix(s.as_fd(), &[], b"suffix")?, 0);
372
373 Ok(())
374 }
375
376 #[test]
377 fn sendmmsg_with_suffix_reports_an_error_without_progress() -> io::Result<()>
378 {
379 let (s, r) = std::os::unix::net::UnixDatagram::pair()?;
380 drop(r);
381
382 let mut payload = *b"payload";
383 let payload_len = payload.len();
384 let mut buf = ReadBuf::new(&mut payload);
385 buf.set_filled(payload_len);
386
387 assert!(sendmmsg_with_suffix(s.as_fd(), &[buf], b"suffix").is_err());
388
389 Ok(())
390 }
391}