add HermitOS support of vectored read/write operations

In general, the I/O interface of hermit-abi is more POSIX-like
interface. Consequently, platform abstraction layer for HermitOS
has slightly adjusted and some inaccuracies remove.
This commit is contained in:
Stefan Lankes 2024-06-07 20:20:51 +02:00
parent e3c3ce62d7
commit 1f125a6716
12 changed files with 189 additions and 94 deletions

View File

@ -1694,6 +1694,12 @@ name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
[[package]]
name = "hermit-abi"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
dependencies = [
"compiler_builtins",
"rustc-std-workspace-alloc",
@ -2636,7 +2642,7 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"hermit-abi 0.3.9",
"libc",
]
@ -5363,7 +5369,7 @@ dependencies = [
"dlmalloc",
"fortanix-sgx-abi",
"hashbrown",
"hermit-abi",
"hermit-abi 0.4.0",
"libc",
"miniz_oxide",
"object 0.36.0",

View File

@ -50,7 +50,7 @@ dlmalloc = { version = "0.2.4", features = ['rustc-dep-of-std'] }
fortanix-sgx-abi = { version = "0.5.0", features = ['rustc-dep-of-std'], public = true }
[target.'cfg(target_os = "hermit")'.dependencies]
hermit-abi = { version = "0.3.9", features = ['rustc-dep-of-std'], public = true }
hermit-abi = { version = "0.4.0", features = ['rustc-dep-of-std'], public = true }
[target.'cfg(target_os = "wasi")'.dependencies]
wasi = { version = "0.11.0", features = ['rustc-dep-of-std'], default-features = false }

View File

@ -1,7 +1,8 @@
#![unstable(reason = "not public", issue = "none", feature = "fd")]
use super::hermit_abi;
use crate::io::{self, Read};
use crate::cmp;
use crate::io::{self, IoSlice, IoSliceMut, Read};
use crate::os::hermit::io::{FromRawFd, OwnedFd, RawFd};
use crate::sys::cvt;
use crate::sys::unsupported;
@ -9,6 +10,10 @@
use crate::os::hermit::io::*;
const fn max_iov() -> usize {
hermit_abi::IOV_MAX
}
#[derive(Debug)]
pub struct FileDesc {
fd: OwnedFd,
@ -21,6 +26,22 @@ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
Ok(result as usize)
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
let ret = cvt(unsafe {
hermit_abi::readv(
self.as_raw_fd(),
bufs.as_mut_ptr() as *mut hermit_abi::iovec as *const hermit_abi::iovec,
cmp::min(bufs.len(), max_iov()),
)
})?;
Ok(ret as usize)
}
#[inline]
pub fn is_read_vectored(&self) -> bool {
true
}
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
let mut me = self;
(&mut me).read_to_end(buf)
@ -32,6 +53,22 @@ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
Ok(result as usize)
}
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
let ret = cvt(unsafe {
hermit_abi::writev(
self.as_raw_fd(),
bufs.as_ptr() as *const hermit_abi::iovec,
cmp::min(bufs.len(), max_iov()),
)
})?;
Ok(ret as usize)
}
#[inline]
pub fn is_write_vectored(&self) -> bool {
true
}
pub fn duplicate(&self) -> io::Result<FileDesc> {
self.duplicate_path(&[])
}

View File

@ -1,7 +1,7 @@
use super::fd::FileDesc;
use super::hermit_abi::{
self, dirent64, stat as stat_struct, DT_DIR, DT_LNK, DT_REG, DT_UNKNOWN, O_APPEND, O_CREAT,
O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG,
O_DIRECTORY, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG,
};
use crate::ffi::{CStr, OsStr, OsString};
use crate::fmt;
@ -62,7 +62,7 @@ pub struct DirEntry {
/// 64-bit inode number
ino: u64,
/// File type
type_: u32,
type_: u8,
/// name of the entry
name: OsString,
}
@ -90,7 +90,7 @@ pub struct FilePermissions {
#[derive(Copy, Clone, Eq, Debug)]
pub struct FileType {
mode: u32,
mode: u8,
}
impl PartialEq for FileType {
@ -112,31 +112,23 @@ pub struct DirBuilder {
impl FileAttr {
pub fn modified(&self) -> io::Result<SystemTime> {
Ok(SystemTime::new(
self.stat_val.st_mtime.try_into().unwrap(),
self.stat_val.st_mtime_nsec.try_into().unwrap(),
))
Ok(SystemTime::new(self.stat_val.st_mtim.tv_sec, self.stat_val.st_mtim.tv_nsec))
}
pub fn accessed(&self) -> io::Result<SystemTime> {
Ok(SystemTime::new(
self.stat_val.st_atime.try_into().unwrap(),
self.stat_val.st_atime_nsec.try_into().unwrap(),
))
Ok(SystemTime::new(self.stat_val.st_atim.tv_sec, self.stat_val.st_atim.tv_nsec))
}
pub fn created(&self) -> io::Result<SystemTime> {
Ok(SystemTime::new(
self.stat_val.st_ctime.try_into().unwrap(),
self.stat_val.st_ctime_nsec.try_into().unwrap(),
))
Ok(SystemTime::new(self.stat_val.st_ctim.tv_sec, self.stat_val.st_ctim.tv_nsec))
}
pub fn size(&self) -> u64 {
self.stat_val.st_size as u64
}
pub fn perm(&self) -> FilePermissions {
FilePermissions { mode: (self.stat_val.st_mode) }
FilePermissions { mode: self.stat_val.st_mode }
}
pub fn file_type(&self) -> FileType {
@ -220,7 +212,7 @@ fn next(&mut self) -> Option<io::Result<DirEntry>> {
let entry = DirEntry {
root: self.inner.root.clone(),
ino: dir.d_ino,
type_: dir.d_type as u32,
type_: dir.d_type,
name: OsString::from_vec(name_bytes.to_vec()),
};
@ -251,7 +243,7 @@ pub fn metadata(&self) -> io::Result<FileAttr> {
}
pub fn file_type(&self) -> io::Result<FileType> {
Ok(FileType { mode: self.type_ as u32 })
Ok(FileType { mode: self.type_ })
}
#[allow(dead_code)]
@ -385,12 +377,12 @@ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
crate::io::default_read_vectored(|buf| self.read(buf), bufs)
self.0.read_vectored(bufs)
}
#[inline]
pub fn is_read_vectored(&self) -> bool {
false
self.0.is_read_vectored()
}
pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
@ -402,12 +394,12 @@ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
}
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
crate::io::default_write_vectored(|buf| self.write(buf), bufs)
self.0.write_vectored(bufs)
}
#[inline]
pub fn is_write_vectored(&self) -> bool {
false
self.0.is_write_vectored()
}
#[inline]
@ -439,13 +431,13 @@ pub fn new() -> DirBuilder {
pub fn mkdir(&self, path: &Path) -> io::Result<()> {
run_path_with_cstr(path, &|path| {
cvt(unsafe { hermit_abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ())
cvt(unsafe { hermit_abi::mkdir(path.as_ptr(), self.mode.into()) }).map(|_| ())
})
}
#[allow(dead_code)]
pub fn set_mode(&mut self, mode: u32) {
self.mode = mode as u32;
self.mode = mode;
}
}
@ -501,8 +493,9 @@ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
}
pub fn readdir(path: &Path) -> io::Result<ReadDir> {
let fd_raw =
run_path_with_cstr(path, &|path| cvt(unsafe { hermit_abi::opendir(path.as_ptr()) }))?;
let fd_raw = run_path_with_cstr(path, &|path| {
cvt(unsafe { hermit_abi::open(path.as_ptr(), O_RDONLY | O_DIRECTORY, 0) })
})?;
let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) };
let root = path.to_path_buf();

View File

@ -10,7 +10,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -
let timespec = timeout.and_then(|dur| {
Some(hermit_abi::timespec {
tv_sec: dur.as_secs().try_into().ok()?,
tv_nsec: dur.subsec_nanos().into(),
tv_nsec: dur.subsec_nanos().try_into().ok()?,
})
});

View File

@ -0,0 +1,82 @@
use crate::marker::PhantomData;
use crate::os::hermit::io::{AsFd, AsRawFd};
use crate::slice;
use hermit_abi::{c_void, iovec};
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct IoSlice<'a> {
vec: iovec,
_p: PhantomData<&'a [u8]>,
}
impl<'a> IoSlice<'a> {
#[inline]
pub fn new(buf: &'a [u8]) -> IoSlice<'a> {
IoSlice {
vec: iovec { iov_base: buf.as_ptr() as *mut u8 as *mut c_void, iov_len: buf.len() },
_p: PhantomData,
}
}
#[inline]
pub fn advance(&mut self, n: usize) {
if self.vec.iov_len < n {
panic!("advancing IoSlice beyond its length");
}
unsafe {
self.vec.iov_len -= n;
self.vec.iov_base = self.vec.iov_base.add(n);
}
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
}
}
#[repr(transparent)]
pub struct IoSliceMut<'a> {
vec: iovec,
_p: PhantomData<&'a mut [u8]>,
}
impl<'a> IoSliceMut<'a> {
#[inline]
pub fn new(buf: &'a mut [u8]) -> IoSliceMut<'a> {
IoSliceMut {
vec: iovec { iov_base: buf.as_mut_ptr() as *mut c_void, iov_len: buf.len() },
_p: PhantomData,
}
}
#[inline]
pub fn advance(&mut self, n: usize) {
if self.vec.iov_len < n {
panic!("advancing IoSliceMut beyond its length");
}
unsafe {
self.vec.iov_len -= n;
self.vec.iov_base = self.vec.iov_base.add(n);
}
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.vec.iov_base as *mut u8, self.vec.iov_len) }
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.vec.iov_base as *mut u8, self.vec.iov_len) }
}
}
pub fn is_terminal(fd: &impl AsFd) -> bool {
let fd = fd.as_fd();
hermit_abi::isatty(fd.as_raw_fd())
}

View File

@ -23,7 +23,6 @@
pub mod fd;
pub mod fs;
pub mod futex;
#[path = "../unsupported/io.rs"]
pub mod io;
pub mod net;
pub mod os;

View File

@ -175,12 +175,12 @@ pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> {
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
crate::io::default_read_vectored(|b| self.read(b), bufs)
self.0.read_vectored(bufs)
}
#[inline]
pub fn is_read_vectored(&self) -> bool {
false
self.0.is_read_vectored()
}
fn recv_from_with_flags(&self, buf: &mut [u8], flags: i32) -> io::Result<(usize, SocketAddr)> {
@ -209,16 +209,15 @@ pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
let sz = cvt(unsafe { netc::write(self.0.as_raw_fd(), buf.as_ptr(), buf.len()) })?;
Ok(sz.try_into().unwrap())
self.0.write(buf)
}
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
crate::io::default_write_vectored(|b| self.write(b), bufs)
self.0.write_vectored(bufs)
}
pub fn is_write_vectored(&self) -> bool {
false
self.0.is_write_vectored()
}
pub fn set_timeout(&self, dur: Option<Duration>, kind: i32) -> io::Result<()> {
@ -265,7 +264,7 @@ pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
Shutdown::Read => netc::SHUT_RD,
Shutdown::Both => netc::SHUT_RDWR,
};
cvt(unsafe { netc::shutdown_socket(self.as_raw_fd(), how) })?;
cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?;
Ok(())
}

View File

@ -198,5 +198,5 @@ pub fn exit(code: i32) -> ! {
}
pub fn getpid() -> u32 {
unsafe { hermit_abi::getpid() }
unsafe { hermit_abi::getpid() as u32 }
}

View File

@ -1,6 +1,9 @@
use super::hermit_abi;
use crate::io;
use crate::io::{IoSlice, IoSliceMut};
use crate::mem::ManuallyDrop;
use crate::os::hermit::io::FromRawFd;
use crate::sys::fd::FileDesc;
pub struct Stdin;
pub struct Stdout;
@ -13,12 +16,14 @@ pub const fn new() -> Stdin {
}
impl io::Read for Stdin {
fn read(&mut self, data: &mut [u8]) -> io::Result<usize> {
self.read_vectored(&mut [IoSliceMut::new(data)])
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDIN_FILENO)).read(buf) }
}
fn read_vectored(&mut self, _data: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
Ok(0)
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
unsafe {
ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDIN_FILENO)).read_vectored(bufs)
}
}
#[inline]
@ -34,27 +39,13 @@ pub const fn new() -> Stdout {
}
impl io::Write for Stdout {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
let len;
unsafe { len = hermit_abi::write(1, data.as_ptr() as *const u8, data.len()) }
if len < 0 {
Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stdout is not able to print"))
} else {
Ok(len as usize)
}
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDOUT_FILENO)).write(buf) }
}
fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result<usize> {
let len;
unsafe { len = hermit_abi::write(1, data.as_ptr() as *const u8, data.len()) }
if len < 0 {
Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stdout is not able to print"))
} else {
Ok(len as usize)
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
unsafe {
ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDOUT_FILENO)).write_vectored(bufs)
}
}
@ -75,27 +66,13 @@ pub const fn new() -> Stderr {
}
impl io::Write for Stderr {
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
let len;
unsafe { len = hermit_abi::write(2, data.as_ptr() as *const u8, data.len()) }
if len < 0 {
Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stderr is not able to print"))
} else {
Ok(len as usize)
}
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
unsafe { ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDERR_FILENO)).write(buf) }
}
fn write_vectored(&mut self, data: &[IoSlice<'_>]) -> io::Result<usize> {
let len;
unsafe { len = hermit_abi::write(2, data.as_ptr() as *const u8, data.len()) }
if len < 0 {
Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Stderr is not able to print"))
} else {
Ok(len as usize)
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
unsafe {
ManuallyDrop::new(FileDesc::from_raw_fd(hermit_abi::STDERR_FILENO)).write_vectored(bufs)
}
}
@ -109,10 +86,10 @@ fn flush(&mut self) -> io::Result<()> {
}
}
pub const STDIN_BUF_SIZE: usize = 0;
pub const STDIN_BUF_SIZE: usize = 128;
pub fn is_ebadf(_err: &io::Error) -> bool {
true
pub fn is_ebadf(err: &io::Error) -> bool {
err.raw_os_error() == Some(hermit_abi::EBADF)
}
pub fn panic_output() -> Option<impl io::Write> {

View File

@ -98,5 +98,5 @@ pub fn into_id(self) -> Tid {
}
pub fn available_parallelism() -> io::Result<NonZero<usize>> {
unsafe { Ok(NonZero::new_unchecked(hermit_abi::get_processor_count())) }
unsafe { Ok(NonZero::new_unchecked(hermit_abi::available_parallelism())) }
}

View File

@ -1,11 +1,13 @@
#![allow(dead_code)]
use super::hermit_abi::{self, timespec, CLOCK_MONOTONIC, CLOCK_REALTIME, NSEC_PER_SEC};
use super::hermit_abi::{self, timespec, CLOCK_MONOTONIC, CLOCK_REALTIME};
use crate::cmp::Ordering;
use crate::ops::{Add, AddAssign, Sub, SubAssign};
use crate::time::Duration;
use core::hash::{Hash, Hasher};
const NSEC_PER_SEC: i32 = 1_000_000_000;
#[derive(Copy, Clone, Debug)]
struct Timespec {
t: timespec,
@ -16,8 +18,8 @@ const fn zero() -> Timespec {
Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } }
}
const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec {
assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64);
const fn new(tv_sec: i64, tv_nsec: i32) -> Timespec {
assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC);
// SAFETY: The assert above checks tv_nsec is within the valid range
Timespec { t: timespec { tv_sec: tv_sec, tv_nsec: tv_nsec } }
}
@ -32,7 +34,7 @@ fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
} else {
Duration::new(
(self.t.tv_sec - 1 - other.t.tv_sec) as u64,
self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - other.t.tv_nsec as u32,
(self.t.tv_nsec + NSEC_PER_SEC - other.t.tv_nsec) as u32,
)
})
} else {
@ -48,9 +50,9 @@ fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
// Nano calculations can't overflow because nanos are <1B which fit
// in a u32.
let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32;
if nsec >= NSEC_PER_SEC as u32 {
nsec -= NSEC_PER_SEC as u32;
let mut nsec = other.subsec_nanos() + u32::try_from(self.t.tv_nsec).unwrap();
if nsec >= NSEC_PER_SEC.try_into().unwrap() {
nsec -= u32::try_from(NSEC_PER_SEC).unwrap();
secs = secs.checked_add(1)?;
}
Some(Timespec { t: timespec { tv_sec: secs, tv_nsec: nsec as _ } })
@ -200,7 +202,7 @@ fn sub(self, other: Instant) -> Duration {
pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero());
impl SystemTime {
pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime {
pub fn new(tv_sec: i64, tv_nsec: i32) -> SystemTime {
SystemTime(Timespec::new(tv_sec, tv_nsec))
}