use crate::CoreError;
use crate::error::syscall_ret;
use std::io::Error as IoError;
use std::time::Duration;
#[inline(always)]
fn errno() -> i32 {
IoError::last_os_error().raw_os_error().unwrap_or(0)
}
pub struct Fd(RawFd);
use std::os::unix::io::{AsRawFd, RawFd};
impl AsRawFd for Fd {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
impl Fd {
#[inline(always)]
pub(crate) fn new(fd: RawFd, op: &'static str) -> Result<Self, CoreError> {
if fd < 0 {
Err(CoreError::sys(errno(), op))
} else {
Ok(Self(fd))
}
}
#[inline(always)]
pub unsafe fn from_owned_raw_fd(fd: RawFd, op: &'static str) -> Result<Self, CoreError> {
Self::new(fd, op)
}
pub fn eventfd(init: u32) -> Result<Self, CoreError> {
let fd = unsafe { libc::eventfd(init, libc::EFD_CLOEXEC | libc::EFD_NONBLOCK) };
syscall_ret(fd, "eventfd")?;
Self::new(fd, "eventfd")
}
pub fn timerfd() -> Result<Self, CoreError> {
let fd = unsafe {
libc::timerfd_create(
libc::CLOCK_MONOTONIC,
libc::TFD_CLOEXEC | libc::TFD_NONBLOCK,
)
};
syscall_ret(fd, "timerfd_create")?;
Self::new(fd, "timerfd_create")
}
#[inline(always)]
pub(crate) fn raw(&self) -> RawFd {
self.0
}
pub fn dup(&self) -> Result<Self, CoreError> {
let r = loop {
let d = unsafe { libc::dup(self.0) };
if d < 0 && errno() == libc::EINTR {
continue;
}
break d;
};
if r < 0 {
let e = errno();
Err(CoreError::sys(e, "dup"))
} else {
unsafe { Self::from_owned_raw_fd(r, "dup") }
}
}
pub fn dup2(&self, target: RawFd) -> Result<(), CoreError> {
loop {
let r = unsafe { libc::dup2(self.0, target) };
if r < 0 {
let e = errno();
if e == libc::EINTR {
continue;
}
return syscall_ret(r, "dup2");
}
return Ok(());
}
}
pub fn set_nonblock(&self) -> Result<(), CoreError> {
let flags = unsafe { libc::fcntl(self.0, libc::F_GETFL) };
syscall_ret(flags, "fcntl(F_GETFL)")?;
let r = unsafe { libc::fcntl(self.0, libc::F_SETFL, flags | libc::O_NONBLOCK) };
syscall_ret(r, "fcntl(F_SETFL)")
}
pub fn set_cloexec(&self) -> Result<(), CoreError> {
let flags = unsafe { libc::fcntl(self.0, libc::F_GETFD) };
syscall_ret(flags, "fcntl(F_GETFD)")?;
let r = unsafe { libc::fcntl(self.0, libc::F_SETFD, flags | libc::FD_CLOEXEC) };
syscall_ret(r, "fcntl(F_SETFD)")
}
pub fn read_slice(&self, buf: &mut [u8]) -> Result<Option<usize>, CoreError> {
self.read_raw(buf.as_mut_ptr(), buf.len())
}
pub fn seek_set(&self, offset: i64) -> Result<u64, CoreError> {
loop {
let pos = unsafe { libc::lseek(self.0, offset as libc::off_t, libc::SEEK_SET) };
if pos < 0 {
let e = errno();
if e == libc::EINTR {
continue;
}
return Err(CoreError::sys(e, "lseek"));
}
return Ok(pos as u64);
}
}
pub fn write_slice(&self, buf: &[u8]) -> Result<Option<usize>, CoreError> {
self.write_raw(buf.as_ptr(), buf.len())
}
pub fn read_u64_blocking(&self) -> Result<u64, CoreError> {
let mut bytes = [0u8; std::mem::size_of::<u64>()];
loop {
let n =
unsafe { libc::read(self.0, bytes.as_mut_ptr() as *mut libc::c_void, bytes.len()) };
if n == bytes.len() as isize {
return Ok(u64::from_ne_bytes(bytes));
}
if n < 0 {
let e = errno();
if e == libc::EINTR {
continue;
}
return Err(CoreError::sys(e, "read_u64_blocking"));
}
return Err(CoreError::sys(libc::EIO, "read_u64_blocking:short_read"));
}
}
pub fn read_u64(&self) -> Result<Option<u64>, CoreError> {
let mut bytes = [0u8; std::mem::size_of::<u64>()];
match self.read_slice(&mut bytes)? {
Some(n) if n == bytes.len() => Ok(Some(u64::from_ne_bytes(bytes))),
Some(_) => Err(CoreError::sys(libc::EIO, "read_u64")),
None => Ok(None),
}
}
pub fn write_u64(&self, value: u64) -> Result<Option<usize>, CoreError> {
self.write_slice(&value.to_ne_bytes())
}
pub fn set_timer_oneshot(&self, delay: Option<Duration>) -> Result<(), CoreError> {
let mut spec: libc::itimerspec = unsafe { std::mem::zeroed() };
if let Some(delay) = delay {
let delay = delay.max(Duration::from_nanos(1));
spec.it_value.tv_sec = delay.as_secs() as libc::time_t;
spec.it_value.tv_nsec = delay.subsec_nanos() as libc::c_long;
}
let ret = unsafe { libc::timerfd_settime(self.raw(), 0, &spec, std::ptr::null_mut()) };
syscall_ret(ret, "timerfd_settime")
}
pub(crate) fn read_raw(&self, buf: *mut u8, count: usize) -> Result<Option<usize>, CoreError> {
loop {
let n = unsafe { libc::read(self.0, buf as *mut libc::c_void, count) };
if n < 0 {
let e = errno();
if e == libc::EINTR {
continue;
}
if e == libc::EAGAIN || e == libc::EWOULDBLOCK {
return Ok(None);
}
return Err(CoreError::sys(e, "read"));
}
return Ok(Some(n as usize));
}
}
pub(crate) fn write_raw(
&self,
buf: *const u8,
count: usize,
) -> Result<Option<usize>, CoreError> {
loop {
let n = unsafe { libc::write(self.0, buf as *const libc::c_void, count) };
if n < 0 {
let e = errno();
if e == libc::EINTR {
continue;
}
if e == libc::EAGAIN || e == libc::EWOULDBLOCK {
return Ok(None);
}
return Err(CoreError::sys(e, "write"));
}
return Ok(Some(n as usize));
}
}
}
impl Drop for Fd {
fn drop(&mut self) {
if self.0 >= 0 {
unsafe {
libc::close(self.0);
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Token(pub(crate) u64);
#[allow(dead_code)]
impl Token {
#[inline(always)]
pub(crate) fn new(val: u64) -> Self {
Self(val)
}
#[inline(always)]
pub(crate) fn val(&self) -> u64 {
self.0
}
}
#[derive(Clone, Copy, Debug)]
pub struct Event {
pub token: Token,
pub readable: bool,
pub priority: bool,
pub writable: bool,
pub error: bool,
pub hangup: bool,
}
const _: () = assert!(std::mem::size_of::<Event>() == 16);
const _: () = assert!(std::mem::align_of::<Event>() == 8);