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

EventFd type #1945

Merged
merged 1 commit into from
Dec 10, 2023
Merged

EventFd type #1945

merged 1 commit into from
Dec 10, 2023

Conversation

JonathanWoollett-Light
Copy link
Contributor

@JonathanWoollett-Light JonathanWoollett-Light commented Dec 18, 2022

Added an EventFd type that makes usage a little more convenient and explicit.

CHANGELOG.md Outdated Show resolved Hide resolved
Errno::result(res).map(|r| Self(unsafe { OwnedFd::from_raw_fd(r) }))
}
/// [`EventFd::write`] with `1`.
pub fn arm(&self) -> Result<usize> {
Copy link
Member

Choose a reason for hiding this comment

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

What is the meaning of the return value, here and for defuse?

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 passes through the return value of unistd::write.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, but what does that mean? What does it even mean to write to an eventfd ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this is answered by #1945 (comment)

/// Constructs [`EventFd`] with the given `init_val` and `flags`.
///
/// Wrapper around [`libc::eventfd`].
pub fn value_and_flags(init_val: u32, flags: EfdFlags) -> Result<Self> {
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 idiomatic to name these constructors from_XXX

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Renamed with from_ prefix.

unistd::write(self.0.as_raw_fd(),&0u64.to_ne_bytes())
}
/// Writes a given `value` to the file descriptor.
pub fn write(&self, value: u64) -> Result<usize> {
Copy link
Member

Choose a reason for hiding this comment

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

What is the meaning of writing a value to the file descriptor?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is literally what it is doing, although I would agree its not the clearest documentation although I'm not sure what to replace it with.

https://man7.org/linux/man-pages/man2/write.2.html

write() writes up to count bytes from the buffer starting at buf to the file referred to by the file descriptor fd.

Copy link
Member

Choose a reason for hiding this comment

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

But what is even the purpose of writing to an eventFD? Is there any reason why anybody would ever want to do that?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When you poll on an EventFd you can consider that the event fires when a non-zero value can be read from the EventFd. By writing a value to the EventFd you are setting it up to trigger value events when polled.

If you write 2 here then poll 3 times on the EventFd the 1st 2 polls will return and the 3rd will wait.

There are many reasons to manually rearm events, one is testing.

Copy link
Member

Choose a reason for hiding this comment

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

Ok. Could you please rewrite the docs then, in terms of "arming" instead of "writing". We should strive to be as high-level as possible while remaining zero-cost. Also, is there any point in passing through the return value of unistd::write?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Could you please rewrite the docs then, in terms of "arming" instead of "writing". We should strive to be as high-level as possible while remaining zero-cost.

I've updated the docs.

Also, is there any point in passing through the return value of unistd::write?

I'm not aware of a specific use-case but I would tend to favour returning a value rather than dropping it. I don't see harm in returning it.

Copy link
Member

@SteveLauC SteveLauC left a comment

Choose a reason for hiding this comment

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

Left some comments

src/sys/eventfd.rs Outdated Show resolved Hide resolved
pub fn eventfd(initval: libc::c_uint, flags: EfdFlags) -> Result<OwnedFd> {
let res = unsafe { libc::eventfd(initval, flags.bits()) };

Errno::result(res).map(|r| unsafe { OwnedFd::from_raw_fd(r) })
}

#[derive(Debug)]
pub struct EventFd(pub OwnedFd);
Copy link
Member

Choose a reason for hiding this comment

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

I generally prefer to not expose the inner OwnedFd type, instead, we should impl AsFd for EventFd

But if the OwnedFd way is easier to use than impl AsFd in some cases, I am totally fine to leave it as-it.

And, maybe we can make it #[repr(transparent)]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

By making the field public it allows a user to deconstruct it which AsFd doesn't allow, so for whatever reason they want they can extract the OwnedFd into another type.

I've added #[repr(transparent)].

Copy link
Member

Choose a reason for hiding this comment

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

What about impl From<EventFd> for OwnedFd, this is how the std handles them I think

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What is the downside of inner item being public? I don't see any.

Copy link
Member

Choose a reason for hiding this comment

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

Still, functionally, no difference, but I would like to be consistent with the std:

impl From<ChildStderr> for OwnedFd
impl From<ChildStdin> for OwnedFd
impl From<ChildStdout> for OwnedFd
impl From<File> for OwnedFd
impl From<PidFd> for OwnedFd
impl From<TcpListener> for OwnedFd

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, I've made the change.

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 make that OwnedFd field private given that we have impl From<EventFd for OwnedFd now

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated to remove pub.

src/sys/eventfd.rs Outdated Show resolved Hide resolved
src/sys/eventfd.rs Outdated Show resolved Hide resolved
self.0.as_fd()
}
}
impl AsRawFd for EventFd {
Copy link
Member

Choose a reason for hiding this comment

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

Do you need to impl AsRawFd for it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think so, is there any alternative approach to get the raw file descriptor you would suggest?

Copy link
Member

Choose a reason for hiding this comment

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

I was thinking that we have AsFd, then we can get a BorrowedFd, then we can extract the raw fd by calling .as_raw_fd() on the BorrowedFd, functionally, they have no difference, but this approach may discourage our user from using the raw fd

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think its generally more ergonomic for users to have AsRawFd rather than not have it. I could see someone complaining about not having it, but I can't see someone complaining about having it. I don't see any harm in having it.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, it has no harm, let's keep it

Copy link
Member

@SteveLauC SteveLauC left a comment

Choose a reason for hiding this comment

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

Thanks!

@SteveLauC SteveLauC added this pull request to the merge queue Dec 10, 2023
Merged via the queue into nix-rust:master with commit 9113d60 Dec 10, 2023
35 checks passed
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

4 participants