-
Notifications
You must be signed in to change notification settings - Fork 0
Add sendmmsg #2
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
base: master
Are you sure you want to change the base?
Add sendmmsg #2
Conversation
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.
+1
macro_rules! define_mmsg_if_supported { | ||
($($item:item)*) => { | ||
$( | ||
#[cfg(all( |
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.
all() is not needed here.
@@ -172,6 +172,20 @@ macro_rules! man_links { | |||
}; | |||
} | |||
|
|||
macro_rules! define_mmsg_if_supported { |
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.
Sorry to be a nag about macros here, but is it needed at all? From the looks of it, it replaces the #[cfg()]
directives but that looks not a standard thing in Rust(we just sprinkle cfg()
around the blocks we need usually
} | ||
|
||
/// Number of received bytes. | ||
pub fn recieved_bytes(&self) -> u32 { |
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.
pub fn recieved_bytes(&self) -> u32 { | |
pub fn received_bytes(&self) -> u32 { |
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.
By the way, this method is duplicated by transmitted_bytes
on non-mutable implementation, maybe worth to extract such methods to the base impl
?
|
||
assert!(recvd == sent); | ||
|
||
assert!(recv_batched_msgs[0].recieved_bytes() == DATA.len().try_into().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.
The testcase makes sense, I just have few questions/suggestions but they can be argued with:
- what happens if the target value is not 10, but 1,000,000 - where is the upper bound defined, can it be retrieved via API or else how do we decide the batch size?
- The value 20 for the
recv_batched_msgs
is used so it would store the"Hello World!"
but I wonder - can we pass an exact len of that string(13bytes) in there? Basically to leave no gaps in the usage and make it very clear that the buffer needs to be just big enough to hold all the data so no misinterpretation would happen(I find test code very useful to understand the usage of code). - Can we annotate the
sendmmsg()
along the lines thatThis emits all the messages in a single syscall
and same forrecvmmsg()
?
That's all!
pub struct MMsgHdr<'addr, 'bufs, 'control> { | ||
inner: sys::mmsghdr, | ||
#[allow(clippy::type_complexity)] | ||
_lifetimes: PhantomData<(&'addr SockAddr, &'bufs IoSlice<'bufs>, &'control [u8])>, |
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 have hard time understanding that but I can match it to rust-lang#583 which may have been used as a reference but.. could you explain it a bit?
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.
You use PantomData to tell the compiler that you hold some data even if you don't see it directly in the code. In that case mmsghdr holds msghdr which inside keeps pointers to the address, buffers and and control buffer. This way you tell the compiler that those 3 things must outlive MsgHdr and MMsgHdr even though you don't hold rust references directly.
For example this is UB:
use libc;
struct MsgHdr {
inner: libc::msghdr
}
impl MsgHdr {
pub fn new() -> Self {
unsafe { std::mem::zeroed() }
}
pub fn with_control(mut self, buf: &[u8]) -> Self {
self.inner.msg_control = buf.as_ptr() as *mut _;
self.inner.msg_controllen = buf.len() as _;
self
}
}
fn foo(_m: &MsgHdr) {
}
fn main() {
let ctrl = Box::new([0; 6]);
let m = MsgHdr::new().with_control(ctrl.as_ref());
drop(ctrl);
// UB! _m still holds pointer to control even though it has been released.
foo(&m)
}
With PhantomData this will not compile:
use libc;
struct MsgHdr<'control> {
inner: libc::msghdr,
_control: std::marker::PhantomData<&'control [u8]>
}
impl<'control> MsgHdr<'control> {
pub fn new() -> Self {
unsafe { std::mem::zeroed() }
}
pub fn with_control(mut self, buf: &'control [u8]) -> Self {
self.inner.msg_control = buf.as_ptr() as *mut _;
self.inner.msg_controllen = buf.len() as _;
self
}
}
fn foo(_m: &MsgHdr) {
}
fn main() {
let ctrl = Box::new([0; 6]);
let m = MsgHdr::new().with_control(ctrl.as_ref());
drop(ctrl);
foo(&m)
}
You will get:
error[E0505]: cannot move out of `ctrl` because it is borrowed
--> src/main.rs:27:10
|
25 | let ctrl = Box::new([0; 6]);
| ---- binding `ctrl` declared here
26 | let m = MsgHdr::new().with_control(ctrl.as_ref());
| ---- borrow of `ctrl` occurs here
27 | drop(ctrl);
| ^^^^ move out of `ctrl` occurs here
28 | foo(&m)
| -- borrow later used here
|
Without PhantomData you could just store the reference to the thing it holds, but then your type keeps unnecessary 8 bytes per reference or 16 bytes per slice reference for no reason.
This is example without PhantomData. MsgHdr is 72 bytes instead of 56:
use libc;
struct MsgHdr<'control> {
inner: libc::msghdr,
_control: std::mem::MaybeUninit<&'control [u8]>
}
impl<'control> MsgHdr<'control> {
pub fn new() -> Self {
Self {
inner: unsafe { std::mem::zeroed() },
_control: std::mem::MaybeUninit::uninit(),
}
}
pub fn with_control(mut self, buf: &'control [u8]) -> Self {
self.inner.msg_control = buf.as_ptr() as *mut _;
self.inner.msg_controllen = buf.len() as _;
self
}
}
fn foo(_m: &MsgHdr) {
}
fn main() {
let ctrl = Box::new([0; 6]);
let m = MsgHdr::new().with_control(ctrl.as_ref());
drop(ctrl);
foo(&m);
}
This PR adds support in socket2 crate for *mmsg syscalls which would allow to send/recv multiple messages in a single syscall.
Since there already is a draft PR on the same issue in socket2 repo of someone else, hence it doesn't make sense to add another one there. Not sure when that PR would be merged into main, hence for now keeping a separate repo to further neptun improvement optimizations.