Bug 1410702 - P2: Handle EINTR from recvmsg/sendmsg calls. r?kinetik
MozReview-Commit-ID: juVD61XSzL
--- a/media/audioipc/audioipc/src/msg.rs
+++ b/media/audioipc/audioipc/src/msg.rs
@@ -1,14 +1,32 @@
use libc;
use std::io;
use std::mem;
use std::os::unix::io::RawFd;
use std::ptr;
+fn cvt(r: libc::ssize_t) -> io::Result<usize> {
+ if r == -1 {
+ Err(io::Error::last_os_error())
+ } else {
+ Ok(r as usize)
+ }
+}
+
+// Convert return of -1 into error message, handling retry on EINTR
+fn cvt_r<F: FnMut() -> libc::ssize_t>(mut f: F) -> io::Result<usize> {
+ loop {
+ match cvt(f()) {
+ Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {},
+ other => return other,
+ }
+ }
+}
+
// Note: The following fields must be laid out together, the OS expects them
// to be part of a single allocation.
#[repr(C)]
struct CmsgSpace {
cmsghdr: libc::cmsghdr,
#[cfg(not(target_os = "macos"))]
__padding: [usize; 0],
data: libc::c_int,
@@ -54,22 +72,17 @@ pub fn sendmsg(fd: RawFd, to_send: &[u8]
iovec.iov_len = to_send.len();
cmsg.cmsghdr.cmsg_len = cmsg_len() as _;
cmsg.cmsghdr.cmsg_level = libc::SOL_SOCKET;
cmsg.cmsghdr.cmsg_type = libc::SCM_RIGHTS;
cmsg.data = fd_to_send.unwrap_or(-1);
- let result = unsafe { libc::sendmsg(fd, &msghdr, 0) };
- if result >= 0 {
- Ok(result as usize)
- } else {
- Err(io::Error::last_os_error())
- }
+ cvt_r(|| unsafe { libc::sendmsg(fd, &msghdr, 0) })
}
pub fn recvmsg(fd: RawFd, to_recv: &mut [u8]) -> io::Result<(usize, Option<RawFd>)> {
let mut msghdr: libc::msghdr = unsafe { mem::zeroed() };
let mut iovec: libc::iovec = unsafe { mem::zeroed() };
let mut cmsg: CmsgSpace = unsafe { mem::zeroed() };
msghdr.msg_iov = &mut iovec as *mut _;
@@ -80,31 +93,27 @@ pub fn recvmsg(fd: RawFd, to_recv: &mut
iovec.iov_base = if to_recv.is_empty() {
// Empty Vecs have a non-null pointer.
ptr::null_mut()
} else {
to_recv.as_ptr() as *const _ as *mut _
};
iovec.iov_len = to_recv.len();
- let result = unsafe { libc::recvmsg(fd, &mut msghdr, 0) };
- if result >= 0 {
- let fd = if msghdr.msg_controllen == cmsg_space() as _ &&
- cmsg.cmsghdr.cmsg_len == cmsg_len() as _ &&
- cmsg.cmsghdr.cmsg_level == libc::SOL_SOCKET &&
- cmsg.cmsghdr.cmsg_type == libc::SCM_RIGHTS {
- Some(cmsg.data)
- } else {
- None
- };
+ let result = try!(cvt_r(|| unsafe { libc::recvmsg(fd, &mut msghdr, 0) }));
+ let fd = if msghdr.msg_controllen == cmsg_space() as _ &&
+ cmsg.cmsghdr.cmsg_len == cmsg_len() as _ &&
+ cmsg.cmsghdr.cmsg_level == libc::SOL_SOCKET &&
+ cmsg.cmsghdr.cmsg_type == libc::SCM_RIGHTS {
+ Some(cmsg.data)
+ } else {
+ None
+ };
- Ok((result as usize, fd))
- } else {
- Err(io::Error::last_os_error())
- }
+ Ok((result as usize, fd))
}
#[cfg(test)]
mod tests {
use libc;
use std::mem;
use std::os::unix::net::UnixStream;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};