-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
signal: use eventfd instead of UnixStream for signal notification #7845
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?
Changes from all commits
60142c5
f7cbb0a
76f49b8
af29bd6
9a36bea
3b62b56
da7a4f1
b48c950
55221b8
e7e66d0
5d05e27
0fc1242
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 |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| use std::{ | ||
| io, | ||
| os::fd::{AsRawFd, FromRawFd, OwnedFd}, | ||
| }; | ||
|
|
||
| use mio::{event, unix::SourceFd}; | ||
|
|
||
| #[derive(Debug)] | ||
| pub(crate) struct Sender { | ||
| fd: OwnedFd, | ||
| } | ||
|
|
||
| #[derive(Debug)] | ||
| pub(crate) struct Receiver { | ||
| fd: OwnedFd, | ||
| } | ||
|
|
||
| impl event::Source for Receiver { | ||
| fn register( | ||
| &mut self, | ||
| registry: &mio::Registry, | ||
| token: mio::Token, | ||
| interests: mio::Interest, | ||
| ) -> io::Result<()> { | ||
| SourceFd(&self.fd.as_raw_fd()).register(registry, token, interests) | ||
| } | ||
|
|
||
| fn reregister( | ||
| &mut self, | ||
| registry: &mio::Registry, | ||
| token: mio::Token, | ||
| interests: mio::Interest, | ||
| ) -> io::Result<()> { | ||
| SourceFd(&self.fd.as_raw_fd()).reregister(registry, token, interests) | ||
| } | ||
|
|
||
| fn deregister(&mut self, registry: &mio::Registry) -> io::Result<()> { | ||
| SourceFd(&self.fd.as_raw_fd()).deregister(registry) | ||
| } | ||
| } | ||
|
|
||
| impl Sender { | ||
| pub(crate) fn new() -> std::io::Result<Self> { | ||
| // SAFETY: it's ok to call libc API | ||
| let fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) }; | ||
| if fd == -1 { | ||
| return Err(io::Error::last_os_error()); | ||
| } | ||
| Ok(Sender { | ||
| // SAFETY: fd just opened by the above libc::eventfd | ||
| fd: unsafe { OwnedFd::from_raw_fd(fd) }, | ||
| }) | ||
| } | ||
|
|
||
| pub(crate) fn receiver(&self) -> std::io::Result<Receiver> { | ||
| Ok(Receiver { | ||
| fd: self.fd.try_clone()?, | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| impl Sender { | ||
|
Member
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. This could be merged with the |
||
| pub(crate) fn write(&self) -> std::io::Result<usize> { | ||
| // SAFETY: it's ok to call libc API | ||
| let r = unsafe { libc::eventfd_write(self.fd.as_raw_fd(), 1) }; | ||
| if r == 0 { | ||
| Ok(0) | ||
|
Member
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. There is a small different between the eventfd and unixstream impls here. |
||
| } else { | ||
| Err(std::io::Error::last_os_error()) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl Receiver { | ||
| pub(crate) fn read(&mut self) -> std::io::Result<libc::c_int> { | ||
| let fd = self.fd.as_raw_fd(); | ||
| let mut value: libc::eventfd_t = 0; | ||
|
|
||
| // SAFETY: it's ok to call libc API | ||
| let r = unsafe { libc::eventfd_read(fd, &mut value as *mut libc::eventfd_t) }; | ||
| if r == 0 { | ||
| Ok(0) | ||
| } else { | ||
| Err(std::io::Error::last_os_error()) | ||
|
Member
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. Since it is non-blocking it may return EAGAIN (ErrorKind::WouldBlock) which is not an error. let err = std::io::Error::last_os_error();
// On a non-blocking eventfd, it is expected to get an EAGAIN
// when the counter is 0.
if err.kind() == std::io::ErrorKind::WouldBlock {
Ok(0)
} else {
Err(err)
} |
||
| } | ||
| } | ||
| } | ||
|
|
||
| pub(crate) struct OsExtraData { | ||
| sender: Sender, | ||
| } | ||
|
|
||
| impl OsExtraData { | ||
| pub(crate) fn new() -> std::io::Result<Self> { | ||
| Sender::new().map(|sender| Self { sender }) | ||
| } | ||
| } | ||
|
|
||
| impl OsExtraData { | ||
| pub(crate) fn receiver(&self) -> std::io::Result<Receiver> { | ||
| self.sender.receiver() | ||
| } | ||
|
|
||
| pub(crate) fn sender(&self) -> &Sender { | ||
| &self.sender | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| use mio::net::UnixStream; | ||
| use std::io::{self, Read, Write}; | ||
| use std::mem::ManuallyDrop; | ||
| use std::os::unix::io::{AsRawFd, FromRawFd}; | ||
|
|
||
| pub(crate) struct Sender { | ||
| inner: UnixStream, | ||
| } | ||
|
|
||
| #[derive(Debug)] | ||
| pub(crate) struct Receiver { | ||
| inner: UnixStream, | ||
| } | ||
|
|
||
| impl mio::event::Source for Receiver { | ||
| fn register( | ||
| &mut self, | ||
| registry: &mio::Registry, | ||
| token: mio::Token, | ||
| interests: mio::Interest, | ||
| ) -> io::Result<()> { | ||
| self.inner.register(registry, token, interests) | ||
| } | ||
|
|
||
| fn reregister( | ||
| &mut self, | ||
| registry: &mio::Registry, | ||
| token: mio::Token, | ||
| interests: mio::Interest, | ||
| ) -> io::Result<()> { | ||
| self.inner.reregister(registry, token, interests) | ||
| } | ||
|
|
||
| fn deregister(&mut self, registry: &mio::Registry) -> io::Result<()> { | ||
| self.inner.deregister(registry) | ||
| } | ||
| } | ||
|
|
||
| impl Sender { | ||
| pub(crate) fn write(&self) -> std::io::Result<usize> { | ||
| (&self.inner).write(&[1]) | ||
| } | ||
| } | ||
|
|
||
| impl Receiver { | ||
| pub(crate) fn read(&mut self) -> std::io::Result<libc::c_int> { | ||
| // Drain the pipe completely so we can receive a new readiness event | ||
| // if another signal has come in. | ||
| let mut buf = [0; 128]; | ||
| #[allow(clippy::unused_io_amount)] | ||
| loop { | ||
| match self.inner.read(&mut buf) { | ||
| Ok(0) => panic!("EOF on self-pipe"), | ||
| Ok(_) => continue, // Keep reading | ||
| Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => break, | ||
| Err(e) => { | ||
| return Err(e); | ||
| } | ||
| } | ||
| } | ||
| Ok(0) | ||
| } | ||
| } | ||
|
|
||
| pub(crate) fn channel() -> std::io::Result<(Sender, Receiver)> { | ||
| let (sender, receiver) = UnixStream::pair()?; | ||
| Ok((Sender { inner: sender }, Receiver { inner: receiver })) | ||
| } | ||
|
|
||
| pub(crate) struct OsExtraData { | ||
| sender: Sender, | ||
| receiver: Receiver, | ||
| } | ||
|
|
||
| impl OsExtraData { | ||
| pub(crate) fn new() -> std::io::Result<Self> { | ||
| let (sender, receiver) = channel()?; | ||
| Ok(Self { sender, receiver }) | ||
| } | ||
| } | ||
|
|
||
| impl OsExtraData { | ||
|
Member
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. This impl could be merged with the one above |
||
| pub(crate) fn receiver(&self) -> std::io::Result<Receiver> { | ||
| let receiver_fd = self.receiver.inner.as_raw_fd(); | ||
| // SAFETY: fd owned by receiver is opened | ||
| let original = | ||
| ManuallyDrop::new(unsafe { std::os::unix::net::UnixStream::from_raw_fd(receiver_fd) }); | ||
| let inner = UnixStream::from_std(original.try_clone()?); | ||
| Ok(Receiver { inner }) | ||
| } | ||
|
|
||
| pub(crate) fn sender(&self) -> &Sender { | ||
| &self.sender | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -142,25 +142,28 @@ impl Globals { | |
| } | ||
| } | ||
|
|
||
| fn globals_init() -> Globals | ||
| fn globals_init() -> std::io::Result<Globals> | ||
| where | ||
| OsExtraData: 'static + Send + Sync + Default, | ||
| OsExtraData: 'static + Send + Sync, | ||
| OsStorage: 'static + Send + Sync + Default, | ||
| { | ||
| Globals { | ||
| extra: OsExtraData::default(), | ||
| Ok(Globals { | ||
| extra: OsExtraData::new()?, | ||
| registry: Registry::new(OsStorage::default()), | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| pub(crate) fn globals() -> &'static Globals | ||
| pub(crate) fn globals() -> std::io::Result<&'static Globals> | ||
| where | ||
| OsExtraData: 'static + Send + Sync + Default, | ||
| OsExtraData: 'static + Send + Sync, | ||
| OsStorage: 'static + Send + Sync + Default, | ||
| { | ||
| static GLOBALS: OnceLock<Globals> = OnceLock::new(); | ||
| static GLOBALS: OnceLock<std::io::Result<Globals>> = OnceLock::new(); | ||
|
Contributor
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. Drive by review: I think you should store |
||
|
|
||
| GLOBALS.get_or_init(globals_init) | ||
| match GLOBALS.get_or_init(globals_init) { | ||
| Ok(globals) => Ok(globals), | ||
| Err(e) => Err(std::io::Error::new(e.kind(), e.to_string())), | ||
| } | ||
| } | ||
|
|
||
| #[cfg(all(test, not(loom)))] | ||
|
|
||
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.
Comparing with what mio does, this seems to not have a bunch of logic to reset the eventfd and so on... Is it missing here?
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.
A read on fd returned by eventfd resets eventfd. Here's
libc::eventfd_readin Receiver::read() function.