Merge pull request #133 from dignifiedquire/isolate-nix

refactor: replace nix with libc
This commit is contained in:
Stjepan Glavina 2020-05-31 09:11:58 -07:00 committed by GitHub
commit c5e5c25952
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 471 additions and 90 deletions

View File

@ -44,7 +44,7 @@ features = ["rt-threaded"]
optional = true
[target.'cfg(unix)'.dependencies]
nix = "0.17.0"
libc = "0.2.70"
[target.'cfg(windows)'.dependencies]
wepoll-binding = "2.0.2"

View File

@ -678,7 +678,7 @@ impl Async<TcpStream> {
socket.connect(&addr.into()).or_else(|err| {
// Check for EINPROGRESS on Unix and WSAEWOULDBLOCK on Windows.
#[cfg(unix)]
let in_progress = err.raw_os_error() == Some(nix::libc::EINPROGRESS);
let in_progress = err.raw_os_error() == Some(libc::EINPROGRESS);
#[cfg(windows)]
let in_progress = err.kind() == io::ErrorKind::WouldBlock;
@ -1006,7 +1006,7 @@ impl Async<UnixStream> {
socket
.connect(&socket2::SockAddr::unix(path)?)
.or_else(|err| {
if err.raw_os_error() == Some(nix::libc::EINPROGRESS) {
if err.raw_os_error() == Some(libc::EINPROGRESS) {
Ok(())
} else {
Err(err)

View File

@ -114,19 +114,20 @@ fn notifier() -> io::Result<(Socket, Socket)> {
#[cfg(target_os = "linux")]
mod linux {
use super::*;
use nix::sys::eventfd::{eventfd, EfdFlags};
use crate::sys::eventfd::eventfd;
use crate::sys::unistd;
use std::os::unix::io::AsRawFd;
pub(crate) struct EventFd(std::os::unix::io::RawFd);
impl EventFd {
pub fn new() -> Result<Self, std::io::Error> {
let fd = eventfd(0, EfdFlags::EFD_CLOEXEC | EfdFlags::EFD_NONBLOCK).map_err(io_err)?;
let fd = eventfd(0, libc::EFD_CLOEXEC | libc::EFD_NONBLOCK)?;
Ok(EventFd(fd))
}
pub fn try_clone(&self) -> Result<EventFd, io::Error> {
nix::unistd::dup(self.0).map(EventFd).map_err(io_err)
unistd::dup(self.0).map(EventFd)
}
}
@ -138,28 +139,21 @@ mod linux {
impl Drop for EventFd {
fn drop(&mut self) {
let _ = nix::unistd::close(self.0);
}
}
fn io_err(err: nix::Error) -> io::Error {
match err {
nix::Error::Sys(code) => code.into(),
err => io::Error::new(io::ErrorKind::Other, Box::new(err)),
let _ = unistd::close(self.0);
}
}
impl Read for &EventFd {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {
nix::unistd::read(self.0, buf).map_err(io_err)
unistd::read(self.0, buf)
}
}
impl Write for &EventFd {
#[inline]
fn write(&mut self, buf: &[u8]) -> std::result::Result<usize, std::io::Error> {
nix::unistd::write(self.0, buf).map_err(io_err)
unistd::write(self.0, buf)
}
#[inline]

View File

@ -122,6 +122,7 @@ mod context;
mod io_event;
mod reactor;
mod run;
mod sys;
mod task;
mod thread_local;
mod throttle;

View File

@ -32,13 +32,14 @@ use std::time::{Duration, Instant};
use crossbeam_queue::ArrayQueue;
use futures_util::future;
#[cfg(unix)]
use nix::fcntl::{fcntl, FcntlArg, OFlag};
use once_cell::sync::Lazy;
use slab::Slab;
#[cfg(windows)]
use socket2::Socket;
#[cfg(unix)]
use crate::sys::fcntl::{fcntl, FcntlArg};
use crate::io_event::IoEvent;
/// The reactor.
@ -103,9 +104,9 @@ impl Reactor {
// Put the I/O handle in non-blocking mode.
#[cfg(unix)]
{
let flags = fcntl(raw, FcntlArg::F_GETFL).map_err(io_err)?;
let flags = OFlag::from_bits_truncate(flags) | OFlag::O_NONBLOCK;
fcntl(raw, FcntlArg::F_SETFL(flags)).map_err(io_err)?;
let flags = fcntl(raw, FcntlArg::F_GETFL)?;
let flags = flags | libc::O_NONBLOCK;
fcntl(raw, FcntlArg::F_SETFL(flags))?;
}
#[cfg(windows)]
{
@ -429,15 +430,6 @@ impl Source {
}
}
/// Converts a [`nix::Error`] into [`io::Error`].
#[cfg(unix)]
fn io_err(err: nix::Error) -> io::Error {
match err {
nix::Error::Sys(code) => code.into(),
err => io::Error::new(io::ErrorKind::Other, Box::new(err)),
}
}
/// Raw bindings to epoll (Linux, Android, illumos).
#[cfg(any(target_os = "linux", target_os = "android", target_os = "illumos"))]
mod sys {
@ -446,24 +438,22 @@ mod sys {
use std::os::unix::io::RawFd;
use std::time::Duration;
use nix::sys::epoll::{
epoll_create1, epoll_ctl, epoll_wait, EpollCreateFlags, EpollEvent, EpollFlags, EpollOp,
use crate::sys::epoll::{
epoll_create1, epoll_ctl, epoll_wait, EpollEvent, EpollFlags, EpollOp,
};
use super::io_err;
pub struct Reactor(RawFd);
impl Reactor {
pub fn new() -> io::Result<Reactor> {
let epoll_fd = epoll_create1(EpollCreateFlags::EPOLL_CLOEXEC).map_err(io_err)?;
let epoll_fd = epoll_create1()?;
Ok(Reactor(epoll_fd))
}
pub fn register(&self, fd: RawFd, key: usize) -> io::Result<()> {
let ev = &mut EpollEvent::new(EpollFlags::empty(), key as u64);
epoll_ctl(self.0, EpollOp::EpollCtlAdd, fd, Some(ev)).map_err(io_err)
let ev = &mut EpollEvent::new(0, key as u64);
epoll_ctl(self.0, EpollOp::EpollCtlAdd, fd, Some(ev))
}
pub fn reregister(&self, fd: RawFd, key: usize, read: bool, write: bool) -> io::Result<()> {
let mut flags = EpollFlags::EPOLLONESHOT;
let mut flags = libc::EPOLLONESHOT;
if read {
flags |= read_flags();
}
@ -471,10 +461,10 @@ mod sys {
flags |= write_flags();
}
let ev = &mut EpollEvent::new(flags, key as u64);
epoll_ctl(self.0, EpollOp::EpollCtlMod, fd, Some(ev)).map_err(io_err)
epoll_ctl(self.0, EpollOp::EpollCtlMod, fd, Some(ev))
}
pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
epoll_ctl(self.0, EpollOp::EpollCtlDel, fd, None).map_err(io_err)
epoll_ctl(self.0, EpollOp::EpollCtlDel, fd, None)
}
pub fn wait(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<usize> {
let timeout_ms = timeout
@ -487,15 +477,15 @@ mod sys {
})
.and_then(|t| t.as_millis().try_into().ok())
.unwrap_or(-1);
events.len = epoll_wait(self.0, &mut events.list, timeout_ms).map_err(io_err)?;
events.len = epoll_wait(self.0, &mut events.list, timeout_ms)?;
Ok(events.len)
}
}
fn read_flags() -> EpollFlags {
EpollFlags::EPOLLIN | EpollFlags::EPOLLRDHUP
libc::EPOLLIN | libc::EPOLLRDHUP
}
fn write_flags() -> EpollFlags {
EpollFlags::EPOLLOUT
libc::EPOLLOUT
}
pub struct Events {
@ -510,8 +500,8 @@ mod sys {
}
pub fn iter(&self) -> impl Iterator<Item = Event> + '_ {
self.list[..self.len].iter().map(|ev| Event {
readable: ev.events().intersects(read_flags()),
writable: ev.events().intersects(write_flags()),
readable: (ev.events() & read_flags()) > 0,
writable: (ev.events() & write_flags()) > 0,
key: ev.data() as usize,
})
}
@ -537,64 +527,46 @@ mod sys {
use std::os::unix::io::RawFd;
use std::time::Duration;
use nix::errno::Errno;
use nix::fcntl::{fcntl, FcntlArg, FdFlag};
use nix::libc;
use nix::sys::event::{kevent_ts, kqueue, EventFilter, EventFlag, FilterFlag, KEvent};
use super::io_err;
use crate::sys::event::{kevent_ts, kqueue, FilterFlag, KEvent};
use crate::sys::fcntl::{fcntl, FcntlArg};
pub struct Reactor(RawFd);
impl Reactor {
pub fn new() -> io::Result<Reactor> {
let fd = kqueue().map_err(io_err)?;
fcntl(fd, FcntlArg::F_SETFD(FdFlag::FD_CLOEXEC)).map_err(io_err)?;
let fd = kqueue()?;
fcntl(fd, FcntlArg::F_SETFD(libc::FD_CLOEXEC))?;
Ok(Reactor(fd))
}
pub fn register(&self, _fd: RawFd, _key: usize) -> io::Result<()> {
Ok(())
}
pub fn reregister(&self, fd: RawFd, key: usize, read: bool, write: bool) -> io::Result<()> {
let mut read_flags = EventFlag::EV_ONESHOT | EventFlag::EV_RECEIPT;
let mut write_flags = EventFlag::EV_ONESHOT | EventFlag::EV_RECEIPT;
let mut read_flags = libc::EV_ONESHOT | libc::EV_RECEIPT;
let mut write_flags = libc::EV_ONESHOT | libc::EV_RECEIPT;
if read {
read_flags |= EventFlag::EV_ADD;
read_flags |= libc::EV_ADD;
} else {
read_flags |= EventFlag::EV_DELETE;
read_flags |= libc::EV_DELETE;
}
if write {
write_flags |= EventFlag::EV_ADD;
write_flags |= libc::EV_ADD;
} else {
write_flags |= EventFlag::EV_DELETE;
write_flags |= libc::EV_DELETE;
}
let udata = key as _;
let changelist = [
KEvent::new(
fd as _,
EventFilter::EVFILT_READ,
read_flags,
FFLAGS,
0,
udata,
),
KEvent::new(
fd as _,
EventFilter::EVFILT_WRITE,
write_flags,
FFLAGS,
0,
udata,
),
KEvent::new(fd as _, libc::EVFILT_READ, read_flags, FFLAGS, 0, udata),
KEvent::new(fd as _, libc::EVFILT_WRITE, write_flags, FFLAGS, 0, udata),
];
let mut eventlist = changelist;
kevent_ts(self.0, &changelist, &mut eventlist, None).map_err(io_err)?;
kevent_ts(self.0, &changelist, &mut eventlist, None)?;
for ev in &eventlist {
// Explanation for ignoring EPIPE: https://github.com/tokio-rs/mio/issues/582
let (flags, data) = (ev.flags(), ev.data());
if flags.contains(EventFlag::EV_ERROR)
if (flags & libc::EV_ERROR) == 1
&& data != 0
&& data != Errno::ENOENT as _
&& data != Errno::EPIPE as _
&& data != libc::ENOENT as _
&& data != libc::EPIPE as _
{
return Err(io::Error::from_raw_os_error(data as _));
}
@ -602,16 +574,16 @@ mod sys {
Ok(())
}
pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
let flags = EventFlag::EV_RECEIPT | EventFlag::EV_DELETE;
let flags = libc::EV_RECEIPT | libc::EV_DELETE;
let changelist = [
KEvent::new(fd as _, EventFilter::EVFILT_WRITE, flags, FFLAGS, 0, 0),
KEvent::new(fd as _, EventFilter::EVFILT_READ, flags, FFLAGS, 0, 0),
KEvent::new(fd as _, libc::EVFILT_WRITE, flags, FFLAGS, 0, 0),
KEvent::new(fd as _, libc::EVFILT_READ, flags, FFLAGS, 0, 0),
];
let mut eventlist = changelist;
kevent_ts(self.0, &changelist, &mut eventlist, None).map_err(io_err)?;
kevent_ts(self.0, &changelist, &mut eventlist, None)?;
for ev in &eventlist {
let (flags, data) = (ev.flags(), ev.data());
if flags.contains(EventFlag::EV_ERROR) && data != 0 && data != Errno::ENOENT as _ {
if (flags & libc::EV_ERROR == 1) && data != 0 && data != libc::ENOENT as _ {
return Err(io::Error::from_raw_os_error(data as _));
}
}
@ -622,11 +594,11 @@ mod sys {
tv_sec: t.as_secs() as libc::time_t,
tv_nsec: t.subsec_nanos() as libc::c_long,
});
events.len = kevent_ts(self.0, &[], &mut events.list, timeout).map_err(io_err)?;
events.len = kevent_ts(self.0, &[], &mut events.list, timeout)?;
Ok(events.len)
}
}
const FFLAGS: FilterFlag = FilterFlag::empty();
const FFLAGS: FilterFlag = 0;
pub struct Events {
list: Box<[KEvent]>,
@ -634,16 +606,16 @@ mod sys {
}
impl Events {
pub fn new() -> Events {
let flags = EventFlag::empty();
let event = KEvent::new(0, EventFilter::EVFILT_USER, flags, FFLAGS, 0, 0);
let flags = 0;
let event = KEvent::new(0, libc::EVFILT_USER, flags, FFLAGS, 0, 0);
let list = vec![event; 1000].into_boxed_slice();
let len = 0;
Events { list, len }
}
pub fn iter(&self) -> impl Iterator<Item = Event> + '_ {
self.list[..self.len].iter().map(|ev| Event {
readable: ev.filter() == EventFilter::EVFILT_READ,
writable: ev.filter() == EventFilter::EVFILT_WRITE,
readable: ev.filter() == libc::EVFILT_READ,
writable: ev.filter() == libc::EVFILT_WRITE,
key: ev.udata() as usize,
})
}

414
src/sys.rs Normal file
View File

@ -0,0 +1,414 @@
#[cfg(target_os = "linux")]
pub mod eventfd {
use super::check_err;
use std::os::unix::io::RawFd;
pub type EfdFlags = libc::c_int;
pub fn eventfd(initval: libc::c_uint, flags: EfdFlags) -> Result<RawFd, std::io::Error> {
let res = unsafe { libc::eventfd(initval, flags) };
check_err(res).map(|r| r as RawFd)
}
}
#[cfg(target_os = "linux")]
pub mod unistd {
use super::check_err;
use std::os::unix::io::RawFd;
pub fn close(fd: RawFd) -> Result<(), std::io::Error> {
let res = unsafe { libc::close(fd) };
check_err(res).map(drop)
}
pub fn dup(oldfd: RawFd) -> Result<RawFd, std::io::Error> {
let res = unsafe { libc::dup(oldfd) };
check_err(res)
}
pub fn read(fd: RawFd, buf: &mut [u8]) -> Result<usize, std::io::Error> {
let res = unsafe {
libc::read(
fd,
buf.as_mut_ptr() as *mut libc::c_void,
buf.len() as libc::size_t,
)
};
check_err(res as _).map(|r| r as usize)
}
pub fn write(fd: RawFd, buf: &[u8]) -> Result<usize, std::io::Error> {
let res = unsafe {
libc::write(
fd,
buf.as_ptr() as *const libc::c_void,
buf.len() as libc::size_t,
)
};
check_err(res as _).map(|r| r as usize)
}
}
#[cfg(unix)]
pub mod fcntl {
use super::check_err;
use std::os::unix::io::RawFd;
pub type OFlag = libc::c_int;
pub type FdFlag = libc::c_int;
#[allow(non_camel_case_types)]
#[allow(dead_code)]
/// Arguments passed to `fcntl`.
pub enum FcntlArg {
F_GETFL,
F_SETFL(OFlag),
F_SETFD(FdFlag),
}
/// Thin wrapper around `libc::fcntl`.
///
/// See [`fcntl(2)`](http://man7.org/linux/man-pages/man2/fcntl.2.html) for details.
pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<libc::c_int, std::io::Error> {
let res = unsafe {
match arg {
FcntlArg::F_GETFL => libc::fcntl(fd, libc::F_GETFL),
FcntlArg::F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag),
FcntlArg::F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag),
}
};
check_err(res)
}
}
#[cfg(unix)]
fn check_err(res: libc::c_int) -> Result<libc::c_int, std::io::Error> {
if res == -1 {
return Err(std::io::Error::last_os_error());
}
Ok(res)
}
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly",
))]
/// Kqueue.
pub mod event {
use super::check_err;
use std::os::unix::io::RawFd;
#[cfg(any(
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd"
))]
#[allow(non_camel_case_types)]
type type_of_nchanges = libc::c_int;
#[cfg(target_os = "netbsd")]
#[allow(non_camel_case_types)]
type type_of_nchanges = libc::size_t;
#[cfg(target_os = "netbsd")]
#[allow(non_camel_case_types)]
type type_of_event_filter = u32;
#[cfg(not(target_os = "netbsd"))]
#[allow(non_camel_case_types)]
type type_of_event_filter = i16;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "openbsd"
))]
#[allow(non_camel_case_types)]
type type_of_udata = *mut libc::c_void;
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos"
))]
#[allow(non_camel_case_types)]
type type_of_data = libc::intptr_t;
#[cfg(any(target_os = "netbsd"))]
#[allow(non_camel_case_types)]
type type_of_udata = libc::intptr_t;
#[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
#[allow(non_camel_case_types)]
type type_of_data = libc::int64_t;
#[derive(Clone, Copy)]
#[repr(C)]
pub struct KEvent(libc::kevent);
unsafe impl Send for KEvent {}
impl KEvent {
pub fn new(
ident: libc::uintptr_t,
filter: EventFilter,
flags: EventFlag,
fflags: FilterFlag,
data: libc::intptr_t,
udata: libc::intptr_t,
) -> KEvent {
KEvent(libc::kevent {
ident,
filter: filter as type_of_event_filter,
flags,
fflags,
data: data as type_of_data,
udata: udata as type_of_udata,
})
}
pub fn filter(&self) -> EventFilter {
unsafe { std::mem::transmute(self.0.filter as type_of_event_filter) }
}
pub fn flags(&self) -> EventFlag {
self.0.flags
}
pub fn data(&self) -> libc::intptr_t {
self.0.data as libc::intptr_t
}
pub fn udata(&self) -> libc::intptr_t {
self.0.udata as libc::intptr_t
}
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "openbsd"
))]
pub type EventFlag = u16;
#[cfg(any(target_os = "netbsd"))]
pub type EventFlag = u32;
pub type FilterFlag = u32;
#[cfg(target_os = "netbsd")]
pub type EventFilter = u32;
#[cfg(not(target_os = "netbsd"))]
pub type EventFilter = i16;
pub fn kqueue() -> Result<RawFd, std::io::Error> {
let res = unsafe { libc::kqueue() };
check_err(res)
}
pub fn kevent_ts(
kq: RawFd,
changelist: &[KEvent],
eventlist: &mut [KEvent],
timeout_opt: Option<libc::timespec>,
) -> Result<usize, std::io::Error> {
let res = unsafe {
libc::kevent(
kq,
changelist.as_ptr() as *const libc::kevent,
changelist.len() as type_of_nchanges,
eventlist.as_mut_ptr() as *mut libc::kevent,
eventlist.len() as type_of_nchanges,
if let Some(ref timeout) = timeout_opt {
timeout as *const libc::timespec
} else {
std::ptr::null()
},
)
};
check_err(res).map(|r| r as usize)
}
}
#[cfg(any(target_os = "linux", target_os = "android", target_os = "illumos"))]
/// Epoll.
pub mod epoll {
use super::check_err;
use std::os::unix::io::RawFd;
#[macro_use]
mod dlsym {
// Based on https://github.com/tokio-rs/mio/blob/v0.6.x/src/sys/unix/dlsym.rs
// I feel very sad including this code, but I have not found a better way
// to check for the existence of a symbol in Rust.
use std::marker;
use std::mem;
use std::sync::atomic::{AtomicUsize, Ordering};
macro_rules! dlsym {
(fn $name:ident($($t:ty),*) -> $ret:ty) => (
#[allow(bad_style)]
static $name: $crate::sys::epoll::dlsym::DlSym<unsafe extern fn($($t),*) -> $ret> =
$crate::sys::epoll::dlsym::DlSym {
name: concat!(stringify!($name), "\0"),
addr: std::sync::atomic::AtomicUsize::new(0),
_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,
}
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[repr(i32)]
pub enum EpollOp {
EpollCtlAdd = libc::EPOLL_CTL_ADD,
EpollCtlDel = libc::EPOLL_CTL_DEL,
EpollCtlMod = libc::EPOLL_CTL_MOD,
}
pub type EpollFlags = libc::c_int;
pub fn epoll_create1() -> Result<RawFd, std::io::Error> {
// According to libuv, `EPOLL_CLOEXEC` is not defined on Android API <
// 21. But `EPOLL_CLOEXEC` is an alias for `O_CLOEXEC` on that platform,
// so we use it instead.
#[cfg(target_os = "android")]
const CLOEXEC: libc::c_int = libc::O_CLOEXEC;
#[cfg(not(target_os = "android"))]
const CLOEXEC: libc::c_int = libc::EPOLL_CLOEXEC;
let fd = unsafe {
// Emulate epoll_create1 if not available.
dlsym!(fn epoll_create1(libc::c_int) -> libc::c_int);
match epoll_create1.get() {
Some(epoll_create1_fn) => check_err(epoll_create1_fn(CLOEXEC))?,
None => {
let fd = check_err(libc::epoll_create(1024))?;
drop(set_cloexec(fd));
fd
}
}
};
Ok(fd)
}
unsafe fn set_cloexec(fd: libc::c_int) -> Result<(), std::io::Error> {
let flags = libc::fcntl(fd, libc::F_GETFD);
check_err(libc::fcntl(fd, libc::F_SETFD, flags | libc::FD_CLOEXEC)).map(|_| ())
}
pub fn epoll_ctl<'a, T>(
epfd: RawFd,
op: EpollOp,
fd: RawFd,
event: T,
) -> Result<(), std::io::Error>
where
T: Into<Option<&'a mut EpollEvent>>,
{
let mut event: Option<&mut EpollEvent> = event.into();
if event.is_none() && op != EpollOp::EpollCtlDel {
Err(std::io::Error::from_raw_os_error(libc::EINVAL))
} else {
let res = unsafe {
if let Some(ref mut event) = event {
libc::epoll_ctl(epfd, op as libc::c_int, fd, &mut event.event)
} else {
libc::epoll_ctl(epfd, op as libc::c_int, fd, std::ptr::null_mut())
}
};
check_err(res).map(drop)
}
}
pub fn epoll_wait(
epfd: RawFd,
events: &mut [EpollEvent],
timeout_ms: isize,
) -> Result<usize, std::io::Error> {
let res = unsafe {
libc::epoll_wait(
epfd,
events.as_mut_ptr() as *mut libc::epoll_event,
events.len() as libc::c_int,
timeout_ms as libc::c_int,
)
};
check_err(res).map(|r| r as usize)
}
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct EpollEvent {
event: libc::epoll_event,
}
impl EpollEvent {
pub fn new(events: EpollFlags, data: u64) -> Self {
EpollEvent {
event: libc::epoll_event {
events: events as u32,
u64: data,
},
}
}
pub fn empty() -> Self {
unsafe { std::mem::zeroed::<EpollEvent>() }
}
pub fn events(&self) -> EpollFlags {
self.event.events as libc::c_int
}
pub fn data(&self) -> u64 {
self.event.u64
}
}
}