mirror of https://github.com/smol-rs/polling
feat: Add support for HermitOS
HermitOS is a microkernel target aiming to provide a simple OS for virtualized applications. It recently added support for the poll() and eventfd() system calls, which means we can target it with our poll() based backend. Hermit does not have a traditional libc; instead it uses hermit-abi. However rustix does not support using hermit-abi as its underlying layer yet. So we have to build a shim layer until it does. Closes #177 cc bytecodealliance/rustix#1012 Signed-off-by: John Nunley <dev@notgull.net>
This commit is contained in:
parent
634a77c264
commit
50454d1cea
|
@ -62,16 +62,10 @@ jobs:
|
|||
RUSTFLAGS: ${{ env.RUSTFLAGS }} --cfg polling_test_epoll_pipe
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
- run: cargo hack build --feature-powerset --no-dev-deps
|
||||
- name: Add rust-src
|
||||
if: startsWith(matrix.rust, 'nightly')
|
||||
run: rustup component add rust-src
|
||||
# TODO: broken due to https://github.com/rust-lang/rust/pull/119026.
|
||||
# - name: Check selected Tier 3 targets
|
||||
# if: startsWith(matrix.rust, 'nightly') && matrix.os == 'ubuntu-latest'
|
||||
# run: cargo check -Z build-std --target=riscv32imc-esp-espidf
|
||||
- name: Check haiku
|
||||
if: startsWith(matrix.rust, 'nightly') && matrix.os == 'ubuntu-latest'
|
||||
run: cargo check -Z build-std --target x86_64-unknown-haiku
|
||||
- name: Clone async-io
|
||||
run: git clone https://github.com/smol-rs/async-io.git
|
||||
# The async-io Cargo.toml already has a patch section at the bottom, so we
|
||||
|
@ -93,6 +87,9 @@ jobs:
|
|||
run: rustup update stable
|
||||
- name: Install cross
|
||||
uses: taiki-e/install-action@cross
|
||||
- name: Add rust-src
|
||||
if: startsWith(matrix.rust, 'nightly')
|
||||
run: rustup component add rust-src
|
||||
# We don't test BSDs, since we already test them in Cirrus.
|
||||
- name: Android
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
|
@ -118,9 +115,17 @@ jobs:
|
|||
rustup target add x86_64-unknown-illumos
|
||||
cargo build --target x86_64-unknown-illumos
|
||||
- name: Redox
|
||||
if: startsWith(matrix.rust, 'nightly') && matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
rustup target add x86_64-unknown-redox
|
||||
cargo check --target x86_64-unknown-redox
|
||||
- name: HermitOS
|
||||
if: startsWith(matrix.rust, 'nightly') && matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
cargo -Zbuild-std check --target x86_64-unknown-hermit
|
||||
- name: Check haiku
|
||||
if: startsWith(matrix.rust, 'nightly') && matrix.os == 'ubuntu-latest'
|
||||
run: cargo check -Z build-std --target x86_64-unknown-haiku
|
||||
|
||||
wine:
|
||||
runs-on: ubuntu-22.04
|
||||
|
|
|
@ -21,8 +21,10 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||
cfg-if = "1"
|
||||
tracing = { version = "0.1.37", default-features = false }
|
||||
|
||||
[target.'cfg(any(unix, target_os = "fuchsia", target_os = "vxworks"))'.dependencies]
|
||||
rustix = { version = "0.38.31", features = ["event", "fs", "pipe", "process", "std", "time"], default-features = false }
|
||||
[target.'cfg(any(unix, target_os = "fuchsia", target_os = "vxworks"))'.dependencies.rustix]
|
||||
version = "0.38.31"
|
||||
features = ["event", "fs", "pipe", "process", "std", "time"]
|
||||
default-features = false
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
concurrent-queue = "2.2.0"
|
||||
|
@ -43,6 +45,9 @@ features = [
|
|||
"Win32_System_WindowsProgramming",
|
||||
]
|
||||
|
||||
[target.'cfg(target_os = "hermit")'.dependencies.hermit-abi]
|
||||
version = "0.3.9"
|
||||
|
||||
[dev-dependencies]
|
||||
easy-parallel = "3.1.0"
|
||||
fastrand = "2.0.0"
|
||||
|
|
|
@ -12,11 +12,11 @@ https://docs.rs/polling)
|
|||
Portable interface to epoll, kqueue, event ports, and IOCP.
|
||||
|
||||
Supported platforms:
|
||||
- [epoll](https://en.wikipedia.org/wiki/Epoll): Linux, Android
|
||||
- [epoll](https://en.wikipedia.org/wiki/Epoll): Linux, Android, RedoxOS
|
||||
- [kqueue](https://en.wikipedia.org/wiki/Kqueue): macOS, iOS, tvOS, watchOS, FreeBSD, NetBSD, OpenBSD,
|
||||
DragonFly BSD
|
||||
- [event ports](https://illumos.org/man/port_create): illumos, Solaris
|
||||
- [poll](https://en.wikipedia.org/wiki/Poll_(Unix)): VxWorks, Fuchsia, other Unix systems
|
||||
- [poll](https://en.wikipedia.org/wiki/Poll_(Unix)): VxWorks, Fuchsia, HermitOS, other Unix systems
|
||||
- [IOCP](https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports): Windows, Wine (version 7.13+)
|
||||
|
||||
Polling is done in oneshot mode, which means interest in I/O events needs to be reset after
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//! - [kqueue](https://en.wikipedia.org/wiki/Kqueue): macOS, iOS, tvOS, watchOS, FreeBSD, NetBSD, OpenBSD,
|
||||
//! DragonFly BSD
|
||||
//! - [event ports](https://illumos.org/man/port_create): illumos, Solaris
|
||||
//! - [poll](https://en.wikipedia.org/wiki/Poll_(Unix)): VxWorks, Fuchsia, other Unix systems
|
||||
//! - [poll](https://en.wikipedia.org/wiki/Poll_(Unix)): VxWorks, Fuchsia, HermitOS, other Unix systems
|
||||
//! - [IOCP](https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports): Windows, Wine (version 7.13+)
|
||||
//!
|
||||
//! By default, polling is done in oneshot mode, which means interest in I/O events needs to
|
||||
|
@ -107,6 +107,7 @@ cfg_if! {
|
|||
use kqueue as sys;
|
||||
} else if #[cfg(any(
|
||||
target_os = "vxworks",
|
||||
target_os = "hermit",
|
||||
target_os = "fuchsia",
|
||||
target_os = "horizon",
|
||||
unix,
|
||||
|
@ -1017,8 +1018,11 @@ impl fmt::Debug for Poller {
|
|||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(unix)] {
|
||||
if #[cfg(any(unix, target_os = "hermit"))] {
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::io::{AsRawFd, RawFd, AsFd, BorrowedFd};
|
||||
#[cfg(target_os = "hermit")]
|
||||
use std::os::hermit::io::{AsRawFd, RawFd, AsFd, BorrowedFd};
|
||||
|
||||
/// A resource with a raw file descriptor.
|
||||
pub trait AsRawSource {
|
||||
|
|
203
src/poll.rs
203
src/poll.rs
|
@ -6,8 +6,12 @@ use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
|||
use std::sync::{Condvar, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use rustix::event::{poll, PollFd, PollFlags};
|
||||
#[cfg(not(target_os = "hermit"))]
|
||||
use rustix::fd::{AsFd, AsRawFd, BorrowedFd};
|
||||
#[cfg(target_os = "hermit")]
|
||||
use std::os::hermit::io::{AsFd, AsRawFd, BorrowedFd};
|
||||
|
||||
use syscall::{poll, PollFd, PollFlags};
|
||||
|
||||
// std::os::unix doesn't exist on Fuchsia
|
||||
type RawFd = std::os::raw::c_int;
|
||||
|
@ -443,7 +447,182 @@ fn cvt_mode_as_remove(mode: PollMode) -> io::Result<bool> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "espidf"))]
|
||||
#[cfg(unix)]
|
||||
mod syscall {
|
||||
pub(super) use rustix::event::{poll, PollFd, PollFlags};
|
||||
|
||||
#[cfg(target_os = "espidf")]
|
||||
pub(super) use rustix::event::{eventfd, EventfdFlags};
|
||||
#[cfg(target_os = "espidf")]
|
||||
pub(super) use rustix::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
|
||||
#[cfg(target_os = "espidf")]
|
||||
pub(super) use rustix::io::{read, write};
|
||||
}
|
||||
|
||||
#[cfg(target_os = "hermit")]
|
||||
mod syscall {
|
||||
// TODO: Remove this shim once HermitOS is supported in Rustix.
|
||||
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::BitOr;
|
||||
|
||||
pub(super) use std::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
|
||||
|
||||
/// Create an eventfd.
|
||||
pub(super) fn eventfd(count: u64, _flags: EventfdFlags) -> io::Result<OwnedFd> {
|
||||
let fd = unsafe { hermit_abi::eventfd(count, 0) };
|
||||
|
||||
if fd < 0 {
|
||||
Err(io::Error::from_raw_os_error(unsafe {
|
||||
hermit_abi::get_errno()
|
||||
}))
|
||||
} else {
|
||||
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Read some bytes.
|
||||
pub(super) fn read(fd: BorrowedFd<'_>, bytes: &mut [u8]) -> io::Result<usize> {
|
||||
let count = unsafe { hermit_abi::read(fd.as_raw_fd(), bytes.as_mut_ptr(), bytes.len()) };
|
||||
|
||||
cvt(count)
|
||||
}
|
||||
|
||||
/// Write some bytes.
|
||||
pub(super) fn write(fd: BorrowedFd<'_>, bytes: &[u8]) -> io::Result<usize> {
|
||||
let count = unsafe { hermit_abi::write(fd.as_raw_fd(), bytes.as_ptr(), bytes.len()) };
|
||||
|
||||
cvt(count)
|
||||
}
|
||||
|
||||
/// Safe wrapper around the `poll` system call.
|
||||
pub(super) fn poll(fds: &mut [PollFd<'_>], timeout: i32) -> io::Result<usize> {
|
||||
let call = unsafe {
|
||||
hermit_abi::poll(
|
||||
fds.as_mut_ptr() as *mut hermit_abi::pollfd,
|
||||
fds.len(),
|
||||
timeout,
|
||||
)
|
||||
};
|
||||
|
||||
cvt(call as isize)
|
||||
}
|
||||
|
||||
/// Safe wrapper around `pollfd`.
|
||||
#[repr(transparent)]
|
||||
pub(super) struct PollFd<'a> {
|
||||
inner: hermit_abi::pollfd,
|
||||
_lt: PhantomData<BorrowedFd<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> PollFd<'a> {
|
||||
pub(super) fn from_borrowed_fd(fd: BorrowedFd<'a>, inflags: PollFlags) -> Self {
|
||||
Self {
|
||||
inner: hermit_abi::pollfd {
|
||||
fd: fd.as_raw_fd(),
|
||||
events: inflags.0,
|
||||
revents: 0,
|
||||
},
|
||||
_lt: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn revents(&self) -> PollFlags {
|
||||
PollFlags(self.inner.revents)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsFd for PollFd<'_> {
|
||||
fn as_fd(&self) -> BorrowedFd<'_> {
|
||||
unsafe { BorrowedFd::borrow_raw(self.inner.fd) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PollFd<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("PollFd")
|
||||
.field("fd", &format_args!("0x{:x}", self.inner.fd))
|
||||
.field("events", &PollFlags(self.inner.events))
|
||||
.field("revents", &PollFlags(self.inner.revents))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper around polling flags.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub(super) struct PollFlags(i16);
|
||||
|
||||
impl PollFlags {
|
||||
/// Empty set of flags.
|
||||
pub(super) const fn empty() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
|
||||
pub(super) const IN: PollFlags = PollFlags(hermit_abi::POLLIN);
|
||||
pub(super) const OUT: PollFlags = PollFlags(hermit_abi::POLLOUT);
|
||||
pub(super) const WRBAND: PollFlags = PollFlags(hermit_abi::POLLWRBAND);
|
||||
pub(super) const ERR: PollFlags = PollFlags(hermit_abi::POLLERR);
|
||||
pub(super) const HUP: PollFlags = PollFlags(hermit_abi::POLLHUP);
|
||||
pub(super) const PRI: PollFlags = PollFlags(hermit_abi::POLLPRI);
|
||||
|
||||
/// Tell if this contains some flags.
|
||||
pub(super) fn contains(self, flags: PollFlags) -> bool {
|
||||
self.0 & flags.0 != 0
|
||||
}
|
||||
|
||||
/// Set a flag.
|
||||
pub(super) fn set(&mut self, flags: PollFlags, set: bool) {
|
||||
if set {
|
||||
self.0 |= flags.0;
|
||||
} else {
|
||||
self.0 &= !(flags.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Tell if this is empty.
|
||||
pub(super) fn is_empty(self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
/// Tell if this intersects with some flags.
|
||||
pub(super) fn intersects(self, flags: PollFlags) -> bool {
|
||||
self.contains(flags)
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr for PollFlags {
|
||||
type Output = PollFlags;
|
||||
|
||||
fn bitor(self, rhs: Self) -> Self::Output {
|
||||
Self(self.0 | rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(super) struct EventfdFlags;
|
||||
|
||||
impl EventfdFlags {
|
||||
pub(super) fn empty() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a number to an actual result.
|
||||
#[inline]
|
||||
fn cvt(len: isize) -> io::Result<usize> {
|
||||
if len < 0 {
|
||||
Err(io::Error::from_raw_os_error(unsafe {
|
||||
hermit_abi::get_errno()
|
||||
}))
|
||||
} else {
|
||||
Ok(len as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "espidf", target_os = "hermit")))]
|
||||
mod notify {
|
||||
use std::io;
|
||||
|
||||
|
@ -534,16 +713,14 @@ mod notify {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "espidf")]
|
||||
#[cfg(any(target_os = "espidf", target_os = "hermit"))]
|
||||
mod notify {
|
||||
use std::io;
|
||||
use std::mem;
|
||||
|
||||
use rustix::event::PollFlags;
|
||||
use rustix::event::{eventfd, EventfdFlags};
|
||||
|
||||
use rustix::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
|
||||
use rustix::io::{read, write};
|
||||
use super::syscall::{
|
||||
eventfd, read, write, AsFd, AsRawFd, BorrowedFd, EventfdFlags, OwnedFd, PollFlags, RawFd,
|
||||
};
|
||||
|
||||
/// A notification pipe.
|
||||
///
|
||||
|
@ -573,8 +750,8 @@ mod notify {
|
|||
|
||||
let flags = EventfdFlags::empty();
|
||||
let event_fd = eventfd(0, flags).map_err(|err| {
|
||||
match err {
|
||||
rustix::io::Errno::PERM => {
|
||||
match io::Error::from(err) {
|
||||
err if err.kind() == io::ErrorKind::PermissionDenied => {
|
||||
// EPERM can happen if the eventfd isn't initialized yet.
|
||||
// Tell the user to call esp_vfs_eventfd_register.
|
||||
io::Error::new(
|
||||
|
@ -582,7 +759,7 @@ mod notify {
|
|||
"failed to initialize eventfd for polling, try calling `esp_vfs_eventfd_register`"
|
||||
)
|
||||
},
|
||||
err => io::Error::from(err),
|
||||
err => err,
|
||||
}
|
||||
})?;
|
||||
|
||||
|
@ -601,14 +778,14 @@ mod notify {
|
|||
|
||||
/// Notifies the `Poller` instance via the eventfd file descriptor.
|
||||
pub(super) fn notify(&self) -> Result<(), io::Error> {
|
||||
write(&self.event_fd, &1u64.to_ne_bytes())?;
|
||||
write(self.event_fd.as_fd(), &1u64.to_ne_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Pops a notification (if any) from the eventfd file descriptor.
|
||||
pub(super) fn pop_notification(&self) -> Result<(), io::Error> {
|
||||
read(&self.event_fd, &mut [0; mem::size_of::<u64>()])?;
|
||||
read(self.event_fd.as_fd(), &mut [0; mem::size_of::<u64>()])?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue