#![allow(non_camel_case_types)]
#![allow(unused_unsafe)]
use crate::hostcalls_impl::{ClockEventData, FdEventData};
use crate::sys::host_impl;
use crate::{wasi, Error, Result};
use yanix::clock::{clock_getres, clock_gettime, ClockId};
fn wasi_clock_id_to_unix(clock_id: wasi::__wasi_clockid_t) -> Result<ClockId> {
match clock_id {
wasi::__WASI_CLOCKID_REALTIME => Ok(ClockId::Realtime),
wasi::__WASI_CLOCKID_MONOTONIC => Ok(ClockId::Monotonic),
wasi::__WASI_CLOCKID_PROCESS_CPUTIME_ID => Ok(ClockId::ProcessCPUTime),
wasi::__WASI_CLOCKID_THREAD_CPUTIME_ID => Ok(ClockId::ThreadCPUTime),
_ => Err(Error::EINVAL),
}
}
pub(crate) fn clock_res_get(clock_id: wasi::__wasi_clockid_t) -> Result<wasi::__wasi_timestamp_t> {
let clock_id = wasi_clock_id_to_unix(clock_id)?;
let timespec = clock_getres(clock_id)?;
(timespec.tv_sec as wasi::__wasi_timestamp_t)
.checked_mul(1_000_000_000)
.and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as wasi::__wasi_timestamp_t))
.map_or(Err(Error::EOVERFLOW), |resolution| {
if resolution == 0 {
Err(Error::EINVAL)
} else {
Ok(resolution)
}
})
}
pub(crate) fn clock_time_get(clock_id: wasi::__wasi_clockid_t) -> Result<wasi::__wasi_timestamp_t> {
let clock_id = wasi_clock_id_to_unix(clock_id)?;
let timespec = clock_gettime(clock_id)?;
(timespec.tv_sec as wasi::__wasi_timestamp_t)
.checked_mul(1_000_000_000)
.and_then(|sec_ns| sec_ns.checked_add(timespec.tv_nsec as wasi::__wasi_timestamp_t))
.map_or(Err(Error::EOVERFLOW), Ok)
}
pub(crate) fn poll_oneoff(
timeout: Option<ClockEventData>,
fd_events: Vec<FdEventData>,
events: &mut Vec<wasi::__wasi_event_t>,
) -> Result<()> {
use std::{convert::TryInto, os::unix::prelude::AsRawFd};
use yanix::{
poll::{poll, PollFd, PollFlags},
Errno,
};
if fd_events.is_empty() && timeout.is_none() {
return Ok(());
}
let mut poll_fds: Vec<_> = fd_events
.iter()
.map(|event| {
let mut flags = PollFlags::empty();
match event.r#type {
wasi::__WASI_EVENTTYPE_FD_READ => flags.insert(PollFlags::POLLIN),
wasi::__WASI_EVENTTYPE_FD_WRITE => flags.insert(PollFlags::POLLOUT),
_ => unreachable!(),
};
unsafe { PollFd::new(event.descriptor.as_raw_fd(), flags) }
})
.collect();
let poll_timeout = timeout.map_or(-1, |timeout| {
let delay = timeout.delay / 1_000_000; delay.try_into().unwrap_or(libc::c_int::max_value())
});
log::debug!("poll_oneoff poll_timeout = {:?}", poll_timeout);
let ready = loop {
match poll(&mut poll_fds, poll_timeout) {
Err(_) => {
if Errno::last() == Errno::EINTR {
continue;
}
return Err(host_impl::errno_from_nix(Errno::last()));
}
Ok(ready) => break ready,
}
};
Ok(if ready == 0 {
poll_oneoff_handle_timeout_event(timeout.expect("timeout should not be None"), events)
} else {
let ready_events = fd_events.into_iter().zip(poll_fds.into_iter()).take(ready);
poll_oneoff_handle_fd_event(ready_events, events)?
})
}
fn poll_oneoff_handle_timeout_event(
timeout: ClockEventData,
events: &mut Vec<wasi::__wasi_event_t>,
) {
events.push(wasi::__wasi_event_t {
userdata: timeout.userdata,
r#type: wasi::__WASI_EVENTTYPE_CLOCK,
error: wasi::__WASI_ERRNO_SUCCESS,
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: 0,
flags: 0,
},
},
});
}
fn poll_oneoff_handle_fd_event<'a>(
ready_events: impl Iterator<Item = (FdEventData<'a>, yanix::poll::PollFd)>,
events: &mut Vec<wasi::__wasi_event_t>,
) -> Result<()> {
use std::{convert::TryInto, os::unix::prelude::AsRawFd};
use yanix::{file::fionread, poll::PollFlags};
for (fd_event, poll_fd) in ready_events {
log::debug!("poll_oneoff_handle_fd_event fd_event = {:?}", fd_event);
log::debug!("poll_oneoff_handle_fd_event poll_fd = {:?}", poll_fd);
let revents = match poll_fd.revents() {
Some(revents) => revents,
None => continue,
};
log::debug!("poll_oneoff_handle_fd_event revents = {:?}", revents);
let nbytes = if fd_event.r#type == wasi::__WASI_EVENTTYPE_FD_READ {
unsafe { fionread(fd_event.descriptor.as_raw_fd())? }
} else {
0
};
let output_event = if revents.contains(PollFlags::POLLNVAL) {
wasi::__wasi_event_t {
userdata: fd_event.userdata,
r#type: fd_event.r#type,
error: wasi::__WASI_ERRNO_BADF,
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: 0,
flags: wasi::__WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP,
},
},
}
} else if revents.contains(PollFlags::POLLERR) {
wasi::__wasi_event_t {
userdata: fd_event.userdata,
r#type: fd_event.r#type,
error: wasi::__WASI_ERRNO_IO,
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: 0,
flags: wasi::__WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP,
},
},
}
} else if revents.contains(PollFlags::POLLHUP) {
wasi::__wasi_event_t {
userdata: fd_event.userdata,
r#type: fd_event.r#type,
error: wasi::__WASI_ERRNO_SUCCESS,
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: 0,
flags: wasi::__WASI_EVENTRWFLAGS_FD_READWRITE_HANGUP,
},
},
}
} else if revents.contains(PollFlags::POLLIN) | revents.contains(PollFlags::POLLOUT) {
wasi::__wasi_event_t {
userdata: fd_event.userdata,
r#type: fd_event.r#type,
error: wasi::__WASI_ERRNO_SUCCESS,
u: wasi::__wasi_event_u_t {
fd_readwrite: wasi::__wasi_event_fd_readwrite_t {
nbytes: nbytes.try_into()?,
flags: 0,
},
},
}
} else {
continue;
};
events.push(output_event);
}
Ok(())
}