summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys/hermit
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/sys/hermit')
-rw-r--r--library/std/src/sys/hermit/alloc.rs31
-rw-r--r--library/std/src/sys/hermit/args.rs94
-rw-r--r--library/std/src/sys/hermit/condvar.rs90
-rw-r--r--library/std/src/sys/hermit/env.rs9
-rw-r--r--library/std/src/sys/hermit/fd.rs87
-rw-r--r--library/std/src/sys/hermit/fs.rs408
-rw-r--r--library/std/src/sys/hermit/memchr.rs1
-rw-r--r--library/std/src/sys/hermit/mod.rs156
-rw-r--r--library/std/src/sys/hermit/mutex.rs216
-rw-r--r--library/std/src/sys/hermit/net.rs492
-rw-r--r--library/std/src/sys/hermit/os.rs178
-rw-r--r--library/std/src/sys/hermit/rwlock.rs144
-rw-r--r--library/std/src/sys/hermit/stdio.rs120
-rw-r--r--library/std/src/sys/hermit/thread.rs112
-rw-r--r--library/std/src/sys/hermit/thread_local_dtor.rs36
-rw-r--r--library/std/src/sys/hermit/time.rs156
16 files changed, 2330 insertions, 0 deletions
diff --git a/library/std/src/sys/hermit/alloc.rs b/library/std/src/sys/hermit/alloc.rs
new file mode 100644
index 000000000..d153914e7
--- /dev/null
+++ b/library/std/src/sys/hermit/alloc.rs
@@ -0,0 +1,31 @@
+use crate::alloc::{GlobalAlloc, Layout, System};
+use crate::ptr;
+use crate::sys::hermit::abi;
+
+#[stable(feature = "alloc_system_type", since = "1.28.0")]
+unsafe impl GlobalAlloc for System {
+ #[inline]
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ abi::malloc(layout.size(), layout.align())
+ }
+
+ unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+ let addr = abi::malloc(layout.size(), layout.align());
+
+ if !addr.is_null() {
+ ptr::write_bytes(addr, 0x00, layout.size());
+ }
+
+ addr
+ }
+
+ #[inline]
+ unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+ abi::free(ptr, layout.size(), layout.align())
+ }
+
+ #[inline]
+ unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+ abi::realloc(ptr, layout.size(), layout.align(), new_size)
+ }
+}
diff --git a/library/std/src/sys/hermit/args.rs b/library/std/src/sys/hermit/args.rs
new file mode 100644
index 000000000..1c7e1dd8d
--- /dev/null
+++ b/library/std/src/sys/hermit/args.rs
@@ -0,0 +1,94 @@
+use crate::ffi::OsString;
+use crate::fmt;
+use crate::vec;
+
+/// One-time global initialization.
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
+ imp::init(argc, argv)
+}
+
+/// One-time global cleanup.
+pub unsafe fn cleanup() {
+ imp::cleanup()
+}
+
+/// Returns the command line arguments
+pub fn args() -> Args {
+ imp::args()
+}
+
+pub struct Args {
+ iter: vec::IntoIter<OsString>,
+}
+
+impl fmt::Debug for Args {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.iter.as_slice().fmt(f)
+ }
+}
+
+impl !Send for Args {}
+impl !Sync for Args {}
+
+impl Iterator for Args {
+ type Item = OsString;
+ fn next(&mut self) -> Option<OsString> {
+ self.iter.next()
+ }
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+impl ExactSizeIterator for Args {
+ fn len(&self) -> usize {
+ self.iter.len()
+ }
+}
+
+impl DoubleEndedIterator for Args {
+ fn next_back(&mut self) -> Option<OsString> {
+ self.iter.next_back()
+ }
+}
+
+mod imp {
+ use super::Args;
+ use crate::ffi::{CStr, OsString};
+ use crate::os::unix::ffi::OsStringExt;
+ use crate::ptr;
+
+ use crate::sys_common::mutex::StaticMutex;
+
+ static mut ARGC: isize = 0;
+ static mut ARGV: *const *const u8 = ptr::null();
+ static LOCK: StaticMutex = StaticMutex::new();
+
+ pub unsafe fn init(argc: isize, argv: *const *const u8) {
+ let _guard = LOCK.lock();
+ ARGC = argc;
+ ARGV = argv;
+ }
+
+ pub unsafe fn cleanup() {
+ let _guard = LOCK.lock();
+ ARGC = 0;
+ ARGV = ptr::null();
+ }
+
+ pub fn args() -> Args {
+ Args { iter: clone().into_iter() }
+ }
+
+ fn clone() -> Vec<OsString> {
+ unsafe {
+ let _guard = LOCK.lock();
+ (0..ARGC)
+ .map(|i| {
+ let cstr = CStr::from_ptr(*ARGV.offset(i) as *const i8);
+ OsStringExt::from_vec(cstr.to_bytes().to_vec())
+ })
+ .collect()
+ }
+ }
+}
diff --git a/library/std/src/sys/hermit/condvar.rs b/library/std/src/sys/hermit/condvar.rs
new file mode 100644
index 000000000..22059ca0d
--- /dev/null
+++ b/library/std/src/sys/hermit/condvar.rs
@@ -0,0 +1,90 @@
+use crate::ffi::c_void;
+use crate::ptr;
+use crate::sync::atomic::{AtomicUsize, Ordering::SeqCst};
+use crate::sys::hermit::abi;
+use crate::sys::locks::Mutex;
+use crate::sys_common::lazy_box::{LazyBox, LazyInit};
+use crate::time::Duration;
+
+// The implementation is inspired by Andrew D. Birrell's paper
+// "Implementing Condition Variables with Semaphores"
+
+pub struct Condvar {
+ counter: AtomicUsize,
+ sem1: *const c_void,
+ sem2: *const c_void,
+}
+
+pub(crate) type MovableCondvar = LazyBox<Condvar>;
+
+impl LazyInit for Condvar {
+ fn init() -> Box<Self> {
+ Box::new(Self::new())
+ }
+}
+
+unsafe impl Send for Condvar {}
+unsafe impl Sync for Condvar {}
+
+impl Condvar {
+ pub fn new() -> Self {
+ let mut condvar =
+ Self { counter: AtomicUsize::new(0), sem1: ptr::null(), sem2: ptr::null() };
+ unsafe {
+ let _ = abi::sem_init(&mut condvar.sem1, 0);
+ let _ = abi::sem_init(&mut condvar.sem2, 0);
+ }
+ condvar
+ }
+
+ pub unsafe fn notify_one(&self) {
+ if self.counter.load(SeqCst) > 0 {
+ self.counter.fetch_sub(1, SeqCst);
+ abi::sem_post(self.sem1);
+ abi::sem_timedwait(self.sem2, 0);
+ }
+ }
+
+ pub unsafe fn notify_all(&self) {
+ let counter = self.counter.swap(0, SeqCst);
+ for _ in 0..counter {
+ abi::sem_post(self.sem1);
+ }
+ for _ in 0..counter {
+ abi::sem_timedwait(self.sem2, 0);
+ }
+ }
+
+ pub unsafe fn wait(&self, mutex: &Mutex) {
+ self.counter.fetch_add(1, SeqCst);
+ mutex.unlock();
+ abi::sem_timedwait(self.sem1, 0);
+ abi::sem_post(self.sem2);
+ mutex.lock();
+ }
+
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+ self.counter.fetch_add(1, SeqCst);
+ mutex.unlock();
+ let millis = dur.as_millis().min(u32::MAX as u128) as u32;
+
+ let res = if millis > 0 {
+ abi::sem_timedwait(self.sem1, millis)
+ } else {
+ abi::sem_trywait(self.sem1)
+ };
+
+ abi::sem_post(self.sem2);
+ mutex.lock();
+ res == 0
+ }
+}
+
+impl Drop for Condvar {
+ fn drop(&mut self) {
+ unsafe {
+ let _ = abi::sem_destroy(self.sem1);
+ let _ = abi::sem_destroy(self.sem2);
+ }
+ }
+}
diff --git a/library/std/src/sys/hermit/env.rs b/library/std/src/sys/hermit/env.rs
new file mode 100644
index 000000000..7a0fcb31e
--- /dev/null
+++ b/library/std/src/sys/hermit/env.rs
@@ -0,0 +1,9 @@
+pub mod os {
+ pub const FAMILY: &str = "";
+ pub const OS: &str = "hermit";
+ pub const DLL_PREFIX: &str = "";
+ pub const DLL_SUFFIX: &str = "";
+ pub const DLL_EXTENSION: &str = "";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
diff --git a/library/std/src/sys/hermit/fd.rs b/library/std/src/sys/hermit/fd.rs
new file mode 100644
index 000000000..c400f5f2c
--- /dev/null
+++ b/library/std/src/sys/hermit/fd.rs
@@ -0,0 +1,87 @@
+#![unstable(reason = "not public", issue = "none", feature = "fd")]
+
+use crate::io::{self, Read};
+use crate::mem;
+use crate::sys::cvt;
+use crate::sys::hermit::abi;
+use crate::sys::unsupported;
+use crate::sys_common::AsInner;
+
+#[derive(Debug)]
+pub struct FileDesc {
+ fd: i32,
+}
+
+impl FileDesc {
+ pub fn new(fd: i32) -> FileDesc {
+ FileDesc { fd }
+ }
+
+ pub fn raw(&self) -> i32 {
+ self.fd
+ }
+
+ /// Extracts the actual file descriptor without closing it.
+ pub fn into_raw(self) -> i32 {
+ let fd = self.fd;
+ mem::forget(self);
+ fd
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ let result = unsafe { abi::read(self.fd, buf.as_mut_ptr(), buf.len()) };
+ cvt(result as i32)
+ }
+
+ pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
+ let mut me = self;
+ (&mut me).read_to_end(buf)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ let result = unsafe { abi::write(self.fd, buf.as_ptr(), buf.len()) };
+ cvt(result as i32)
+ }
+
+ pub fn duplicate(&self) -> io::Result<FileDesc> {
+ self.duplicate_path(&[])
+ }
+ pub fn duplicate_path(&self, _path: &[u8]) -> io::Result<FileDesc> {
+ unsupported()
+ }
+
+ pub fn nonblocking(&self) -> io::Result<bool> {
+ Ok(false)
+ }
+
+ pub fn set_cloexec(&self) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> {
+ unsupported()
+ }
+}
+
+impl<'a> Read for &'a FileDesc {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ (**self).read(buf)
+ }
+}
+
+impl AsInner<i32> for FileDesc {
+ fn as_inner(&self) -> &i32 {
+ &self.fd
+ }
+}
+
+impl Drop for FileDesc {
+ fn drop(&mut self) {
+ // Note that errors are ignored when closing a file descriptor. The
+ // reason for this is that if an error occurs we don't actually know if
+ // the file descriptor was closed or not, and if we retried (for
+ // something like EINTR), we might close another valid file descriptor
+ // (opened after we closed ours.
+ let _ = unsafe { abi::close(self.fd) };
+ }
+}
diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs
new file mode 100644
index 000000000..fa9a7fb19
--- /dev/null
+++ b/library/std/src/sys/hermit/fs.rs
@@ -0,0 +1,408 @@
+use crate::ffi::{CStr, CString, OsString};
+use crate::fmt;
+use crate::hash::{Hash, Hasher};
+use crate::io::{self, Error, ErrorKind};
+use crate::io::{IoSlice, IoSliceMut, ReadBuf, SeekFrom};
+use crate::os::unix::ffi::OsStrExt;
+use crate::path::{Path, PathBuf};
+use crate::sys::cvt;
+use crate::sys::hermit::abi;
+use crate::sys::hermit::abi::{O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY};
+use crate::sys::hermit::fd::FileDesc;
+use crate::sys::time::SystemTime;
+use crate::sys::unsupported;
+
+pub use crate::sys_common::fs::{copy, try_exists};
+//pub use crate::sys_common::fs::remove_dir_all;
+
+fn cstr(path: &Path) -> io::Result<CString> {
+ Ok(CString::new(path.as_os_str().as_bytes())?)
+}
+
+#[derive(Debug)]
+pub struct File(FileDesc);
+
+pub struct FileAttr(!);
+
+pub struct ReadDir(!);
+
+pub struct DirEntry(!);
+
+#[derive(Clone, Debug)]
+pub struct OpenOptions {
+ // generic
+ read: bool,
+ write: bool,
+ append: bool,
+ truncate: bool,
+ create: bool,
+ create_new: bool,
+ // system-specific
+ mode: i32,
+}
+
+pub struct FilePermissions(!);
+
+pub struct FileType(!);
+
+#[derive(Debug)]
+pub struct DirBuilder {}
+
+impl FileAttr {
+ pub fn size(&self) -> u64 {
+ self.0
+ }
+
+ pub fn perm(&self) -> FilePermissions {
+ self.0
+ }
+
+ pub fn file_type(&self) -> FileType {
+ self.0
+ }
+
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ self.0
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ self.0
+ }
+
+ pub fn created(&self) -> io::Result<SystemTime> {
+ self.0
+ }
+}
+
+impl Clone for FileAttr {
+ fn clone(&self) -> FileAttr {
+ self.0
+ }
+}
+
+impl FilePermissions {
+ pub fn readonly(&self) -> bool {
+ self.0
+ }
+
+ pub fn set_readonly(&mut self, _readonly: bool) {
+ self.0
+ }
+}
+
+impl Clone for FilePermissions {
+ fn clone(&self) -> FilePermissions {
+ self.0
+ }
+}
+
+impl PartialEq for FilePermissions {
+ fn eq(&self, _other: &FilePermissions) -> bool {
+ self.0
+ }
+}
+
+impl Eq for FilePermissions {}
+
+impl fmt::Debug for FilePermissions {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0
+ }
+}
+
+impl FileType {
+ pub fn is_dir(&self) -> bool {
+ self.0
+ }
+
+ pub fn is_file(&self) -> bool {
+ self.0
+ }
+
+ pub fn is_symlink(&self) -> bool {
+ self.0
+ }
+}
+
+impl Clone for FileType {
+ fn clone(&self) -> FileType {
+ self.0
+ }
+}
+
+impl Copy for FileType {}
+
+impl PartialEq for FileType {
+ fn eq(&self, _other: &FileType) -> bool {
+ self.0
+ }
+}
+
+impl Eq for FileType {}
+
+impl Hash for FileType {
+ fn hash<H: Hasher>(&self, _h: &mut H) {
+ self.0
+ }
+}
+
+impl fmt::Debug for FileType {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0
+ }
+}
+
+impl fmt::Debug for ReadDir {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0
+ }
+}
+
+impl Iterator for ReadDir {
+ type Item = io::Result<DirEntry>;
+
+ fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ self.0
+ }
+}
+
+impl DirEntry {
+ pub fn path(&self) -> PathBuf {
+ self.0
+ }
+
+ pub fn file_name(&self) -> OsString {
+ self.0
+ }
+
+ pub fn metadata(&self) -> io::Result<FileAttr> {
+ self.0
+ }
+
+ pub fn file_type(&self) -> io::Result<FileType> {
+ self.0
+ }
+}
+
+impl OpenOptions {
+ pub fn new() -> OpenOptions {
+ OpenOptions {
+ // generic
+ read: false,
+ write: false,
+ append: false,
+ truncate: false,
+ create: false,
+ create_new: false,
+ // system-specific
+ mode: 0x777,
+ }
+ }
+
+ pub fn read(&mut self, read: bool) {
+ self.read = read;
+ }
+ pub fn write(&mut self, write: bool) {
+ self.write = write;
+ }
+ pub fn append(&mut self, append: bool) {
+ self.append = append;
+ }
+ pub fn truncate(&mut self, truncate: bool) {
+ self.truncate = truncate;
+ }
+ pub fn create(&mut self, create: bool) {
+ self.create = create;
+ }
+ pub fn create_new(&mut self, create_new: bool) {
+ self.create_new = create_new;
+ }
+
+ fn get_access_mode(&self) -> io::Result<i32> {
+ match (self.read, self.write, self.append) {
+ (true, false, false) => Ok(O_RDONLY),
+ (false, true, false) => Ok(O_WRONLY),
+ (true, true, false) => Ok(O_RDWR),
+ (false, _, true) => Ok(O_WRONLY | O_APPEND),
+ (true, _, true) => Ok(O_RDWR | O_APPEND),
+ (false, false, false) => {
+ Err(io::const_io_error!(ErrorKind::InvalidInput, "invalid access mode"))
+ }
+ }
+ }
+
+ fn get_creation_mode(&self) -> io::Result<i32> {
+ match (self.write, self.append) {
+ (true, false) => {}
+ (false, false) => {
+ if self.truncate || self.create || self.create_new {
+ return Err(io::const_io_error!(
+ ErrorKind::InvalidInput,
+ "invalid creation mode",
+ ));
+ }
+ }
+ (_, true) => {
+ if self.truncate && !self.create_new {
+ return Err(io::const_io_error!(
+ ErrorKind::InvalidInput,
+ "invalid creation mode",
+ ));
+ }
+ }
+ }
+
+ Ok(match (self.create, self.truncate, self.create_new) {
+ (false, false, false) => 0,
+ (true, false, false) => O_CREAT,
+ (false, true, false) => O_TRUNC,
+ (true, true, false) => O_CREAT | O_TRUNC,
+ (_, _, true) => O_CREAT | O_EXCL,
+ })
+ }
+}
+
+impl File {
+ pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
+ let path = cstr(path)?;
+ File::open_c(&path, opts)
+ }
+
+ pub fn open_c(path: &CStr, opts: &OpenOptions) -> io::Result<File> {
+ let mut flags = opts.get_access_mode()?;
+ flags = flags | opts.get_creation_mode()?;
+
+ let mode;
+ if flags & O_CREAT == O_CREAT {
+ mode = opts.mode;
+ } else {
+ mode = 0;
+ }
+
+ let fd = unsafe { cvt(abi::open(path.as_ptr(), flags, mode))? };
+ Ok(File(FileDesc::new(fd as i32)))
+ }
+
+ pub fn file_attr(&self) -> io::Result<FileAttr> {
+ Err(Error::from_raw_os_error(22))
+ }
+
+ pub fn fsync(&self) -> io::Result<()> {
+ Err(Error::from_raw_os_error(22))
+ }
+
+ pub fn datasync(&self) -> io::Result<()> {
+ self.fsync()
+ }
+
+ pub fn truncate(&self, _size: u64) -> io::Result<()> {
+ Err(Error::from_raw_os_error(22))
+ }
+
+ pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
+ self.0.read(buf)
+ }
+
+ pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ crate::io::default_read_vectored(|buf| self.read(buf), bufs)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ false
+ }
+
+ pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
+ crate::io::default_read_buf(|buf| self.read(buf), buf)
+ }
+
+ pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
+ self.0.write(buf)
+ }
+
+ pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
+ crate::io::default_write_vectored(|buf| self.write(buf), bufs)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ false
+ }
+
+ pub fn flush(&self) -> io::Result<()> {
+ Ok(())
+ }
+
+ pub fn seek(&self, _pos: SeekFrom) -> io::Result<u64> {
+ Err(Error::from_raw_os_error(22))
+ }
+
+ pub fn duplicate(&self) -> io::Result<File> {
+ Err(Error::from_raw_os_error(22))
+ }
+
+ pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
+ Err(Error::from_raw_os_error(22))
+ }
+}
+
+impl DirBuilder {
+ pub fn new() -> DirBuilder {
+ DirBuilder {}
+ }
+
+ pub fn mkdir(&self, _p: &Path) -> io::Result<()> {
+ unsupported()
+ }
+}
+
+pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
+ unsupported()
+}
+
+pub fn unlink(path: &Path) -> io::Result<()> {
+ let name = cstr(path)?;
+ let _ = unsafe { cvt(abi::unlink(name.as_ptr()))? };
+ Ok(())
+}
+
+pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> {
+ match perm.0 {}
+}
+
+pub fn rmdir(_p: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
+ //unsupported()
+ Ok(())
+}
+
+pub fn readlink(_p: &Path) -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn symlink(_original: &Path, _link: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub fn stat(_p: &Path) -> io::Result<FileAttr> {
+ unsupported()
+}
+
+pub fn lstat(_p: &Path) -> io::Result<FileAttr> {
+ unsupported()
+}
+
+pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
+ unsupported()
+}
diff --git a/library/std/src/sys/hermit/memchr.rs b/library/std/src/sys/hermit/memchr.rs
new file mode 100644
index 000000000..996748219
--- /dev/null
+++ b/library/std/src/sys/hermit/memchr.rs
@@ -0,0 +1 @@
+pub use core::slice::memchr::{memchr, memrchr};
diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs
new file mode 100644
index 000000000..60b7a973c
--- /dev/null
+++ b/library/std/src/sys/hermit/mod.rs
@@ -0,0 +1,156 @@
+//! System bindings for HermitCore
+//!
+//! This module contains the facade (aka platform-specific) implementations of
+//! OS level functionality for HermitCore.
+//!
+//! This is all super highly experimental and not actually intended for
+//! wide/production use yet, it's still all in the experimental category. This
+//! will likely change over time.
+//!
+//! Currently all functions here are basically stubs that immediately return
+//! errors. The hope is that with a portability lint we can turn actually just
+//! remove all this and just omit parts of the standard library if we're
+//! compiling for wasm. That way it's a compile time error for something that's
+//! guaranteed to be a runtime error!
+
+#![allow(unsafe_op_in_unsafe_fn)]
+
+use crate::intrinsics;
+use crate::os::raw::c_char;
+
+pub mod alloc;
+pub mod args;
+#[path = "../unix/cmath.rs"]
+pub mod cmath;
+pub mod env;
+pub mod fd;
+pub mod fs;
+#[path = "../unsupported/io.rs"]
+pub mod io;
+pub mod memchr;
+pub mod net;
+pub mod os;
+#[path = "../unix/os_str.rs"]
+pub mod os_str;
+#[path = "../unix/path.rs"]
+pub mod path;
+#[path = "../unsupported/pipe.rs"]
+pub mod pipe;
+#[path = "../unsupported/process.rs"]
+pub mod process;
+pub mod stdio;
+pub mod thread;
+pub mod thread_local_dtor;
+#[path = "../unsupported/thread_local_key.rs"]
+pub mod thread_local_key;
+pub mod time;
+
+mod condvar;
+mod mutex;
+mod rwlock;
+
+pub mod locks {
+ pub use super::condvar::*;
+ pub use super::mutex::*;
+ pub use super::rwlock::*;
+}
+
+use crate::io::ErrorKind;
+
+#[allow(unused_extern_crates)]
+pub extern crate hermit_abi as abi;
+
+pub fn unsupported<T>() -> crate::io::Result<T> {
+ Err(unsupported_err())
+}
+
+pub fn unsupported_err() -> crate::io::Error {
+ crate::io::const_io_error!(
+ crate::io::ErrorKind::Unsupported,
+ "operation not supported on HermitCore yet",
+ )
+}
+
+#[no_mangle]
+pub extern "C" fn floor(x: f64) -> f64 {
+ unsafe { intrinsics::floorf64(x) }
+}
+
+pub fn abort_internal() -> ! {
+ unsafe {
+ abi::abort();
+ }
+}
+
+// FIXME: just a workaround to test the system
+pub fn hashmap_random_keys() -> (u64, u64) {
+ (1, 2)
+}
+
+// This function is needed by the panic runtime. The symbol is named in
+// pre-link args for the target specification, so keep that in sync.
+#[cfg(not(test))]
+#[no_mangle]
+// NB. used by both libunwind and libpanic_abort
+pub extern "C" fn __rust_abort() {
+ abort_internal();
+}
+
+// SAFETY: must be called only once during runtime initialization.
+// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
+pub unsafe fn init(argc: isize, argv: *const *const u8) {
+ let _ = net::init();
+ args::init(argc, argv);
+}
+
+// SAFETY: must be called only once during runtime cleanup.
+// NOTE: this is not guaranteed to run, for example when the program aborts.
+pub unsafe fn cleanup() {
+ args::cleanup();
+}
+
+#[cfg(not(test))]
+#[no_mangle]
+pub unsafe extern "C" fn runtime_entry(
+ argc: i32,
+ argv: *const *const c_char,
+ env: *const *const c_char,
+) -> ! {
+ use crate::sys::hermit::thread_local_dtor::run_dtors;
+ extern "C" {
+ fn main(argc: isize, argv: *const *const c_char) -> i32;
+ }
+
+ // initialize environment
+ os::init_environment(env as *const *const i8);
+
+ let result = main(argc as isize, argv);
+
+ run_dtors();
+ abi::exit(result);
+}
+
+pub fn decode_error_kind(errno: i32) -> ErrorKind {
+ match errno {
+ x if x == 13 as i32 => ErrorKind::PermissionDenied,
+ x if x == 98 as i32 => ErrorKind::AddrInUse,
+ x if x == 99 as i32 => ErrorKind::AddrNotAvailable,
+ x if x == 11 as i32 => ErrorKind::WouldBlock,
+ x if x == 103 as i32 => ErrorKind::ConnectionAborted,
+ x if x == 111 as i32 => ErrorKind::ConnectionRefused,
+ x if x == 104 as i32 => ErrorKind::ConnectionReset,
+ x if x == 17 as i32 => ErrorKind::AlreadyExists,
+ x if x == 4 as i32 => ErrorKind::Interrupted,
+ x if x == 22 as i32 => ErrorKind::InvalidInput,
+ x if x == 2 as i32 => ErrorKind::NotFound,
+ x if x == 107 as i32 => ErrorKind::NotConnected,
+ x if x == 1 as i32 => ErrorKind::PermissionDenied,
+ x if x == 32 as i32 => ErrorKind::BrokenPipe,
+ x if x == 110 as i32 => ErrorKind::TimedOut,
+ _ => ErrorKind::Uncategorized,
+ }
+}
+
+pub fn cvt(result: i32) -> crate::io::Result<usize> {
+ if result < 0 { Err(crate::io::Error::from_raw_os_error(-result)) } else { Ok(result as usize) }
+}
diff --git a/library/std/src/sys/hermit/mutex.rs b/library/std/src/sys/hermit/mutex.rs
new file mode 100644
index 000000000..eb15a04ff
--- /dev/null
+++ b/library/std/src/sys/hermit/mutex.rs
@@ -0,0 +1,216 @@
+use crate::cell::UnsafeCell;
+use crate::collections::VecDeque;
+use crate::hint;
+use crate::ops::{Deref, DerefMut, Drop};
+use crate::ptr;
+use crate::sync::atomic::{AtomicUsize, Ordering};
+use crate::sys::hermit::abi;
+
+/// This type provides a lock based on busy waiting to realize mutual exclusion
+///
+/// # Description
+///
+/// This structure behaves a lot like a common mutex. There are some differences:
+///
+/// - By using busy waiting, it can be used outside the runtime.
+/// - It is a so called ticket lock and is completely fair.
+#[cfg_attr(target_arch = "x86_64", repr(align(128)))]
+#[cfg_attr(not(target_arch = "x86_64"), repr(align(64)))]
+struct Spinlock<T: ?Sized> {
+ queue: AtomicUsize,
+ dequeue: AtomicUsize,
+ data: UnsafeCell<T>,
+}
+
+unsafe impl<T: ?Sized + Send> Sync for Spinlock<T> {}
+unsafe impl<T: ?Sized + Send> Send for Spinlock<T> {}
+
+/// A guard to which the protected data can be accessed
+///
+/// When the guard falls out of scope it will release the lock.
+struct SpinlockGuard<'a, T: ?Sized + 'a> {
+ dequeue: &'a AtomicUsize,
+ data: &'a mut T,
+}
+
+impl<T> Spinlock<T> {
+ pub const fn new(user_data: T) -> Spinlock<T> {
+ Spinlock {
+ queue: AtomicUsize::new(0),
+ dequeue: AtomicUsize::new(1),
+ data: UnsafeCell::new(user_data),
+ }
+ }
+
+ #[inline]
+ fn obtain_lock(&self) {
+ let ticket = self.queue.fetch_add(1, Ordering::SeqCst) + 1;
+ let mut counter: u16 = 0;
+ while self.dequeue.load(Ordering::SeqCst) != ticket {
+ counter += 1;
+ if counter < 100 {
+ hint::spin_loop();
+ } else {
+ counter = 0;
+ unsafe {
+ abi::yield_now();
+ }
+ }
+ }
+ }
+
+ #[inline]
+ pub unsafe fn lock(&self) -> SpinlockGuard<'_, T> {
+ self.obtain_lock();
+ SpinlockGuard { dequeue: &self.dequeue, data: &mut *self.data.get() }
+ }
+}
+
+impl<T: ?Sized + Default> Default for Spinlock<T> {
+ fn default() -> Spinlock<T> {
+ Spinlock::new(Default::default())
+ }
+}
+
+impl<'a, T: ?Sized> Deref for SpinlockGuard<'a, T> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ &*self.data
+ }
+}
+
+impl<'a, T: ?Sized> DerefMut for SpinlockGuard<'a, T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut *self.data
+ }
+}
+
+impl<'a, T: ?Sized> Drop for SpinlockGuard<'a, T> {
+ /// The dropping of the SpinlockGuard will release the lock it was created from.
+ fn drop(&mut self) {
+ self.dequeue.fetch_add(1, Ordering::SeqCst);
+ }
+}
+
+/// Realize a priority queue for tasks
+struct PriorityQueue {
+ queues: [Option<VecDeque<abi::Tid>>; abi::NO_PRIORITIES],
+ prio_bitmap: u64,
+}
+
+impl PriorityQueue {
+ pub const fn new() -> PriorityQueue {
+ PriorityQueue {
+ queues: [
+ None, None, None, None, None, None, None, None, None, None, None, None, None, None,
+ None, None, None, None, None, None, None, None, None, None, None, None, None, None,
+ None, None, None,
+ ],
+ prio_bitmap: 0,
+ }
+ }
+
+ /// Add a task id by its priority to the queue
+ pub fn push(&mut self, prio: abi::Priority, id: abi::Tid) {
+ let i: usize = prio.into().into();
+ self.prio_bitmap |= (1 << i) as u64;
+ if let Some(queue) = &mut self.queues[i] {
+ queue.push_back(id);
+ } else {
+ let mut queue = VecDeque::new();
+ queue.push_back(id);
+ self.queues[i] = Some(queue);
+ }
+ }
+
+ fn pop_from_queue(&mut self, queue_index: usize) -> Option<abi::Tid> {
+ if let Some(queue) = &mut self.queues[queue_index] {
+ let id = queue.pop_front();
+
+ if queue.is_empty() {
+ self.prio_bitmap &= !(1 << queue_index as u64);
+ }
+
+ id
+ } else {
+ None
+ }
+ }
+
+ /// Pop the task handle with the highest priority from the queue
+ pub fn pop(&mut self) -> Option<abi::Tid> {
+ for i in 0..abi::NO_PRIORITIES {
+ if self.prio_bitmap & (1 << i) != 0 {
+ return self.pop_from_queue(i);
+ }
+ }
+
+ None
+ }
+}
+
+struct MutexInner {
+ locked: bool,
+ blocked_task: PriorityQueue,
+}
+
+impl MutexInner {
+ pub const fn new() -> MutexInner {
+ MutexInner { locked: false, blocked_task: PriorityQueue::new() }
+ }
+}
+
+pub struct Mutex {
+ inner: Spinlock<MutexInner>,
+}
+
+pub type MovableMutex = Mutex;
+
+unsafe impl Send for Mutex {}
+unsafe impl Sync for Mutex {}
+
+impl Mutex {
+ pub const fn new() -> Mutex {
+ Mutex { inner: Spinlock::new(MutexInner::new()) }
+ }
+
+ #[inline]
+ pub unsafe fn init(&mut self) {}
+
+ #[inline]
+ pub unsafe fn lock(&self) {
+ loop {
+ let mut guard = self.inner.lock();
+ if guard.locked == false {
+ guard.locked = true;
+ return;
+ } else {
+ let prio = abi::get_priority();
+ let id = abi::getpid();
+
+ guard.blocked_task.push(prio, id);
+ abi::block_current_task();
+ drop(guard);
+ abi::yield_now();
+ }
+ }
+ }
+
+ #[inline]
+ pub unsafe fn unlock(&self) {
+ let mut guard = self.inner.lock();
+ guard.locked = false;
+ if let Some(tid) = guard.blocked_task.pop() {
+ abi::wakeup_task(tid);
+ }
+ }
+
+ #[inline]
+ pub unsafe fn try_lock(&self) -> bool {
+ let mut guard = self.inner.lock();
+ if guard.locked == false {
+ guard.locked = true;
+ }
+ guard.locked
+ }
+}
diff --git a/library/std/src/sys/hermit/net.rs b/library/std/src/sys/hermit/net.rs
new file mode 100644
index 000000000..745476171
--- /dev/null
+++ b/library/std/src/sys/hermit/net.rs
@@ -0,0 +1,492 @@
+use crate::fmt;
+use crate::io::{self, ErrorKind, IoSlice, IoSliceMut};
+use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr};
+use crate::str;
+use crate::sync::Arc;
+use crate::sys::hermit::abi;
+use crate::sys::hermit::abi::IpAddress::{Ipv4, Ipv6};
+use crate::sys::unsupported;
+use crate::sys_common::AsInner;
+use crate::time::Duration;
+
+/// Checks whether the HermitCore's socket interface has been started already, and
+/// if not, starts it.
+pub fn init() -> io::Result<()> {
+ if abi::network_init() < 0 {
+ return Err(io::const_io_error!(
+ ErrorKind::Uncategorized,
+ "Unable to initialize network interface",
+ ));
+ }
+
+ Ok(())
+}
+
+#[derive(Debug, Clone)]
+pub struct Socket(abi::Handle);
+
+impl AsInner<abi::Handle> for Socket {
+ fn as_inner(&self) -> &abi::Handle {
+ &self.0
+ }
+}
+
+impl Drop for Socket {
+ fn drop(&mut self) {
+ let _ = abi::tcpstream::close(self.0);
+ }
+}
+
+// Arc is used to count the number of used sockets.
+// Only if all sockets are released, the drop
+// method will close the socket.
+#[derive(Clone)]
+pub struct TcpStream(Arc<Socket>);
+
+impl TcpStream {
+ pub fn connect(addr: io::Result<&SocketAddr>) -> io::Result<TcpStream> {
+ let addr = addr?;
+
+ match abi::tcpstream::connect(addr.ip().to_string().as_bytes(), addr.port(), None) {
+ Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))),
+ _ => Err(io::const_io_error!(
+ ErrorKind::Uncategorized,
+ "Unable to initiate a connection on a socket",
+ )),
+ }
+ }
+
+ pub fn connect_timeout(saddr: &SocketAddr, duration: Duration) -> io::Result<TcpStream> {
+ match abi::tcpstream::connect(
+ saddr.ip().to_string().as_bytes(),
+ saddr.port(),
+ Some(duration.as_millis() as u64),
+ ) {
+ Ok(handle) => Ok(TcpStream(Arc::new(Socket(handle)))),
+ _ => Err(io::const_io_error!(
+ ErrorKind::Uncategorized,
+ "Unable to initiate a connection on a socket",
+ )),
+ }
+ }
+
+ pub fn set_read_timeout(&self, duration: Option<Duration>) -> io::Result<()> {
+ abi::tcpstream::set_read_timeout(*self.0.as_inner(), duration.map(|d| d.as_millis() as u64))
+ .map_err(|_| {
+ io::const_io_error!(ErrorKind::Uncategorized, "Unable to set timeout value")
+ })
+ }
+
+ pub fn set_write_timeout(&self, duration: Option<Duration>) -> io::Result<()> {
+ abi::tcpstream::set_write_timeout(
+ *self.0.as_inner(),
+ duration.map(|d| d.as_millis() as u64),
+ )
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "Unable to set timeout value"))
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ let duration = abi::tcpstream::get_read_timeout(*self.0.as_inner()).map_err(|_| {
+ io::const_io_error!(ErrorKind::Uncategorized, "Unable to determine timeout value")
+ })?;
+
+ Ok(duration.map(|d| Duration::from_millis(d)))
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ let duration = abi::tcpstream::get_write_timeout(*self.0.as_inner()).map_err(|_| {
+ io::const_io_error!(ErrorKind::Uncategorized, "Unable to determine timeout value")
+ })?;
+
+ Ok(duration.map(|d| Duration::from_millis(d)))
+ }
+
+ pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
+ abi::tcpstream::peek(*self.0.as_inner(), buf)
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "peek failed"))
+ }
+
+ pub fn read(&self, buffer: &mut [u8]) -> io::Result<usize> {
+ self.read_vectored(&mut [IoSliceMut::new(buffer)])
+ }
+
+ pub fn read_vectored(&self, ioslice: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ let mut size: usize = 0;
+
+ for i in ioslice.iter_mut() {
+ let ret = abi::tcpstream::read(*self.0.as_inner(), &mut i[0..]).map_err(|_| {
+ io::const_io_error!(ErrorKind::Uncategorized, "Unable to read on socket")
+ })?;
+
+ if ret != 0 {
+ size += ret;
+ }
+ }
+
+ Ok(size)
+ }
+
+ #[inline]
+ pub fn is_read_vectored(&self) -> bool {
+ true
+ }
+
+ pub fn write(&self, buffer: &[u8]) -> io::Result<usize> {
+ self.write_vectored(&[IoSlice::new(buffer)])
+ }
+
+ pub fn write_vectored(&self, ioslice: &[IoSlice<'_>]) -> io::Result<usize> {
+ let mut size: usize = 0;
+
+ for i in ioslice.iter() {
+ size += abi::tcpstream::write(*self.0.as_inner(), i).map_err(|_| {
+ io::const_io_error!(ErrorKind::Uncategorized, "Unable to write on socket")
+ })?;
+ }
+
+ Ok(size)
+ }
+
+ #[inline]
+ pub fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ let (ipaddr, port) = abi::tcpstream::peer_addr(*self.0.as_inner())
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "peer_addr failed"))?;
+
+ let saddr = match ipaddr {
+ Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port),
+ Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port),
+ _ => {
+ return Err(io::const_io_error!(ErrorKind::Uncategorized, "peer_addr failed"));
+ }
+ };
+
+ Ok(saddr)
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unsupported()
+ }
+
+ pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
+ abi::tcpstream::shutdown(*self.0.as_inner(), how as i32)
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to shutdown socket"))
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpStream> {
+ Ok(self.clone())
+ }
+
+ pub fn set_linger(&self, _linger: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn linger(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
+ pub fn set_nodelay(&self, mode: bool) -> io::Result<()> {
+ abi::tcpstream::set_nodelay(*self.0.as_inner(), mode)
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "set_nodelay failed"))
+ }
+
+ pub fn nodelay(&self) -> io::Result<bool> {
+ abi::tcpstream::nodelay(*self.0.as_inner())
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "nodelay failed"))
+ }
+
+ pub fn set_ttl(&self, tll: u32) -> io::Result<()> {
+ abi::tcpstream::set_tll(*self.0.as_inner(), tll)
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to set TTL"))
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ abi::tcpstream::get_tll(*self.0.as_inner())
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "unable to get TTL"))
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unsupported()
+ }
+
+ pub fn set_nonblocking(&self, mode: bool) -> io::Result<()> {
+ abi::tcpstream::set_nonblocking(*self.0.as_inner(), mode).map_err(|_| {
+ io::const_io_error!(ErrorKind::Uncategorized, "unable to set blocking mode")
+ })
+ }
+}
+
+impl fmt::Debug for TcpStream {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ Ok(())
+ }
+}
+
+#[derive(Clone)]
+pub struct TcpListener(SocketAddr);
+
+impl TcpListener {
+ pub fn bind(addr: io::Result<&SocketAddr>) -> io::Result<TcpListener> {
+ let addr = addr?;
+
+ Ok(TcpListener(*addr))
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ Ok(self.0)
+ }
+
+ pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
+ let (handle, ipaddr, port) = abi::tcplistener::accept(self.0.port())
+ .map_err(|_| io::const_io_error!(ErrorKind::Uncategorized, "accept failed"))?;
+ let saddr = match ipaddr {
+ Ipv4(ref addr) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(addr.0)), port),
+ Ipv6(ref addr) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(addr.0)), port),
+ _ => {
+ return Err(io::const_io_error!(ErrorKind::Uncategorized, "accept failed"));
+ }
+ };
+
+ Ok((TcpStream(Arc::new(Socket(handle))), saddr))
+ }
+
+ pub fn duplicate(&self) -> io::Result<TcpListener> {
+ Ok(self.clone())
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unsupported()
+ }
+
+ pub fn set_only_v6(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn only_v6(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unsupported()
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+}
+
+impl fmt::Debug for TcpListener {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ Ok(())
+ }
+}
+
+pub struct UdpSocket(abi::Handle);
+
+impl UdpSocket {
+ pub fn bind(_: io::Result<&SocketAddr>) -> io::Result<UdpSocket> {
+ unsupported()
+ }
+
+ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
+ unsupported()
+ }
+
+ pub fn socket_addr(&self) -> io::Result<SocketAddr> {
+ unsupported()
+ }
+
+ pub fn recv_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unsupported()
+ }
+
+ pub fn peek_from(&self, _: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
+ unsupported()
+ }
+
+ pub fn send_to(&self, _: &[u8], _: &SocketAddr) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn duplicate(&self) -> io::Result<UdpSocket> {
+ unsupported()
+ }
+
+ pub fn set_read_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn set_write_timeout(&self, _: Option<Duration>) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn read_timeout(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
+ pub fn write_timeout(&self) -> io::Result<Option<Duration>> {
+ unsupported()
+ }
+
+ pub fn set_broadcast(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn broadcast(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn set_multicast_loop_v4(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn multicast_loop_v4(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn set_multicast_ttl_v4(&self, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
+ unsupported()
+ }
+
+ pub fn set_multicast_loop_v6(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn multicast_loop_v6(&self) -> io::Result<bool> {
+ unsupported()
+ }
+
+ pub fn join_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn join_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn leave_multicast_v4(&self, _: &Ipv4Addr, _: &Ipv4Addr) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn leave_multicast_v6(&self, _: &Ipv6Addr, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn set_ttl(&self, _: u32) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn ttl(&self) -> io::Result<u32> {
+ unsupported()
+ }
+
+ pub fn take_error(&self) -> io::Result<Option<io::Error>> {
+ unsupported()
+ }
+
+ pub fn set_nonblocking(&self, _: bool) -> io::Result<()> {
+ unsupported()
+ }
+
+ pub fn recv(&self, _: &mut [u8]) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn peek(&self, _: &mut [u8]) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn send(&self, _: &[u8]) -> io::Result<usize> {
+ unsupported()
+ }
+
+ pub fn connect(&self, _: io::Result<&SocketAddr>) -> io::Result<()> {
+ unsupported()
+ }
+}
+
+impl fmt::Debug for UdpSocket {
+ fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ Ok(())
+ }
+}
+
+pub struct LookupHost(!);
+
+impl LookupHost {
+ pub fn port(&self) -> u16 {
+ self.0
+ }
+}
+
+impl Iterator for LookupHost {
+ type Item = SocketAddr;
+ fn next(&mut self) -> Option<SocketAddr> {
+ self.0
+ }
+}
+
+impl TryFrom<&str> for LookupHost {
+ type Error = io::Error;
+
+ fn try_from(_v: &str) -> io::Result<LookupHost> {
+ unsupported()
+ }
+}
+
+impl<'a> TryFrom<(&'a str, u16)> for LookupHost {
+ type Error = io::Error;
+
+ fn try_from(_v: (&'a str, u16)) -> io::Result<LookupHost> {
+ unsupported()
+ }
+}
+
+#[allow(nonstandard_style)]
+pub mod netc {
+ pub const AF_INET: u8 = 0;
+ pub const AF_INET6: u8 = 1;
+ pub type sa_family_t = u8;
+
+ #[derive(Copy, Clone)]
+ pub struct in_addr {
+ pub s_addr: u32,
+ }
+
+ #[derive(Copy, Clone)]
+ pub struct sockaddr_in {
+ pub sin_family: sa_family_t,
+ pub sin_port: u16,
+ pub sin_addr: in_addr,
+ }
+
+ #[derive(Copy, Clone)]
+ pub struct in6_addr {
+ pub s6_addr: [u8; 16],
+ }
+
+ #[derive(Copy, Clone)]
+ pub struct sockaddr_in6 {
+ pub sin6_family: sa_family_t,
+ pub sin6_port: u16,
+ pub sin6_addr: in6_addr,
+ pub sin6_flowinfo: u32,
+ pub sin6_scope_id: u32,
+ }
+
+ #[derive(Copy, Clone)]
+ pub struct sockaddr {}
+
+ pub type socklen_t = usize;
+}
diff --git a/library/std/src/sys/hermit/os.rs b/library/std/src/sys/hermit/os.rs
new file mode 100644
index 000000000..8f927df85
--- /dev/null
+++ b/library/std/src/sys/hermit/os.rs
@@ -0,0 +1,178 @@
+use crate::collections::HashMap;
+use crate::error::Error as StdError;
+use crate::ffi::{CStr, OsStr, OsString};
+use crate::fmt;
+use crate::io;
+use crate::marker::PhantomData;
+use crate::os::unix::ffi::OsStringExt;
+use crate::path::{self, PathBuf};
+use crate::str;
+use crate::sync::Mutex;
+use crate::sys::hermit::abi;
+use crate::sys::memchr;
+use crate::sys::unsupported;
+use crate::vec;
+
+pub fn errno() -> i32 {
+ 0
+}
+
+pub fn error_string(_errno: i32) -> String {
+ "operation successful".to_string()
+}
+
+pub fn getcwd() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+pub fn chdir(_: &path::Path) -> io::Result<()> {
+ unsupported()
+}
+
+pub struct SplitPaths<'a>(!, PhantomData<&'a ()>);
+
+pub fn split_paths(_unparsed: &OsStr) -> SplitPaths<'_> {
+ panic!("unsupported")
+}
+
+impl<'a> Iterator for SplitPaths<'a> {
+ type Item = PathBuf;
+ fn next(&mut self) -> Option<PathBuf> {
+ self.0
+ }
+}
+
+#[derive(Debug)]
+pub struct JoinPathsError;
+
+pub fn join_paths<I, T>(_paths: I) -> Result<OsString, JoinPathsError>
+where
+ I: Iterator<Item = T>,
+ T: AsRef<OsStr>,
+{
+ Err(JoinPathsError)
+}
+
+impl fmt::Display for JoinPathsError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ "not supported on hermit yet".fmt(f)
+ }
+}
+
+impl StdError for JoinPathsError {
+ #[allow(deprecated)]
+ fn description(&self) -> &str {
+ "not supported on hermit yet"
+ }
+}
+
+pub fn current_exe() -> io::Result<PathBuf> {
+ unsupported()
+}
+
+static mut ENV: Option<Mutex<HashMap<OsString, OsString>>> = None;
+
+pub fn init_environment(env: *const *const i8) {
+ unsafe {
+ ENV = Some(Mutex::new(HashMap::new()));
+
+ if env.is_null() {
+ return;
+ }
+
+ let mut guard = ENV.as_ref().unwrap().lock().unwrap();
+ let mut environ = env;
+ while !(*environ).is_null() {
+ if let Some((key, value)) = parse(CStr::from_ptr(*environ).to_bytes()) {
+ guard.insert(key, value);
+ }
+ environ = environ.add(1);
+ }
+ }
+
+ fn parse(input: &[u8]) -> Option<(OsString, OsString)> {
+ // Strategy (copied from glibc): Variable name and value are separated
+ // by an ASCII equals sign '='. Since a variable name must not be
+ // empty, allow variable names starting with an equals sign. Skip all
+ // malformed lines.
+ if input.is_empty() {
+ return None;
+ }
+ let pos = memchr::memchr(b'=', &input[1..]).map(|p| p + 1);
+ pos.map(|p| {
+ (
+ OsStringExt::from_vec(input[..p].to_vec()),
+ OsStringExt::from_vec(input[p + 1..].to_vec()),
+ )
+ })
+ }
+}
+
+pub struct Env {
+ iter: vec::IntoIter<(OsString, OsString)>,
+}
+
+impl !Send for Env {}
+impl !Sync for Env {}
+
+impl Iterator for Env {
+ type Item = (OsString, OsString);
+ fn next(&mut self) -> Option<(OsString, OsString)> {
+ self.iter.next()
+ }
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+}
+
+/// Returns a vector of (variable, value) byte-vector pairs for all the
+/// environment variables of the current process.
+pub fn env() -> Env {
+ unsafe {
+ let guard = ENV.as_ref().unwrap().lock().unwrap();
+ let mut result = Vec::new();
+
+ for (key, value) in guard.iter() {
+ result.push((key.clone(), value.clone()));
+ }
+
+ return Env { iter: result.into_iter() };
+ }
+}
+
+pub fn getenv(k: &OsStr) -> Option<OsString> {
+ unsafe { ENV.as_ref().unwrap().lock().unwrap().get_mut(k).cloned() }
+}
+
+pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
+ unsafe {
+ let (k, v) = (k.to_owned(), v.to_owned());
+ ENV.as_ref().unwrap().lock().unwrap().insert(k, v);
+ }
+ Ok(())
+}
+
+pub fn unsetenv(k: &OsStr) -> io::Result<()> {
+ unsafe {
+ ENV.as_ref().unwrap().lock().unwrap().remove(k);
+ }
+ Ok(())
+}
+
+pub fn temp_dir() -> PathBuf {
+ panic!("no filesystem on hermit")
+}
+
+pub fn home_dir() -> Option<PathBuf> {
+ None
+}
+
+pub fn exit(code: i32) -> ! {
+ unsafe {
+ abi::exit(code);
+ }
+}
+
+pub fn getpid() -> u32 {
+ unsafe { abi::getpid() }
+}
diff --git a/library/std/src/sys/hermit/rwlock.rs b/library/std/src/sys/hermit/rwlock.rs
new file mode 100644
index 000000000..9701bab1f
--- /dev/null
+++ b/library/std/src/sys/hermit/rwlock.rs
@@ -0,0 +1,144 @@
+use crate::cell::UnsafeCell;
+use crate::sys::locks::{MovableCondvar, Mutex};
+use crate::sys_common::lazy_box::{LazyBox, LazyInit};
+
+pub struct RwLock {
+ lock: Mutex,
+ cond: MovableCondvar,
+ state: UnsafeCell<State>,
+}
+
+pub type MovableRwLock = RwLock;
+
+enum State {
+ Unlocked,
+ Reading(usize),
+ Writing,
+}
+
+unsafe impl Send for RwLock {}
+unsafe impl Sync for RwLock {}
+
+// This rwlock implementation is a relatively simple implementation which has a
+// condition variable for readers/writers as well as a mutex protecting the
+// internal state of the lock. A current downside of the implementation is that
+// unlocking the lock will notify *all* waiters rather than just readers or just
+// writers. This can cause lots of "thundering stampede" problems. While
+// hopefully correct this implementation is very likely to want to be changed in
+// the future.
+
+impl RwLock {
+ pub const fn new() -> RwLock {
+ RwLock {
+ lock: Mutex::new(),
+ cond: MovableCondvar::new(),
+ state: UnsafeCell::new(State::Unlocked),
+ }
+ }
+
+ #[inline]
+ pub unsafe fn read(&self) {
+ self.lock.lock();
+ while !(*self.state.get()).inc_readers() {
+ self.cond.wait(&self.lock);
+ }
+ self.lock.unlock();
+ }
+
+ #[inline]
+ pub unsafe fn try_read(&self) -> bool {
+ self.lock.lock();
+ let ok = (*self.state.get()).inc_readers();
+ self.lock.unlock();
+ return ok;
+ }
+
+ #[inline]
+ pub unsafe fn write(&self) {
+ self.lock.lock();
+ while !(*self.state.get()).inc_writers() {
+ self.cond.wait(&self.lock);
+ }
+ self.lock.unlock();
+ }
+
+ #[inline]
+ pub unsafe fn try_write(&self) -> bool {
+ self.lock.lock();
+ let ok = (*self.state.get()).inc_writers();
+ self.lock.unlock();
+ return ok;
+ }
+
+ #[inline]
+ pub unsafe fn read_unlock(&self) {
+ self.lock.lock();
+ let notify = (*self.state.get()).dec_readers();
+ self.lock.unlock();
+ if notify {
+ // FIXME: should only wake up one of these some of the time
+ self.cond.notify_all();
+ }
+ }
+
+ #[inline]
+ pub unsafe fn write_unlock(&self) {
+ self.lock.lock();
+ (*self.state.get()).dec_writers();
+ self.lock.unlock();
+ // FIXME: should only wake up one of these some of the time
+ self.cond.notify_all();
+ }
+}
+
+impl State {
+ fn inc_readers(&mut self) -> bool {
+ match *self {
+ State::Unlocked => {
+ *self = State::Reading(1);
+ true
+ }
+ State::Reading(ref mut cnt) => {
+ *cnt += 1;
+ true
+ }
+ State::Writing => false,
+ }
+ }
+
+ fn inc_writers(&mut self) -> bool {
+ match *self {
+ State::Unlocked => {
+ *self = State::Writing;
+ true
+ }
+ State::Reading(_) | State::Writing => false,
+ }
+ }
+
+ fn dec_readers(&mut self) -> bool {
+ let zero = match *self {
+ State::Reading(ref mut cnt) => {
+ *cnt -= 1;
+ *cnt == 0
+ }
+ State::Unlocked | State::Writing => invalid(),
+ };
+ if zero {
+ *self = State::Unlocked;
+ }
+ zero
+ }
+
+ fn dec_writers(&mut self) {
+ match *self {
+ State::Writing => {}
+ State::Unlocked | State::Reading(_) => invalid(),
+ }
+ *self = State::Unlocked;
+ }
+}
+
+fn invalid() -> ! {
+ panic!("inconsistent rwlock");
+}
diff --git a/library/std/src/sys/hermit/stdio.rs b/library/std/src/sys/hermit/stdio.rs
new file mode 100644
index 000000000..514de1df6
--- /dev/null
+++ b/library/std/src/sys/hermit/stdio.rs
@@ -0,0 +1,120 @@
+use crate::io;
+use crate::io::{IoSlice, IoSliceMut};
+use crate::sys::hermit::abi;
+
+pub struct Stdin;
+pub struct Stdout;
+pub struct Stderr;
+
+impl Stdin {
+ pub const fn new() -> Stdin {
+ 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_vectored(&mut self, _data: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
+ Ok(0)
+ }
+
+ #[inline]
+ fn is_read_vectored(&self) -> bool {
+ true
+ }
+}
+
+impl Stdout {
+ pub const fn new() -> Stdout {
+ Stdout
+ }
+}
+
+impl io::Write for Stdout {
+ fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+ let len;
+
+ unsafe { len = 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, data: &[IoSlice<'_>]) -> io::Result<usize> {
+ let len;
+
+ unsafe { len = 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)
+ }
+ }
+
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl Stderr {
+ pub const fn new() -> Stderr {
+ Stderr
+ }
+}
+
+impl io::Write for Stderr {
+ fn write(&mut self, data: &[u8]) -> io::Result<usize> {
+ let len;
+
+ unsafe { len = 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, data: &[IoSlice<'_>]) -> io::Result<usize> {
+ let len;
+
+ unsafe { len = 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)
+ }
+ }
+
+ #[inline]
+ fn is_write_vectored(&self) -> bool {
+ true
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+pub const STDIN_BUF_SIZE: usize = 0;
+
+pub fn is_ebadf(_err: &io::Error) -> bool {
+ true
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+ Some(Stderr::new())
+}
diff --git a/library/std/src/sys/hermit/thread.rs b/library/std/src/sys/hermit/thread.rs
new file mode 100644
index 000000000..e53a1fea6
--- /dev/null
+++ b/library/std/src/sys/hermit/thread.rs
@@ -0,0 +1,112 @@
+#![allow(dead_code)]
+
+use super::unsupported;
+use crate::ffi::CStr;
+use crate::io;
+use crate::mem;
+use crate::num::NonZeroUsize;
+use crate::sys::hermit::abi;
+use crate::sys::hermit::thread_local_dtor::run_dtors;
+use crate::time::Duration;
+
+pub type Tid = abi::Tid;
+
+pub struct Thread {
+ tid: Tid,
+}
+
+unsafe impl Send for Thread {}
+unsafe impl Sync for Thread {}
+
+pub const DEFAULT_MIN_STACK_SIZE: usize = 1 << 20;
+
+impl Thread {
+ pub unsafe fn new_with_coreid(
+ stack: usize,
+ p: Box<dyn FnOnce()>,
+ core_id: isize,
+ ) -> io::Result<Thread> {
+ let p = Box::into_raw(box p);
+ let tid = abi::spawn2(
+ thread_start,
+ p as usize,
+ abi::Priority::into(abi::NORMAL_PRIO),
+ stack,
+ core_id,
+ );
+
+ return if tid == 0 {
+ // The thread failed to start and as a result p was not consumed. Therefore, it is
+ // safe to reconstruct the box so that it gets deallocated.
+ drop(Box::from_raw(p));
+ Err(io::const_io_error!(io::ErrorKind::Uncategorized, "Unable to create thread!"))
+ } else {
+ Ok(Thread { tid: tid })
+ };
+
+ extern "C" fn thread_start(main: usize) {
+ unsafe {
+ // Finally, let's run some code.
+ Box::from_raw(main as *mut Box<dyn FnOnce()>)();
+
+ // run all destructors
+ run_dtors();
+ }
+ }
+ }
+
+ pub unsafe fn new(stack: usize, p: Box<dyn FnOnce()>) -> io::Result<Thread> {
+ Thread::new_with_coreid(stack, p, -1 /* = no specific core */)
+ }
+
+ #[inline]
+ pub fn yield_now() {
+ unsafe {
+ abi::yield_now();
+ }
+ }
+
+ #[inline]
+ pub fn set_name(_name: &CStr) {
+ // nope
+ }
+
+ #[inline]
+ pub fn sleep(dur: Duration) {
+ unsafe {
+ abi::usleep(dur.as_micros() as u64);
+ }
+ }
+
+ pub fn join(self) {
+ unsafe {
+ let _ = abi::join(self.tid);
+ }
+ }
+
+ #[inline]
+ pub fn id(&self) -> Tid {
+ self.tid
+ }
+
+ #[inline]
+ pub fn into_id(self) -> Tid {
+ let id = self.tid;
+ mem::forget(self);
+ id
+ }
+}
+
+pub fn available_parallelism() -> io::Result<NonZeroUsize> {
+ unsupported()
+}
+
+pub mod guard {
+ pub type Guard = !;
+ pub unsafe fn current() -> Option<Guard> {
+ None
+ }
+ pub unsafe fn init() -> Option<Guard> {
+ None
+ }
+}
diff --git a/library/std/src/sys/hermit/thread_local_dtor.rs b/library/std/src/sys/hermit/thread_local_dtor.rs
new file mode 100644
index 000000000..9b683fce1
--- /dev/null
+++ b/library/std/src/sys/hermit/thread_local_dtor.rs
@@ -0,0 +1,36 @@
+#![cfg(target_thread_local)]
+#![unstable(feature = "thread_local_internals", issue = "none")]
+
+// Simplify dtor registration by using a list of destructors.
+// The this solution works like the implementation of macOS and
+// doesn't additional OS support
+
+use crate::cell::Cell;
+use crate::ptr;
+
+#[thread_local]
+static DTORS: Cell<*mut List> = Cell::new(ptr::null_mut());
+
+type List = Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>;
+
+pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
+ if DTORS.get().is_null() {
+ let v: Box<List> = box Vec::new();
+ DTORS.set(Box::into_raw(v));
+ }
+
+ let list: &mut List = &mut *DTORS.get();
+ list.push((t, dtor));
+}
+
+// every thread call this function to run through all possible destructors
+pub unsafe fn run_dtors() {
+ let mut ptr = DTORS.replace(ptr::null_mut());
+ while !ptr.is_null() {
+ let list = Box::from_raw(ptr);
+ for (ptr, dtor) in list.into_iter() {
+ dtor(ptr);
+ }
+ ptr = DTORS.replace(ptr::null_mut());
+ }
+}
diff --git a/library/std/src/sys/hermit/time.rs b/library/std/src/sys/hermit/time.rs
new file mode 100644
index 000000000..c17e6c8af
--- /dev/null
+++ b/library/std/src/sys/hermit/time.rs
@@ -0,0 +1,156 @@
+#![allow(dead_code)]
+
+use crate::cmp::Ordering;
+use crate::sys::hermit::abi;
+use crate::sys::hermit::abi::timespec;
+use crate::sys::hermit::abi::{CLOCK_MONOTONIC, CLOCK_REALTIME, NSEC_PER_SEC};
+use crate::time::Duration;
+use core::hash::{Hash, Hasher};
+
+#[derive(Copy, Clone, Debug)]
+struct Timespec {
+ t: timespec,
+}
+
+impl Timespec {
+ const fn zero() -> Timespec {
+ Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } }
+ }
+
+ fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
+ if self >= other {
+ Ok(if self.t.tv_nsec >= other.t.tv_nsec {
+ Duration::new(
+ (self.t.tv_sec - other.t.tv_sec) as u64,
+ (self.t.tv_nsec - other.t.tv_nsec) as u32,
+ )
+ } 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,
+ )
+ })
+ } else {
+ match other.sub_timespec(self) {
+ Ok(d) => Err(d),
+ Err(d) => Ok(d),
+ }
+ }
+ }
+
+ fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
+ let mut secs = other
+ .as_secs()
+ .try_into() // <- target type would be `libc::time_t`
+ .ok()
+ .and_then(|secs| self.t.tv_sec.checked_add(secs))?;
+
+ // 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;
+ secs = secs.checked_add(1)?;
+ }
+ Some(Timespec { t: timespec { tv_sec: secs, tv_nsec: nsec as _ } })
+ }
+
+ fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
+ let mut secs = other
+ .as_secs()
+ .try_into() // <- target type would be `libc::time_t`
+ .ok()
+ .and_then(|secs| self.t.tv_sec.checked_sub(secs))?;
+
+ // Similar to above, nanos can't overflow.
+ let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32;
+ if nsec < 0 {
+ nsec += NSEC_PER_SEC as i32;
+ secs = secs.checked_sub(1)?;
+ }
+ Some(Timespec { t: timespec { tv_sec: secs, tv_nsec: nsec as _ } })
+ }
+}
+
+impl PartialEq for Timespec {
+ fn eq(&self, other: &Timespec) -> bool {
+ self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec
+ }
+}
+
+impl Eq for Timespec {}
+
+impl PartialOrd for Timespec {
+ fn partial_cmp(&self, other: &Timespec) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Timespec {
+ fn cmp(&self, other: &Timespec) -> Ordering {
+ let me = (self.t.tv_sec, self.t.tv_nsec);
+ let other = (other.t.tv_sec, other.t.tv_nsec);
+ me.cmp(&other)
+ }
+}
+
+impl Hash for Timespec {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.t.tv_sec.hash(state);
+ self.t.tv_nsec.hash(state);
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct Instant {
+ t: Timespec,
+}
+
+impl Instant {
+ pub fn now() -> Instant {
+ let mut time: Timespec = Timespec::zero();
+ let _ = unsafe { abi::clock_gettime(CLOCK_MONOTONIC, &mut time.t as *mut timespec) };
+
+ Instant { t: time }
+ }
+
+ pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+ self.t.sub_timespec(&other.t).ok()
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant { t: self.t.checked_add_duration(other)? })
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant { t: self.t.checked_sub_duration(other)? })
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+pub struct SystemTime {
+ t: Timespec,
+}
+
+pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
+
+impl SystemTime {
+ pub fn now() -> SystemTime {
+ let mut time: Timespec = Timespec::zero();
+ let _ = unsafe { abi::clock_gettime(CLOCK_REALTIME, &mut time.t as *mut timespec) };
+
+ SystemTime { t: time }
+ }
+
+ pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
+ self.t.sub_timespec(&other.t)
+ }
+
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime { t: self.t.checked_add_duration(other)? })
+ }
+
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+ Some(SystemTime { t: self.t.checked_sub_duration(other)? })
+ }
+}