summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys/unix
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/sys/unix')
-rw-r--r--library/std/src/sys/unix/args.rs1
-rw-r--r--library/std/src/sys/unix/env.rs11
-rw-r--r--library/std/src/sys/unix/fd.rs24
-rw-r--r--library/std/src/sys/unix/fs.rs59
-rw-r--r--library/std/src/sys/unix/mod.rs3
-rw-r--r--library/std/src/sys/unix/net.rs21
-rw-r--r--library/std/src/sys/unix/os.rs36
-rw-r--r--library/std/src/sys/unix/process/mod.rs9
-rw-r--r--library/std/src/sys/unix/process/process_common.rs1
-rw-r--r--library/std/src/sys/unix/process/process_common/tests.rs33
-rw-r--r--library/std/src/sys/unix/process/process_unix.rs5
-rw-r--r--library/std/src/sys/unix/process/process_unsupported.rs56
-rw-r--r--library/std/src/sys/unix/process/process_unsupported/wait_status.rs84
-rw-r--r--library/std/src/sys/unix/process/process_unsupported/wait_status/tests.rs36
-rw-r--r--library/std/src/sys/unix/rand.rs125
-rw-r--r--library/std/src/sys/unix/stack_overflow.rs14
-rw-r--r--library/std/src/sys/unix/thread.rs6
-rw-r--r--library/std/src/sys/unix/thread_local_dtor.rs67
-rw-r--r--library/std/src/sys/unix/time.rs298
19 files changed, 530 insertions, 359 deletions
diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs
index 19334e2af..2da17fabc 100644
--- a/library/std/src/sys/unix/args.rs
+++ b/library/std/src/sys/unix/args.rs
@@ -70,6 +70,7 @@ impl DoubleEndedIterator for Args {
target_os = "redox",
target_os = "vxworks",
target_os = "horizon",
+ target_os = "aix",
target_os = "nto",
target_os = "hurd",
))]
diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs
index c6d8578a6..3bb492fa9 100644
--- a/library/std/src/sys/unix/env.rs
+++ b/library/std/src/sys/unix/env.rs
@@ -261,3 +261,14 @@ pub mod os {
pub const EXE_SUFFIX: &str = "";
pub const EXE_EXTENSION: &str = "";
}
+
+#[cfg(target_os = "aix")]
+pub mod os {
+ pub const FAMILY: &str = "unix";
+ pub const OS: &str = "aix";
+ pub const DLL_PREFIX: &str = "lib";
+ pub const DLL_SUFFIX: &str = ".a";
+ pub const DLL_EXTENSION: &str = "a";
+ pub const EXE_SUFFIX: &str = "";
+ pub const EXE_EXTENSION: &str = "";
+}
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
index 6c4f40842..bf1fb3123 100644
--- a/library/std/src/sys/unix/fd.rs
+++ b/library/std/src/sys/unix/fd.rs
@@ -126,9 +126,17 @@ impl FileDesc {
}
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
- #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "hurd")))]
+ #[cfg(not(any(
+ all(target_os = "linux", not(target_env = "musl")),
+ target_os = "android",
+ target_os = "hurd"
+ )))]
use libc::pread as pread64;
- #[cfg(any(target_os = "linux", target_os = "android", target_os = "hurd"))]
+ #[cfg(any(
+ all(target_os = "linux", not(target_env = "musl")),
+ target_os = "android",
+ target_os = "hurd"
+ ))]
use libc::pread64;
unsafe {
@@ -285,9 +293,17 @@ impl FileDesc {
}
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
- #[cfg(not(any(target_os = "linux", target_os = "android", target_os = "hurd")))]
+ #[cfg(not(any(
+ all(target_os = "linux", not(target_env = "musl")),
+ target_os = "android",
+ target_os = "hurd"
+ )))]
use libc::pwrite as pwrite64;
- #[cfg(any(target_os = "linux", target_os = "android", target_os = "hurd"))]
+ #[cfg(any(
+ all(target_os = "linux", not(target_env = "musl")),
+ target_os = "android",
+ target_os = "hurd"
+ ))]
use libc::pwrite64;
unsafe {
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index 764e1f257..40eb910fd 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -40,13 +40,17 @@ use libc::{c_int, mode_t};
))]
use libc::c_char;
#[cfg(any(
- target_os = "linux",
+ all(target_os = "linux", not(target_env = "musl")),
target_os = "emscripten",
target_os = "android",
- target_os = "hurd",
+ target_os = "hurd"
))]
use libc::dirfd;
-#[cfg(any(target_os = "linux", target_os = "emscripten", target_os = "hurd"))]
+#[cfg(any(
+ all(target_os = "linux", not(target_env = "musl")),
+ target_os = "emscripten",
+ target_os = "hurd"
+))]
use libc::fstatat64;
#[cfg(any(
target_os = "android",
@@ -54,11 +58,13 @@ use libc::fstatat64;
target_os = "fuchsia",
target_os = "redox",
target_os = "illumos",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
+ all(target_os = "linux", target_env = "musl"),
))]
use libc::readdir as readdir64;
-#[cfg(any(target_os = "linux", target_os = "hurd"))]
+#[cfg(any(all(target_os = "linux", not(target_env = "musl")), target_os = "hurd"))]
use libc::readdir64;
#[cfg(any(target_os = "emscripten", target_os = "l4re"))]
use libc::readdir64_r;
@@ -71,6 +77,7 @@ use libc::readdir64_r;
target_os = "l4re",
target_os = "fuchsia",
target_os = "redox",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
@@ -82,7 +89,7 @@ use libc::{
lstat as lstat64, off64_t, open as open64, stat as stat64,
};
#[cfg(not(any(
- target_os = "linux",
+ all(target_os = "linux", not(target_env = "musl")),
target_os = "emscripten",
target_os = "l4re",
target_os = "android",
@@ -93,7 +100,7 @@ use libc::{
lstat as lstat64, off_t as off64_t, open as open64, stat as stat64,
};
#[cfg(any(
- target_os = "linux",
+ all(target_os = "linux", not(target_env = "musl")),
target_os = "emscripten",
target_os = "l4re",
target_os = "hurd"
@@ -288,6 +295,7 @@ unsafe impl Sync for Dir {}
target_os = "illumos",
target_os = "fuchsia",
target_os = "redox",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
@@ -311,6 +319,7 @@ pub struct DirEntry {
target_os = "illumos",
target_os = "fuchsia",
target_os = "redox",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
@@ -320,8 +329,9 @@ struct dirent64_min {
#[cfg(not(any(
target_os = "solaris",
target_os = "illumos",
+ target_os = "aix",
target_os = "nto",
- target_os = "vita"
+ target_os = "vita",
)))]
d_type: u8,
}
@@ -333,6 +343,7 @@ struct dirent64_min {
target_os = "illumos",
target_os = "fuchsia",
target_os = "redox",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
@@ -464,7 +475,22 @@ impl FileAttr {
}
}
-#[cfg(not(any(target_os = "netbsd", target_os = "nto")))]
+#[cfg(target_os = "aix")]
+impl FileAttr {
+ pub fn modified(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::new(self.stat.st_mtime.tv_sec as i64, self.stat.st_mtime.tv_nsec as i64))
+ }
+
+ pub fn accessed(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::new(self.stat.st_atime.tv_sec as i64, self.stat.st_atime.tv_nsec as i64))
+ }
+
+ pub fn created(&self) -> io::Result<SystemTime> {
+ Ok(SystemTime::new(self.stat.st_ctime.tv_sec as i64, self.stat.st_ctime.tv_nsec as i64))
+ }
+}
+
+#[cfg(not(any(target_os = "netbsd", target_os = "nto", target_os = "aix")))]
impl FileAttr {
#[cfg(not(any(
target_os = "vxworks",
@@ -671,6 +697,7 @@ impl Iterator for ReadDir {
target_os = "fuchsia",
target_os = "redox",
target_os = "illumos",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
@@ -748,6 +775,7 @@ impl Iterator for ReadDir {
#[cfg(not(any(
target_os = "solaris",
target_os = "illumos",
+ target_os = "aix",
target_os = "nto",
)))]
d_type: *offset_ptr!(entry_ptr, d_type) as u8,
@@ -772,6 +800,7 @@ impl Iterator for ReadDir {
target_os = "fuchsia",
target_os = "redox",
target_os = "illumos",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
@@ -829,10 +858,10 @@ impl DirEntry {
#[cfg(all(
any(
- target_os = "linux",
+ all(target_os = "linux", not(target_env = "musl")),
target_os = "emscripten",
target_os = "android",
- target_os = "hurd",
+ target_os = "hurd"
),
not(miri)
))]
@@ -858,7 +887,7 @@ impl DirEntry {
#[cfg(any(
not(any(
- target_os = "linux",
+ all(target_os = "linux", not(target_env = "musl")),
target_os = "emscripten",
target_os = "android",
target_os = "hurd",
@@ -874,6 +903,7 @@ impl DirEntry {
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
))]
@@ -886,6 +916,7 @@ impl DirEntry {
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
)))]
@@ -920,6 +951,7 @@ impl DirEntry {
target_os = "espidf",
target_os = "horizon",
target_os = "vita",
+ target_os = "aix",
target_os = "nto",
target_os = "hurd",
))]
@@ -977,6 +1009,7 @@ impl DirEntry {
target_os = "illumos",
target_os = "fuchsia",
target_os = "redox",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
@@ -991,6 +1024,7 @@ impl DirEntry {
target_os = "illumos",
target_os = "fuchsia",
target_os = "redox",
+ target_os = "aix",
target_os = "nto",
target_os = "vita",
target_os = "hurd",
@@ -1391,6 +1425,7 @@ impl FromInner<FileDesc> for File {
}
impl AsFd for File {
+ #[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.0.as_fd()
}
@@ -2025,6 +2060,7 @@ mod remove_dir_impl {
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks",
+ target_os = "aix",
))]
fn is_dir(_ent: &DirEntry) -> Option<bool> {
None
@@ -2035,6 +2071,7 @@ mod remove_dir_impl {
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks",
+ target_os = "aix",
)))]
fn is_dir(ent: &DirEntry) -> Option<bool> {
match ent.entry.d_type {
diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs
index 3edafde71..4b28f6feb 100644
--- a/library/std/src/sys/unix/mod.rs
+++ b/library/std/src/sys/unix/mod.rs
@@ -241,6 +241,7 @@ pub unsafe fn cleanup() {
#[cfg(target_os = "android")]
pub use crate::sys::android::signal;
+#[allow(unused_imports)]
#[cfg(not(target_os = "android"))]
pub use libc::signal;
@@ -278,6 +279,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
libc::ENETUNREACH => NetworkUnreachable,
libc::ENOTCONN => NotConnected,
libc::ENOTDIR => NotADirectory,
+ #[cfg(not(target_os = "aix"))]
libc::ENOTEMPTY => DirectoryNotEmpty,
libc::EPIPE => BrokenPipe,
libc::EROFS => ReadOnlyFilesystem,
@@ -413,7 +415,6 @@ cfg_if::cfg_if! {
} else if #[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))] {
#[link(name = "System")]
#[link(name = "objc")]
- #[link(name = "Security", kind = "framework")]
#[link(name = "Foundation", kind = "framework")]
extern "C" {}
} else if #[cfg(target_os = "fuchsia")] {
diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs
index f450d708d..ec861f9cb 100644
--- a/library/std/src/sys/unix/net.rs
+++ b/library/std/src/sys/unix/net.rs
@@ -6,6 +6,7 @@ use crate::net::{Shutdown, SocketAddr};
use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
use crate::str;
use crate::sys::fd::FileDesc;
+use crate::sys::unix::IsMinusOne;
use crate::sys_common::net::{getsockopt, setsockopt, sockaddr_to_addr};
use crate::sys_common::{AsInner, FromInner, IntoInner};
use crate::time::{Duration, Instant};
@@ -103,7 +104,7 @@ impl Socket {
}
}
- #[cfg(not(any(target_os = "vxworks", target_os = "vita")))]
+ #[cfg(not(target_os = "vxworks"))]
pub fn new_pair(fam: c_int, ty: c_int) -> io::Result<(Socket, Socket)> {
unsafe {
let mut fds = [0, 0];
@@ -135,11 +136,27 @@ impl Socket {
}
}
- #[cfg(any(target_os = "vxworks", target_os = "vita"))]
+ #[cfg(target_os = "vxworks")]
pub fn new_pair(_fam: c_int, _ty: c_int) -> io::Result<(Socket, Socket)> {
unimplemented!()
}
+ pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> {
+ let (addr, len) = addr.into_inner();
+ loop {
+ let result = unsafe { libc::connect(self.as_raw_fd(), addr.as_ptr(), len) };
+ if result.is_minus_one() {
+ let err = crate::sys::os::errno();
+ match err {
+ libc::EINTR => continue,
+ libc::EISCONN => return Ok(()),
+ _ => return Err(io::Error::from_raw_os_error(err)),
+ }
+ }
+ return Ok(());
+ }
+ }
+
pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> {
self.set_nonblocking(true)?;
let r = unsafe {
diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs
index 01ff375d2..dc3c037c0 100644
--- a/library/std/src/sys/unix/os.rs
+++ b/library/std/src/sys/unix/os.rs
@@ -74,6 +74,7 @@ extern "C" {
link_name = "__error"
)]
#[cfg_attr(target_os = "haiku", link_name = "_errnop")]
+ #[cfg_attr(target_os = "aix", link_name = "_Errno")]
fn errno_location() -> *mut c_int;
}
@@ -254,6 +255,41 @@ impl StdError for JoinPathsError {
}
}
+#[cfg(target_os = "aix")]
+pub fn current_exe() -> io::Result<PathBuf> {
+ use crate::io::ErrorKind;
+
+ #[cfg(test)]
+ use realstd::env;
+
+ #[cfg(not(test))]
+ use crate::env;
+
+ let exe_path = env::args().next().ok_or(io::const_io_error!(
+ ErrorKind::NotFound,
+ "an executable path was not found because no arguments were provided through argv"
+ ))?;
+ let path = PathBuf::from(exe_path);
+ if path.is_absolute() {
+ return path.canonicalize();
+ }
+ // Search PWD to infer current_exe.
+ if let Some(pstr) = path.to_str() && pstr.contains("/") {
+ return getcwd().map(|cwd| cwd.join(path))?.canonicalize();
+ }
+ // Search PATH to infer current_exe.
+ if let Some(p) = getenv(OsStr::from_bytes("PATH".as_bytes())) {
+ for search_path in split_paths(&p) {
+ let pb = search_path.join(&path);
+ if pb.is_file() && let Ok(metadata) = crate::fs::metadata(&pb) &&
+ metadata.permissions().mode() & 0o111 != 0 {
+ return pb.canonicalize();
+ }
+ }
+ }
+ Err(io::const_io_error!(ErrorKind::NotFound, "an executable path was not found"))
+}
+
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
pub fn current_exe() -> io::Result<PathBuf> {
unsafe {
diff --git a/library/std/src/sys/unix/process/mod.rs b/library/std/src/sys/unix/process/mod.rs
index 0cf163d9f..074f0a105 100644
--- a/library/std/src/sys/unix/process/mod.rs
+++ b/library/std/src/sys/unix/process/mod.rs
@@ -1,11 +1,13 @@
pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes};
pub use self::process_inner::{ExitStatus, ExitStatusError, Process};
pub use crate::ffi::OsString as EnvKey;
-pub use crate::sys_common::process::CommandEnvs;
#[cfg_attr(any(target_os = "espidf", target_os = "horizon"), allow(unused))]
mod process_common;
+#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))]
+mod process_unsupported;
+
cfg_if::cfg_if! {
if #[cfg(target_os = "fuchsia")] {
#[path = "process_fuchsia.rs"]
@@ -15,8 +17,9 @@ cfg_if::cfg_if! {
#[path = "process_vxworks.rs"]
mod process_inner;
} else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] {
- #[path = "process_unsupported.rs"]
- mod process_inner;
+ mod process_inner {
+ pub use super::process_unsupported::*;
+ }
} else {
#[path = "process_unix.rs"]
mod process_inner;
diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs
index 1ca11a7f9..bac32d9e6 100644
--- a/library/std/src/sys/unix/process/process_common.rs
+++ b/library/std/src/sys/unix/process/process_common.rs
@@ -75,6 +75,7 @@ cfg_if::cfg_if! {
return 0;
}
} else {
+ #[allow(unused_imports)]
pub use libc::{sigemptyset, sigaddset};
}
}
diff --git a/library/std/src/sys/unix/process/process_common/tests.rs b/library/std/src/sys/unix/process/process_common/tests.rs
index 03631e4e3..4e41efc90 100644
--- a/library/std/src/sys/unix/process/process_common/tests.rs
+++ b/library/std/src/sys/unix/process/process_common/tests.rs
@@ -159,3 +159,36 @@ fn test_program_kind() {
);
}
}
+
+// Test that Rust std handles wait status values (`ExitStatus`) the way that Unix does,
+// at least for the values which represent a Unix exit status (`ExitCode`).
+// Should work on every #[cfg(unix)] platform. However:
+#[cfg(not(any(
+ // Fuchsia is not Unix and has totally broken std::os::unix.
+ // https://github.com/rust-lang/rust/issues/58590#issuecomment-836535609
+ target_os = "fuchsia",
+)))]
+#[test]
+fn unix_exit_statuses() {
+ use crate::num::NonZeroI32;
+ use crate::os::unix::process::ExitStatusExt;
+ use crate::process::*;
+
+ for exit_code in 0..=0xff {
+ // FIXME impl From<ExitCode> for ExitStatus and then test that here too;
+ // the two ExitStatus values should be the same
+ let raw_wait_status = exit_code << 8;
+ let exit_status = ExitStatus::from_raw(raw_wait_status);
+
+ assert_eq!(exit_status.code(), Some(exit_code));
+
+ if let Ok(nz) = NonZeroI32::try_from(exit_code) {
+ assert!(!exit_status.success());
+ let es_error = exit_status.exit_ok().unwrap_err();
+ assert_eq!(es_error.code().unwrap(), i32::from(nz));
+ } else {
+ assert!(exit_status.success());
+ assert_eq!(exit_status.exit_ok(), Ok(()));
+ }
+ }
+}
diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
index 564f8c482..72aca4e66 100644
--- a/library/std/src/sys/unix/process/process_unix.rs
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -1074,3 +1074,8 @@ impl crate::os::linux::process::ChildExt for crate::process::Child {
#[cfg(test)]
#[path = "process_unix/tests.rs"]
mod tests;
+
+// See [`process_unsupported_wait_status::compare_with_linux`];
+#[cfg(all(test, target_os = "linux"))]
+#[path = "process_unsupported/wait_status.rs"]
+mod process_unsupported_wait_status;
diff --git a/library/std/src/sys/unix/process/process_unsupported.rs b/library/std/src/sys/unix/process/process_unsupported.rs
index 8e0b971af..2fbb31922 100644
--- a/library/std/src/sys/unix/process/process_unsupported.rs
+++ b/library/std/src/sys/unix/process/process_unsupported.rs
@@ -55,68 +55,20 @@ impl Process {
}
}
-#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)]
-pub struct ExitStatus(c_int);
-
-impl ExitStatus {
- #[cfg_attr(target_os = "horizon", allow(unused))]
- pub fn success(&self) -> bool {
- self.code() == Some(0)
- }
-
- pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
- Err(ExitStatusError(1.try_into().unwrap()))
- }
-
- pub fn code(&self) -> Option<i32> {
- None
- }
-
- pub fn signal(&self) -> Option<i32> {
- None
- }
-
- pub fn core_dumped(&self) -> bool {
- false
- }
-
- pub fn stopped_signal(&self) -> Option<i32> {
- None
- }
-
- pub fn continued(&self) -> bool {
- false
- }
-
- pub fn into_raw(&self) -> c_int {
- 0
- }
-}
-
-/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it without copying.
-impl From<c_int> for ExitStatus {
- fn from(a: c_int) -> ExitStatus {
- ExitStatus(a as i32)
- }
-}
-
-impl fmt::Display for ExitStatus {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "exit code: {}", self.0)
- }
-}
+mod wait_status;
+pub use wait_status::ExitStatus;
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct ExitStatusError(NonZero_c_int);
impl Into<ExitStatus> for ExitStatusError {
fn into(self) -> ExitStatus {
- ExitStatus(self.0.into())
+ ExitStatus::from(c_int::from(self.0))
}
}
impl ExitStatusError {
pub fn code(self) -> Option<NonZeroI32> {
- ExitStatus(self.0.into()).code().map(|st| st.try_into().unwrap())
+ ExitStatus::from(c_int::from(self.0)).code().map(|st| st.try_into().unwrap())
}
}
diff --git a/library/std/src/sys/unix/process/process_unsupported/wait_status.rs b/library/std/src/sys/unix/process/process_unsupported/wait_status.rs
new file mode 100644
index 000000000..72b7ae18c
--- /dev/null
+++ b/library/std/src/sys/unix/process/process_unsupported/wait_status.rs
@@ -0,0 +1,84 @@
+//! Emulated wait status for non-Unix #[cfg(unix) platforms
+//!
+//! Separate module to facilitate testing against a real Unix implementation.
+use core::ffi::NonZero_c_int;
+
+use crate::ffi::c_int;
+use crate::fmt;
+
+use super::ExitStatusError;
+
+/// Emulated wait status for use by `process_unsupported.rs`
+///
+/// Uses the "traditional unix" encoding. For use on platfors which are `#[cfg(unix)]`
+/// but do not actually support subprocesses at all.
+///
+/// These platforms aren't Unix, but are simply pretending to be for porting convenience.
+/// So, we provide a faithful pretence here.
+#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)]
+pub struct ExitStatus {
+ wait_status: c_int,
+}
+
+/// Converts a raw `c_int` to a type-safe `ExitStatus` by wrapping it
+impl From<c_int> for ExitStatus {
+ fn from(wait_status: c_int) -> ExitStatus {
+ ExitStatus { wait_status }
+ }
+}
+
+impl fmt::Display for ExitStatus {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "emulated wait status: {}", self.wait_status)
+ }
+}
+
+impl ExitStatus {
+ pub fn code(&self) -> Option<i32> {
+ // Linux and FreeBSD both agree that values linux 0x80
+ // count as "WIFEXITED" even though this is quite mad.
+ // Likewise the macros disregard all the high bits, so are happy to declare
+ // out-of-range values to be WIFEXITED, WIFSTOPPED, etc.
+ let w = self.wait_status;
+ if (w & 0x7f) == 0 { Some((w & 0xff00) >> 8) } else { None }
+ }
+
+ #[allow(unused)]
+ pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
+ // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
+ // true on all actual versions of Unix, is widely assumed, and is specified in SuS
+ // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not
+ // true for a platform pretending to be Unix, the tests (our doctests, and also
+ // process_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
+ match NonZero_c_int::try_from(self.wait_status) {
+ /* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
+ /* was zero, couldn't convert */ Err(_) => Ok(()),
+ }
+ }
+
+ pub fn signal(&self) -> Option<i32> {
+ let signal = self.wait_status & 0x007f;
+ if signal > 0 && signal < 0x7f { Some(signal) } else { None }
+ }
+
+ pub fn core_dumped(&self) -> bool {
+ self.signal().is_some() && (self.wait_status & 0x80) != 0
+ }
+
+ pub fn stopped_signal(&self) -> Option<i32> {
+ let w = self.wait_status;
+ if (w & 0xff) == 0x7f { Some((w & 0xff00) >> 8) } else { None }
+ }
+
+ pub fn continued(&self) -> bool {
+ self.wait_status == 0xffff
+ }
+
+ pub fn into_raw(&self) -> c_int {
+ self.wait_status
+ }
+}
+
+#[cfg(test)]
+#[path = "wait_status/tests.rs"] // needed because of strange layout of process_unsupported
+mod tests;
diff --git a/library/std/src/sys/unix/process/process_unsupported/wait_status/tests.rs b/library/std/src/sys/unix/process/process_unsupported/wait_status/tests.rs
new file mode 100644
index 000000000..5132eab10
--- /dev/null
+++ b/library/std/src/sys/unix/process/process_unsupported/wait_status/tests.rs
@@ -0,0 +1,36 @@
+// Note that tests in this file are run on Linux as well as on platforms using process_unsupported
+
+// Test that our emulation exactly matches Linux
+//
+// This test runs *on Linux* but it tests
+// the implementation used on non-Unix `#[cfg(unix)]` platforms.
+//
+// I.e. we're using Linux as a proxy for "trad unix".
+#[cfg(target_os = "linux")]
+#[test]
+fn compare_with_linux() {
+ use super::ExitStatus as Emulated;
+ use crate::os::unix::process::ExitStatusExt as _;
+ use crate::process::ExitStatus as Real;
+
+ // Check that we handle out-of-range values similarly, too.
+ for wstatus in -0xf_ffff..0xf_ffff {
+ let emulated = Emulated::from(wstatus);
+ let real = Real::from_raw(wstatus);
+
+ macro_rules! compare { { $method:ident } => {
+ assert_eq!(
+ emulated.$method(),
+ real.$method(),
+ "{wstatus:#x}.{}()",
+ stringify!($method),
+ );
+ } }
+ compare!(code);
+ compare!(signal);
+ compare!(core_dumped);
+ compare!(stopped_signal);
+ compare!(continued);
+ compare!(into_raw);
+ }
+}
diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs
index fbf158f56..2825d1677 100644
--- a/library/std/src/sys/unix/rand.rs
+++ b/library/std/src/sys/unix/rand.rs
@@ -62,18 +62,15 @@ mod imp {
unsafe { getrandom(buf.as_mut_ptr().cast(), buf.len(), libc::GRND_NONBLOCK) }
}
- #[cfg(any(target_os = "espidf", target_os = "horizon"))]
+ #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "freebsd"))]
fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
- unsafe { libc::getrandom(buf.as_mut_ptr().cast(), buf.len(), 0) }
- }
-
- #[cfg(target_os = "freebsd")]
- fn getrandom(buf: &mut [u8]) -> libc::ssize_t {
- // FIXME: using the above when libary std's libc is updated
+ #[cfg(not(target_os = "freebsd"))]
+ use libc::getrandom;
+ #[cfg(target_os = "freebsd")]
extern "C" {
fn getrandom(
- buffer: *mut libc::c_void,
- length: libc::size_t,
+ buf: *mut libc::c_void,
+ buflen: libc::size_t,
flags: libc::c_uint,
) -> libc::ssize_t;
}
@@ -154,40 +151,65 @@ mod imp {
}
}
-#[cfg(target_os = "macos")]
+#[cfg(target_vendor = "apple")]
mod imp {
- use crate::fs::File;
- use crate::io::Read;
- use crate::sys::os::errno;
- use crate::sys::weak::weak;
+ use crate::io;
use libc::{c_int, c_void, size_t};
- fn getentropy_fill_bytes(v: &mut [u8]) -> bool {
- weak!(fn getentropy(*mut c_void, size_t) -> c_int);
-
- getentropy
- .get()
- .map(|f| {
- // getentropy(2) permits a maximum buffer size of 256 bytes
- for s in v.chunks_mut(256) {
- let ret = unsafe { f(s.as_mut_ptr() as *mut c_void, s.len()) };
- if ret == -1 {
- panic!("unexpected getentropy error: {}", errno());
- }
- }
- true
- })
- .unwrap_or(false)
+ #[inline(always)]
+ fn random_failure() -> ! {
+ panic!("unexpected random generation error: {}", io::Error::last_os_error());
}
- pub fn fill_bytes(v: &mut [u8]) {
- if getentropy_fill_bytes(v) {
- return;
+ #[cfg(target_os = "macos")]
+ fn getentropy_fill_bytes(v: &mut [u8]) {
+ extern "C" {
+ fn getentropy(bytes: *mut c_void, count: size_t) -> c_int;
}
- // for older macos which doesn't support getentropy
- let mut file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
- file.read_exact(v).expect("failed to read /dev/urandom")
+ // getentropy(2) permits a maximum buffer size of 256 bytes
+ for s in v.chunks_mut(256) {
+ let ret = unsafe { getentropy(s.as_mut_ptr().cast(), s.len()) };
+ if ret == -1 {
+ random_failure()
+ }
+ }
+ }
+
+ #[cfg(not(target_os = "macos"))]
+ fn ccrandom_fill_bytes(v: &mut [u8]) {
+ extern "C" {
+ fn CCRandomGenerateBytes(bytes: *mut c_void, count: size_t) -> c_int;
+ }
+
+ let ret = unsafe { CCRandomGenerateBytes(v.as_mut_ptr().cast(), v.len()) };
+ if ret == -1 {
+ random_failure()
+ }
+ }
+
+ pub fn fill_bytes(v: &mut [u8]) {
+ // All supported versions of macOS (10.12+) support getentropy.
+ //
+ // `getentropy` is measurably faster (via Divan) then the other alternatives so its preferred
+ // when usable.
+ #[cfg(target_os = "macos")]
+ getentropy_fill_bytes(v);
+
+ // On Apple platforms, `CCRandomGenerateBytes` and `SecRandomCopyBytes` simply
+ // call into `CCRandomCopyBytes` with `kCCRandomDefault`. `CCRandomCopyBytes`
+ // manages a CSPRNG which is seeded from the kernel's CSPRNG and which runs on
+ // its own thread accessed via GCD. This seems needlessly heavyweight for our purposes
+ // so we only use it on non-Mac OSes where the better entrypoints are blocked.
+ //
+ // `CCRandomGenerateBytes` is used instead of `SecRandomCopyBytes` because the former is accessible
+ // via `libSystem` (libc) while the other needs to link to `Security.framework`.
+ //
+ // Note that while `getentropy` has a available attribute in the macOS headers, the lack
+ // of a header in the iOS (and others) SDK means that its can cause app store rejections.
+ // Just use `CCRandomGenerateBytes` instead.
+ #[cfg(not(target_os = "macos"))]
+ ccrandom_fill_bytes(v);
}
}
@@ -206,36 +228,7 @@ mod imp {
}
}
-// On iOS and MacOS `SecRandomCopyBytes` calls `CCRandomCopyBytes` with
-// `kCCRandomDefault`. `CCRandomCopyBytes` manages a CSPRNG which is seeded
-// from `/dev/random` and which runs on its own thread accessed via GCD.
-// This seems needlessly heavyweight for the purposes of generating two u64s
-// once per thread in `hashmap_random_keys`. Therefore `SecRandomCopyBytes` is
-// only used on iOS where direct access to `/dev/urandom` is blocked by the
-// sandbox.
-#[cfg(any(target_os = "ios", target_os = "tvos", target_os = "watchos"))]
-mod imp {
- use crate::io;
- use crate::ptr;
- use libc::{c_int, size_t};
-
- enum SecRandom {}
-
- #[allow(non_upper_case_globals)]
- const kSecRandomDefault: *const SecRandom = ptr::null();
-
- extern "C" {
- fn SecRandomCopyBytes(rnd: *const SecRandom, count: size_t, bytes: *mut u8) -> c_int;
- }
-
- pub fn fill_bytes(v: &mut [u8]) {
- let ret = unsafe { SecRandomCopyBytes(kSecRandomDefault, v.len(), v.as_mut_ptr()) };
- if ret == -1 {
- panic!("couldn't generate random bytes: {}", io::Error::last_os_error());
- }
- }
-}
-
+// FIXME: once the 10.x release becomes the minimum, this can be dropped for simplification.
#[cfg(target_os = "netbsd")]
mod imp {
use crate::ptr;
diff --git a/library/std/src/sys/unix/stack_overflow.rs b/library/std/src/sys/unix/stack_overflow.rs
index 73c530786..3dbab4cc4 100644
--- a/library/std/src/sys/unix/stack_overflow.rs
+++ b/library/std/src/sys/unix/stack_overflow.rs
@@ -134,9 +134,19 @@ mod imp {
// OpenBSD requires this flag for stack mapping
// otherwise the said mapping will fail as a no-op on most systems
// and has a different meaning on FreeBSD
- #[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux",))]
+ #[cfg(any(
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "linux",
+ target_os = "dragonfly",
+ ))]
let flags = MAP_PRIVATE | MAP_ANON | libc::MAP_STACK;
- #[cfg(not(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux",)))]
+ #[cfg(not(any(
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "linux",
+ target_os = "dragonfly",
+ )))]
let flags = MAP_PRIVATE | MAP_ANON;
let stackp =
mmap64(ptr::null_mut(), SIGSTKSZ + page_size(), PROT_READ | PROT_WRITE, flags, -1, 0);
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
index 311ed9502..29db9468e 100644
--- a/library/std/src/sys/unix/thread.rs
+++ b/library/std/src/sys/unix/thread.rs
@@ -207,7 +207,9 @@ impl Thread {
pub fn set_name(name: &CStr) {
unsafe {
let thread_self = libc::find_thread(ptr::null_mut());
- libc::rename_thread(thread_self, name.as_ptr());
+ let res = libc::rename_thread(thread_self, name.as_ptr());
+ // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
+ debug_assert_eq!(res, libc::B_OK);
}
}
@@ -218,6 +220,7 @@ impl Thread {
target_os = "redox",
target_os = "vxworks",
target_os = "hurd",
+ target_os = "aix",
))]
pub fn set_name(_name: &CStr) {
// Newlib, Emscripten, and VxWorks have no way to set a thread name.
@@ -317,6 +320,7 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> {
target_os = "macos",
target_os = "solaris",
target_os = "illumos",
+ target_os = "aix",
))] {
#[allow(unused_assignments)]
#[allow(unused_mut)]
diff --git a/library/std/src/sys/unix/thread_local_dtor.rs b/library/std/src/sys/unix/thread_local_dtor.rs
index fba2a676f..06399e8a2 100644
--- a/library/std/src/sys/unix/thread_local_dtor.rs
+++ b/library/std/src/sys/unix/thread_local_dtor.rs
@@ -11,28 +11,47 @@
// Note, however, that we run on lots older linuxes, as well as cross
// compiling from a newer linux to an older linux, so we also have a
// fallback implementation to use as well.
+#[allow(unexpected_cfgs)]
#[cfg(any(target_os = "linux", target_os = "fuchsia", target_os = "redox", target_os = "hurd"))]
+// FIXME: The Rust compiler currently omits weakly function definitions (i.e.,
+// __cxa_thread_atexit_impl) and its metadata from LLVM IR.
+#[no_sanitize(cfi, kcfi)]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
use crate::mem;
use crate::sys_common::thread_local_dtor::register_dtor_fallback;
+ /// This is necessary because the __cxa_thread_atexit_impl implementation
+ /// std links to by default may be a C or C++ implementation that was not
+ /// compiled using the Clang integer normalization option.
+ #[cfg(not(sanitizer_cfi_normalize_integers))]
+ #[cfi_encoding = "i"]
+ #[repr(transparent)]
+ pub struct c_int(pub libc::c_int);
+
extern "C" {
#[linkage = "extern_weak"]
static __dso_handle: *mut u8;
#[linkage = "extern_weak"]
- static __cxa_thread_atexit_impl: *const libc::c_void;
+ static __cxa_thread_atexit_impl: Option<
+ extern "C" fn(
+ unsafe extern "C" fn(*mut libc::c_void),
+ *mut libc::c_void,
+ *mut libc::c_void,
+ ) -> c_int,
+ >;
}
- if !__cxa_thread_atexit_impl.is_null() {
- type F = unsafe extern "C" fn(
- dtor: unsafe extern "C" fn(*mut u8),
- arg: *mut u8,
- dso_handle: *mut u8,
- ) -> libc::c_int;
- mem::transmute::<*const libc::c_void, F>(__cxa_thread_atexit_impl)(
- dtor,
- t,
- &__dso_handle as *const _ as *mut _,
- );
+
+ if let Some(f) = __cxa_thread_atexit_impl {
+ unsafe {
+ f(
+ mem::transmute::<
+ unsafe extern "C" fn(*mut u8),
+ unsafe extern "C" fn(*mut libc::c_void),
+ >(dtor),
+ t.cast(),
+ &__dso_handle as *const _ as *mut _,
+ );
+ }
return;
}
register_dtor_fallback(t, dtor);
@@ -48,17 +67,16 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
// workaround below is to register, via _tlv_atexit, a custom DTOR list once per
// thread. thread_local dtors are pushed to the DTOR list without calling
// _tlv_atexit.
-#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))]
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
- use crate::cell::Cell;
- use crate::mem;
+ use crate::cell::{Cell, RefCell};
use crate::ptr;
#[thread_local]
static REGISTERED: Cell<bool> = Cell::new(false);
#[thread_local]
- static mut DTORS: Vec<(*mut u8, unsafe extern "C" fn(*mut u8))> = Vec::new();
+ static DTORS: RefCell<Vec<(*mut u8, unsafe extern "C" fn(*mut u8))>> = RefCell::new(Vec::new());
if !REGISTERED.get() {
_tlv_atexit(run_dtors, ptr::null_mut());
@@ -69,21 +87,28 @@ pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
}
- let list = &mut DTORS;
- list.push((t, dtor));
+ match DTORS.try_borrow_mut() {
+ Ok(mut dtors) => dtors.push((t, dtor)),
+ Err(_) => rtabort!("global allocator may not use TLS"),
+ }
unsafe extern "C" fn run_dtors(_: *mut u8) {
- let mut list = mem::take(&mut DTORS);
+ let mut list = DTORS.take();
while !list.is_empty() {
for (ptr, dtor) in list {
dtor(ptr);
}
- list = mem::take(&mut DTORS);
+ list = DTORS.take();
}
}
}
-#[cfg(any(target_os = "vxworks", target_os = "horizon", target_os = "emscripten"))]
+#[cfg(any(
+ target_os = "vxworks",
+ target_os = "horizon",
+ target_os = "emscripten",
+ target_os = "aix"
+))]
#[cfg_attr(target_family = "wasm", allow(unused))] // might remain unused depending on target details (e.g. wasm32-unknown-emscripten)
pub unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) {
use crate::sys_common::thread_local_dtor::register_dtor_fallback;
diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs
index 4fe61b284..f2e86a4fb 100644
--- a/library/std/src/sys/unix/time.rs
+++ b/library/std/src/sys/unix/time.rs
@@ -1,8 +1,6 @@
use crate::fmt;
use crate::time::Duration;
-pub use self::inner::Instant;
-
const NSEC_PER_SEC: u64 = 1_000_000_000;
pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
#[allow(dead_code)] // Used for pthread condvar timeouts
@@ -40,6 +38,10 @@ impl SystemTime {
SystemTime { t: Timespec::new(tv_sec, tv_nsec) }
}
+ pub fn now() -> SystemTime {
+ SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) }
+ }
+
pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
self.t.sub_timespec(&other.t)
}
@@ -74,11 +76,65 @@ impl Timespec {
}
const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec {
+ // On Apple OS, dates before epoch are represented differently than on other
+ // Unix platforms: e.g. 1/10th of a second before epoch is represented as `seconds=-1`
+ // and `nanoseconds=100_000_000` on other platforms, but is `seconds=0` and
+ // `nanoseconds=-900_000_000` on Apple OS.
+ //
+ // To compensate, we first detect this special case by checking if both
+ // seconds and nanoseconds are in range, and then correct the value for seconds
+ // and nanoseconds to match the common unix representation.
+ //
+ // Please note that Apple OS nonetheless accepts the standard unix format when
+ // setting file times, which makes this compensation round-trippable and generally
+ // transparent.
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "tvos",
+ target_os = "watchos"
+ ))]
+ let (tv_sec, tv_nsec) =
+ if (tv_sec <= 0 && tv_sec > i64::MIN) && (tv_nsec < 0 && tv_nsec > -1_000_000_000) {
+ (tv_sec - 1, tv_nsec + 1_000_000_000)
+ } else {
+ (tv_sec, tv_nsec)
+ };
assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64);
// SAFETY: The assert above checks tv_nsec is within the valid range
Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds(tv_nsec as u32) } }
}
+ pub fn now(clock: libc::clockid_t) -> Timespec {
+ use crate::mem::MaybeUninit;
+ use crate::sys::cvt;
+
+ // Try to use 64-bit time in preparation for Y2038.
+ #[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ target_pointer_width = "32",
+ not(target_arch = "riscv32")
+ ))]
+ {
+ use crate::sys::weak::weak;
+
+ // __clock_gettime64 was added to 32-bit arches in glibc 2.34,
+ // and it handles both vDSO calls and ENOSYS fallbacks itself.
+ weak!(fn __clock_gettime64(libc::clockid_t, *mut __timespec64) -> libc::c_int);
+
+ if let Some(clock_gettime64) = __clock_gettime64.get() {
+ let mut t = MaybeUninit::uninit();
+ cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap();
+ return Timespec::from(unsafe { t.assume_init() });
+ }
+ }
+
+ let mut t = MaybeUninit::uninit();
+ cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap();
+ Timespec::from(unsafe { t.assume_init() })
+ }
+
pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
if self >= other {
// NOTE(eddyb) two aspects of this `if`-`else` are required for LLVM
@@ -216,209 +272,59 @@ impl From<__timespec64> for Timespec {
}
}
-#[cfg(any(
- all(target_os = "macos", any(not(target_arch = "aarch64"))),
- target_os = "ios",
- target_os = "watchos",
- target_os = "tvos"
-))]
-mod inner {
- use crate::sync::atomic::{AtomicU64, Ordering};
- use crate::sys::cvt;
- use crate::sys_common::mul_div_u64;
- use crate::time::Duration;
-
- use super::{SystemTime, Timespec, NSEC_PER_SEC};
-
- #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
- pub struct Instant {
- t: u64,
- }
-
- #[repr(C)]
- #[derive(Copy, Clone)]
- struct mach_timebase_info {
- numer: u32,
- denom: u32,
- }
- type mach_timebase_info_t = *mut mach_timebase_info;
- type kern_return_t = libc::c_int;
-
- impl Instant {
- pub fn now() -> Instant {
- extern "C" {
- fn mach_absolute_time() -> u64;
- }
- Instant { t: unsafe { mach_absolute_time() } }
- }
-
- pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
- let diff = self.t.checked_sub(other.t)?;
- let info = info();
- let nanos = mul_div_u64(diff, info.numer as u64, info.denom as u64);
- Some(Duration::new(nanos / NSEC_PER_SEC, (nanos % NSEC_PER_SEC) as u32))
- }
-
- pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
- Some(Instant { t: self.t.checked_add(checked_dur2intervals(other)?)? })
- }
-
- pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
- Some(Instant { t: self.t.checked_sub(checked_dur2intervals(other)?)? })
- }
- }
-
- impl SystemTime {
- pub fn now() -> SystemTime {
- use crate::ptr;
-
- let mut s = libc::timeval { tv_sec: 0, tv_usec: 0 };
- cvt(unsafe { libc::gettimeofday(&mut s, ptr::null_mut()) }).unwrap();
- return SystemTime::from(s);
- }
- }
-
- impl From<libc::timeval> for Timespec {
- fn from(t: libc::timeval) -> Timespec {
- Timespec::new(t.tv_sec as i64, 1000 * t.tv_usec as i64)
- }
- }
-
- impl From<libc::timeval> for SystemTime {
- fn from(t: libc::timeval) -> SystemTime {
- SystemTime { t: Timespec::from(t) }
- }
- }
-
- fn checked_dur2intervals(dur: &Duration) -> Option<u64> {
- let nanos =
- dur.as_secs().checked_mul(NSEC_PER_SEC)?.checked_add(dur.subsec_nanos() as u64)?;
- let info = info();
- Some(mul_div_u64(nanos, info.denom as u64, info.numer as u64))
- }
-
- fn info() -> mach_timebase_info {
- // INFO_BITS conceptually is an `Option<mach_timebase_info>`. We can do
- // this in 64 bits because we know 0 is never a valid value for the
- // `denom` field.
- //
- // Encoding this as a single `AtomicU64` allows us to use `Relaxed`
- // operations, as we are only interested in the effects on a single
- // memory location.
- static INFO_BITS: AtomicU64 = AtomicU64::new(0);
-
- // If a previous thread has initialized `INFO_BITS`, use it.
- let info_bits = INFO_BITS.load(Ordering::Relaxed);
- if info_bits != 0 {
- return info_from_bits(info_bits);
- }
-
- // ... otherwise learn for ourselves ...
- extern "C" {
- fn mach_timebase_info(info: mach_timebase_info_t) -> kern_return_t;
- }
-
- let mut info = info_from_bits(0);
- unsafe {
- mach_timebase_info(&mut info);
- }
- INFO_BITS.store(info_to_bits(info), Ordering::Relaxed);
- info
- }
-
- #[inline]
- fn info_to_bits(info: mach_timebase_info) -> u64 {
- ((info.denom as u64) << 32) | (info.numer as u64)
- }
-
- #[inline]
- fn info_from_bits(bits: u64) -> mach_timebase_info {
- mach_timebase_info { numer: bits as u32, denom: (bits >> 32) as u32 }
- }
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Instant {
+ t: Timespec,
}
-#[cfg(not(any(
- all(target_os = "macos", any(not(target_arch = "aarch64"))),
- target_os = "ios",
- target_os = "watchos",
- target_os = "tvos"
-)))]
-mod inner {
- use crate::fmt;
- use crate::mem::MaybeUninit;
- use crate::sys::cvt;
- use crate::time::Duration;
-
- use super::{SystemTime, Timespec};
-
- #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
- pub struct Instant {
- t: Timespec,
+impl Instant {
+ pub fn now() -> Instant {
+ // https://www.manpagez.com/man/3/clock_gettime/
+ //
+ // CLOCK_UPTIME_RAW clock that increments monotonically, in the same man-
+ // ner as CLOCK_MONOTONIC_RAW, but that does not incre-
+ // ment while the system is asleep. The returned value
+ // is identical to the result of mach_absolute_time()
+ // after the appropriate mach_timebase conversion is
+ // applied.
+ //
+ // Instant on macos was historically implemented using mach_absolute_time;
+ // we preserve this value domain out of an abundance of caution.
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "tvos"
+ ))]
+ const clock_id: libc::clockid_t = libc::CLOCK_UPTIME_RAW;
+ #[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "tvos"
+ )))]
+ const clock_id: libc::clockid_t = libc::CLOCK_MONOTONIC;
+ Instant { t: Timespec::now(clock_id) }
}
- impl Instant {
- pub fn now() -> Instant {
- #[cfg(target_os = "macos")]
- const clock_id: libc::clockid_t = libc::CLOCK_UPTIME_RAW;
- #[cfg(not(target_os = "macos"))]
- const clock_id: libc::clockid_t = libc::CLOCK_MONOTONIC;
- Instant { t: Timespec::now(clock_id) }
- }
-
- 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)? })
- }
+ pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+ self.t.sub_timespec(&other.t).ok()
}
- impl fmt::Debug for Instant {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Instant")
- .field("tv_sec", &self.t.tv_sec)
- .field("tv_nsec", &self.t.tv_nsec.0)
- .finish()
- }
+ pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant { t: self.t.checked_add_duration(other)? })
}
- impl SystemTime {
- pub fn now() -> SystemTime {
- SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) }
- }
+ pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+ Some(Instant { t: self.t.checked_sub_duration(other)? })
}
+}
- impl Timespec {
- pub fn now(clock: libc::clockid_t) -> Timespec {
- // Try to use 64-bit time in preparation for Y2038.
- #[cfg(all(
- target_os = "linux",
- target_env = "gnu",
- target_pointer_width = "32",
- not(target_arch = "riscv32")
- ))]
- {
- use crate::sys::weak::weak;
-
- // __clock_gettime64 was added to 32-bit arches in glibc 2.34,
- // and it handles both vDSO calls and ENOSYS fallbacks itself.
- weak!(fn __clock_gettime64(libc::clockid_t, *mut super::__timespec64) -> libc::c_int);
-
- if let Some(clock_gettime64) = __clock_gettime64.get() {
- let mut t = MaybeUninit::uninit();
- cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap();
- return Timespec::from(unsafe { t.assume_init() });
- }
- }
-
- let mut t = MaybeUninit::uninit();
- cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap();
- Timespec::from(unsafe { t.assume_init() })
- }
+impl fmt::Debug for Instant {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Instant")
+ .field("tv_sec", &self.t.tv_sec)
+ .field("tv_nsec", &self.t.tv_nsec.0)
+ .finish()
}
}