-
Notifications
You must be signed in to change notification settings - Fork 195
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.
Show resolved
Hide 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()), | ||
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 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(); | ||
} |
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 SockAddrArg
here, but it doesn't seem like the trait ensures that the pointer passed to the closure lives as long as theSockAddrArg
itself.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
SocketAddrArg
because that creates a temporarySocketAddrAny
that only lives for the duration of thewith_sockaddr
call, and we need aSocketAddrAny
that lives for'a
".Another possibility would be to add a
with_socket_addr_any
function toSocketAddrArg
like this:Then users with a family-specific
addr
could doand then
SocketAddrAny
'simpl SocketAddrArg
could skip theas_any()
call:That way users could avoid calling
as_any
themselves. 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.