Commit e6067ed6 authored by Alex Crichton's avatar Alex Crichton
Browse files

Remove dependency on nix

This commit removes the dependency on the `nix` crate in favor of local
bindings. Most of the local bindings are slated to be deleted with the eventual
removal of the `deprecated` module and implementing Unix sockets and pipes out
of tree anyway.

The `nix` crate has grown quite large over time and is a bit of a weighty
dependency, and future extensions to the mio crate, such as adding eventfd
support, would likely require bypassing `nix` regardless to deal with
conditional support for the feature. Additionally mio already has raw logic for
functions like `epoll_create1` and such.
parent a724f381
......@@ -24,8 +24,7 @@ slab = "0.3.0"
net2 = "0.2.19"
[target.'cfg(unix)'.dependencies]
nix = "0.7.0"
libc = "0.2.16"
libc = "0.2.19"
[target.'cfg(windows)'.dependencies]
winapi = "0.2.1"
......@@ -33,7 +32,7 @@ miow = "0.1.4"
kernel32-sys = "0.2"
[dev-dependencies]
env_logger = "0.3.0"
env_logger = { version = "0.3.0", default-features = false }
tempdir = "0.3.4"
bytes = "0.3.0"
......
......@@ -3,7 +3,7 @@ use deprecated::TryAccept;
use io::MapNonBlock;
use std::io::{Read, Write};
use std::path::Path;
pub use nix::sys::socket::Shutdown;
pub use std::net::Shutdown;
use std::process;
pub use sys::Io;
......@@ -22,7 +22,11 @@ impl UnixSocket {
/// Connect the socket to the specified address
pub fn connect<P: AsRef<Path> + ?Sized>(self, addr: &P) -> io::Result<(UnixStream, bool)> {
let complete = try!(self.sys.connect(addr));
let complete = match self.sys.connect(addr) {
Ok(()) => true,
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => false,
Err(e) => return Err(e),
};
Ok((From::from(self.sys), complete))
}
......@@ -87,7 +91,7 @@ impl UnixStream {
}
pub fn shutdown(&self, how: Shutdown) -> io::Result<usize> {
self.sys.shutdown(how)
self.sys.shutdown(how).map(|_| 0)
}
pub fn read_recv_fd(&mut self, buf: &mut [u8]) -> io::Result<(usize, Option<RawFd>)> {
......@@ -217,14 +221,14 @@ pub struct PipeReader {
impl PipeReader {
pub fn from_stdout(stdout: process::ChildStdout) -> io::Result<Self> {
match sys::set_nonblock(&stdout) {
match sys::set_nonblock(stdout.as_raw_fd()) {
Err(e) => return Err(e),
_ => {},
}
return Ok(PipeReader::from(unsafe { Io::from_raw_fd(stdout.into_raw_fd()) }));
}
pub fn from_stderr(stderr: process::ChildStderr) -> io::Result<Self> {
match sys::set_nonblock(&stderr) {
match sys::set_nonblock(stderr.as_raw_fd()) {
Err(e) => return Err(e),
_ => {},
}
......@@ -271,7 +275,7 @@ pub struct PipeWriter {
impl PipeWriter {
pub fn from_stdin(stdin: process::ChildStdin) -> io::Result<Self> {
match sys::set_nonblock(&stdin) {
match sys::set_nonblock(stdin.as_raw_fd()) {
Err(e) => return Err(e),
_ => {},
}
......
......@@ -79,9 +79,6 @@ extern crate lazycell;
extern crate net2;
extern crate slab;
#[cfg(unix)]
extern crate nix;
#[cfg(unix)]
extern crate libc;
......
use std::marker;
use std::mem;
use std::sync::atomic::{AtomicUsize, Ordering};
use libc;
macro_rules! dlsym {
(fn $name:ident($($t:ty),*) -> $ret:ty) => (
#[allow(bad_style)]
static $name: ::sys::unix::dlsym::DlSym<unsafe extern fn($($t),*) -> $ret> =
::sys::unix::dlsym::DlSym {
name: concat!(stringify!($name), "\0"),
addr: ::std::sync::atomic::ATOMIC_USIZE_INIT,
_marker: ::std::marker::PhantomData,
};
)
}
pub struct DlSym<F> {
pub name: &'static str,
pub addr: AtomicUsize,
pub _marker: marker::PhantomData<F>,
}
impl<F> DlSym<F> {
pub fn get(&self) -> Option<&F> {
assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
unsafe {
if self.addr.load(Ordering::SeqCst) == 0 {
self.addr.store(fetch(self.name), Ordering::SeqCst);
}
if self.addr.load(Ordering::SeqCst) == 1 {
None
} else {
mem::transmute::<&AtomicUsize, Option<&F>>(&self.addr)
}
}
}
}
unsafe fn fetch(name: &str) -> usize {
assert_eq!(name.as_bytes()[name.len() - 1], 0);
match libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) as usize {
0 => 1,
n => n,
}
}
use {convert, io, Ready, PollOpt, Token};
use event::Event;
use libc::{c_int, c_void};
use libc;
use nix::sys::epoll::{EPOLLERR, EPOLLHUP, EPOLLRDHUP, EPOLLONESHOT};
use nix::sys::epoll::{EPOLLET, EPOLLOUT, EpollEvent, EPOLLIN, EPOLLPRI};
use nix::sys::epoll::{EpollEventKind, epoll_ctl, EpollOp, epoll_wait};
use nix::unistd::close;
use std::mem;
use std::os::unix::io::RawFd;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use std::time::Duration;
use std::{cmp, i32};
use libc::c_int;
use libc;
use libc::{EPOLLERR, EPOLLHUP, EPOLLRDHUP, EPOLLONESHOT};
use libc::{EPOLLET, EPOLLOUT, EPOLLIN, EPOLLPRI};
use {convert, io, Ready, PollOpt, Token};
use event::Event;
use sys::unix::cvt;
use sys::unix::io::set_cloexec;
/// Each Selector has a globally unique(ish) ID associated with it. This ID
/// gets tracked by `TcpStream`, `TcpListener`, etc... when they are first
......@@ -21,37 +23,28 @@ static NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;
#[derive(Debug)]
pub struct Selector {
id: usize,
epfd: RawFd
}
// Emulate `epoll_create` by using `epoll_create1` if it's available and
// otherwise falling back to `epoll_create` followed by a call to set the
// CLOEXEC flag.
unsafe fn epoll_create() -> io::Result<RawFd> {
let name = "epoll_create1\0".as_ptr();
let ptr = libc::dlsym(libc::RTLD_DEFAULT, name as *const _);
let fd;
if ptr.is_null() {
fd = libc::epoll_create(1024);
if fd > 0 {
libc::ioctl(fd, libc::FIOCLEX);
}
} else {
type EpollCreate1 = unsafe extern fn(c_int) -> c_int;
let epoll_create1 = mem::transmute::<*mut c_void, EpollCreate1>(ptr);
fd = epoll_create1(libc::EPOLL_CLOEXEC);
}
if fd >= 0 {
Ok(fd)
} else {
Err(io::Error::last_os_error())
}
epfd: RawFd,
}
impl Selector {
pub fn new() -> io::Result<Selector> {
let epfd = try!(unsafe { epoll_create() });
let epfd = unsafe {
// Emulate `epoll_create` by using `epoll_create1` if it's available
// and otherwise falling back to `epoll_create` followed by a call to
// set the CLOEXEC flag.
dlsym!(fn epoll_create1(c_int) -> c_int);
match epoll_create1.get() {
Some(epoll_create1_fn) => {
try!(cvt(epoll_create1_fn(libc::EPOLL_CLOEXEC)))
}
None => {
let fd = try!(cvt(libc::epoll_create(1024)));
drop(set_cloexec(fd));
fd
}
}
};
// offset by 1 to avoid choosing 0 as the id of a selector
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed) + 1;
......@@ -68,28 +61,25 @@ impl Selector {
/// Wait for events from the OS
pub fn select(&self, evts: &mut Events, awakener: Token, timeout: Option<Duration>) -> io::Result<bool> {
use std::{cmp, i32, slice};
let timeout_ms = timeout
.map(|to| cmp::min(convert::millis(to), i32::MAX as u64) as i32)
.unwrap_or(-1);
let dst = unsafe {
slice::from_raw_parts_mut(
evts.events.as_mut_ptr(),
evts.events.capacity())
};
// Wait for epoll events for at most timeout_ms milliseconds
let cnt = try!(epoll_wait(self.epfd, dst, timeout_ms as isize)
.map_err(super::from_nix_error));
unsafe { evts.events.set_len(cnt); }
for i in 0..cnt {
if evts.get(i).map(|e| e.token()) == Some(awakener) {
evts.events.remove(i);
return Ok(true);
unsafe {
evts.events.set_len(0);
let cnt = try!(cvt(libc::epoll_wait(self.epfd,
evts.events.as_mut_ptr(),
evts.events.capacity() as i32,
timeout_ms)));
let cnt = cnt as usize;
evts.events.set_len(cnt);
for i in 0..cnt {
if evts.events[i].u64 as usize == awakener.into() {
evts.events.remove(i);
return Ok(true);
}
}
}
......@@ -98,24 +88,28 @@ impl Selector {
/// Register event interests for the given IO handle with the OS
pub fn register(&self, fd: RawFd, token: Token, interests: Ready, opts: PollOpt) -> io::Result<()> {
let info = EpollEvent {
let mut info = libc::epoll_event {
events: ioevent_to_epoll(interests, opts),
data: usize::from(token) as u64
u64: usize::from(token) as u64
};
epoll_ctl(self.epfd, EpollOp::EpollCtlAdd, fd, &info)
.map_err(super::from_nix_error)
unsafe {
try!(cvt(libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_ADD, fd, &mut info)));
Ok(())
}
}
/// Register event interests for the given IO handle with the OS
pub fn reregister(&self, fd: RawFd, token: Token, interests: Ready, opts: PollOpt) -> io::Result<()> {
let info = EpollEvent {
let mut info = libc::epoll_event {
events: ioevent_to_epoll(interests, opts),
data: usize::from(token) as u64
u64: usize::from(token) as u64
};
epoll_ctl(self.epfd, EpollOp::EpollCtlMod, fd, &info)
.map_err(super::from_nix_error)
unsafe {
try!(cvt(libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_MOD, fd, &mut info)));
Ok(())
}
}
/// Deregister event interests for the given IO handle with the OS
......@@ -123,58 +117,62 @@ impl Selector {
// The &info argument should be ignored by the system,
// but linux < 2.6.9 required it to be not null.
// For compatibility, we provide a dummy EpollEvent.
let info = EpollEvent {
events: EpollEventKind::empty(),
data: 0
let mut info = libc::epoll_event {
events: 0,
u64: 0,
};
epoll_ctl(self.epfd, EpollOp::EpollCtlDel, fd, &info)
.map_err(super::from_nix_error)
unsafe {
try!(cvt(libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_DEL, fd, &mut info)));
Ok(())
}
}
}
fn ioevent_to_epoll(interest: Ready, opts: PollOpt) -> EpollEventKind {
let mut kind = EpollEventKind::empty();
fn ioevent_to_epoll(interest: Ready, opts: PollOpt) -> u32 {
let mut kind = 0;
if interest.is_readable() {
if opts.is_urgent() {
kind.insert(EPOLLPRI);
kind |= EPOLLPRI;
} else {
kind.insert(EPOLLIN);
kind |= EPOLLIN;
}
}
if interest.is_writable() {
kind.insert(EPOLLOUT);
kind |= EPOLLOUT;
}
if interest.is_hup() {
kind.insert(EPOLLRDHUP);
kind |= EPOLLRDHUP;
}
if opts.is_edge() {
kind.insert(EPOLLET);
kind |= EPOLLET;
}
if opts.is_oneshot() {
kind.insert(EPOLLONESHOT);
kind |= EPOLLONESHOT;
}
if opts.is_level() {
kind.remove(EPOLLET);
kind &= !EPOLLET;
}
kind
kind as u32
}
impl Drop for Selector {
fn drop(&mut self) {
let _ = close(self.epfd);
unsafe {
let _ = libc::close(self.epfd);
}
}
}
pub struct Events {
events: Vec<EpollEvent>,
events: Vec<libc::epoll_event>,
}
impl Events {
......@@ -197,36 +195,36 @@ impl Events {
#[inline]
pub fn get(&self, idx: usize) -> Option<Event> {
self.events.get(idx).map(|event| {
let epoll = event.events;
let epoll = event.events as c_int;
let mut kind = Ready::none();
if epoll.contains(EPOLLIN) | epoll.contains(EPOLLPRI) {
if (epoll & EPOLLIN) != 0 || (epoll & EPOLLPRI) != 0 {
kind = kind | Ready::readable();
}
if epoll.contains(EPOLLOUT) {
if (epoll & EPOLLOUT) != 0 {
kind = kind | Ready::writable();
}
// EPOLLHUP - Usually means a socket error happened
if epoll.contains(EPOLLERR) {
if (epoll & EPOLLERR) != 0 {
kind = kind | Ready::error();
}
if epoll.contains(EPOLLRDHUP) | epoll.contains(EPOLLHUP) {
if (epoll & EPOLLRDHUP) != 0 || (epoll & EPOLLHUP) != 0 {
kind = kind | Ready::hup();
}
let token = self.events[idx].data;
let token = self.events[idx].u64;
Event::new(kind, Token(token as usize))
})
}
pub fn push_event(&mut self, event: Event) {
self.events.push(EpollEvent {
self.events.push(libc::epoll_event {
events: ioevent_to_epoll(event.kind(), PollOpt::empty()),
data: usize::from(event.token()) as u64
u64: usize::from(event.token()) as u64
});
}
}
use {io, poll, Evented, Ready, Poll, PollOpt, Token};
use std::fs::File;
use std::io::{Read, Write};
use std::mem;
use std::os::unix::io::{IntoRawFd, AsRawFd, FromRawFd, RawFd};
use nix::fcntl::FcntlArg::F_SETFL;
use nix::fcntl::{fcntl, O_NONBLOCK};
pub fn set_nonblock(s: &AsRawFd) -> io::Result<()> {
fcntl(s.as_raw_fd(), F_SETFL(O_NONBLOCK)).map_err(super::from_nix_error)
.map(|_| ())
use libc;
use {io, Evented, Ready, Poll, PollOpt, Token};
use unix::EventedFd;
use sys::unix::cvt;
pub fn set_nonblock(fd: libc::c_int) -> io::Result<()> {
unsafe {
let mut nonblocking = 1 as libc::c_ulong;
cvt(libc::ioctl(fd, libc::FIONBIO, &mut nonblocking)).map(|_| ())
}
}
pub fn set_cloexec(fd: libc::c_int) -> io::Result<()> {
unsafe {
cvt(libc::ioctl(fd, libc::FIOCLEX)).map(|_| ())
}
}
/*
*
......@@ -19,86 +29,75 @@ pub fn set_nonblock(s: &AsRawFd) -> io::Result<()> {
#[derive(Debug)]
pub struct Io {
fd: RawFd,
fd: File,
}
impl Io {
pub fn try_clone(&self) -> io::Result<Io> {
Ok(Io { fd: try!(self.fd.try_clone()) })
}
}
impl FromRawFd for Io {
unsafe fn from_raw_fd(fd: RawFd) -> Io {
Io { fd: fd }
Io { fd: File::from_raw_fd(fd) }
}
}
impl IntoRawFd for Io {
fn into_raw_fd(self) -> RawFd {
// Forget self to prevent drop() from closing self.fd.
let fd = self.fd;
mem::forget(self);
fd
self.fd.into_raw_fd()
}
}
impl AsRawFd for Io {
fn as_raw_fd(&self) -> RawFd {
self.fd
self.fd.as_raw_fd()
}
}
impl Evented for Io {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
poll::selector(poll).register(self.fd, token, interest, opts)
EventedFd(&self.as_raw_fd()).register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
poll::selector(poll).reregister(self.fd, token, interest, opts)
EventedFd(&self.as_raw_fd()).reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
poll::selector(poll).deregister(self.fd)
EventedFd(&self.as_raw_fd()).deregister(poll)
}
}
impl Read for Io {
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
<&Io as Read>::read(&mut &*self, dst)
(&self.fd).read(dst)
}
}
impl<'a> Read for &'a Io {
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
use nix::unistd::read;
read(self.as_raw_fd(), dst)
.map_err(super::from_nix_error)
(&self.fd).read(dst)
}
}
impl Write for Io {
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
<&Io as Write>::write(&mut &*self, src)
(&self.fd).write(src)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
(&self.fd).flush()
}
}
impl<'a> Write for &'a Io {
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
use nix::unistd::write;
write(self.as_raw_fd(), src)
.map_err(super::from_nix_error)
(&self.fd).write(src)
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
(&self.fd).flush()
}
}
impl Drop for Io {
fn drop(&mut self) {
use nix::unistd::close;
let _ = close(self.as_raw_fd());
}
}
use {io, Ready, PollOpt, Token};
use event::{self, Event};
use nix::libc::timespec;
use nix::unistd::close;
use nix::sys::event::{EventFilter, EventFlag, FilterFlag, KEvent, kqueue, kevent, kevent_ts};
use nix::sys::event::{EV_ADD, EV_CLEAR, EV_DELETE, EV_DISABLE, EV_ENABLE, EV_EOF, EV_ERROR, EV_ONESHOT};
use libc::{self, time_t};
use std::{cmp, fmt, slice};
use std::cell::UnsafeCell;
use std::{cmp, fmt};
use std::cell::RefCell;
use std::os::unix::io::RawFd;
use std::collections::HashMap;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use std::time::Duration;
use libc::{self, time_t};
use {io, Ready, PollOpt, Token};
use event::{self, Event};
use sys::unix::cvt;
use sys::unix::io::set_cloexec;
/// Each Selector has a globally unique(ish) ID associated with it. This ID
/// gets tracked by `TcpStream`, `TcpListener`, etc... when they are first
/// registered with the `Selector`. If a type that is previously associatd with
......@@ -22,29 +22,20 @@ static NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;
pub struct Selector {
id: usize,
kq: RawFd,
changes: UnsafeCell<Vec<KEvent>>,
changes: RefCell<KeventList>,
}
#[cfg(not(target_os = "netbsd"))]
type UData = usize;
#[cfg(target_os = "netbsd")]
type UData = isize;
impl Selector {
pub fn new() -> io::Result<Selector> {
// offset by 1 to avoid choosing 0 as the id of a selector
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed) + 1;
let kq = try!(kqueue().map_err(super::from_nix_error));