nix icon indicating copy to clipboard operation
nix copied to clipboard

FdSet: allow conversion from iterable of AsFd

Open rhelmot opened this issue 10 months ago • 8 comments

What does this PR do

Adds an implementation of FromIterator and From for FdSet, allowing them to be constructed from iterables of types that implement AsFd.

Checklist:

  • [X] I have read CONTRIBUTING.md
  • [X] I have written necessary tests and rustdoc comments
  • [X] A change log has been added if this PR modifies nix's API

rhelmot avatar Apr 22 '24 18:04 rhelmot

I'm not sure what the better type is, but I think the iterator item type should be the same as the argument type ofFdSet::insert, so that the implementation would be just

for fd in iter.into_iter() {
    result.insert(fd)
}

instead of duplicating the unsafe implementation of insert.

would that be ok: https://github.com/nix-rust/nix/commit/7def327db8683ca31f8e684390b21675ac7fd5e5 ?

Or would it be an API breaking change to also change insert?

TheJonny avatar Apr 23 '24 00:04 TheJonny

The goal here was definitely to avoid having to add a map in the user's construction code. What if we factored the unsafe code into another function that both insert and this could use?

rhelmot avatar Apr 23 '24 03:04 rhelmot

src/poll.rs has a comment why BorrowedFd is used as argument type instead f F: AsFd, which also applies here, I think:

https://github.com/nix-rust/nix/blob/f129095ac6e76505cfa6711cc78e2b5f4430070e/src/poll.rs#L41

AsFd would allow passing an OwnedFd or File, which would be closed on drop, leaving the FdSet with closed fds.

you can reproduce it if you change your test a bit

fn test_fdset_from_iterable() {
    let (r1, w1) = pipe().unwrap();
    let (r2, w2) = pipe().unwrap();
    let reads = [r1, r2]
        .into_iter()
        .map(|fd| (fd.as_raw_fd(), fd))
        .collect::<std::collections::HashMap<_, _>>();
    let writes = [w1, w2];
    let reads_fdset: FdSet = reads.values().into();
    let writes_fdset: FdSet = writes.into();
    for w in writes_fdset.fds(None) {
        nix::unistd::write(w, b"x").expect("should work");
    }
   /// ...
}

panics with EBADF.

TheJonny avatar Apr 23 '24 08:04 TheJonny

The FdSet interfaces indeed have a reason why they are using BorrowedFd rather than Fd: AsFd, see this PR


And for that unsafe code snippet,

unsafe { libc::FD_SET(fd.as_raw_fd(), &mut result.set) };

I am not sure I understand what this means,

The goal here was definitely to avoid having to add a map in the user's construction code.

would you like to elaborate on it a bit?

SteveLauC avatar Apr 23 '24 14:04 SteveLauC

The FdSet interfaces indeed have a reason

we might document it :)

TheJonny avatar Apr 23 '24 18:04 TheJonny

appart from type issue, I like the idea of the PR: having a way to construct an FdSet from an iterable!

TheJonny avatar Apr 23 '24 18:04 TheJonny

The FdSet interfaces indeed have a reason

we might document it :)

Yeah, I think we can document it in the comments

SteveLauC avatar Apr 24 '24 00:04 SteveLauC

would you like to elaborate on it a bit?

The goal was to avoid this snippet from the linked commit: writes.iter().map(|x| x.as_fd()).into()

I have not forgotten this PR, I am simply sidetracked onto other projects for a bit :)

rhelmot avatar Apr 28 '24 19:04 rhelmot