native: Implement timeouts for unix networking

This commit has an implementation of the previous commit's timeout interface for
I/O objects on unix platforms. For implementation details, see the large comment
at the end of libnative/io/net.rs which talks about the general strategy taken.

Thankfully, all of these implementations can share code because they're
performing all the same operations.

This commit does not implement timeouts for named pipes on windows, only tcp/udp
objects on windows (which are quite similar to their unix equivalents).
This commit is contained in:
Alex Crichton
2014-04-25 20:50:22 -07:00
parent e27f27c858
commit 295e0a04ad
7 changed files with 493 additions and 157 deletions

View File

@@ -16,9 +16,12 @@ use std::io;
use std::mem;
use std::rt::rtio;
use std::sync::arc::UnsafeArc;
use std::unstable::mutex;
use super::{IoResult, retry, keep_going};
use super::{IoResult, retry};
use super::net;
use super::util;
use super::c;
use super::file::fd_t;
fn unix_socket(ty: libc::c_int) -> IoResult<fd_t> {
@@ -55,6 +58,13 @@ fn addr_to_sockaddr_un(addr: &CString) -> IoResult<(libc::sockaddr_storage, uint
struct Inner {
fd: fd_t,
lock: mutex::NativeMutex,
}
impl Inner {
fn new(fd: fd_t) -> Inner {
Inner { fd: fd, lock: unsafe { mutex::NativeMutex::new() } }
}
}
impl Drop for Inner {
@@ -64,7 +74,7 @@ impl Drop for Inner {
fn connect(addr: &CString, ty: libc::c_int,
timeout: Option<u64>) -> IoResult<Inner> {
let (addr, len) = try!(addr_to_sockaddr_un(addr));
let inner = Inner { fd: try!(unix_socket(ty)) };
let inner = Inner::new(try!(unix_socket(ty)));
let addrp = &addr as *_ as *libc::sockaddr;
let len = len as libc::socklen_t;
@@ -84,7 +94,7 @@ fn connect(addr: &CString, ty: libc::c_int,
fn bind(addr: &CString, ty: libc::c_int) -> IoResult<Inner> {
let (addr, len) = try!(addr_to_sockaddr_un(addr));
let inner = Inner { fd: try!(unix_socket(ty)) };
let inner = Inner::new(try!(unix_socket(ty)));
let addrp = &addr as *libc::sockaddr_storage;
match unsafe {
libc::bind(inner.fd, addrp as *libc::sockaddr, len as libc::socklen_t)
@@ -100,54 +110,74 @@ fn bind(addr: &CString, ty: libc::c_int) -> IoResult<Inner> {
pub struct UnixStream {
inner: UnsafeArc<Inner>,
read_deadline: u64,
write_deadline: u64,
}
impl UnixStream {
pub fn connect(addr: &CString,
timeout: Option<u64>) -> IoResult<UnixStream> {
connect(addr, libc::SOCK_STREAM, timeout).map(|inner| {
UnixStream { inner: UnsafeArc::new(inner) }
UnixStream::new(UnsafeArc::new(inner))
})
}
fn new(inner: UnsafeArc<Inner>) -> UnixStream {
UnixStream {
inner: inner,
read_deadline: 0,
write_deadline: 0,
}
}
fn fd(&self) -> fd_t { unsafe { (*self.inner.get()).fd } }
#[cfg(target_os = "linux")]
fn lock_nonblocking(&self) {}
#[cfg(not(target_os = "linux"))]
fn lock_nonblocking<'a>(&'a self) -> net::Guard<'a> {
let ret = net::Guard {
fd: self.fd(),
guard: unsafe { (*self.inner.get()).lock.lock() },
};
assert!(util::set_nonblocking(self.fd(), true).is_ok());
ret
}
}
impl rtio::RtioPipe for UnixStream {
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
let ret = retry(|| unsafe {
libc::recv(self.fd(),
buf.as_ptr() as *mut libc::c_void,
let fd = self.fd();
let dolock = || self.lock_nonblocking();
let doread = |nb| unsafe {
let flags = if nb {c::MSG_DONTWAIT} else {0};
libc::recv(fd,
buf.as_mut_ptr() as *mut libc::c_void,
buf.len() as libc::size_t,
0) as libc::c_int
});
if ret == 0 {
Err(io::standard_error(io::EndOfFile))
} else if ret < 0 {
Err(super::last_error())
} else {
Ok(ret as uint)
}
flags) as libc::c_int
};
net::read(fd, self.read_deadline, dolock, doread)
}
fn write(&mut self, buf: &[u8]) -> IoResult<()> {
let ret = keep_going(buf, |buf, len| unsafe {
libc::send(self.fd(),
let fd = self.fd();
let dolock = || self.lock_nonblocking();
let dowrite = |nb: bool, buf: *u8, len: uint| unsafe {
let flags = if nb {c::MSG_DONTWAIT} else {0};
libc::send(fd,
buf as *mut libc::c_void,
len as libc::size_t,
0) as i64
});
if ret < 0 {
Err(super::last_error())
} else {
Ok(())
flags)
};
match net::write(fd, self.write_deadline, buf, true, dolock, dowrite) {
Ok(_) => Ok(()),
Err(e) => Err(e)
}
}
fn clone(&self) -> Box<rtio::RtioPipe:Send> {
box UnixStream {
inner: self.inner.clone(),
} as Box<rtio::RtioPipe:Send>
box UnixStream::new(self.inner.clone()) as Box<rtio::RtioPipe:Send>
}
fn close_write(&mut self) -> IoResult<()> {
@@ -156,6 +186,17 @@ impl rtio::RtioPipe for UnixStream {
fn close_read(&mut self) -> IoResult<()> {
super::mkerr_libc(unsafe { libc::shutdown(self.fd(), libc::SHUT_RD) })
}
fn set_timeout(&mut self, timeout: Option<u64>) {
let deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
self.read_deadline = deadline;
self.write_deadline = deadline;
}
fn set_read_timeout(&mut self, timeout: Option<u64>) {
self.read_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
}
fn set_write_timeout(&mut self, timeout: Option<u64>) {
self.write_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
}
}
////////////////////////////////////////////////////////////////////////////////
@@ -202,7 +243,7 @@ impl UnixAcceptor {
pub fn native_accept(&mut self) -> IoResult<UnixStream> {
if self.deadline != 0 {
try!(util::accept_deadline(self.fd(), self.deadline));
try!(util::await(self.fd(), Some(self.deadline), util::Readable));
}
let mut storage: libc::sockaddr_storage = unsafe { intrinsics::init() };
let storagep = &mut storage as *mut libc::sockaddr_storage;
@@ -214,7 +255,7 @@ impl UnixAcceptor {
&mut size as *mut libc::socklen_t) as libc::c_int
}) {
-1 => Err(super::last_error()),
fd => Ok(UnixStream { inner: UnsafeArc::new(Inner { fd: fd }) })
fd => Ok(UnixStream::new(UnsafeArc::new(Inner::new(fd))))
}
}
}