diff options
Diffstat (limited to 'library/std/src')
34 files changed, 701 insertions, 222 deletions
diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index e8d0132f4b9..b8959316de1 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -1534,3 +1534,20 @@ fn read_large_dir() { entry.unwrap(); } } + +/// Test the fallback for getting the metadata of files like hiberfil.sys that +/// Windows holds a special lock on, preventing normal means of querying +/// metadata. See #96980. +/// +/// Note this fails in CI because `hiberfil.sys` does not actually exist there. +/// Therefore it's marked as ignored. +#[test] +#[ignore] +#[cfg(windows)] +fn hiberfil_sys() { + let hiberfil = Path::new(r"C:\hiberfil.sys"); + assert_eq!(true, hiberfil.try_exists().unwrap()); + fs::symlink_metadata(hiberfil).unwrap(); + fs::metadata(hiberfil).unwrap(); + assert_eq!(true, hiberfil.exists()); +} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index f4f2e3f2434..18f7f6a35e9 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2577,6 +2577,7 @@ impl<T: Read> Read for Take<T> { let max = cmp::min(buf.len() as u64, self.limit) as usize; let n = self.inner.read(&mut buf[..max])?; + assert!(n as u64 <= self.limit, "number of read bytes exceeds limit"); self.limit -= n as u64; Ok(n) } diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index e3b62894d0f..4d3736f7914 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -220,7 +220,7 @@ fn handle_ebadf<T>(r: io::Result<T>, default: T) -> io::Result<T> { /// /// fn main() -> io::Result<()> { /// let mut buffer = String::new(); -/// let mut stdin = io::stdin(); // We get `Stdin` here. +/// let stdin = io::stdin(); // We get `Stdin` here. /// stdin.read_line(&mut buffer)?; /// Ok(()) /// } diff --git a/library/std/src/io/tests.rs b/library/std/src/io/tests.rs index d5a8c93b0ce..f357f33ec52 100644 --- a/library/std/src/io/tests.rs +++ b/library/std/src/io/tests.rs @@ -583,6 +583,25 @@ fn test_write_all_vectored() { } } +// Issue 94981 +#[test] +#[should_panic = "number of read bytes exceeds limit"] +fn test_take_wrong_length() { + struct LieAboutSize(bool); + + impl Read for LieAboutSize { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + // Lie about the read size at first time of read. + if core::mem::take(&mut self.0) { Ok(buf.len() + 1) } else { Ok(buf.len()) } + } + } + + let mut buffer = vec![0; 4]; + let mut reader = LieAboutSize(true).take(4); + // Primed the `Limit` by lying about the read size. + let _ = reader.read(&mut buffer[..]); +} + #[bench] fn bench_take_read(b: &mut test::Bencher) { b.iter(|| { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 9fd426d3ac4..6b0c0ad7c21 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -276,6 +276,7 @@ #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(int_error_internals)] +#![feature(is_some_with)] #![feature(maybe_uninit_slice)] #![feature(maybe_uninit_write_slice)] #![feature(mixed_integer_ops)] diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs index d661a13edc5..a463bc41db7 100644 --- a/library/std/src/os/fd/owned.rs +++ b/library/std/src/os/fd/owned.rs @@ -356,7 +356,7 @@ impl From<OwnedFd> for crate::net::UdpSocket { } } -#[stable(feature = "io_safety", since = "1.63.0")] +#[stable(feature = "asfd_ptrs", since = "1.64.0")] /// This impl allows implementing traits that require `AsFd` on Arc. /// ``` /// # #[cfg(any(unix, target_os = "wasi"))] mod group_cfg { @@ -379,7 +379,7 @@ impl<T: AsFd> AsFd for crate::sync::Arc<T> { } } -#[stable(feature = "io_safety", since = "1.63.0")] +#[stable(feature = "asfd_ptrs", since = "1.64.0")] impl<T: AsFd> AsFd for Box<T> { #[inline] fn as_fd(&self) -> BorrowedFd<'_> { diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index a1df72a8a04..6fbaa42c768 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -141,7 +141,6 @@ pub mod openbsd; pub mod redox; #[cfg(target_os = "solaris")] pub mod solaris; - #[cfg(target_os = "solid_asp3")] pub mod solid; #[cfg(target_os = "vxworks")] diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index cef546487f3..411cc0925c4 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -90,6 +90,7 @@ pub mod thread; target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "watchos", target_os = "macos", target_os = "netbsd", target_os = "openbsd" diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 1d6083e66e1..cc3a8858793 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -12,6 +12,7 @@ use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, Owned target_os = "freebsd", target_os = "ios", target_os = "macos", + target_os = "watchos", target_os = "netbsd", target_os = "openbsd" ))] @@ -30,6 +31,7 @@ use crate::time::Duration; target_os = "freebsd", target_os = "ios", target_os = "macos", + target_os = "watchos", target_os = "netbsd", target_os = "openbsd" ))] @@ -238,6 +240,7 @@ impl UnixStream { target_os = "freebsd", target_os = "ios", target_os = "macos", + target_os = "watchos", target_os = "netbsd", target_os = "openbsd" ))] diff --git a/library/std/src/os/unix/ucred.rs b/library/std/src/os/unix/ucred.rs index 32e6430d3f6..ae4faf27b4d 100644 --- a/library/std/src/os/unix/ucred.rs +++ b/library/std/src/os/unix/ucred.rs @@ -36,7 +36,7 @@ pub use self::impl_linux::peer_cred; ))] pub use self::impl_bsd::peer_cred; -#[cfg(any(target_os = "macos", target_os = "ios",))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] pub use self::impl_mac::peer_cred; #[cfg(any(target_os = "linux", target_os = "android"))] @@ -97,7 +97,7 @@ pub mod impl_bsd { } } -#[cfg(any(target_os = "macos", target_os = "ios",))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] pub mod impl_mac { use super::UCred; use crate::os::unix::io::AsRawFd; diff --git a/library/std/src/os/unix/ucred/tests.rs b/library/std/src/os/unix/ucred/tests.rs index 42d79418cf7..e63a2fc248e 100644 --- a/library/std/src/os/unix/ucred/tests.rs +++ b/library/std/src/os/unix/ucred/tests.rs @@ -9,6 +9,7 @@ use libc::{getegid, geteuid, getpid}; target_os = "freebsd", target_os = "ios", target_os = "macos", + target_os = "watchos", target_os = "openbsd" ))] fn test_socket_pair() { @@ -25,7 +26,7 @@ fn test_socket_pair() { } #[test] -#[cfg(any(target_os = "linux", target_os = "ios", target_os = "macos",))] +#[cfg(any(target_os = "linux", target_os = "ios", target_os = "macos", target_os = "watchos"))] fn test_socket_pair_pids(arg: Type) -> RetType { // Create two connected sockets and get their peer credentials. let (sock_a, sock_b) = UnixStream::pair().unwrap(); diff --git a/library/std/src/primitive_docs.rs b/library/std/src/primitive_docs.rs index 401c8159988..b8e5461640c 100644 --- a/library/std/src/primitive_docs.rs +++ b/library/std/src/primitive_docs.rs @@ -996,7 +996,7 @@ impl<T> (T,) {} // Fake impl that's only really used for docs. #[cfg(doc)] #[stable(feature = "rust1", since = "1.0.0")] -#[doc(tuple_variadic)] +#[cfg_attr(not(bootstrap), doc(fake_variadic))] /// This trait is implemented on arbitrary-length tuples. impl<T: Clone> Clone for (T,) { fn clone(&self) -> Self { @@ -1007,7 +1007,7 @@ impl<T: Clone> Clone for (T,) { // Fake impl that's only really used for docs. #[cfg(doc)] #[stable(feature = "rust1", since = "1.0.0")] -#[doc(tuple_variadic)] +#[cfg_attr(not(bootstrap), doc(fake_variadic))] /// This trait is implemented on arbitrary-length tuples. impl<T: Copy> Copy for (T,) { // empty @@ -1441,11 +1441,16 @@ mod prim_ref {} /// Note that all of this is not portable to platforms where function pointers and data pointers /// have different sizes. /// -/// ### Traits +/// ### Trait implementations /// -/// Function pointers implement the following traits: +/// In this documentation the shorthand `fn (T₁, T₂, …, Tₙ)` is used to represent non-variadic +/// function pointers of varying length. Note that this is a convenience notation to avoid +/// repetitive documentation, not valid Rust syntax. +/// +/// Due to a temporary restriction in Rust's type system, these traits are only implemented on +/// functions that take 12 arguments or less, with the `"Rust"` and `"C"` ABIs. In the future, this +/// may change: /// -/// * [`Clone`] /// * [`PartialEq`] /// * [`Eq`] /// * [`PartialOrd`] @@ -1454,15 +1459,50 @@ mod prim_ref {} /// * [`Pointer`] /// * [`Debug`] /// +/// The following traits are implemented for function pointers with any number of arguments and +/// any ABI. These traits have implementations that are automatically generated by the compiler, +/// so are not limited by missing language features: +/// +/// * [`Clone`] +/// * [`Copy`] +/// * [`Send`] +/// * [`Sync`] +/// * [`Unpin`] +/// * [`UnwindSafe`] +/// * [`RefUnwindSafe`] +/// /// [`Hash`]: hash::Hash /// [`Pointer`]: fmt::Pointer +/// [`UnwindSafe`]: panic::UnwindSafe +/// [`RefUnwindSafe`]: panic::RefUnwindSafe /// -/// Due to a temporary restriction in Rust's type system, these traits are only implemented on -/// functions that take 12 arguments or less, with the `"Rust"` and `"C"` ABIs. In the future, this -/// may change. -/// -/// In addition, function pointers of *any* signature, ABI, or safety are [`Copy`], and all *safe* -/// function pointers implement [`Fn`], [`FnMut`], and [`FnOnce`]. This works because these traits -/// are specially known to the compiler. +/// In addition, all *safe* function pointers implement [`Fn`], [`FnMut`], and [`FnOnce`], because +/// these traits are specially known to the compiler. #[stable(feature = "rust1", since = "1.0.0")] mod prim_fn {} + +// Required to make auto trait impls render. +// See src/librustdoc/passes/collect_trait_impls.rs:collect_trait_impls +#[doc(hidden)] +#[cfg(not(bootstrap))] +impl<Ret, T> fn(T) -> Ret {} + +// Fake impl that's only really used for docs. +#[cfg(doc)] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(bootstrap), doc(fake_variadic))] +/// This trait is implemented on function pointers with any number of arguments. +impl<Ret, T> Clone for fn(T) -> Ret { + fn clone(&self) -> Self { + loop {} + } +} + +// Fake impl that's only really used for docs. +#[cfg(doc)] +#[stable(feature = "rust1", since = "1.0.0")] +#[cfg_attr(not(bootstrap), doc(fake_variadic))] +/// This trait is implemented on function pointers with any number of arguments. +impl<Ret, T> Copy for fn(T) -> Ret { + // empty +} diff --git a/library/std/src/sys/solid/abi/mod.rs b/library/std/src/sys/solid/abi/mod.rs index 1afc83f766d..8440d572cfb 100644 --- a/library/std/src/sys/solid/abi/mod.rs +++ b/library/std/src/sys/solid/abi/mod.rs @@ -4,32 +4,6 @@ mod fs; pub mod sockets; pub use self::fs::*; -#[inline(always)] -pub fn breakpoint_program_exited(tid: usize) { - unsafe { - match () { - // SOLID_BP_PROGRAM_EXITED = 15 - #[cfg(target_arch = "arm")] - () => core::arch::asm!("bkpt #15", in("r0") tid), - #[cfg(target_arch = "aarch64")] - () => core::arch::asm!("hlt #15", in("x0") tid), - } - } -} - -#[inline(always)] -pub fn breakpoint_abort() { - unsafe { - match () { - // SOLID_BP_CSABORT = 16 - #[cfg(target_arch = "arm")] - () => core::arch::asm!("bkpt #16"), - #[cfg(target_arch = "aarch64")] - () => core::arch::asm!("hlt #16"), - } - } -} - // `solid_types.h` pub use super::itron::abi::{ER, ER_ID, E_TMOUT, ID}; diff --git a/library/std/src/sys/solid/mod.rs b/library/std/src/sys/solid/mod.rs index 2d21e4764fc..778a589d1b7 100644 --- a/library/std/src/sys/solid/mod.rs +++ b/library/std/src/sys/solid/mod.rs @@ -76,20 +76,9 @@ pub fn decode_error_kind(code: i32) -> crate::io::ErrorKind { error::decode_error_kind(code) } -#[inline(always)] +#[inline] pub fn abort_internal() -> ! { - loop { - abi::breakpoint_abort(); - } -} - -// 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(); + unsafe { libc::abort() } } pub fn hashmap_random_keys() -> (u64, u64) { diff --git a/library/std/src/sys/solid/os.rs b/library/std/src/sys/solid/os.rs index 719d95bbe50..b5649d6e0ff 100644 --- a/library/std/src/sys/solid/os.rs +++ b/library/std/src/sys/solid/os.rs @@ -11,7 +11,7 @@ use crate::path::{self, PathBuf}; use crate::sys_common::rwlock::StaticRwLock; use crate::vec; -use super::{abi, error, itron, memchr}; +use super::{error, itron, memchr}; // `solid` directly maps `errno`s to μITRON error codes. impl itron::error::ItronError { @@ -184,11 +184,8 @@ pub fn home_dir() -> Option<PathBuf> { None } -pub fn exit(_code: i32) -> ! { - let tid = itron::task::try_current_task_id().unwrap_or(0); - loop { - abi::breakpoint_program_exited(tid as usize); - } +pub fn exit(code: i32) -> ! { + rtabort!("exit({}) called", code); } pub fn getpid() -> u32 { diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index 79964e2b238..a342f0f5e85 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -151,7 +151,7 @@ mod imp { } } -#[cfg(any(target_os = "macos", target_os = "ios"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] mod imp { use super::Args; use crate::ffi::CStr; @@ -192,7 +192,7 @@ mod imp { // for i in (0..[args count]) // res.push([args objectAtIndex:i]) // res - #[cfg(target_os = "ios")] + #[cfg(any(target_os = "ios", target_os = "watchos"))] pub fn args() -> Args { use crate::ffi::OsString; use crate::mem; diff --git a/library/std/src/sys/unix/env.rs b/library/std/src/sys/unix/env.rs index 4d8391656a4..c9ba661c829 100644 --- a/library/std/src/sys/unix/env.rs +++ b/library/std/src/sys/unix/env.rs @@ -31,6 +31,17 @@ pub mod os { pub const EXE_EXTENSION: &str = ""; } +#[cfg(target_os = "watchos")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "watchos"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".dylib"; + pub const DLL_EXTENSION: &str = "dylib"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} + #[cfg(target_os = "freebsd")] pub mod os { pub const FAMILY: &str = "unix"; diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs index 137ca3a7633..30812dabb4e 100644 --- a/library/std/src/sys/unix/fd.rs +++ b/library/std/src/sys/unix/fd.rs @@ -47,6 +47,7 @@ const READ_LIMIT: usize = libc::ssize_t::MAX as usize; target_os = "macos", target_os = "netbsd", target_os = "openbsd", + target_os = "watchos", ))] const fn max_iov() -> usize { libc::IOV_MAX as usize @@ -67,7 +68,8 @@ const fn max_iov() -> usize { target_os = "macos", target_os = "netbsd", target_os = "openbsd", - target_os = "horizon" + target_os = "horizon", + target_os = "watchos", )))] const fn max_iov() -> usize { 16 // The minimum value required by POSIX. diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 8b0bbd6a55c..7c882469440 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -17,6 +17,7 @@ use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; all(target_os = "linux", target_env = "gnu"), target_os = "macos", target_os = "ios", + target_os = "watchos", ))] use crate::sys::weak::syscall; #[cfg(target_os = "macos")] @@ -27,6 +28,7 @@ use libc::{c_int, mode_t}; #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "watchos", all(target_os = "linux", target_env = "gnu") ))] use libc::c_char; @@ -443,7 +445,8 @@ impl FileAttr { target_os = "freebsd", target_os = "openbsd", target_os = "macos", - target_os = "ios" + target_os = "ios", + target_os = "watchos", ))] pub fn created(&self) -> io::Result<SystemTime> { Ok(SystemTime::new(self.stat.st_birthtime as i64, self.stat.st_birthtime_nsec as i64)) @@ -453,7 +456,8 @@ impl FileAttr { target_os = "freebsd", target_os = "openbsd", target_os = "macos", - target_os = "ios" + target_os = "ios", + target_os = "watchos", )))] pub fn created(&self) -> io::Result<SystemTime> { cfg_has_statx! { @@ -707,6 +711,7 @@ impl DirEntry { #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "watchos", target_os = "linux", target_os = "emscripten", target_os = "android", @@ -737,6 +742,7 @@ impl DirEntry { #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "watchos", target_os = "netbsd", target_os = "openbsd", target_os = "freebsd", @@ -754,6 +760,7 @@ impl DirEntry { #[cfg(not(any( target_os = "macos", target_os = "ios", + target_os = "watchos", target_os = "netbsd", target_os = "openbsd", target_os = "freebsd", @@ -911,11 +918,11 @@ impl File { cvt_r(|| unsafe { os_fsync(self.as_raw_fd()) })?; return Ok(()); - #[cfg(any(target_os = "macos", target_os = "ios"))] + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] unsafe fn os_fsync(fd: c_int) -> c_int { libc::fcntl(fd, libc::F_FULLFSYNC) } - #[cfg(not(any(target_os = "macos", target_os = "ios")))] + #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "watchos")))] unsafe fn os_fsync(fd: c_int) -> c_int { libc::fsync(fd) } @@ -925,7 +932,7 @@ impl File { cvt_r(|| unsafe { os_datasync(self.as_raw_fd()) })?; return Ok(()); - #[cfg(any(target_os = "macos", target_os = "ios"))] + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] unsafe fn os_datasync(fd: c_int) -> c_int { libc::fcntl(fd, libc::F_FULLFSYNC) } @@ -946,7 +953,8 @@ impl File { target_os = "linux", target_os = "macos", target_os = "netbsd", - target_os = "openbsd" + target_os = "openbsd", + target_os = "watchos", )))] unsafe fn os_datasync(fd: c_int) -> c_int { libc::fsync(fd) @@ -1396,7 +1404,8 @@ fn open_to_and_set_permissions( target_os = "linux", target_os = "android", target_os = "macos", - target_os = "ios" + target_os = "ios", + target_os = "watchos", )))] pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { let (mut reader, reader_metadata) = open_from(from)?; @@ -1423,7 +1432,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { } } -#[cfg(any(target_os = "macos", target_os = "ios"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { use crate::sync::atomic::{AtomicBool, Ordering}; diff --git a/library/std/src/sys/unix/futex.rs b/library/std/src/sys/unix/futex.rs index ab516a7f76d..8d5b540212a 100644 --- a/library/std/src/sys/unix/futex.rs +++ b/library/std/src/sys/unix/futex.rs @@ -240,17 +240,25 @@ pub fn futex_wake_all(futex: &AtomicU32) { } #[cfg(target_os = "fuchsia")] -mod zircon { - type zx_time_t = i64; - type zx_futex_t = crate::sync::atomic::AtomicU32; - type zx_handle_t = u32; - type zx_status_t = i32; +pub mod zircon { + pub type zx_futex_t = crate::sync::atomic::AtomicU32; + pub type zx_handle_t = u32; + pub type zx_status_t = i32; + pub type zx_time_t = i64; pub const ZX_HANDLE_INVALID: zx_handle_t = 0; - pub const ZX_ERR_TIMED_OUT: zx_status_t = -21; + pub const ZX_TIME_INFINITE: zx_time_t = zx_time_t::MAX; + pub const ZX_OK: zx_status_t = 0; + pub const ZX_ERR_INVALID_ARGS: zx_status_t = -10; + pub const ZX_ERR_BAD_HANDLE: zx_status_t = -11; + pub const ZX_ERR_WRONG_TYPE: zx_status_t = -12; + pub const ZX_ERR_BAD_STATE: zx_status_t = -20; + pub const ZX_ERR_TIMED_OUT: zx_status_t = -21; + extern "C" { + pub fn zx_clock_get_monotonic() -> zx_time_t; pub fn zx_futex_wait( value_ptr: *const zx_futex_t, current_value: zx_futex_t, @@ -258,7 +266,8 @@ mod zircon { deadline: zx_time_t, ) -> zx_status_t; pub fn zx_futex_wake(value_ptr: *const zx_futex_t, wake_count: u32) -> zx_status_t; - pub fn zx_clock_get_monotonic() -> zx_time_t; + pub fn zx_futex_wake_single_owner(value_ptr: *const zx_futex_t) -> zx_status_t; + pub fn zx_thread_self() -> zx_handle_t; } } @@ -287,3 +296,8 @@ pub fn futex_wake(futex: &AtomicU32) -> bool { unsafe { zircon::zx_futex_wake(futex, 1) }; false } + +#[cfg(target_os = "fuchsia")] +pub fn futex_wake_all(futex: &AtomicU32) { + unsafe { zircon::zx_futex_wake(futex, u32::MAX) }; +} diff --git a/library/std/src/sys/unix/locks/fuchsia_mutex.rs b/library/std/src/sys/unix/locks/fuchsia_mutex.rs new file mode 100644 index 00000000000..ce427599c3b --- /dev/null +++ b/library/std/src/sys/unix/locks/fuchsia_mutex.rs @@ -0,0 +1,165 @@ +//! A priority inheriting mutex for Fuchsia. +//! +//! This is a port of the [mutex in Fuchsia's libsync]. Contrary to the original, +//! it does not abort the process when reentrant locking is detected, but deadlocks. +//! +//! Priority inheritance is achieved by storing the owning thread's handle in an +//! atomic variable. Fuchsia's futex operations support setting an owner thread +//! for a futex, which can boost that thread's priority while the futex is waited +//! upon. +//! +//! libsync is licenced under the following BSD-style licence: +//! +//! Copyright 2016 The Fuchsia Authors. +//! +//! Redistribution and use in source and binary forms, with or without +//! modification, are permitted provided that the following conditions are +//! met: +//! +//! * Redistributions of source code must retain the above copyright +//! notice, this list of conditions and the following disclaimer. +//! * Redistributions in binary form must reproduce the above +//! copyright notice, this list of conditions and the following +//! disclaimer in the documentation and/or other materials provided +//! with the distribution. +//! +//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +//! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +//! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +//! A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +//! OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +//! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +//! LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +//! DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +//! THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +//! (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +//! OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//! +//! [mutex in Fuchsia's libsync]: https://cs.opensource.google/fuchsia/fuchsia/+/main:zircon/system/ulib/sync/mutex.c + +use crate::sync::atomic::{ + AtomicU32, + Ordering::{Acquire, Relaxed, Release}, +}; +use crate::sys::futex::zircon::{ + zx_futex_wait, zx_futex_wake_single_owner, zx_handle_t, zx_thread_self, ZX_ERR_BAD_HANDLE, + ZX_ERR_BAD_STATE, ZX_ERR_INVALID_ARGS, ZX_ERR_TIMED_OUT, ZX_ERR_WRONG_TYPE, ZX_OK, + ZX_TIME_INFINITE, +}; + +// The lowest two bits of a `zx_handle_t` are always set, so the lowest bit is used to mark the +// mutex as contested by clearing it. +const CONTESTED_BIT: u32 = 1; +// This can never be a valid `zx_handle_t`. +const UNLOCKED: u32 = 0; + +pub type MovableMutex = Mutex; + +pub struct Mutex { + futex: AtomicU32, +} + +#[inline] +fn to_state(owner: zx_handle_t) -> u32 { + owner +} + +#[inline] +fn to_owner(state: u32) -> zx_handle_t { + state | CONTESTED_BIT +} + +#[inline] +fn is_contested(state: u32) -> bool { + state & CONTESTED_BIT == 0 +} + +#[inline] +fn mark_contested(state: u32) -> u32 { + state & !CONTESTED_BIT +} + +impl Mutex { + #[inline] + pub const fn new() -> Mutex { + Mutex { futex: AtomicU32::new(UNLOCKED) } + } + + #[inline] + pub unsafe fn init(&mut self) {} + + #[inline] + pub unsafe fn try_lock(&self) -> bool { + let thread_self = zx_thread_self(); + self.futex.compare_exchange(UNLOCKED, to_state(thread_self), Acquire, Relaxed).is_ok() + } + + #[inline] + pub unsafe fn lock(&self) { + let thread_self = zx_thread_self(); + if let Err(state) = + self.futex.compare_exchange(UNLOCKED, to_state(thread_self), Acquire, Relaxed) + { + self.lock_contested(state, thread_self); + } + } + + #[cold] + fn lock_contested(&self, mut state: u32, thread_self: zx_handle_t) { + let owned_state = mark_contested(to_state(thread_self)); + loop { + // Mark the mutex as contested if it is not already. + let contested = mark_contested(state); + if is_contested(state) + || self.futex.compare_exchange(state, contested, Relaxed, Relaxed).is_ok() + { + // The mutex has been marked as contested, wait for the state to change. + unsafe { + match zx_futex_wait( + &self.futex, + AtomicU32::new(contested), + to_owner(state), + ZX_TIME_INFINITE, + ) { + ZX_OK | ZX_ERR_BAD_STATE | ZX_ERR_TIMED_OUT => (), + // Note that if a thread handle is reused after its associated thread + // exits without unlocking the mutex, an arbitrary thread's priority + // could be boosted by the wait, but there is currently no way to + // prevent that. + ZX_ERR_INVALID_ARGS | ZX_ERR_BAD_HANDLE | ZX_ERR_WRONG_TYPE => { + panic!( + "either the current thread is trying to lock a mutex it has + already locked, or the previous owner did not unlock the mutex + before exiting" + ) + } + error => panic!("unexpected error in zx_futex_wait: {error}"), + } + } + } + + // The state has changed or a wakeup occured, try to lock the mutex. + match self.futex.compare_exchange(UNLOCKED, owned_state, Acquire, Relaxed) { + Ok(_) => return, + Err(updated) => state = updated, + } + } + } + + #[inline] + pub unsafe fn unlock(&self) { + if is_contested(self.futex.swap(UNLOCKED, Release)) { + // The woken thread will mark the mutex as contested again, + // and return here, waking until there are no waiters left, + // in which case this is a noop. + self.wake(); + } + } + + #[cold] + fn wake(&self) { + unsafe { + zx_futex_wake_single_owner(&self.futex); + } + } +} diff --git a/library/std/src/sys/unix/locks/futex_condvar.rs b/library/std/src/sys/unix/locks/futex_condvar.rs new file mode 100644 index 00000000000..c0576c17880 --- /dev/null +++ b/library/std/src/sys/unix/locks/futex_condvar.rs @@ -0,0 +1,58 @@ +use super::Mutex; +use crate::sync::atomic::{AtomicU32, Ordering::Relaxed}; +use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all}; +use crate::time::Duration; + +pub type MovableCondvar = Condvar; + +pub struct Condvar { + // The value of this atomic is simply incremented on every notification. + // This is used by `.wait()` to not miss any notifications after + // unlocking the mutex and before waiting for notifications. + futex: AtomicU32, +} + +impl Condvar { + #[inline] + pub const fn new() -> Self { + Self { futex: AtomicU32::new(0) } + } + + // All the memory orderings here are `Relaxed`, + // because synchronization is done by unlocking and locking the mutex. + + pub unsafe fn notify_one(&self) { + self.futex.fetch_add(1, Relaxed); + futex_wake(&self.futex); + } + + pub unsafe fn notify_all(&self) { + self.futex.fetch_add(1, Relaxed); + futex_wake_all(&self.futex); + } + + pub unsafe fn wait(&self, mutex: &Mutex) { + self.wait_optional_timeout(mutex, None); + } + + pub unsafe fn wait_timeout(&self, mutex: &Mutex, timeout: Duration) -> bool { + self.wait_optional_timeout(mutex, Some(timeout)) + } + + unsafe fn wait_optional_timeout(&self, mutex: &Mutex, timeout: Option<Duration>) -> bool { + // Examine the notification counter _before_ we unlock the mutex. + let futex_value = self.futex.load(Relaxed); + + // Unlock the mutex before going to sleep. + mutex.unlock(); + + // Wait, but only if there hasn't been any + // notification since we unlocked the mutex. + let r = futex_wait(&self.futex, futex_value, timeout); + + // Lock the mutex again. + mutex.lock(); + + r + } +} diff --git a/library/std/src/sys/unix/locks/futex.rs b/library/std/src/sys/unix/locks/futex_mutex.rs index a9a1a32c5af..99ba86e5f99 100644 --- a/library/std/src/sys/unix/locks/futex.rs +++ b/library/std/src/sys/unix/locks/futex_mutex.rs @@ -2,11 +2,9 @@ use crate::sync::atomic::{ AtomicU32, Ordering::{Acquire, Relaxed, Release}, }; -use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all}; -use crate::time::Duration; +use crate::sys::futex::{futex_wait, futex_wake}; pub type MovableMutex = Mutex; -pub type MovableCondvar = Condvar; pub struct Mutex { /// 0: unlocked @@ -101,55 +99,3 @@ impl Mutex { futex_wake(&self.futex); } } - -pub struct Condvar { - // The value of this atomic is simply incremented on every notification. - // This is used by `.wait()` to not miss any notifications after - // unlocking the mutex and before waiting for notifications. - futex: AtomicU32, -} - -impl Condvar { - #[inline] - pub const fn new() -> Self { - Self { futex: AtomicU32::new(0) } - } - - // All the memory orderings here are `Relaxed`, - // because synchronization is done by unlocking and locking the mutex. - - pub unsafe fn notify_one(&self) { - self.futex.fetch_add(1, Relaxed); - futex_wake(&self.futex); - } - - pub unsafe fn notify_all(&self) { - self.futex.fetch_add(1, Relaxed); - futex_wake_all(&self.futex); - } - - pub unsafe fn wait(&self, mutex: &Mutex) { - self.wait_optional_timeout(mutex, None); - } - - pub unsafe fn wait_timeout(&self, mutex: &Mutex, timeout: Duration) -> bool { - self.wait_optional_timeout(mutex, Some(timeout)) - } - - unsafe fn wait_optional_timeout(&self, mutex: &Mutex, timeout: Option<Duration>) -> bool { - // Examine the notification counter _before_ we unlock the mutex. - let futex_value = self.futex.load(Relaxed); - - // Unlock the mutex before going to sleep. - mutex.unlock(); - - // Wait, but only if there hasn't been any - // notification since we unlocked the mutex. - let r = futex_wait(&self.futex, futex_value, timeout); - - // Lock the mutex again. - mutex.lock(); - - r - } -} diff --git a/library/std/src/sys/unix/locks/mod.rs b/library/std/src/sys/unix/locks/mod.rs index 03400efa3c9..f5f92f69358 100644 --- a/library/std/src/sys/unix/locks/mod.rs +++ b/library/std/src/sys/unix/locks/mod.rs @@ -7,10 +7,19 @@ cfg_if::cfg_if! { target_os = "openbsd", target_os = "dragonfly", ))] { - mod futex; + mod futex_mutex; mod futex_rwlock; - pub(crate) use futex::{Mutex, MovableMutex, MovableCondvar}; + mod futex_condvar; + pub(crate) use futex_mutex::{Mutex, MovableMutex}; pub(crate) use futex_rwlock::{RwLock, MovableRwLock}; + pub(crate) use futex_condvar::MovableCondvar; + } else if #[cfg(target_os = "fuchsia")] { + mod fuchsia_mutex; + mod futex_rwlock; + mod futex_condvar; + pub(crate) use fuchsia_mutex::{Mutex, MovableMutex}; + pub(crate) use futex_rwlock::{RwLock, MovableRwLock}; + pub(crate) use futex_condvar::MovableCondvar; } else { mod pthread_mutex; mod pthread_rwlock; diff --git a/library/std/src/sys/unix/locks/pthread_condvar.rs b/library/std/src/sys/unix/locks/pthread_condvar.rs index 78f10f0534c..abf27e7db78 100644 --- a/library/std/src/sys/unix/locks/pthread_condvar.rs +++ b/library/std/src/sys/unix/locks/pthread_condvar.rs @@ -37,6 +37,7 @@ impl Condvar { #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "watchos", target_os = "l4re", target_os = "android", target_os = "redox" @@ -58,6 +59,7 @@ impl Condvar { #[cfg(not(any( target_os = "macos", target_os = "ios", + target_os = "watchos", target_os = "l4re", target_os = "android", target_os = "redox", @@ -102,6 +104,7 @@ impl Condvar { #[cfg(not(any( target_os = "macos", target_os = "ios", + target_os = "watchos", target_os = "android", target_os = "espidf", target_os = "horizon" @@ -135,6 +138,7 @@ impl Condvar { #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "watchos", target_os = "android", target_os = "espidf", target_os = "horizon" diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 34a023b02c4..3d0d91460f7 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -86,6 +86,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) { // The poll on Darwin doesn't set POLLNVAL for closed fds. target_os = "macos", target_os = "ios", + target_os = "watchos", target_os = "redox", target_os = "l4re", target_os = "horizon", @@ -329,7 +330,7 @@ cfg_if::cfg_if! { // See #41582 and https://blog.achernya.com/2013/03/os-x-has-silly-libsystem.html #[link(name = "resolv")] extern "C" {} - } else if #[cfg(target_os = "ios")] { + } else if #[cfg(any(target_os = "ios", target_os = "watchos"))] { #[link(name = "System")] #[link(name = "objc")] #[link(name = "Security", kind = "framework")] diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 7252ad32184..46545a0839f 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -61,7 +61,7 @@ extern "C" { )] #[cfg_attr(any(target_os = "solaris", target_os = "illumos"), link_name = "___errno")] #[cfg_attr( - any(target_os = "macos", target_os = "ios", target_os = "freebsd"), + any(target_os = "macos", target_os = "ios", target_os = "freebsd", target_os = "watchos"), link_name = "__error" )] #[cfg_attr(target_os = "haiku", link_name = "_errnop")] @@ -361,7 +361,7 @@ pub fn current_exe() -> io::Result<PathBuf> { } } -#[cfg(any(target_os = "macos", target_os = "ios"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] pub fn current_exe() -> io::Result<PathBuf> { unsafe { let mut sz: u32 = 0; @@ -598,6 +598,7 @@ pub fn home_dir() -> Option<PathBuf> { #[cfg(any( target_os = "android", target_os = "ios", + target_os = "watchos", target_os = "emscripten", target_os = "redox", target_os = "vxworks", @@ -610,6 +611,7 @@ pub fn home_dir() -> Option<PathBuf> { #[cfg(not(any( target_os = "android", target_os = "ios", + target_os = "watchos", target_os = "emscripten", target_os = "redox", target_os = "vxworks", diff --git a/library/std/src/sys/unix/rand.rs b/library/std/src/sys/unix/rand.rs index 56d01074c20..bf49204881d 100644 --- a/library/std/src/sys/unix/rand.rs +++ b/library/std/src/sys/unix/rand.rs @@ -14,6 +14,7 @@ pub fn hashmap_random_keys() -> (u64, u64) { unix, not(target_os = "macos"), not(target_os = "ios"), + not(target_os = "watchos"), not(target_os = "openbsd"), not(target_os = "freebsd"), not(target_os = "netbsd"), @@ -195,7 +196,7 @@ mod imp { // 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(target_os = "ios")] +#[cfg(any(target_os = "ios", target_os = "watchos"))] mod imp { use crate::io; use crate::ptr; diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs index d191e1fe7a6..36a3fa6023b 100644 --- a/library/std/src/sys/unix/thread.rs +++ b/library/std/src/sys/unix/thread.rs @@ -139,7 +139,7 @@ impl Thread { } } - #[cfg(any(target_os = "macos", target_os = "ios"))] + #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] pub fn set_name(name: &CStr) { unsafe { libc::pthread_setname_np(name.as_ptr()); @@ -285,7 +285,7 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> { ))] { #[cfg(any(target_os = "android", target_os = "linux"))] { - let quota = cgroup2_quota().max(1); + let quota = cgroups::quota().max(1); let mut set: libc::cpu_set_t = unsafe { mem::zeroed() }; unsafe { if libc::sched_getaffinity(0, mem::size_of::<libc::cpu_set_t>(), &mut set) == 0 { @@ -379,49 +379,88 @@ pub fn available_parallelism() -> io::Result<NonZeroUsize> { } } -/// Returns cgroup CPU quota in core-equivalents, rounded down, or usize::MAX if the quota cannot -/// be determined or is not set. #[cfg(any(target_os = "android", target_os = "linux"))] -fn cgroup2_quota() -> usize { +mod cgroups { + //! Currently not covered + //! * cgroup v2 in non-standard mountpoints + //! * paths containing control characters or spaces, since those would be escaped in procfs + //! output and we don't unescape + use crate::borrow::Cow; use crate::ffi::OsString; use crate::fs::{try_exists, File}; use crate::io::Read; + use crate::io::{BufRead, BufReader}; use crate::os::unix::ffi::OsStringExt; + use crate::path::Path; use crate::path::PathBuf; + use crate::str::from_utf8; - let mut quota = usize::MAX; - if cfg!(miri) { - // Attempting to open a file fails under default flags due to isolation. - // And Miri does not have parallelism anyway. - return quota; - } - - let _: Option<()> = try { - let mut buf = Vec::with_capacity(128); - // find our place in the cgroup hierarchy - File::open("/proc/self/cgroup").ok()?.read_to_end(&mut buf).ok()?; - let cgroup_path = buf - .split(|&c| c == b'\n') - .filter_map(|line| { - let mut fields = line.splitn(3, |&c| c == b':'); - // expect cgroupv2 which has an empty 2nd field - if fields.nth(1) != Some(b"") { - return None; - } - let path = fields.last()?; - // skip leading slash - Some(path[1..].to_owned()) - }) - .next()?; - let cgroup_path = PathBuf::from(OsString::from_vec(cgroup_path)); + #[derive(PartialEq)] + enum Cgroup { + V1, + V2, + } + + /// Returns cgroup CPU quota in core-equivalents, rounded down or usize::MAX if the quota cannot + /// be determined or is not set. + pub(super) fn quota() -> usize { + let mut quota = usize::MAX; + if cfg!(miri) { + // Attempting to open a file fails under default flags due to isolation. + // And Miri does not have parallelism anyway. + return quota; + } + + let _: Option<()> = try { + let mut buf = Vec::with_capacity(128); + // find our place in the cgroup hierarchy + File::open("/proc/self/cgroup").ok()?.read_to_end(&mut buf).ok()?; + let (cgroup_path, version) = + buf.split(|&c| c == b'\n').fold(None, |previous, line| { + let mut fields = line.splitn(3, |&c| c == b':'); + // 2nd field is a list of controllers for v1 or empty for v2 + let version = match fields.nth(1) { + Some(b"") => Cgroup::V2, + Some(controllers) + if from_utf8(controllers) + .is_ok_and(|c| c.split(",").any(|c| c == "cpu")) => + { + Cgroup::V1 + } + _ => return previous, + }; + + // already-found v1 trumps v2 since it explicitly specifies its controllers + if previous.is_some() && version == Cgroup::V2 { + return previous; + } + + let path = fields.last()?; + // skip leading slash + Some((path[1..].to_owned(), version)) + })?; + let cgroup_path = PathBuf::from(OsString::from_vec(cgroup_path)); + + quota = match version { + Cgroup::V1 => quota_v1(cgroup_path), + Cgroup::V2 => quota_v2(cgroup_path), + }; + }; + + quota + } + + fn quota_v2(group_path: PathBuf) -> usize { + let mut quota = usize::MAX; let mut path = PathBuf::with_capacity(128); let mut read_buf = String::with_capacity(20); + // standard mount location defined in file-hierarchy(7) manpage let cgroup_mount = "/sys/fs/cgroup"; path.push(cgroup_mount); - path.push(&cgroup_path); + path.push(&group_path); path.push("cgroup.controllers"); @@ -432,30 +471,134 @@ fn cgroup2_quota() -> usize { path.pop(); - while path.starts_with(cgroup_mount) { - path.push("cpu.max"); + let _: Option<()> = try { + while path.starts_with(cgroup_mount) { + path.push("cpu.max"); + + read_buf.clear(); + + if File::open(&path).and_then(|mut f| f.read_to_string(&mut read_buf)).is_ok() { + let raw_quota = read_buf.lines().next()?; + let mut raw_quota = raw_quota.split(' '); + let limit = raw_quota.next()?; + let period = raw_quota.next()?; + match (limit.parse::<usize>(), period.parse::<usize>()) { + (Ok(limit), Ok(period)) => { + quota = quota.min(limit / period); + } + _ => {} + } + } - read_buf.clear(); + path.pop(); // pop filename + path.pop(); // pop dir + } + }; - if File::open(&path).and_then(|mut f| f.read_to_string(&mut read_buf)).is_ok() { - let raw_quota = read_buf.lines().next()?; - let mut raw_quota = raw_quota.split(' '); - let limit = raw_quota.next()?; - let period = raw_quota.next()?; - match (limit.parse::<usize>(), period.parse::<usize>()) { - (Ok(limit), Ok(period)) => { - quota = quota.min(limit / period); - } + quota + } + + fn quota_v1(group_path: PathBuf) -> usize { + let mut quota = usize::MAX; + let mut path = PathBuf::with_capacity(128); + let mut read_buf = String::with_capacity(20); + + // Hardcode commonly used locations mentioned in the cgroups(7) manpage + // if that doesn't work scan mountinfo and adjust `group_path` for bind-mounts + let mounts: &[fn(&Path) -> Option<(_, &Path)>] = &[ + |p| Some((Cow::Borrowed("/sys/fs/cgroup/cpu"), p)), + |p| Some((Cow::Borrowed("/sys/fs/cgroup/cpu,cpuacct"), p)), + // this can be expensive on systems with tons of mountpoints + // but we only get to this point when /proc/self/cgroups explicitly indicated + // this process belongs to a cpu-controller cgroup v1 and the defaults didn't work + find_mountpoint, + ]; + + for mount in mounts { + let Some((mount, group_path)) = mount(&group_path) else { continue }; + + path.clear(); + path.push(mount.as_ref()); + path.push(&group_path); + + // skip if we guessed the mount incorrectly + if matches!(try_exists(&path), Err(_) | Ok(false)) { + continue; + } + + while path.starts_with(mount.as_ref()) { + let mut parse_file = |name| { + path.push(name); + read_buf.clear(); + + let f = File::open(&path); + path.pop(); // restore buffer before any early returns + f.ok()?.read_to_string(&mut read_buf).ok()?; + let parsed = read_buf.trim().parse::<usize>().ok()?; + + Some(parsed) + }; + + let limit = parse_file("cpu.cfs_quota_us"); + let period = parse_file("cpu.cfs_period_us"); + + match (limit, period) { + (Some(limit), Some(period)) => quota = quota.min(limit / period), _ => {} } + + path.pop(); } - path.pop(); // pop filename - path.pop(); // pop dir + // we passed the try_exists above so we should have traversed the correct hierarchy + // when reaching this line + break; } - }; - quota + quota + } + + /// Scan mountinfo for cgroup v1 mountpoint with a cpu controller + /// + /// If the cgroupfs is a bind mount then `group_path` is adjusted to skip + /// over the already-included prefix + fn find_mountpoint(group_path: &Path) -> Option<(Cow<'static, str>, &Path)> { + let mut reader = BufReader::new(File::open("/proc/self/mountinfo").ok()?); + let mut line = String::with_capacity(256); + loop { + line.clear(); + if reader.read_line(&mut line).ok()? == 0 { + break; + } + + let line = line.trim(); + let mut items = line.split(' '); + + let sub_path = items.nth(3)?; + let mount_point = items.next()?; + let mount_opts = items.next_back()?; + let filesystem_type = items.nth_back(1)?; + + if filesystem_type != "cgroup" || !mount_opts.split(',').any(|opt| opt == "cpu") { + // not a cgroup / not a cpu-controller + continue; + } + + let sub_path = Path::new(sub_path).strip_prefix("/").ok()?; + + if !group_path.starts_with(sub_path) { + // this is a bind-mount and the bound subdirectory + // does not contain the cgroup this process belongs to + continue; + } + + let trimmed_group_path = group_path.strip_prefix(sub_path).ok()?; + + return Some((Cow::Owned(mount_point.to_owned()), trimmed_group_path)); + } + + None + } } #[cfg(all( diff --git a/library/std/src/sys/unix/thread_parker.rs b/library/std/src/sys/unix/thread_parker.rs index 9f4d4f7e736..ca1a7138fde 100644 --- a/library/std/src/sys/unix/thread_parker.rs +++ b/library/std/src/sys/unix/thread_parker.rs @@ -52,7 +52,12 @@ unsafe fn wait_timeout( ) { // Use the system clock on systems that do not support pthread_condattr_setclock. // This unfortunately results in problems when the system time changes. - #[cfg(any(target_os = "macos", target_os = "ios", target_os = "espidf"))] + #[cfg(any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "espidf" + ))] let (now, dur) = { use super::time::SystemTime; use crate::cmp::min; @@ -73,7 +78,12 @@ unsafe fn wait_timeout( (now, dur) }; // Use the monotonic clock on other systems. - #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "espidf")))] + #[cfg(not(any( + target_os = "macos", + target_os = "ios", + target_os = "watchos", + target_os = "espidf" + )))] let (now, dur) = { use super::time::Timespec; @@ -111,6 +121,7 @@ impl Parker { if #[cfg(any( target_os = "macos", target_os = "ios", + target_os = "watchos", target_os = "l4re", target_os = "android", target_os = "redox" diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs index d114af49d26..dff973f59d1 100644 --- a/library/std/src/sys/unix/time.rs +++ b/library/std/src/sys/unix/time.rs @@ -141,7 +141,7 @@ impl From<libc::timespec> for Timespec { } } -#[cfg(any(target_os = "macos", target_os = "ios"))] +#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))] mod inner { use crate::sync::atomic::{AtomicU64, Ordering}; use crate::sys::cvt; @@ -257,7 +257,7 @@ mod inner { } } -#[cfg(not(any(target_os = "macos", target_os = "ios")))] +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "watchos")))] mod inner { use crate::fmt; use crate::mem::MaybeUninit; diff --git a/library/std/src/sys/windows/fs.rs b/library/std/src/sys/windows/fs.rs index e9b10069077..4d3162f1254 100644 --- a/library/std/src/sys/windows/fs.rs +++ b/library/std/src/sys/windows/fs.rs @@ -155,22 +155,7 @@ impl DirEntry { } pub fn metadata(&self) -> io::Result<FileAttr> { - Ok(FileAttr { - attributes: self.data.dwFileAttributes, - creation_time: self.data.ftCreationTime, - last_access_time: self.data.ftLastAccessTime, - last_write_time: self.data.ftLastWriteTime, - file_size: ((self.data.nFileSizeHigh as u64) << 32) | (self.data.nFileSizeLow as u64), - reparse_tag: if self.data.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { - // reserved unless this is a reparse point - self.data.dwReserved0 - } else { - 0 - }, - volume_serial_number: None, - number_of_links: None, - file_index: None, - }) + Ok(self.data.into()) } } @@ -879,6 +864,26 @@ impl FileAttr { self.file_index } } +impl From<c::WIN32_FIND_DATAW> for FileAttr { + fn from(wfd: c::WIN32_FIND_DATAW) -> Self { + FileAttr { + attributes: wfd.dwFileAttributes, + creation_time: wfd.ftCreationTime, + last_access_time: wfd.ftLastAccessTime, + last_write_time: wfd.ftLastWriteTime, + file_size: ((wfd.nFileSizeHigh as u64) << 32) | (wfd.nFileSizeLow as u64), + reparse_tag: if wfd.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { + // reserved unless this is a reparse point + wfd.dwReserved0 + } else { + 0 + }, + volume_serial_number: None, + number_of_links: None, + file_index: None, + } + } +} fn to_u64(ft: &c::FILETIME) -> u64 { (ft.dwLowDateTime as u64) | ((ft.dwHighDateTime as u64) << 32) @@ -1145,22 +1150,73 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> { } pub fn stat(path: &Path) -> io::Result<FileAttr> { - let mut opts = OpenOptions::new(); - // No read or write permissions are necessary - opts.access_mode(0); - // This flag is so we can open directories too - opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); - let file = File::open(path, &opts)?; - file.file_attr() + metadata(path, ReparsePoint::Follow) } pub fn lstat(path: &Path) -> io::Result<FileAttr> { + metadata(path, ReparsePoint::Open) +} + +#[repr(u32)] +#[derive(Clone, Copy, PartialEq, Eq)] +enum ReparsePoint { + Follow = 0, + Open = c::FILE_FLAG_OPEN_REPARSE_POINT, +} +impl ReparsePoint { + fn as_flag(self) -> u32 { + self as u32 + } +} + +fn metadata(path: &Path, reparse: ReparsePoint) -> io::Result<FileAttr> { let mut opts = OpenOptions::new(); // No read or write permissions are necessary opts.access_mode(0); - opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); - let file = File::open(path, &opts)?; - file.file_attr() + opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | reparse.as_flag()); + + // Attempt to open the file normally. + // If that fails with `ERROR_SHARING_VIOLATION` then retry using `FindFirstFileW`. + // If the fallback fails for any reason we return the original error. + match File::open(path, &opts) { + Ok(file) => file.file_attr(), + Err(e) if e.raw_os_error() == Some(c::ERROR_SHARING_VIOLATION as _) => { + // `ERROR_SHARING_VIOLATION` will almost never be returned. + // Usually if a file is locked you can still read some metadata. + // However, there are special system files, such as + // `C:\hiberfil.sys`, that are locked in a way that denies even that. + unsafe { + let path = maybe_verbatim(path)?; + + // `FindFirstFileW` accepts wildcard file names. + // Fortunately wildcards are not valid file names and + // `ERROR_SHARING_VIOLATION` means the file exists (but is locked) + // therefore it's safe to assume the file name given does not + // include wildcards. + let mut wfd = mem::zeroed(); + let handle = c::FindFirstFileW(path.as_ptr(), &mut wfd); + + if handle == c::INVALID_HANDLE_VALUE { + // This can fail if the user does not have read access to the + // directory. + Err(e) + } else { + // We no longer need the find handle. + c::FindClose(handle); + + // `FindFirstFileW` reads the cached file information from the + // directory. The downside is that this metadata may be outdated. + let attrs = FileAttr::from(wfd); + if reparse == ReparsePoint::Follow && attrs.file_type().is_symlink() { + Err(e) + } else { + Ok(attrs) + } + } + } + } + Err(e) => Err(e), + } } pub fn set_perm(p: &Path, perm: FilePermissions) -> io::Result<()> { diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index f5730a2cea5..c13bda32823 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -18,7 +18,7 @@ use libc::{c_int, c_void}; cfg_if::cfg_if! { if #[cfg(any( target_os = "dragonfly", target_os = "freebsd", - target_os = "ios", target_os = "macos", + target_os = "ios", target_os = "macos", target_os = "watchos", target_os = "openbsd", target_os = "netbsd", target_os = "illumos", target_os = "solaris", target_os = "haiku", target_os = "l4re"))] { use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index d28c7b58b20..44c8a50fd86 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -1577,10 +1577,15 @@ fn _assert_sync_and_send() { /// /// On Linux: /// - It may overcount the amount of parallelism available when limited by a -/// process-wide affinity mask or cgroup quotas and cgroup2 fs or `sched_getaffinity()` can't be +/// process-wide affinity mask or cgroup quotas and `sched_getaffinity()` or cgroup fs can't be /// queried, e.g. due to sandboxing. /// - It may undercount the amount of parallelism if the current thread's affinity mask /// does not reflect the process' cpuset, e.g. due to pinned threads. +/// - If the process is in a cgroup v1 cpu controller, this may need to +/// scan mountpoints to find the corresponding cgroup v1 controller, +/// which may take time on systems with large numbers of mountpoints. +/// (This does not apply to cgroup v2, or to processes not in a +/// cgroup.) /// /// On all targets: /// - It may overcount the amount of parallelism available when running in a VM |
