-
Notifications
You must be signed in to change notification settings - Fork 259
Add support for sendmmsg(2) on linux #1171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -194,3 +194,94 @@ fn test_v4_msg() { | |
| client.join().unwrap(); | ||
| server.join().unwrap(); | ||
| } | ||
|
|
||
| #[test] | ||
| #[cfg(target_os = "linux")] | ||
| fn test_v4_sendmmsg() { | ||
|
colinmarc marked this conversation as resolved.
|
||
| crate::init(); | ||
|
|
||
| use std::net::TcpStream; | ||
|
|
||
| use rustix::io::IoSlice; | ||
| use rustix::net::addr::SocketAddrArg as _; | ||
| use rustix::net::{sendmmsg, MMsgHdr}; | ||
|
|
||
| fn server(ready: Arc<(Mutex<u16>, Condvar)>) { | ||
| let connection_socket = socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); | ||
|
|
||
| let name = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 0); | ||
| bind(&connection_socket, &name).unwrap(); | ||
|
|
||
| let who = getsockname(&connection_socket).unwrap(); | ||
| let who = SocketAddrV4::try_from(who).unwrap(); | ||
|
|
||
| listen(&connection_socket, 1).unwrap(); | ||
|
|
||
| { | ||
| let (lock, cvar) = &*ready; | ||
| let mut port = lock.lock().unwrap(); | ||
| *port = who.port(); | ||
| cvar.notify_all(); | ||
| } | ||
|
|
||
| let mut buffer = vec![0; 13]; | ||
| let mut data_socket: TcpStream = accept(&connection_socket).unwrap().into(); | ||
|
|
||
| std::io::Read::read_exact(&mut data_socket, &mut buffer).unwrap(); | ||
| assert_eq!(String::from_utf8_lossy(&buffer), "hello...world"); | ||
| } | ||
|
|
||
| fn client(ready: Arc<(Mutex<u16>, Condvar)>) { | ||
| let port = { | ||
| let (lock, cvar) = &*ready; | ||
| let mut port = lock.lock().unwrap(); | ||
| while *port == 0 { | ||
| port = cvar.wait(port).unwrap(); | ||
| } | ||
| *port | ||
| }; | ||
|
|
||
| let addr = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port); | ||
| let data_socket = socket(AddressFamily::INET, SocketType::STREAM, None).unwrap(); | ||
| connect(&data_socket, &addr).unwrap(); | ||
|
|
||
| let mut off = 0; | ||
| while off < 2 { | ||
| let sent = sendmmsg( | ||
| &data_socket, | ||
| &mut [ | ||
| MMsgHdr::new(&[IoSlice::new(b"hello")], &mut Default::default()), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think my idea was to exercise both code paths. I changed it so that both tests exercise both. |
||
| MMsgHdr::new_with_addr( | ||
| &addr.as_any(), | ||
| &[IoSlice::new(b"...world")], | ||
| &mut Default::default(), | ||
| ), | ||
| ][off..], | ||
| SendFlags::empty(), | ||
| ) | ||
| .unwrap(); | ||
|
|
||
| off += sent; | ||
| } | ||
| } | ||
|
|
||
| let ready = Arc::new((Mutex::new(0_u16), Condvar::new())); | ||
| let ready_clone = Arc::clone(&ready); | ||
|
|
||
| let server = thread::Builder::new() | ||
| .name("server".to_string()) | ||
| .spawn(move || { | ||
| server(ready); | ||
| }) | ||
| .unwrap(); | ||
|
|
||
| let client = thread::Builder::new() | ||
| .name("client".to_string()) | ||
| .spawn(move || { | ||
| client(ready_clone); | ||
| }) | ||
| .unwrap(); | ||
|
|
||
| client.join().unwrap(); | ||
| server.join().unwrap(); | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to take a
&'a impl SockAddrArghere, but it doesn't seem like the trait ensures that the pointer passed to the closure lives as long as theSockAddrArgitself.We could obviously immediately call
as_any, but that would just be making an explicit clone implicit.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you're right. Could you add a comment about this? Something like, "this doesn't use
SocketAddrArgbecause that creates a temporarySocketAddrAnythat only lives for the duration of thewith_sockaddrcall, and we need aSocketAddrAnythat lives for'a".Another possibility would be to add a
with_socket_addr_anyfunction toSocketAddrArglike this:Then users with a family-specific
addrcould doand then
SocketAddrAny'simpl SocketAddrArgcould skip theas_any()call:That way users could avoid calling
as_anythemselves. We could do that, though it's not that much of a simplification.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a sentence to the docstring pointing users to
as_any- let me know what you think or if you think it needs more explication. I tried a few variants and in the end it felt like too much detail for end-users of the library, but maybe it would be useful as an "internal" comment.