mirror of https://github.com/stjepang/smol
415 lines
12 KiB
Rust
415 lines
12 KiB
Rust
#[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
|
|
}
|
|
}
|
|
}
|