Skip to content
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

Implement sendfile on FreeBSD and Darwin #901

Merged
merged 1 commit into from
May 29, 2018
Merged

Implement sendfile on FreeBSD and Darwin #901

merged 1 commit into from
May 29, 2018

Conversation

morrowa
Copy link
Contributor

@morrowa morrowa commented May 16, 2018

This PR exposes the sendfile system call on libc's supported BSD-likes:

  • FreeBSD
  • Darwin (macOS/iOS)

DragonFly could be supported in the future, but I was unable to build rustc to test.

Note that NetBSD has no equivalent system call.

Copy link
Member

@asomers asomers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to the comments I made inline, please add a CHANGELOG entry.

#[allow(missing_debug_implementations)]
#[allow(missing_copy_implementations)]
#[repr(C)]
pub struct SfHdTr(libc::sf_hdtr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add a PhantomData field to keep sf_hdtr's pointers from dangling. See AioCb::from_mut_slice in src/sys/aio.rs for an example.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! Does it look correct?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

pub struct SfFlags: c_int {
SF_NODISKIO;
SF_MNOWAIT;
// SF_NOCACHE; // TODO: not yet exported by libc
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you opened a PR with libc for SF_NOCACHE support? If not, you should. It's very easy to get PRs accepted to libc. When you do, you should also include SF_USER_READAHEAD, which is new in FreeBSD 12.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Working on a PR against libc now. The tests fail when I add the FreeBSD 12 constant and run them on my FreeBSD 11.1 machine. Once I figure that out, I'll submit it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's because libc's tests run on FreeBSD 11.1. If you're adding a 12.0-specific symbol, you'll have to add it to the skip_fn section of libc-test/build.rs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libc_bitflags!{
pub struct SfFlags: c_int {
SF_NODISKIO;
SF_MNOWAIT;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SF_MNOWAIT is obsolete in FreeBSD 11.0 and above. The last release that makes use of it, 10.4, goes EoL on Oct 31 of this year. So I'd rather not expose this symbol in Nix unless there is specific demand.

}

#[cfg(target_os = "freebsd")]
pub fn sendfile(in_fd: RawFd,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add some usage documentation?

let (mut rd, wr) = UnixStream::pair().unwrap();

// Call the test method
let (res, bytes_written) = sendfile(tmp.as_raw_fd(), wr.as_raw_fd(), body_offset as off_t, 0, Some(&hdtr), SfFlags::empty(), 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrap to 80 columns, please.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I just run rustfmt on the files I've touched?

pub fn sendfile(in_fd: RawFd,
out_sock: RawFd,
offset: off_t,
count: usize,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be more Rusty for count to be an Option<usize> instead of giving a special meaning to 0.

@morrowa
Copy link
Contributor Author

morrowa commented May 16, 2018

Thanks for the very quick feedback! I'll update this PR soon.

#[allow(missing_debug_implementations)]
#[allow(missing_copy_implementations)]
#[repr(C)]
pub struct SfHdTr(libc::sf_hdtr);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd love to see a better name for this struct compared to the cryptic shorthand libc uses. SendfileHeader I think would work

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about SendfileHeaderTrailer?

pub struct SfFlags: c_int {
SF_NODISKIO;
SF_SYNC;
// SF_USER_READAHEAD; // TODO: waiting on libc release
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nix uses a git dependency on libc (except when we release), so you can go ahead and enable these now.

#[allow(missing_debug_implementations)]
#[allow(missing_copy_implementations)]
#[repr(C)]
pub struct SfHdTr(libc::sf_hdtr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes

@asomers
Copy link
Member

asomers commented May 19, 2018

I would still like to see some doc comments and a CHANGELOG entry. I would consider Dragonfly support to be optional, since we don't test that platform anyway.

@morrowa
Copy link
Contributor Author

morrowa commented May 19, 2018

I haven't been able to build rustc on DragonFly after several tries, so I will not implement that in this PR. I'll add the doc comments, add the change log entry, clean up my TODOs, rebase, and ping you when it's ready. Thanks again!

@morrowa morrowa changed the title WIP: Implement sendfile on BSD-likes Implement sendfile on FreeBSD and Darwin May 21, 2018
@morrowa
Copy link
Contributor Author

morrowa commented May 21, 2018

@asomers and @Susurrus this is ready for re-review! I believe it's complete.


libc_bitflags!{
pub struct SfFlags: c_int {
SF_NODISKIO;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add some documentation for the flags?

///
/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
///
/// For more information, see `man 2 sendfile`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a link, like other man pages references throughout our docs.

/// included in the returned count of bytes written. The values of `offset` and `count` do
/// not apply to headers or trailers.
///
/// `readahead` specifies how many pages may be cached in memory ahead of the page currently
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/how many pages may be cached/the minimum number of pages to cache/

tmp.write_all(body.as_bytes()).unwrap();

// Prepare headers and trailers for sendfile
let headers: Vec<IoVec<&[u8]>> = header_strings
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense for the API to accept a slice of slices instead of a slice of IoVecs? That would simplify the user's code. In fact, if you did this, then SendfileHeaderTrailer wouldn't even need to be public.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about a slice of byte slices? I feel like it makes the API even less obtuse.

@morrowa
Copy link
Contributor Author

morrowa commented May 22, 2018

@asomers ready for another look!

pub struct SendfileHeaderTrailer<'a>(libc::sf_hdtr, PhantomData<&'a [IoVec<&'a [u8]>]>);
struct SendfileHeaderTrailer<'a>(
libc::sf_hdtr,
Option<Vec<IoVec<&'a [u8]>>>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it necessary to store the Option<Vec< fields in SendfileHeaderTrailer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because it's creating the IoVec structs from byte slices, it's now the logical owner of those structs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It still doesn't own the data, however. PhantomData should be sufficient to keep the caller from dropping the byte slices. Or is there something I'm missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't own the byte slices, but it does own all of the IoVec instances, which do occupy memory.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, that makes sense. In that case, this PR LGTM. All it needs is a squash.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Squashed!

Copy link
Member

@asomers asomers left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved! I'll leave it up for a few days, in case @Susurrus has any comments.

Copy link
Contributor

@Susurrus Susurrus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few minor style fixups on my end. r+ once these are all corrected.

}
}

cfg_if! {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need a new one I don't think and could be bundled into the above macro, yes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The header/trailer struct is the same across FreeBSD and Darwin, but the flags and function signatures are different, so they can't share all code. I could nest the ifs, but this felt like it caused less rightward drift.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was referring only to the cfg_if! macro instance. Can you only have one if/else chain within each macro instance?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, if I try to put multiple independent if/else chains inside a single macro block, it won't compile. :(

/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
///
/// For more information, see [the sendfile(2) man page.](http://man7.org/linux/man-pages/man2/sendfile.2.html)
#[cfg(any(target_os = "linux", target_os = "android"))]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please alphabetize the OSes listed here.


#[cfg(any(target_os = "linux", target_os = "android"))]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please alphabetize the target OS list here.

use nix::unistd::{close, pipe, read};
use nix::sys::sendfile::sendfile;
cfg_if! {
if #[cfg(any(target_os = "linux", target_os = "android"))] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please alphabetize the target OS list here.

if #[cfg(any(target_os = "linux", target_os = "android"))] {
use nix::unistd::{close, pipe, read};
}
else if #[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put else expressions on the same line as the closing brace as suggested by the style guidelines.

(Errno::result(return_code).and(Ok(())), bytes_sent)
}
}
else if #[cfg(any(target_os = "ios", target_os = "macos"))] {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please put else expressions on the same line as the closing brace as suggested by the style guidelines.

@morrowa
Copy link
Contributor Author

morrowa commented May 28, 2018

@Susurrus style comments addressed! Ready for another look.

@Susurrus
Copy link
Contributor

bors r+

bors bot added a commit that referenced this pull request May 28, 2018
901: Implement sendfile on FreeBSD and Darwin r=Susurrus a=morrowa

This PR exposes the `sendfile` system call on libc's supported BSD-likes:
* FreeBSD
* Darwin (macOS/iOS)

DragonFly could be supported in the future, but I was unable to build rustc to test.

Note that NetBSD has no equivalent system call.

Co-authored-by: Andrew Morrow <andrew.d.morrow@gmail.com>
@bors
Copy link
Contributor

bors bot commented May 29, 2018

Build failed

@asomers
Copy link
Member

asomers commented May 29, 2018

The test failure is weird. It looks like Nix's code is wrong, and AFAICT this should've been failing ever since 5378945, but it never failed until now. There have been no recent changes in either libc or japaric/cross that look related. I don't know why it just began to fail.

@morrowa
Copy link
Contributor Author

morrowa commented May 29, 2018

A recent PR merged into libc changed the libc::bind function signature for 64-bit Android. That's what's causing the build failure. I'm quite confident it's unrelated to these changes.

@asomers
Copy link
Member

asomers commented May 29, 2018

You're right; I just couldn't find that with git blame for some reason.

@Susurrus
Copy link
Contributor

Please see #906 for a fix.

bors bot added a commit that referenced this pull request May 29, 2018
906: Fix bind() on Android 64-bit r=Susurrus a=Susurrus

libc fixed `bind()` for Android 64-bit targets, so change our
code to match.

PRs are failing (like #901) so let's get this merged.

CC @asomers @morrowa 

Co-authored-by: Bryant Mairs <bryantmairs@google.com>
@Susurrus
Copy link
Contributor

bors r+

bors bot added a commit that referenced this pull request May 29, 2018
901: Implement sendfile on FreeBSD and Darwin r=Susurrus a=morrowa

This PR exposes the `sendfile` system call on libc's supported BSD-likes:
* FreeBSD
* Darwin (macOS/iOS)

DragonFly could be supported in the future, but I was unable to build rustc to test.

Note that NetBSD has no equivalent system call.

Co-authored-by: Andrew Morrow <andrew.d.morrow@gmail.com>
@bors
Copy link
Contributor

bors bot commented May 29, 2018

@bors bors bot merged commit 325c43c into nix-rust:master May 29, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants