diff options
Diffstat (limited to 'library/std/src/sys')
55 files changed, 899 insertions, 545 deletions
diff --git a/library/std/src/sys/alloc/unix.rs b/library/std/src/sys/alloc/unix.rs index 266b69cdc1e..1af9d766290 100644 --- a/library/std/src/sys/alloc/unix.rs +++ b/library/std/src/sys/alloc/unix.rs @@ -71,6 +71,7 @@ cfg_if::cfg_if! { } } else { #[inline] + #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { let mut out = ptr::null_mut(); // We prefer posix_memalign over aligned_alloc since it is more widely available, and diff --git a/library/std/src/sys/dbg.rs b/library/std/src/sys/dbg.rs deleted file mode 100644 index 383ed84cfa2..00000000000 --- a/library/std/src/sys/dbg.rs +++ /dev/null @@ -1,229 +0,0 @@ -//! Debugging aids. - -/// Presence of a debugger. The debugger being concerned -/// is expected to use the OS API to debug this process. -#[derive(Copy, Clone, Debug)] -#[allow(unused)] -pub(crate) enum DebuggerPresence { - /// The debugger is attached to this process. - Detected, - /// The debugger is not attached to this process. - NotDetected, -} - -#[cfg(target_os = "windows")] -mod os { - use super::DebuggerPresence; - - #[link(name = "kernel32")] - extern "system" { - fn IsDebuggerPresent() -> i32; - } - - pub(super) fn is_debugger_present() -> Option<DebuggerPresence> { - // SAFETY: No state is shared between threads. The call reads - // a field from the Thread Environment Block using the OS API - // as required by the documentation. - if unsafe { IsDebuggerPresent() } != 0 { - Some(DebuggerPresence::Detected) - } else { - Some(DebuggerPresence::NotDetected) - } - } -} - -#[cfg(any(target_vendor = "apple", target_os = "freebsd"))] -mod os { - use libc::{CTL_KERN, KERN_PROC, KERN_PROC_PID, c_int, sysctl}; - - use super::DebuggerPresence; - use crate::io::{Cursor, Read, Seek, SeekFrom}; - use crate::process; - - const P_TRACED: i32 = 0x00000800; - - // The assumption is that the kernel structures available to the - // user space may not shrink or repurpose the existing fields over - // time. The kernels normally adhere to that for the backward - // compatibility of the user space. - - // The macOS 14.5 SDK comes with a header `MacOSX14.5.sdk/usr/include/sys/sysctl.h` - // that defines `struct kinfo_proc` be of `648` bytes on the 64-bit system. That has - // not changed since macOS 10.13 (released in 2017) at least, validated by building - // a C program in XCode while changing the build target. Apple provides this example - // for reference: https://developer.apple.com/library/archive/qa/qa1361/_index.html. - #[cfg(target_vendor = "apple")] - const KINFO_PROC_SIZE: usize = if cfg!(target_pointer_width = "64") { 648 } else { 492 }; - #[cfg(target_vendor = "apple")] - const KINFO_PROC_FLAGS_OFFSET: u64 = if cfg!(target_pointer_width = "64") { 32 } else { 16 }; - - // Works for FreeBSD stable (13.3, 13.4) and current (14.0, 14.1). - // The size of the structure has stayed the same for a long time, - // at least since 2005: - // https://lists.freebsd.org/pipermail/freebsd-stable/2005-November/019899.html - #[cfg(target_os = "freebsd")] - const KINFO_PROC_SIZE: usize = if cfg!(target_pointer_width = "64") { 1088 } else { 768 }; - #[cfg(target_os = "freebsd")] - const KINFO_PROC_FLAGS_OFFSET: u64 = if cfg!(target_pointer_width = "64") { 368 } else { 296 }; - - pub(super) fn is_debugger_present() -> Option<DebuggerPresence> { - debug_assert_ne!(KINFO_PROC_SIZE, 0); - - let mut flags = [0u8; 4]; // `ki_flag` under FreeBSD and `p_flag` under macOS. - let mut mib = [CTL_KERN, KERN_PROC, KERN_PROC_PID, process::id() as c_int]; - let mut info_size = KINFO_PROC_SIZE; - let mut kinfo_proc = [0u8; KINFO_PROC_SIZE]; - - // SAFETY: No state is shared with other threads. The sysctl call - // is safe according to the documentation. - if unsafe { - sysctl( - mib.as_mut_ptr(), - mib.len() as u32, - kinfo_proc.as_mut_ptr().cast(), - &mut info_size, - core::ptr::null_mut(), - 0, - ) - } != 0 - { - return None; - } - debug_assert_eq!(info_size, KINFO_PROC_SIZE); - - let mut reader = Cursor::new(kinfo_proc); - reader.seek(SeekFrom::Start(KINFO_PROC_FLAGS_OFFSET)).ok()?; - reader.read_exact(&mut flags).ok()?; - // Just in case, not limiting this to the little-endian systems. - let flags = i32::from_ne_bytes(flags); - - if flags & P_TRACED != 0 { - Some(DebuggerPresence::Detected) - } else { - Some(DebuggerPresence::NotDetected) - } - } -} - -#[cfg(target_os = "linux")] -mod os { - use super::DebuggerPresence; - use crate::fs::File; - use crate::io::Read; - - pub(super) fn is_debugger_present() -> Option<DebuggerPresence> { - // This function is crafted with the following goals: - // * Memory efficiency: It avoids crashing the panicking process due to - // out-of-memory (OOM) conditions by not using large heap buffers or - // allocating significant stack space, which could lead to stack overflow. - // * Minimal binary size: The function uses a minimal set of facilities - // from the standard library to avoid increasing the resulting binary size. - // - // To achieve these goals, the function does not use `[std::io::BufReader]` - // and instead reads the file byte by byte using a sliding window approach. - // It's important to note that the "/proc/self/status" pseudo-file is synthesized - // by the Virtual File System (VFS), meaning it is not read from a slow or - // non-volatile storage medium so buffering might not be as beneficial because - // all data is read from memory, though this approach does incur a syscall for - // each byte read. - // - // We cannot make assumptions about the file size or the position of the - // target prefix ("TracerPid:"), so the function does not use - // `[std::fs::read_to_string]` thus not employing UTF-8 to ASCII checking, - // conversion, or parsing as we're looking for an ASCII prefix. - // - // These condiderations make the function deviate from the familiar concise pattern - // of searching for a string in a text file. - - fn read_byte(file: &mut File) -> Option<u8> { - let mut buffer = [0]; - file.read_exact(&mut buffer).ok()?; - Some(buffer[0]) - } - - // The ASCII prefix of the datum we're interested in. - const TRACER_PID: &[u8] = b"TracerPid:\t"; - - let mut file = File::open("/proc/self/status").ok()?; - let mut matched = 0; - - // Look for the `TRACER_PID` prefix. - while let Some(byte) = read_byte(&mut file) { - if byte == TRACER_PID[matched] { - matched += 1; - if matched == TRACER_PID.len() { - break; - } - } else { - matched = 0; - } - } - - // Was the prefix found? - if matched != TRACER_PID.len() { - return None; - } - - // It was; get the ASCII representation of the first digit - // of the PID. That is enough to see if there is a debugger - // attached as the kernel does not pad the PID on the left - // with the leading zeroes. - let byte = read_byte(&mut file)?; - if byte.is_ascii_digit() && byte != b'0' { - Some(DebuggerPresence::Detected) - } else { - Some(DebuggerPresence::NotDetected) - } - } -} - -#[cfg(not(any( - target_os = "windows", - target_vendor = "apple", - target_os = "freebsd", - target_os = "linux" -)))] -mod os { - pub(super) fn is_debugger_present() -> Option<super::DebuggerPresence> { - None - } -} - -/// Detect the debugger presence. -/// -/// The code does not try to detect the debugger at all costs (e.g., when anti-debugger -/// tricks are at play), it relies on the interfaces provided by the OS. -/// -/// Return value: -/// * `None`: it's not possible to conclude whether the debugger is attached to this -/// process or not. When checking for the presence of the debugger, the detection logic -/// encountered an issue, such as the OS API throwing an error or the feature not being -/// implemented. -/// * `Some(DebuggerPresence::Detected)`: yes, the debugger is attached -/// to this process. -/// * `Some(DebuggerPresence::NotDetected)`: no, the debugger is not -/// attached to this process. -pub(crate) fn is_debugger_present() -> Option<DebuggerPresence> { - if cfg!(miri) { None } else { os::is_debugger_present() } -} - -/// Execute the breakpoint instruction if the debugger presence is detected. -/// Useful for breaking into the debugger without the need to set a breakpoint -/// in the debugger. -/// -/// Note that there is a race between attaching or detaching the debugger, and running the -/// breakpoint instruction. This is nonetheless memory-safe, like [`crate::process::abort`] -/// is. In case the debugger is attached and the function is about -/// to run the breakpoint instruction yet right before that the debugger detaches, the -/// process will crash due to running the breakpoint instruction and the debugger not -/// handling the trap exception. -pub(crate) fn breakpoint_if_debugging() -> Option<DebuggerPresence> { - let debugger_present = is_debugger_present(); - if let Some(DebuggerPresence::Detected) = debugger_present { - // SAFETY: Executing the breakpoint instruction. No state is shared - // or modified by this code. - unsafe { core::intrinsics::breakpoint() }; - } - - debugger_present -} diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index df25b84fbbe..f17dd47dece 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -11,7 +11,6 @@ mod personality; pub mod anonymous_pipe; pub mod backtrace; pub mod cmath; -pub mod dbg; pub mod exit_guard; pub mod os_str; pub mod path; diff --git a/library/std/src/sys/os_str/bytes.rs b/library/std/src/sys/os_str/bytes.rs index 992767211d0..8e0609fe48c 100644 --- a/library/std/src/sys/os_str/bytes.rs +++ b/library/std/src/sys/os_str/bytes.rs @@ -2,7 +2,6 @@ //! systems: just a `Vec<u8>`/`[u8]`. use core::clone::CloneToUninit; -use core::ptr::addr_of_mut; use crate::borrow::Cow; use crate::collections::TryReserveError; @@ -355,6 +354,6 @@ unsafe impl CloneToUninit for Slice { #[cfg_attr(debug_assertions, track_caller)] unsafe fn clone_to_uninit(&self, dst: *mut Self) { // SAFETY: we're just a wrapper around [u8] - unsafe { self.inner.clone_to_uninit(addr_of_mut!((*dst).inner)) } + unsafe { self.inner.clone_to_uninit(&raw mut (*dst).inner) } } } diff --git a/library/std/src/sys/os_str/wtf8.rs b/library/std/src/sys/os_str/wtf8.rs index 6fbbec7a945..b3834388df6 100644 --- a/library/std/src/sys/os_str/wtf8.rs +++ b/library/std/src/sys/os_str/wtf8.rs @@ -1,7 +1,6 @@ //! The underlying OsString/OsStr implementation on Windows is a //! wrapper around the "WTF-8" encoding; see the `wtf8` module for more. use core::clone::CloneToUninit; -use core::ptr::addr_of_mut; use crate::borrow::Cow; use crate::collections::TryReserveError; @@ -278,6 +277,6 @@ unsafe impl CloneToUninit for Slice { #[cfg_attr(debug_assertions, track_caller)] unsafe fn clone_to_uninit(&self, dst: *mut Self) { // SAFETY: we're just a wrapper around Wtf8 - unsafe { self.inner.clone_to_uninit(addr_of_mut!((*dst).inner)) } + unsafe { self.inner.clone_to_uninit(&raw mut (*dst).inner) } } } diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs index f49ef947174..b62afb40a61 100644 --- a/library/std/src/sys/pal/hermit/mod.rs +++ b/library/std/src/sys/pal/hermit/mod.rs @@ -92,7 +92,11 @@ pub unsafe extern "C" fn runtime_entry( unsafe { crate::sys::thread_local::destructors::run(); } - unsafe { hermit_abi::exit(result) } + crate::rt::thread_cleanup(); + + unsafe { + hermit_abi::exit(result); + } } #[inline] diff --git a/library/std/src/sys/pal/hermit/net.rs b/library/std/src/sys/pal/hermit/net.rs index 416469c0037..d9baa091a23 100644 --- a/library/std/src/sys/pal/hermit/net.rs +++ b/library/std/src/sys/pal/hermit/net.rs @@ -192,7 +192,7 @@ impl Socket { buf.as_mut_ptr(), buf.len(), flags, - core::ptr::addr_of_mut!(storage) as *mut _, + (&raw mut storage) as *mut _, &mut addrlen, ) })?; @@ -298,7 +298,7 @@ impl Socket { netc::ioctl( self.as_raw_fd(), netc::FIONBIO, - core::ptr::addr_of_mut!(nonblocking) as *mut core::ffi::c_void, + (&raw mut nonblocking) as *mut core::ffi::c_void, ) }) .map(drop) diff --git a/library/std/src/sys/pal/hermit/thread.rs b/library/std/src/sys/pal/hermit/thread.rs index 4c0c0919f47..41f2c3e2123 100644 --- a/library/std/src/sys/pal/hermit/thread.rs +++ b/library/std/src/sys/pal/hermit/thread.rs @@ -53,6 +53,7 @@ impl Thread { // run all destructors crate::sys::thread_local::destructors::run(); + crate::rt::thread_cleanup(); } } } diff --git a/library/std/src/sys/pal/hermit/time.rs b/library/std/src/sys/pal/hermit/time.rs index 99166b15602..e0b6eb76b03 100644 --- a/library/std/src/sys/pal/hermit/time.rs +++ b/library/std/src/sys/pal/hermit/time.rs @@ -107,8 +107,7 @@ pub struct Instant(Timespec); impl Instant { pub fn now() -> Instant { let mut time: Timespec = Timespec::zero(); - let _ = - unsafe { hermit_abi::clock_gettime(CLOCK_MONOTONIC, core::ptr::addr_of_mut!(time.t)) }; + let _ = unsafe { hermit_abi::clock_gettime(CLOCK_MONOTONIC, &raw mut time.t) }; Instant(time) } @@ -209,8 +208,7 @@ impl SystemTime { pub fn now() -> SystemTime { let mut time: Timespec = Timespec::zero(); - let _ = - unsafe { hermit_abi::clock_gettime(CLOCK_REALTIME, core::ptr::addr_of_mut!(time.t)) }; + let _ = unsafe { hermit_abi::clock_gettime(CLOCK_REALTIME, &raw mut time.t) }; SystemTime(time) } diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs index a943e3a581a..8438a61e90f 100644 --- a/library/std/src/sys/pal/unix/args.rs +++ b/library/std/src/sys/pal/unix/args.rs @@ -113,6 +113,7 @@ impl DoubleEndedIterator for Args { target_os = "nto", target_os = "hurd", target_os = "rtems", + target_os = "nuttx", ))] mod imp { use crate::ffi::c_char; diff --git a/library/std/src/sys/pal/unix/env.rs b/library/std/src/sys/pal/unix/env.rs index b2d399b8791..2aee0b5d460 100644 --- a/library/std/src/sys/pal/unix/env.rs +++ b/library/std/src/sys/pal/unix/env.rs @@ -283,3 +283,14 @@ pub mod os { pub const EXE_SUFFIX: &str = ""; pub const EXE_EXTENSION: &str = ""; } + +#[cfg(target_os = "nuttx")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "nuttx"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} diff --git a/library/std/src/sys/pal/unix/fd.rs b/library/std/src/sys/pal/unix/fd.rs index 135042779ad..6a28799ca55 100644 --- a/library/std/src/sys/pal/unix/fd.rs +++ b/library/std/src/sys/pal/unix/fd.rs @@ -98,7 +98,12 @@ impl FileDesc { Ok(ret as usize) } - #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))] + #[cfg(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + )))] pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { let ret = cvt(unsafe { libc::readv( @@ -110,14 +115,24 @@ impl FileDesc { Ok(ret as usize) } - #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] + #[cfg(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + ))] pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { io::default_read_vectored(|b| self.read(b), bufs) } #[inline] pub fn is_read_vectored(&self) -> bool { - cfg!(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))) + cfg!(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + ))) } pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> { @@ -297,7 +312,12 @@ impl FileDesc { Ok(ret as usize) } - #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))] + #[cfg(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + )))] pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { let ret = cvt(unsafe { libc::writev( @@ -309,14 +329,24 @@ impl FileDesc { Ok(ret as usize) } - #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] + #[cfg(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + ))] pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { io::default_write_vectored(|b| self.write(b), bufs) } #[inline] pub fn is_write_vectored(&self) -> bool { - cfg!(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))) + cfg!(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + ))) } #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index dade6d6bbae..39aabf0b2d6 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -479,6 +479,7 @@ impl FileAttr { target_os = "vita", target_os = "hurd", target_os = "rtems", + target_os = "nuttx", )))] pub fn modified(&self) -> io::Result<SystemTime> { #[cfg(target_pointer_width = "32")] @@ -501,7 +502,7 @@ impl FileAttr { SystemTime::new(self.stat.st_mtime as i64, 0) } - #[cfg(any(target_os = "horizon", target_os = "hurd"))] + #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))] pub fn modified(&self) -> io::Result<SystemTime> { SystemTime::new(self.stat.st_mtim.tv_sec as i64, self.stat.st_mtim.tv_nsec as i64) } @@ -513,6 +514,7 @@ impl FileAttr { target_os = "vita", target_os = "hurd", target_os = "rtems", + target_os = "nuttx", )))] pub fn accessed(&self) -> io::Result<SystemTime> { #[cfg(target_pointer_width = "32")] @@ -535,7 +537,7 @@ impl FileAttr { SystemTime::new(self.stat.st_atime as i64, 0) } - #[cfg(any(target_os = "horizon", target_os = "hurd"))] + #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))] pub fn accessed(&self) -> io::Result<SystemTime> { SystemTime::new(self.stat.st_atim.tv_sec as i64, self.stat.st_atim.tv_nsec as i64) } @@ -738,7 +740,7 @@ impl Iterator for ReadDir { // // Like for uninitialized contents, converting entry_ptr to `&dirent64` // would not be legal. However, unique to dirent64 is that we don't even - // get to use `addr_of!((*entry_ptr).d_name)` because that operation + // get to use `&raw const (*entry_ptr).d_name` because that operation // requires the full extent of *entry_ptr to be in bounds of the same // allocation, which is not necessarily the case here. // @@ -752,7 +754,7 @@ impl Iterator for ReadDir { } else { #[allow(deref_nullptr)] { - ptr::addr_of!((*ptr::null::<dirent64>()).$field) + &raw const (*ptr::null::<dirent64>()).$field } } }}; @@ -866,6 +868,7 @@ impl Drop for Dir { target_os = "horizon", target_os = "vxworks", target_os = "rtems", + target_os = "nuttx", )))] { let fd = unsafe { libc::dirfd(self.0) }; @@ -1000,6 +1003,13 @@ impl DirEntry { self.entry.d_fileno as u64 } + #[cfg(target_os = "nuttx")] + pub fn ino(&self) -> u64 { + // Leave this 0 for now, as NuttX does not provide an inode number + // in its directory entries. + 0 + } + #[cfg(any( target_os = "netbsd", target_os = "openbsd", @@ -1327,7 +1337,8 @@ impl File { target_os = "redox", target_os = "espidf", target_os = "horizon", - target_os = "vxworks" + target_os = "vxworks", + target_os = "nuttx", )))] let to_timespec = |time: Option<SystemTime>| match time { Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), @@ -1342,7 +1353,7 @@ impl File { None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), }; cfg_if::cfg_if! { - if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "vxworks"))] { + if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "vxworks", target_os = "nuttx"))] { // Redox doesn't appear to support `UTIME_OMIT`. // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore // the same as for Redox. @@ -1374,7 +1385,7 @@ impl File { } cvt(unsafe { libc::fsetattrlist( self.as_raw_fd(), - core::ptr::addr_of!(attrlist).cast::<libc::c_void>().cast_mut(), + (&raw const attrlist).cast::<libc::c_void>().cast_mut(), buf.as_ptr().cast::<libc::c_void>().cast_mut(), num_times * mem::size_of::<libc::timespec>(), 0 @@ -1933,7 +1944,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { libc::copyfile_state_get( state.0, libc::COPYFILE_STATE_COPIED as u32, - core::ptr::addr_of_mut!(bytes_copied) as *mut libc::c_void, + (&raw mut bytes_copied) as *mut libc::c_void, ) })?; Ok(bytes_copied as u64) diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 0d63b1119d5..4fe18daa204 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -222,6 +222,7 @@ static ON_BROKEN_PIPE_FLAG_USED: crate::sync::atomic::AtomicBool = target_os = "horizon", target_os = "vxworks", target_os = "vita", + target_os = "nuttx", )))] pub(crate) fn on_broken_pipe_flag_used() -> bool { ON_BROKEN_PIPE_FLAG_USED.load(crate::sync::atomic::Ordering::Relaxed) @@ -425,7 +426,7 @@ cfg_if::cfg_if! { } } -#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] +#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] mod unsupported { use crate::io; diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs index e98232ba89a..6a67bb0a101 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/pal/unix/net.rs @@ -38,19 +38,19 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> { // We may need to trigger a glibc workaround. See on_resolver_failure() for details. on_resolver_failure(); - #[cfg(not(target_os = "espidf"))] + #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] if err == libc::EAI_SYSTEM { return Err(io::Error::last_os_error()); } - #[cfg(not(target_os = "espidf"))] + #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] let detail = unsafe { // We can't always expect a UTF-8 environment. When we don't get that luxury, // it's better to give a low-quality error message than none at all. CStr::from_ptr(libc::gai_strerror(err)).to_string_lossy() }; - #[cfg(target_os = "espidf")] + #[cfg(any(target_os = "espidf", target_os = "nuttx"))] let detail = ""; Err(io::Error::new( @@ -329,7 +329,7 @@ impl Socket { buf.as_mut_ptr() as *mut c_void, buf.len(), flags, - core::ptr::addr_of_mut!(storage) as *mut _, + (&raw mut storage) as *mut _, &mut addrlen, ) })?; diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 503f8915256..f983d174ed6 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -48,6 +48,7 @@ extern "C" { target_os = "openbsd", target_os = "android", target_os = "redox", + target_os = "nuttx", target_env = "newlib" ), link_name = "__errno" @@ -399,6 +400,7 @@ pub fn current_exe() -> io::Result<PathBuf> { target_os = "linux", target_os = "hurd", target_os = "android", + target_os = "nuttx", target_os = "emscripten" ))] pub fn current_exe() -> io::Result<PathBuf> { @@ -610,7 +612,7 @@ pub unsafe fn environ() -> *mut *const *const c_char { extern "C" { static mut environ: *const *const c_char; } - ptr::addr_of_mut!(environ) + &raw mut environ } static ENV_LOCK: RwLock<()> = RwLock::new(()); @@ -717,6 +719,7 @@ pub fn home_dir() -> Option<PathBuf> { target_os = "espidf", target_os = "horizon", target_os = "vita", + target_os = "nuttx", all(target_vendor = "apple", not(target_os = "macos")), ))] unsafe fn fallback() -> Option<OsString> { @@ -730,6 +733,7 @@ pub fn home_dir() -> Option<PathBuf> { target_os = "espidf", target_os = "horizon", target_os = "vita", + target_os = "nuttx", all(target_vendor = "apple", not(target_os = "macos")), )))] unsafe fn fallback() -> Option<OsString> { diff --git a/library/std/src/sys/pal/unix/process/mod.rs b/library/std/src/sys/pal/unix/process/mod.rs index 074f0a105e3..2751d51c44d 100644 --- a/library/std/src/sys/pal/unix/process/mod.rs +++ b/library/std/src/sys/pal/unix/process/mod.rs @@ -2,10 +2,10 @@ 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; -#[cfg_attr(any(target_os = "espidf", target_os = "horizon"), allow(unused))] +#[cfg_attr(any(target_os = "espidf", target_os = "horizon", target_os = "nuttx"), allow(unused))] mod process_common; -#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] +#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] mod process_unsupported; cfg_if::cfg_if! { @@ -16,7 +16,7 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "vxworks")] { #[path = "process_vxworks.rs"] mod process_inner; - } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] { + } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] { mod process_inner { pub use super::process_unsupported::*; } diff --git a/library/std/src/sys/pal/unix/process/process_fuchsia.rs b/library/std/src/sys/pal/unix/process/process_fuchsia.rs index 34ff464aa37..5d0110cf55d 100644 --- a/library/std/src/sys/pal/unix/process/process_fuchsia.rs +++ b/library/std/src/sys/pal/unix/process/process_fuchsia.rs @@ -178,7 +178,7 @@ impl Process { zx_cvt(zx_object_get_info( self.handle.raw(), ZX_INFO_PROCESS, - core::ptr::addr_of_mut!(proc_info) as *mut libc::c_void, + (&raw mut proc_info) as *mut libc::c_void, mem::size_of::<zx_info_process_t>(), &mut actual, &mut avail, @@ -215,7 +215,7 @@ impl Process { zx_cvt(zx_object_get_info( self.handle.raw(), ZX_INFO_PROCESS, - core::ptr::addr_of_mut!(proc_info) as *mut libc::c_void, + (&raw mut proc_info) as *mut libc::c_void, mem::size_of::<zx_info_process_t>(), &mut actual, &mut avail, diff --git a/library/std/src/sys/pal/unix/process/process_unix.rs b/library/std/src/sys/pal/unix/process/process_unix.rs index d812aa0e02c..5d30f388da1 100644 --- a/library/std/src/sys/pal/unix/process/process_unix.rs +++ b/library/std/src/sys/pal/unix/process/process_unix.rs @@ -788,15 +788,15 @@ impl Command { let mut iov = [IoSlice::new(b"")]; let mut msg: libc::msghdr = mem::zeroed(); - msg.msg_iov = core::ptr::addr_of_mut!(iov) as *mut _; + msg.msg_iov = (&raw mut iov) as *mut _; msg.msg_iovlen = 1; // only attach cmsg if we successfully acquired the pidfd if pidfd >= 0 { msg.msg_controllen = mem::size_of_val(&cmsg.buf) as _; - msg.msg_control = core::ptr::addr_of_mut!(cmsg.buf) as *mut _; + msg.msg_control = (&raw mut cmsg.buf) as *mut _; - let hdr = CMSG_FIRSTHDR(core::ptr::addr_of_mut!(msg) as *mut _); + let hdr = CMSG_FIRSTHDR((&raw mut msg) as *mut _); (*hdr).cmsg_level = SOL_SOCKET; (*hdr).cmsg_type = SCM_RIGHTS; (*hdr).cmsg_len = CMSG_LEN(SCM_MSG_LEN as _) as _; @@ -838,17 +838,17 @@ impl Command { let mut msg: libc::msghdr = mem::zeroed(); - msg.msg_iov = core::ptr::addr_of_mut!(iov) as *mut _; + msg.msg_iov = (&raw mut iov) as *mut _; msg.msg_iovlen = 1; msg.msg_controllen = mem::size_of::<Cmsg>() as _; - msg.msg_control = core::ptr::addr_of_mut!(cmsg) as *mut _; + msg.msg_control = (&raw mut cmsg) as *mut _; match cvt_r(|| libc::recvmsg(sock.as_raw(), &mut msg, libc::MSG_CMSG_CLOEXEC)) { Err(_) => return -1, Ok(_) => {} } - let hdr = CMSG_FIRSTHDR(core::ptr::addr_of_mut!(msg) as *mut _); + let hdr = CMSG_FIRSTHDR((&raw mut msg) as *mut _); if hdr.is_null() || (*hdr).cmsg_level != SOL_SOCKET || (*hdr).cmsg_type != SCM_RIGHTS diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index e0a0d0973c6..ac0858e1de8 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -426,8 +426,8 @@ mod imp { match sysctlbyname.get() { Some(fcn) if unsafe { fcn(oid.as_ptr(), - ptr::addr_of_mut!(guard).cast(), - ptr::addr_of_mut!(size), + (&raw mut guard).cast(), + &raw mut size, ptr::null_mut(), 0) == 0 } => guard, diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 7fe9b6c3e52..04024661836 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -117,13 +117,15 @@ impl Thread { pub fn set_name(name: &CStr) { const PR_SET_NAME: libc::c_int = 15; unsafe { - libc::prctl( + let res = libc::prctl( PR_SET_NAME, name.as_ptr(), 0 as libc::c_ulong, 0 as libc::c_ulong, 0 as libc::c_ulong, ); + // We have no good way of propagating errors here, but in debug-builds let's check that this actually worked. + debug_assert_eq!(res, 0); } } @@ -140,7 +142,12 @@ impl Thread { } } - #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))] + #[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd", + target_os = "nuttx" + ))] pub fn set_name(name: &CStr) { unsafe { libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); @@ -253,7 +260,7 @@ impl Thread { tv_nsec: nsecs, }; secs -= ts.tv_sec as u64; - let ts_ptr = core::ptr::addr_of_mut!(ts); + let ts_ptr = &raw mut ts; if libc::nanosleep(ts_ptr, ts_ptr) == -1 { assert_eq!(os::errno(), libc::EINTR); secs += ts.tv_sec as u64; @@ -442,8 +449,8 @@ pub fn available_parallelism() -> io::Result<NonZero<usize>> { libc::sysctl( mib.as_mut_ptr(), 2, - core::ptr::addr_of_mut!(cpus) as *mut _, - core::ptr::addr_of_mut!(cpus_size) as *mut _, + (&raw mut cpus) as *mut _, + (&raw mut cpus_size) as *mut _, ptr::null_mut(), 0, ) @@ -517,7 +524,7 @@ mod cgroups { use crate::borrow::Cow; use crate::ffi::OsString; use crate::fs::{File, exists}; - use crate::io::{BufRead, BufReader, Read}; + use crate::io::{BufRead, Read}; use crate::os::unix::ffi::OsStringExt; use crate::path::{Path, PathBuf}; use crate::str::from_utf8; @@ -690,7 +697,7 @@ mod cgroups { /// 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 reader = File::open_buffered("/proc/self/mountinfo").ok()?; let mut line = String::with_capacity(256); loop { line.clear(); @@ -747,12 +754,15 @@ unsafe fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize { } // No point in looking up __pthread_get_minstack() on non-glibc platforms. -#[cfg(all(not(all(target_os = "linux", target_env = "gnu")), not(target_os = "netbsd")))] +#[cfg(all( + not(all(target_os = "linux", target_env = "gnu")), + not(any(target_os = "netbsd", target_os = "nuttx")) +))] unsafe fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { libc::PTHREAD_STACK_MIN } -#[cfg(target_os = "netbsd")] +#[cfg(any(target_os = "netbsd", target_os = "nuttx"))] unsafe fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { static STACK: crate::sync::OnceLock<usize> = crate::sync::OnceLock::new(); diff --git a/library/std/src/sys/pal/wasip2/mod.rs b/library/std/src/sys/pal/wasip2/mod.rs index 17b26543bd7..320712fdcc9 100644 --- a/library/std/src/sys/pal/wasip2/mod.rs +++ b/library/std/src/sys/pal/wasip2/mod.rs @@ -20,7 +20,6 @@ pub mod futex; #[path = "../wasi/io.rs"] pub mod io; -#[path = "../wasi/net.rs"] pub mod net; #[path = "../wasi/os.rs"] pub mod os; diff --git a/library/std/src/sys/pal/wasip2/net.rs b/library/std/src/sys/pal/wasip2/net.rs new file mode 100644 index 00000000000..c40eb229ba9 --- /dev/null +++ b/library/std/src/sys/pal/wasip2/net.rs @@ -0,0 +1,379 @@ +#![deny(unsafe_op_in_unsafe_fn)] + +use libc::{c_int, c_void, size_t}; + +use super::fd::WasiFd; +use crate::ffi::CStr; +use crate::io::{self, BorrowedBuf, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::net::{Shutdown, SocketAddr}; +use crate::os::wasi::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; +use crate::sys::unsupported; +use crate::sys_common::net::{TcpListener, getsockopt, setsockopt, sockaddr_to_addr}; +use crate::sys_common::{AsInner, FromInner, IntoInner}; +use crate::time::{Duration, Instant}; +use crate::{cmp, mem, str}; + +pub extern crate libc as netc; + +#[allow(non_camel_case_types)] +pub type wrlen_t = size_t; + +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +pub fn cvt<T: IsMinusOne>(t: T) -> crate::io::Result<T> { + if t.is_minus_one() { Err(crate::io::Error::last_os_error()) } else { Ok(t) } +} + +pub fn cvt_r<T, F>(mut f: F) -> crate::io::Result<T> +where + T: IsMinusOne, + F: FnMut() -> T, +{ + loop { + match cvt(f()) { + Err(ref e) if e.is_interrupted() => {} + other => return other, + } + } +} + +pub fn cvt_gai(err: c_int) -> io::Result<()> { + if err == 0 { + return Ok(()); + } + + if err == netc::EAI_SYSTEM { + return Err(io::Error::last_os_error()); + } + + let detail = unsafe { + str::from_utf8(CStr::from_ptr(netc::gai_strerror(err)).to_bytes()).unwrap().to_owned() + }; + + Err(io::Error::new( + io::ErrorKind::Uncategorized, + &format!("failed to lookup address information: {detail}")[..], + )) +} + +pub fn init() {} + +pub struct Socket(WasiFd); + +impl Socket { + pub fn new(addr: &SocketAddr, ty: c_int) -> io::Result<Socket> { + let fam = match *addr { + SocketAddr::V4(..) => netc::AF_INET, + SocketAddr::V6(..) => netc::AF_INET6, + }; + Socket::new_raw(fam, ty) + } + + pub fn new_raw(fam: c_int, ty: c_int) -> io::Result<Socket> { + let fd = cvt(unsafe { netc::socket(fam, ty, 0) })?; + Ok(unsafe { Self::from_raw_fd(fd) }) + } + + pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> { + let (addr, len) = addr.into_inner(); + cvt_r(|| unsafe { netc::connect(self.as_raw_fd(), addr.as_ptr(), len) })?; + Ok(()) + } + + pub fn connect_timeout(&self, addr: &SocketAddr, timeout: Duration) -> io::Result<()> { + self.set_nonblocking(true)?; + let r = self.connect(addr); + self.set_nonblocking(false)?; + + match r { + Ok(_) => return Ok(()), + // there's no ErrorKind for EINPROGRESS + Err(ref e) if e.raw_os_error() == Some(netc::EINPROGRESS) => {} + Err(e) => return Err(e), + } + + let mut pollfd = netc::pollfd { fd: self.as_raw_fd(), events: netc::POLLOUT, revents: 0 }; + + if timeout.as_secs() == 0 && timeout.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let start = Instant::now(); + + loop { + let elapsed = start.elapsed(); + if elapsed >= timeout { + return Err(io::const_io_error!(io::ErrorKind::TimedOut, "connection timed out")); + } + + let timeout = timeout - elapsed; + let mut timeout = timeout + .as_secs() + .saturating_mul(1_000) + .saturating_add(timeout.subsec_nanos() as u64 / 1_000_000); + if timeout == 0 { + timeout = 1; + } + + let timeout = cmp::min(timeout, c_int::MAX as u64) as c_int; + + match unsafe { netc::poll(&mut pollfd, 1, timeout) } { + -1 => { + let err = io::Error::last_os_error(); + if !err.is_interrupted() { + return Err(err); + } + } + 0 => {} + _ => { + // WASI poll does not return POLLHUP or POLLERR in revents. Check if the + // connnection actually succeeded and return ok only when the socket is + // ready and no errors were found. + if let Some(e) = self.take_error()? { + return Err(e); + } + + return Ok(()); + } + } + } + } + + pub fn accept( + &self, + storage: *mut netc::sockaddr, + len: *mut netc::socklen_t, + ) -> io::Result<Socket> { + let fd = cvt_r(|| unsafe { netc::accept(self.as_raw_fd(), storage, len) })?; + Ok(unsafe { Self::from_raw_fd(fd) }) + } + + pub fn duplicate(&self) -> io::Result<Socket> { + unsupported() + } + + fn recv_with_flags(&self, mut buf: BorrowedCursor<'_>, flags: c_int) -> io::Result<()> { + let ret = cvt(unsafe { + netc::recv( + self.as_raw_fd(), + buf.as_mut().as_mut_ptr() as *mut c_void, + buf.capacity(), + flags, + ) + })?; + unsafe { + buf.advance_unchecked(ret as usize); + } + Ok(()) + } + + pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), 0)?; + Ok(buf.len()) + } + + pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> { + let mut buf = BorrowedBuf::from(buf); + self.recv_with_flags(buf.unfilled(), netc::MSG_PEEK)?; + Ok(buf.len()) + } + + pub fn read_buf(&self, buf: BorrowedCursor<'_>) -> io::Result<()> { + self.recv_with_flags(buf, 0) + } + + pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> { + io::default_read_vectored(|b| self.read(b), bufs) + } + + #[inline] + pub fn is_read_vectored(&self) -> bool { + false + } + + fn recv_from_with_flags( + &self, + buf: &mut [u8], + flags: c_int, + ) -> io::Result<(usize, SocketAddr)> { + let mut storage: netc::sockaddr_storage = unsafe { mem::zeroed() }; + let mut addrlen = mem::size_of_val(&storage) as netc::socklen_t; + + let n = cvt(unsafe { + netc::recvfrom( + self.as_raw_fd(), + buf.as_mut_ptr() as *mut c_void, + buf.len(), + flags, + core::ptr::addr_of_mut!(storage) as *mut _, + &mut addrlen, + ) + })?; + Ok((n as usize, sockaddr_to_addr(&storage, addrlen as usize)?)) + } + + pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, 0) + } + + pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { + self.recv_from_with_flags(buf, netc::MSG_PEEK) + } + + fn write(&self, buf: &[u8]) -> io::Result<usize> { + let len = cmp::min(buf.len(), <wrlen_t>::MAX as usize) as wrlen_t; + let ret = cvt(unsafe { + netc::send(self.as_raw(), buf.as_ptr() as *const c_void, len, netc::MSG_NOSIGNAL) + })?; + Ok(ret as usize) + } + + pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> { + io::default_write_vectored(|b| self.write(b), bufs) + } + + #[inline] + pub fn is_write_vectored(&self) -> bool { + false + } + + pub fn set_timeout(&self, dur: Option<Duration>, kind: c_int) -> io::Result<()> { + let timeout = match dur { + Some(dur) => { + if dur.as_secs() == 0 && dur.subsec_nanos() == 0 { + return Err(io::Error::ZERO_TIMEOUT); + } + + let secs = dur.as_secs().try_into().unwrap_or(netc::time_t::MAX); + let mut timeout = netc::timeval { + tv_sec: secs, + tv_usec: dur.subsec_micros() as netc::suseconds_t, + }; + if timeout.tv_sec == 0 && timeout.tv_usec == 0 { + timeout.tv_usec = 1; + } + timeout + } + None => netc::timeval { tv_sec: 0, tv_usec: 0 }, + }; + setsockopt(self, netc::SOL_SOCKET, kind, timeout) + } + + pub fn timeout(&self, kind: c_int) -> io::Result<Option<Duration>> { + let raw: netc::timeval = getsockopt(self, netc::SOL_SOCKET, kind)?; + if raw.tv_sec == 0 && raw.tv_usec == 0 { + Ok(None) + } else { + let sec = raw.tv_sec as u64; + let nsec = (raw.tv_usec as u32) * 1000; + Ok(Some(Duration::new(sec, nsec))) + } + } + + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + let how = match how { + Shutdown::Write => netc::SHUT_WR, + Shutdown::Read => netc::SHUT_RD, + Shutdown::Both => netc::SHUT_RDWR, + }; + cvt(unsafe { netc::shutdown(self.as_raw_fd(), how) })?; + Ok(()) + } + + pub fn set_linger(&self, _linger: Option<Duration>) -> io::Result<()> { + unsupported() + } + + pub fn linger(&self) -> io::Result<Option<Duration>> { + unsupported() + } + + pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { + setsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY, nodelay as c_int) + } + + pub fn nodelay(&self) -> io::Result<bool> { + let raw: c_int = getsockopt(self, netc::IPPROTO_TCP, netc::TCP_NODELAY)?; + Ok(raw != 0) + } + + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + let mut nonblocking = nonblocking as c_int; + cvt(unsafe { netc::ioctl(self.as_raw_fd(), netc::FIONBIO, &mut nonblocking) }).map(drop) + } + + pub fn take_error(&self) -> io::Result<Option<io::Error>> { + let raw: c_int = getsockopt(self, netc::SOL_SOCKET, netc::SO_ERROR)?; + if raw == 0 { Ok(None) } else { Ok(Some(io::Error::from_raw_os_error(raw as i32))) } + } + + // This is used by sys_common code to abstract over Windows and Unix. + pub fn as_raw(&self) -> RawFd { + self.as_raw_fd() + } +} + +impl AsInner<WasiFd> for Socket { + #[inline] + fn as_inner(&self) -> &WasiFd { + &self.0 + } +} + +impl IntoInner<WasiFd> for Socket { + fn into_inner(self) -> WasiFd { + self.0 + } +} + +impl FromInner<WasiFd> for Socket { + fn from_inner(inner: WasiFd) -> Socket { + Socket(inner) + } +} + +impl AsFd for Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} + +impl AsRawFd for Socket { + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl IntoRawFd for Socket { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl FromRawFd for Socket { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + unsafe { Self(FromRawFd::from_raw_fd(raw_fd)) } + } +} + +impl AsInner<Socket> for TcpListener { + #[inline] + fn as_inner(&self) -> &Socket { + &self.socket() + } +} diff --git a/library/std/src/sys/pal/windows/api.rs b/library/std/src/sys/pal/windows/api.rs index 9e336ff2d47..ebe207fde93 100644 --- a/library/std/src/sys/pal/windows/api.rs +++ b/library/std/src/sys/pal/windows/api.rs @@ -30,7 +30,6 @@ //! should go in sys/pal/windows/mod.rs rather than here. See `IoResult` as an example. use core::ffi::c_void; -use core::ptr::addr_of; use super::c; @@ -186,7 +185,7 @@ unsafe trait SizedSetFileInformation: Sized { unsafe impl<T: SizedSetFileInformation> SetFileInformation for T { const CLASS: i32 = T::CLASS; fn as_ptr(&self) -> *const c_void { - addr_of!(*self).cast::<c_void>() + (&raw const *self).cast::<c_void>() } fn size(&self) -> u32 { win32_size_of::<Self>() diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs index be26356bb40..aab471e28ea 100644 --- a/library/std/src/sys/pal/windows/fs.rs +++ b/library/std/src/sys/pal/windows/fs.rs @@ -1,5 +1,3 @@ -use core::ptr::addr_of; - use super::api::{self, WinError}; use super::{IoResult, to_u16s}; use crate::borrow::Cow; @@ -325,7 +323,7 @@ impl File { let result = c::SetFileInformationByHandle( handle.as_raw_handle(), c::FileEndOfFileInfo, - ptr::addr_of!(eof).cast::<c_void>(), + (&raw const eof).cast::<c_void>(), mem::size_of::<c::FILE_END_OF_FILE_INFO>() as u32, ); if result == 0 { @@ -364,7 +362,7 @@ impl File { cvt(c::GetFileInformationByHandleEx( self.handle.as_raw_handle(), c::FileAttributeTagInfo, - ptr::addr_of_mut!(attr_tag).cast(), + (&raw mut attr_tag).cast(), mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(), ))?; if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { @@ -396,7 +394,7 @@ impl File { cvt(c::GetFileInformationByHandleEx( self.handle.as_raw_handle(), c::FileBasicInfo, - core::ptr::addr_of_mut!(info) as *mut c_void, + (&raw mut info) as *mut c_void, size as u32, ))?; let mut attr = FileAttr { @@ -428,7 +426,7 @@ impl File { cvt(c::GetFileInformationByHandleEx( self.handle.as_raw_handle(), c::FileStandardInfo, - core::ptr::addr_of_mut!(info) as *mut c_void, + (&raw mut info) as *mut c_void, size as u32, ))?; attr.file_size = info.AllocationSize as u64; @@ -438,7 +436,7 @@ impl File { cvt(c::GetFileInformationByHandleEx( self.handle.as_raw_handle(), c::FileAttributeTagInfo, - ptr::addr_of_mut!(attr_tag).cast(), + (&raw mut attr_tag).cast(), mem::size_of::<c::FILE_ATTRIBUTE_TAG_INFO>().try_into().unwrap(), ))?; if attr_tag.FileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 { @@ -545,22 +543,20 @@ impl File { unsafe { let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag { c::IO_REPARSE_TAG_SYMLINK => { - let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER = - ptr::addr_of_mut!((*buf).rest).cast(); + let info: *mut c::SYMBOLIC_LINK_REPARSE_BUFFER = (&raw mut (*buf).rest).cast(); assert!(info.is_aligned()); ( - ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(), + (&raw mut (*info).PathBuffer).cast::<u16>(), (*info).SubstituteNameOffset / 2, (*info).SubstituteNameLength / 2, (*info).Flags & c::SYMLINK_FLAG_RELATIVE != 0, ) } c::IO_REPARSE_TAG_MOUNT_POINT => { - let info: *mut c::MOUNT_POINT_REPARSE_BUFFER = - ptr::addr_of_mut!((*buf).rest).cast(); + let info: *mut c::MOUNT_POINT_REPARSE_BUFFER = (&raw mut (*buf).rest).cast(); assert!(info.is_aligned()); ( - ptr::addr_of_mut!((*info).PathBuffer).cast::<u16>(), + (&raw mut (*info).PathBuffer).cast::<u16>(), (*info).SubstituteNameOffset / 2, (*info).SubstituteNameLength / 2, false, @@ -643,7 +639,7 @@ impl File { cvt(c::GetFileInformationByHandleEx( self.handle.as_raw_handle(), c::FileBasicInfo, - core::ptr::addr_of_mut!(info) as *mut c_void, + (&raw mut info) as *mut c_void, size as u32, ))?; Ok(info) @@ -790,11 +786,11 @@ impl<'a> Iterator for DirBuffIter<'a> { // it does not seem that reality is so kind, and assuming this // caused crashes in some cases (https://github.com/rust-lang/rust/issues/104530) // presumably, this can be blamed on buggy filesystem drivers, but who knows. - let next_entry = ptr::addr_of!((*info).NextEntryOffset).read_unaligned() as usize; - let length = ptr::addr_of!((*info).FileNameLength).read_unaligned() as usize; - let attrs = ptr::addr_of!((*info).FileAttributes).read_unaligned(); + let next_entry = (&raw const (*info).NextEntryOffset).read_unaligned() as usize; + let length = (&raw const (*info).FileNameLength).read_unaligned() as usize; + let attrs = (&raw const (*info).FileAttributes).read_unaligned(); let name = from_maybe_unaligned( - ptr::addr_of!((*info).FileName).cast::<u16>(), + (&raw const (*info).FileName).cast::<u16>(), length / size_of::<u16>(), ); let is_directory = (attrs & c::FILE_ATTRIBUTE_DIRECTORY) != 0; @@ -1326,7 +1322,7 @@ pub fn copy(from: &Path, to: &Path) -> io::Result<u64> { pfrom.as_ptr(), pto.as_ptr(), Some(callback), - core::ptr::addr_of_mut!(size) as *mut _, + (&raw mut size) as *mut _, ptr::null_mut(), 0, ) @@ -1405,7 +1401,7 @@ pub fn junction_point(original: &Path, link: &Path) -> io::Result<()> { cvt(c::DeviceIoControl( d.as_raw_handle(), c::FSCTL_SET_REPARSE_POINT, - addr_of!(header).cast::<c_void>(), + (&raw const header).cast::<c_void>(), data_len as u32 + 8, ptr::null_mut(), 0, diff --git a/library/std/src/sys/pal/windows/futex.rs b/library/std/src/sys/pal/windows/futex.rs index f16a9f534a3..4d6c4df9a5a 100644 --- a/library/std/src/sys/pal/windows/futex.rs +++ b/library/std/src/sys/pal/windows/futex.rs @@ -57,7 +57,7 @@ pub fn wait_on_address<W: Waitable>( unsafe { let addr = ptr::from_ref(address).cast::<c_void>(); let size = mem::size_of::<W>(); - let compare_addr = ptr::addr_of!(compare).cast::<c_void>(); + let compare_addr = (&raw const compare).cast::<c_void>(); let timeout = timeout.map(dur2timeout).unwrap_or(c::INFINITE); c::WaitOnAddress(addr, compare_addr, size, timeout) == c::TRUE } diff --git a/library/std/src/sys/pal/windows/io.rs b/library/std/src/sys/pal/windows/io.rs index 785a3f6768b..1e7d02908f6 100644 --- a/library/std/src/sys/pal/windows/io.rs +++ b/library/std/src/sys/pal/windows/io.rs @@ -122,7 +122,7 @@ fn msys_tty_on(handle: BorrowedHandle<'_>) -> bool { c::GetFileInformationByHandleEx( handle.as_raw_handle(), c::FileNameInfo, - core::ptr::addr_of_mut!(name_info) as *mut c_void, + (&raw mut name_info) as *mut c_void, size_of::<FILE_NAME_INFO>() as u32, ) }; diff --git a/library/std/src/sys/pal/windows/net.rs b/library/std/src/sys/pal/windows/net.rs index 61a4504cf65..fd62d1f407c 100644 --- a/library/std/src/sys/pal/windows/net.rs +++ b/library/std/src/sys/pal/windows/net.rs @@ -390,7 +390,7 @@ impl Socket { buf.as_mut_ptr() as *mut _, length, flags, - core::ptr::addr_of_mut!(storage) as *mut _, + (&raw mut storage) as *mut _, &mut addrlen, ) }; diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index d8200ef9ca4..a8f6617c9dc 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -375,7 +375,7 @@ impl AnonPipe { let mut overlapped: c::OVERLAPPED = unsafe { crate::mem::zeroed() }; // `hEvent` is unused by `ReadFileEx` and `WriteFileEx`. // Therefore the documentation suggests using it to smuggle a pointer to the callback. - overlapped.hEvent = core::ptr::addr_of_mut!(async_result) as *mut _; + overlapped.hEvent = (&raw mut async_result) as *mut _; // Asynchronous read of the pipe. // If successful, `callback` will be called once it completes. diff --git a/library/std/src/sys/pal/windows/process.rs b/library/std/src/sys/pal/windows/process.rs index 93a6c45ce30..95b51e704f9 100644 --- a/library/std/src/sys/pal/windows/process.rs +++ b/library/std/src/sys/pal/windows/process.rs @@ -368,10 +368,10 @@ impl Command { StartupInfo: si, lpAttributeList: proc_thread_attribute_list.0.as_mut_ptr() as _, }; - si_ptr = core::ptr::addr_of_mut!(si_ex) as _; + si_ptr = (&raw mut si_ex) as _; } else { si.cb = mem::size_of::<c::STARTUPINFOW>() as u32; - si_ptr = core::ptr::addr_of_mut!(si) as _; + si_ptr = (&raw mut si) as _; } unsafe { @@ -953,7 +953,7 @@ fn make_proc_thread_attribute_list( // It's theoretically possible for the attribute count to exceed a u32 value. // Therefore, we ensure that we don't add more attributes than the buffer was initialized for. for (&attribute, value) in attributes.iter().take(attribute_count as usize) { - let value_ptr = core::ptr::addr_of!(*value.data) as _; + let value_ptr = (&raw const *value.data) as _; cvt(unsafe { c::UpdateProcThreadAttribute( proc_thread_attribute_list.0.as_mut_ptr() as _, diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs index 68085d026c4..9754e840d15 100644 --- a/library/std/src/sys/personality/mod.rs +++ b/library/std/src/sys/personality/mod.rs @@ -31,7 +31,7 @@ cfg_if::cfg_if! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "rtems")), + all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "rtems"), not(target_os = "nuttx")), all(target_vendor = "fortanix", target_env = "sgx"), ))] { mod gcc; diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index 16fb8c64c9b..d625814d15b 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -42,6 +42,7 @@ cfg_if::cfg_if! { target_os = "hurd", target_os = "l4re", target_os = "nto", + target_os = "nuttx", ))] { mod unix_legacy; pub use unix_legacy::fill_bytes; diff --git a/library/std/src/sys/sync/condvar/mod.rs b/library/std/src/sys/sync/condvar/mod.rs index 6849cacf88e..d0c998a5597 100644 --- a/library/std/src/sys/sync/condvar/mod.rs +++ b/library/std/src/sys/sync/condvar/mod.rs @@ -12,7 +12,10 @@ cfg_if::cfg_if! { ))] { mod futex; pub use futex::Condvar; - } else if #[cfg(target_family = "unix")] { + } else if #[cfg(any( + target_family = "unix", + target_os = "teeos", + ))] { mod pthread; pub use pthread::Condvar; } else if #[cfg(all(target_os = "windows", target_vendor = "win7"))] { @@ -24,9 +27,6 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "solid_asp3")] { mod itron; pub use itron::Condvar; - } else if #[cfg(target_os = "teeos")] { - mod teeos; - pub use teeos::Condvar; } else if #[cfg(target_os = "xous")] { mod xous; pub use xous::Condvar; diff --git a/library/std/src/sys/sync/condvar/pthread.rs b/library/std/src/sys/sync/condvar/pthread.rs index 5b5e7770b06..986cd0cb7d1 100644 --- a/library/std/src/sys/sync/condvar/pthread.rs +++ b/library/std/src/sys/sync/condvar/pthread.rs @@ -2,31 +2,25 @@ use crate::cell::UnsafeCell; use crate::ptr; use crate::sync::atomic::AtomicPtr; use crate::sync::atomic::Ordering::Relaxed; -use crate::sys::sync::{Mutex, mutex}; +use crate::sys::sync::{Mutex, OnceBox}; #[cfg(not(target_os = "nto"))] use crate::sys::time::TIMESPEC_MAX; #[cfg(target_os = "nto")] use crate::sys::time::TIMESPEC_MAX_CAPPED; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; use crate::time::Duration; struct AllocatedCondvar(UnsafeCell<libc::pthread_cond_t>); pub struct Condvar { - inner: LazyBox<AllocatedCondvar>, + inner: OnceBox<AllocatedCondvar>, mutex: AtomicPtr<libc::pthread_mutex_t>, } -#[inline] -fn raw(c: &Condvar) -> *mut libc::pthread_cond_t { - c.inner.0.get() -} - unsafe impl Send for AllocatedCondvar {} unsafe impl Sync for AllocatedCondvar {} -impl LazyInit for AllocatedCondvar { - fn init() -> Box<Self> { +impl AllocatedCondvar { + fn new() -> Box<Self> { let condvar = Box::new(AllocatedCondvar(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER))); cfg_if::cfg_if! { @@ -37,7 +31,7 @@ impl LazyInit for AllocatedCondvar { target_vendor = "apple", ))] { // `pthread_condattr_setclock` is unfortunately not supported on these platforms. - } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] { + } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "teeos"))] { // NOTE: ESP-IDF's PTHREAD_COND_INITIALIZER support is not released yet // So on that platform, init() should always be called // Moreover, that platform does not have pthread_condattr_setclock support, @@ -82,7 +76,11 @@ impl Drop for AllocatedCondvar { impl Condvar { pub const fn new() -> Condvar { - Condvar { inner: LazyBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) } + Condvar { inner: OnceBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) } + } + + fn get(&self) -> *mut libc::pthread_cond_t { + self.inner.get_or_init(AllocatedCondvar::new).0.get() } #[inline] @@ -98,21 +96,21 @@ impl Condvar { #[inline] pub fn notify_one(&self) { - let r = unsafe { libc::pthread_cond_signal(raw(self)) }; + let r = unsafe { libc::pthread_cond_signal(self.get()) }; debug_assert_eq!(r, 0); } #[inline] pub fn notify_all(&self) { - let r = unsafe { libc::pthread_cond_broadcast(raw(self)) }; + let r = unsafe { libc::pthread_cond_broadcast(self.get()) }; debug_assert_eq!(r, 0); } #[inline] pub unsafe fn wait(&self, mutex: &Mutex) { - let mutex = mutex::raw(mutex); + let mutex = mutex.get_assert_locked(); self.verify(mutex); - let r = libc::pthread_cond_wait(raw(self), mutex); + let r = libc::pthread_cond_wait(self.get(), mutex); debug_assert_eq!(r, 0); } @@ -129,7 +127,7 @@ impl Condvar { pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { use crate::sys::time::Timespec; - let mutex = mutex::raw(mutex); + let mutex = mutex.get_assert_locked(); self.verify(mutex); #[cfg(not(target_os = "nto"))] @@ -144,7 +142,7 @@ impl Condvar { .and_then(|t| t.to_timespec_capped()) .unwrap_or(TIMESPEC_MAX_CAPPED); - let r = libc::pthread_cond_timedwait(raw(self), mutex, &timeout); + let r = libc::pthread_cond_timedwait(self.get(), mutex, &timeout); assert!(r == libc::ETIMEDOUT || r == 0); r == 0 } @@ -162,7 +160,7 @@ impl Condvar { use crate::sys::time::SystemTime; use crate::time::Instant; - let mutex = mutex::raw(mutex); + let mutex = mutex.get_assert_locked(); self.verify(mutex); // OSX implementation of `pthread_cond_timedwait` is buggy @@ -188,7 +186,7 @@ impl Condvar { .and_then(|t| t.to_timespec()) .unwrap_or(TIMESPEC_MAX); - let r = libc::pthread_cond_timedwait(raw(self), mutex, &timeout); + let r = libc::pthread_cond_timedwait(self.get(), mutex, &timeout); debug_assert!(r == libc::ETIMEDOUT || r == 0); // ETIMEDOUT is not a totally reliable method of determining timeout due diff --git a/library/std/src/sys/sync/condvar/sgx.rs b/library/std/src/sys/sync/condvar/sgx.rs index ecb5872f60d..e60715e4b59 100644 --- a/library/std/src/sys/sync/condvar/sgx.rs +++ b/library/std/src/sys/sync/condvar/sgx.rs @@ -1,44 +1,39 @@ use crate::sys::pal::waitqueue::{SpinMutex, WaitQueue, WaitVariable}; -use crate::sys::sync::Mutex; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; +use crate::sys::sync::{Mutex, OnceBox}; use crate::time::Duration; -/// FIXME: `UnsafeList` is not movable. -struct AllocatedCondvar(SpinMutex<WaitVariable<()>>); - pub struct Condvar { - inner: LazyBox<AllocatedCondvar>, -} - -impl LazyInit for AllocatedCondvar { - fn init() -> Box<Self> { - Box::new(AllocatedCondvar(SpinMutex::new(WaitVariable::new(())))) - } + // FIXME: `UnsafeList` is not movable. + inner: OnceBox<SpinMutex<WaitVariable<()>>>, } impl Condvar { pub const fn new() -> Condvar { - Condvar { inner: LazyBox::new() } + Condvar { inner: OnceBox::new() } + } + + fn get(&self) -> &SpinMutex<WaitVariable<()>> { + self.inner.get_or_init(|| Box::new(SpinMutex::new(WaitVariable::new(())))) } #[inline] pub fn notify_one(&self) { - let _ = WaitQueue::notify_one(self.inner.0.lock()); + let _ = WaitQueue::notify_one(self.get().lock()); } #[inline] pub fn notify_all(&self) { - let _ = WaitQueue::notify_all(self.inner.0.lock()); + let _ = WaitQueue::notify_all(self.get().lock()); } pub unsafe fn wait(&self, mutex: &Mutex) { - let guard = self.inner.0.lock(); + let guard = self.get().lock(); WaitQueue::wait(guard, || unsafe { mutex.unlock() }); mutex.lock() } pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - let success = WaitQueue::wait_timeout(&self.inner.0, dur, || unsafe { mutex.unlock() }); + let success = WaitQueue::wait_timeout(self.get(), dur, || unsafe { mutex.unlock() }); mutex.lock(); success } diff --git a/library/std/src/sys/sync/condvar/teeos.rs b/library/std/src/sys/sync/condvar/teeos.rs deleted file mode 100644 index 943867cd761..00000000000 --- a/library/std/src/sys/sync/condvar/teeos.rs +++ /dev/null @@ -1,101 +0,0 @@ -use crate::cell::UnsafeCell; -use crate::ptr; -use crate::sync::atomic::AtomicPtr; -use crate::sync::atomic::Ordering::Relaxed; -use crate::sys::sync::mutex::{self, Mutex}; -use crate::sys::time::TIMESPEC_MAX; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; -use crate::time::Duration; - -extern "C" { - pub fn pthread_cond_timedwait( - cond: *mut libc::pthread_cond_t, - lock: *mut libc::pthread_mutex_t, - adstime: *const libc::timespec, - ) -> libc::c_int; -} - -struct AllocatedCondvar(UnsafeCell<libc::pthread_cond_t>); - -pub struct Condvar { - inner: LazyBox<AllocatedCondvar>, - mutex: AtomicPtr<libc::pthread_mutex_t>, -} - -#[inline] -fn raw(c: &Condvar) -> *mut libc::pthread_cond_t { - c.inner.0.get() -} - -unsafe impl Send for AllocatedCondvar {} -unsafe impl Sync for AllocatedCondvar {} - -impl LazyInit for AllocatedCondvar { - fn init() -> Box<Self> { - let condvar = Box::new(AllocatedCondvar(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER))); - - let r = unsafe { libc::pthread_cond_init(condvar.0.get(), crate::ptr::null()) }; - assert_eq!(r, 0); - - condvar - } -} - -impl Drop for AllocatedCondvar { - #[inline] - fn drop(&mut self) { - let r = unsafe { libc::pthread_cond_destroy(self.0.get()) }; - debug_assert_eq!(r, 0); - } -} - -impl Condvar { - pub const fn new() -> Condvar { - Condvar { inner: LazyBox::new(), mutex: AtomicPtr::new(ptr::null_mut()) } - } - - #[inline] - fn verify(&self, mutex: *mut libc::pthread_mutex_t) { - match self.mutex.compare_exchange(ptr::null_mut(), mutex, Relaxed, Relaxed) { - Ok(_) => {} // Stored the address - Err(n) if n == mutex => {} // Lost a race to store the same address - _ => panic!("attempted to use a condition variable with two mutexes"), - } - } - - #[inline] - pub fn notify_one(&self) { - let r = unsafe { libc::pthread_cond_signal(raw(self)) }; - debug_assert_eq!(r, 0); - } - - #[inline] - pub fn notify_all(&self) { - let r = unsafe { libc::pthread_cond_broadcast(raw(self)) }; - debug_assert_eq!(r, 0); - } - - #[inline] - pub unsafe fn wait(&self, mutex: &Mutex) { - let mutex = unsafe { mutex::raw(mutex) }; - self.verify(mutex); - let r = unsafe { libc::pthread_cond_wait(raw(self), mutex) }; - debug_assert_eq!(r, 0); - } - - pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - use crate::sys::time::Timespec; - - let mutex = unsafe { mutex::raw(mutex) }; - self.verify(mutex); - - let timeout = Timespec::now(libc::CLOCK_MONOTONIC) - .checked_add_duration(&dur) - .and_then(|t| t.to_timespec()) - .unwrap_or(TIMESPEC_MAX); - - let r = unsafe { pthread_cond_timedwait(raw(self), mutex, &timeout) }; - assert!(r == libc::ETIMEDOUT || r == 0); - r == 0 - } -} diff --git a/library/std/src/sys/sync/mod.rs b/library/std/src/sys/sync/mod.rs index 52fac5902a2..0691e967851 100644 --- a/library/std/src/sys/sync/mod.rs +++ b/library/std/src/sys/sync/mod.rs @@ -1,11 +1,14 @@ mod condvar; mod mutex; mod once; +mod once_box; mod rwlock; mod thread_parking; pub use condvar::Condvar; pub use mutex::Mutex; pub use once::{Once, OnceState}; +#[allow(unused)] // Only used on some platforms. +use once_box::OnceBox; pub use rwlock::RwLock; pub use thread_parking::Parker; diff --git a/library/std/src/sys/sync/mutex/mod.rs b/library/std/src/sys/sync/mutex/mod.rs index 73d9bd273de..360df3fc4b5 100644 --- a/library/std/src/sys/sync/mutex/mod.rs +++ b/library/std/src/sys/sync/mutex/mod.rs @@ -19,7 +19,7 @@ cfg_if::cfg_if! { target_os = "teeos", ))] { mod pthread; - pub use pthread::{Mutex, raw}; + pub use pthread::Mutex; } else if #[cfg(all(target_os = "windows", target_vendor = "win7"))] { mod windows7; pub use windows7::{Mutex, raw}; diff --git a/library/std/src/sys/sync/mutex/pthread.rs b/library/std/src/sys/sync/mutex/pthread.rs index 1c407bc2537..87c95f45f96 100644 --- a/library/std/src/sys/sync/mutex/pthread.rs +++ b/library/std/src/sys/sync/mutex/pthread.rs @@ -2,24 +2,19 @@ use crate::cell::UnsafeCell; use crate::io::Error; use crate::mem::{MaybeUninit, forget}; use crate::sys::cvt_nz; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; +use crate::sys::sync::OnceBox; struct AllocatedMutex(UnsafeCell<libc::pthread_mutex_t>); pub struct Mutex { - inner: LazyBox<AllocatedMutex>, -} - -#[inline] -pub unsafe fn raw(m: &Mutex) -> *mut libc::pthread_mutex_t { - m.inner.0.get() + inner: OnceBox<AllocatedMutex>, } unsafe impl Send for AllocatedMutex {} unsafe impl Sync for AllocatedMutex {} -impl LazyInit for AllocatedMutex { - fn init() -> Box<Self> { +impl AllocatedMutex { + fn new() -> Box<Self> { let mutex = Box::new(AllocatedMutex(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER))); // Issue #33770 @@ -60,24 +55,6 @@ impl LazyInit for AllocatedMutex { mutex } - - fn destroy(mutex: Box<Self>) { - // We're not allowed to pthread_mutex_destroy a locked mutex, - // so check first if it's unlocked. - if unsafe { libc::pthread_mutex_trylock(mutex.0.get()) == 0 } { - unsafe { libc::pthread_mutex_unlock(mutex.0.get()) }; - drop(mutex); - } else { - // The mutex is locked. This happens if a MutexGuard is leaked. - // In this case, we just leak the Mutex too. - forget(mutex); - } - } - - fn cancel_init(_: Box<Self>) { - // In this case, we can just drop it without any checks, - // since it cannot have been locked yet. - } } impl Drop for AllocatedMutex { @@ -99,11 +76,33 @@ impl Drop for AllocatedMutex { impl Mutex { #[inline] pub const fn new() -> Mutex { - Mutex { inner: LazyBox::new() } + Mutex { inner: OnceBox::new() } + } + + /// Gets access to the pthread mutex under the assumption that the mutex is + /// locked. + /// + /// This allows skipping the initialization check, as the mutex can only be + /// locked if it is already initialized, and allows relaxing the ordering + /// on the pointer load, since the allocation cannot have been modified + /// since the `lock` and the lock must have occurred on the current thread. + /// + /// # Safety + /// Causes undefined behaviour if the mutex is not locked. + #[inline] + pub(crate) unsafe fn get_assert_locked(&self) -> *mut libc::pthread_mutex_t { + unsafe { self.inner.get_unchecked().0.get() } } #[inline] - pub unsafe fn lock(&self) { + fn get(&self) -> *mut libc::pthread_mutex_t { + // If initialization fails, the mutex is destroyed. This is always sound, + // however, as the mutex cannot have been locked yet. + self.inner.get_or_init(AllocatedMutex::new).0.get() + } + + #[inline] + pub fn lock(&self) { #[cold] #[inline(never)] fn fail(r: i32) -> ! { @@ -111,7 +110,7 @@ impl Mutex { panic!("failed to lock mutex: {error}"); } - let r = libc::pthread_mutex_lock(raw(self)); + let r = unsafe { libc::pthread_mutex_lock(self.get()) }; // As we set the mutex type to `PTHREAD_MUTEX_NORMAL` above, we expect // the lock call to never fail. Unfortunately however, some platforms // (Solaris) do not conform to the standard, and instead always provide @@ -126,13 +125,29 @@ impl Mutex { #[inline] pub unsafe fn unlock(&self) { - let r = libc::pthread_mutex_unlock(raw(self)); + let r = libc::pthread_mutex_unlock(self.get_assert_locked()); debug_assert_eq!(r, 0); } #[inline] - pub unsafe fn try_lock(&self) -> bool { - libc::pthread_mutex_trylock(raw(self)) == 0 + pub fn try_lock(&self) -> bool { + unsafe { libc::pthread_mutex_trylock(self.get()) == 0 } + } +} + +impl Drop for Mutex { + fn drop(&mut self) { + let Some(mutex) = self.inner.take() else { return }; + // We're not allowed to pthread_mutex_destroy a locked mutex, + // so check first if it's unlocked. + if unsafe { libc::pthread_mutex_trylock(mutex.0.get()) == 0 } { + unsafe { libc::pthread_mutex_unlock(mutex.0.get()) }; + drop(mutex); + } else { + // The mutex is locked. This happens if a MutexGuard is leaked. + // In this case, we just leak the Mutex too. + forget(mutex); + } } } diff --git a/library/std/src/sys/sync/mutex/sgx.rs b/library/std/src/sys/sync/mutex/sgx.rs index 65d1e880f7b..8529e857970 100644 --- a/library/std/src/sys/sync/mutex/sgx.rs +++ b/library/std/src/sys/sync/mutex/sgx.rs @@ -1,28 +1,24 @@ use crate::sys::pal::waitqueue::{SpinMutex, WaitQueue, WaitVariable, try_lock_or_false}; -use crate::sys_common::lazy_box::{LazyBox, LazyInit}; - -/// FIXME: `UnsafeList` is not movable. -struct AllocatedMutex(SpinMutex<WaitVariable<bool>>); +use crate::sys::sync::OnceBox; pub struct Mutex { - inner: LazyBox<AllocatedMutex>, -} - -impl LazyInit for AllocatedMutex { - fn init() -> Box<Self> { - Box::new(AllocatedMutex(SpinMutex::new(WaitVariable::new(false)))) - } + // FIXME: `UnsafeList` is not movable. + inner: OnceBox<SpinMutex<WaitVariable<bool>>>, } // Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28 impl Mutex { pub const fn new() -> Mutex { - Mutex { inner: LazyBox::new() } + Mutex { inner: OnceBox::new() } + } + + fn get(&self) -> &SpinMutex<WaitVariable<bool>> { + self.inner.get_or_init(|| Box::new(SpinMutex::new(WaitVariable::new(false)))) } #[inline] pub fn lock(&self) { - let mut guard = self.inner.0.lock(); + let mut guard = self.get().lock(); if *guard.lock_var() { // Another thread has the lock, wait WaitQueue::wait(guard, || {}) @@ -35,7 +31,9 @@ impl Mutex { #[inline] pub unsafe fn unlock(&self) { - let guard = self.inner.0.lock(); + // SAFETY: the mutex was locked by the current thread, so it has been + // initialized already. + let guard = unsafe { self.inner.get_unchecked().lock() }; if let Err(mut guard) = WaitQueue::notify_one(guard) { // No other waiters, unlock *guard.lock_var_mut() = false; @@ -46,7 +44,7 @@ impl Mutex { #[inline] pub fn try_lock(&self) -> bool { - let mut guard = try_lock_or_false!(self.inner.0); + let mut guard = try_lock_or_false!(self.get()); if *guard.lock_var() { // Another thread has the lock false diff --git a/library/std/src/sys/sync/once_box.rs b/library/std/src/sys/sync/once_box.rs new file mode 100644 index 00000000000..1422b5a1721 --- /dev/null +++ b/library/std/src/sys/sync/once_box.rs @@ -0,0 +1,82 @@ +//! A racily-initialized alternative to `OnceLock<Box<T>>`. +//! +//! This is used to implement synchronization primitives that need allocation, +//! like the pthread versions. + +#![allow(dead_code)] // Only used on some platforms. + +use crate::mem::replace; +use crate::ptr::null_mut; +use crate::sync::atomic::AtomicPtr; +use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed}; + +pub(crate) struct OnceBox<T> { + ptr: AtomicPtr<T>, +} + +impl<T> OnceBox<T> { + #[inline] + pub const fn new() -> Self { + Self { ptr: AtomicPtr::new(null_mut()) } + } + + /// Gets access to the value, assuming it is already initialized and this + /// initialization has been observed by the current thread. + /// + /// Since all modifications to the pointer have already been observed, the + /// pointer load in this function can be performed with relaxed ordering, + /// potentially allowing the optimizer to turn code like this: + /// ```rust, ignore + /// once_box.get_or_init(|| Box::new(42)); + /// unsafe { once_box.get_unchecked() } + /// ``` + /// into + /// ```rust, ignore + /// once_box.get_or_init(|| Box::new(42)) + /// ``` + /// + /// # Safety + /// This causes undefined behaviour if the assumption above is violated. + #[inline] + pub unsafe fn get_unchecked(&self) -> &T { + unsafe { &*self.ptr.load(Relaxed) } + } + + #[inline] + pub fn get_or_init(&self, f: impl FnOnce() -> Box<T>) -> &T { + let ptr = self.ptr.load(Acquire); + match unsafe { ptr.as_ref() } { + Some(val) => val, + None => self.initialize(f), + } + } + + #[inline] + pub fn take(&mut self) -> Option<Box<T>> { + let ptr = replace(self.ptr.get_mut(), null_mut()); + if !ptr.is_null() { Some(unsafe { Box::from_raw(ptr) }) } else { None } + } + + #[cold] + fn initialize(&self, f: impl FnOnce() -> Box<T>) -> &T { + let new_ptr = Box::into_raw(f()); + match self.ptr.compare_exchange(null_mut(), new_ptr, AcqRel, Acquire) { + Ok(_) => unsafe { &*new_ptr }, + Err(ptr) => { + // Lost the race to another thread. + // Drop the value we created, and use the one from the other thread instead. + drop(unsafe { Box::from_raw(new_ptr) }); + unsafe { &*ptr } + } + } + } +} + +unsafe impl<T: Send> Send for OnceBox<T> {} +unsafe impl<T: Send + Sync> Sync for OnceBox<T> {} + +impl<T> Drop for OnceBox<T> { + fn drop(&mut self) { + self.take(); + } +} diff --git a/library/std/src/sys/sync/rwlock/queue.rs b/library/std/src/sys/sync/rwlock/queue.rs index 0e658328c2e..733f51cae8c 100644 --- a/library/std/src/sys/sync/rwlock/queue.rs +++ b/library/std/src/sys/sync/rwlock/queue.rs @@ -113,7 +113,7 @@ use crate::mem; use crate::ptr::{self, NonNull, null_mut, without_provenance_mut}; use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release}; use crate::sync::atomic::{AtomicBool, AtomicPtr}; -use crate::thread::{self, Thread}; +use crate::thread::{self, Thread, ThreadId}; // Locking uses exponential backoff. `SPIN_COUNT` indicates how many times the // locking operation will be retried. @@ -200,7 +200,9 @@ impl Node { fn prepare(&mut self) { // Fall back to creating an unnamed `Thread` handle to allow locking in // TLS destructors. - self.thread.get_or_init(|| thread::try_current().unwrap_or_else(Thread::new_unnamed)); + self.thread.get_or_init(|| { + thread::try_current().unwrap_or_else(|| Thread::new_unnamed(ThreadId::new())) + }); self.completed = AtomicBool::new(false); } diff --git a/library/std/src/sys/sync/rwlock/teeos.rs b/library/std/src/sys/sync/rwlock/teeos.rs index ef9b1ab5154..76343022383 100644 --- a/library/std/src/sys/sync/rwlock/teeos.rs +++ b/library/std/src/sys/sync/rwlock/teeos.rs @@ -14,22 +14,22 @@ impl RwLock { #[inline] pub fn read(&self) { - unsafe { self.inner.lock() }; + self.inner.lock() } #[inline] pub fn try_read(&self) -> bool { - unsafe { self.inner.try_lock() } + self.inner.try_lock() } #[inline] pub fn write(&self) { - unsafe { self.inner.lock() }; + self.inner.lock() } #[inline] pub unsafe fn try_write(&self) -> bool { - unsafe { self.inner.try_lock() } + self.inner.try_lock() } #[inline] diff --git a/library/std/src/sys/sync/thread_parking/pthread.rs b/library/std/src/sys/sync/thread_parking/pthread.rs index c64600e9e29..5f195d0bb0c 100644 --- a/library/std/src/sys/sync/thread_parking/pthread.rs +++ b/library/std/src/sys/sync/thread_parking/pthread.rs @@ -3,7 +3,6 @@ use crate::cell::UnsafeCell; use crate::marker::PhantomPinned; use crate::pin::Pin; -use crate::ptr::addr_of_mut; use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; #[cfg(not(target_os = "nto"))] @@ -101,8 +100,8 @@ impl Parker { // This could lead to undefined behaviour when deadlocking. This is avoided // by not deadlocking. Note in particular the unlocking operation before any // panic, as code after the panic could try to park again. - addr_of_mut!((*parker).state).write(AtomicUsize::new(EMPTY)); - addr_of_mut!((*parker).lock).write(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER)); + (&raw mut (*parker).state).write(AtomicUsize::new(EMPTY)); + (&raw mut (*parker).lock).write(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER)); cfg_if::cfg_if! { if #[cfg(any( @@ -112,9 +111,9 @@ impl Parker { target_os = "vita", target_vendor = "apple", ))] { - addr_of_mut!((*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER)); + (&raw mut (*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER)); } else if #[cfg(any(target_os = "espidf", target_os = "horizon"))] { - let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), crate::ptr::null()); + let r = libc::pthread_cond_init((&raw mut (*parker).cvar).cast(), crate::ptr::null()); assert_eq!(r, 0); } else { use crate::mem::MaybeUninit; @@ -123,7 +122,7 @@ impl Parker { assert_eq!(r, 0); let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC); assert_eq!(r, 0); - let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), attr.as_ptr()); + let r = libc::pthread_cond_init((&raw mut (*parker).cvar).cast(), attr.as_ptr()); assert_eq!(r, 0); let r = libc::pthread_condattr_destroy(attr.as_mut_ptr()); assert_eq!(r, 0); diff --git a/library/std/src/sys/sync/thread_parking/windows7.rs b/library/std/src/sys/sync/thread_parking/windows7.rs index cdd59757fe2..8f7e66c46ef 100644 --- a/library/std/src/sys/sync/thread_parking/windows7.rs +++ b/library/std/src/sys/sync/thread_parking/windows7.rs @@ -178,7 +178,7 @@ impl Parker { } fn ptr(&self) -> *const c_void { - core::ptr::addr_of!(self.state).cast::<c_void>() + (&raw const self.state).cast::<c_void>() } } diff --git a/library/std/src/sys/thread_local/destructors/linux_like.rs b/library/std/src/sys/thread_local/destructors/linux_like.rs index c381be0bf8c..f473dc4d79d 100644 --- a/library/std/src/sys/thread_local/destructors/linux_like.rs +++ b/library/std/src/sys/thread_local/destructors/linux_like.rs @@ -47,7 +47,7 @@ pub unsafe fn register(t: *mut u8, dtor: unsafe extern "C" fn(*mut u8)) { dtor, ), t.cast(), - core::ptr::addr_of!(__dso_handle) as *mut _, + (&raw const __dso_handle) as *mut _, ); } } else { diff --git a/library/std/src/sys/thread_local/guard/apple.rs b/library/std/src/sys/thread_local/guard/apple.rs index 6c27f7ae35c..fa25b116622 100644 --- a/library/std/src/sys/thread_local/guard/apple.rs +++ b/library/std/src/sys/thread_local/guard/apple.rs @@ -26,6 +26,7 @@ pub fn enable() { unsafe extern "C" fn run_dtors(_: *mut u8) { unsafe { destructors::run(); + crate::rt::thread_cleanup(); } } } diff --git a/library/std/src/sys/thread_local/guard/key.rs b/library/std/src/sys/thread_local/guard/key.rs index 77575547c14..59581e6f281 100644 --- a/library/std/src/sys/thread_local/guard/key.rs +++ b/library/std/src/sys/thread_local/guard/key.rs @@ -3,10 +3,12 @@ //! that will run all native TLS destructors in the destructor list. use crate::ptr; -use crate::sys::thread_local::destructors; use crate::sys::thread_local::key::{LazyKey, set}; +#[cfg(target_thread_local)] pub fn enable() { + use crate::sys::thread_local::destructors; + static DTORS: LazyKey = LazyKey::new(Some(run)); // Setting the key value to something other than NULL will result in the @@ -18,6 +20,41 @@ pub fn enable() { unsafe extern "C" fn run(_: *mut u8) { unsafe { destructors::run(); + // On platforms with `__cxa_thread_atexit_impl`, `destructors::run` + // does nothing on newer systems as the TLS destructors are + // registered with the system. But because all of those platforms + // call the destructors of TLS keys after the registered ones, this + // function will still be run last (at the time of writing). + crate::rt::thread_cleanup(); + } + } +} + +/// On platforms with key-based TLS, the system runs the destructors for us. +/// We still have to make sure that [`crate::rt::thread_cleanup`] is called, +/// however. This is done by defering the execution of a TLS destructor to +/// the next round of destruction inside the TLS destructors. +#[cfg(not(target_thread_local))] +pub fn enable() { + const DEFER: *mut u8 = ptr::without_provenance_mut(1); + const RUN: *mut u8 = ptr::without_provenance_mut(2); + + static CLEANUP: LazyKey = LazyKey::new(Some(run)); + + unsafe { set(CLEANUP.force(), DEFER) } + + unsafe extern "C" fn run(state: *mut u8) { + if state == DEFER { + // Make sure that this function is run again in the next round of + // TLS destruction. If there is no futher round, there will be leaks, + // but that's okay, `thread_cleanup` is not guaranteed to be called. + unsafe { set(CLEANUP.force(), RUN) } + } else { + debug_assert_eq!(state, RUN); + // If the state is still RUN in the next round of TLS destruction, + // it means that no other TLS destructors defined by this runtime + // have been run, as they would have set the state to DEFER. + crate::rt::thread_cleanup(); } } } diff --git a/library/std/src/sys/thread_local/guard/solid.rs b/library/std/src/sys/thread_local/guard/solid.rs index 054b2d561c8..3bcd46c481d 100644 --- a/library/std/src/sys/thread_local/guard/solid.rs +++ b/library/std/src/sys/thread_local/guard/solid.rs @@ -19,6 +19,9 @@ pub fn enable() { } unsafe extern "C" fn tls_dtor(_unused: *mut u8) { - unsafe { destructors::run() }; + unsafe { + destructors::run(); + crate::rt::thread_cleanup(); + } } } diff --git a/library/std/src/sys/thread_local/guard/windows.rs b/library/std/src/sys/thread_local/guard/windows.rs index bf94f7d6e3d..7ee8e695c75 100644 --- a/library/std/src/sys/thread_local/guard/windows.rs +++ b/library/std/src/sys/thread_local/guard/windows.rs @@ -80,13 +80,13 @@ pub static CALLBACK: unsafe extern "system" fn(*mut c_void, u32, *mut c_void) = unsafe extern "system" fn tls_callback(_h: *mut c_void, dw_reason: u32, _pv: *mut c_void) { if dw_reason == c::DLL_THREAD_DETACH || dw_reason == c::DLL_PROCESS_DETACH { - #[cfg(target_thread_local)] unsafe { + #[cfg(target_thread_local)] super::super::destructors::run(); - } - #[cfg(not(target_thread_local))] - unsafe { + #[cfg(not(target_thread_local))] super::super::key::run_dtors(); + + crate::rt::thread_cleanup(); } } } diff --git a/library/std/src/sys/thread_local/key/xous.rs b/library/std/src/sys/thread_local/key/xous.rs index 295fb848b30..2ab4bba7d8e 100644 --- a/library/std/src/sys/thread_local/key/xous.rs +++ b/library/std/src/sys/thread_local/key/xous.rs @@ -212,4 +212,6 @@ unsafe fn run_dtors() { unsafe { cur = (*cur).next }; } } + + crate::rt::thread_cleanup(); } diff --git a/library/std/src/sys/thread_local/mod.rs b/library/std/src/sys/thread_local/mod.rs index 3d1b91a7ea0..31d3b439060 100644 --- a/library/std/src/sys/thread_local/mod.rs +++ b/library/std/src/sys/thread_local/mod.rs @@ -31,12 +31,15 @@ cfg_if::cfg_if! { ))] { mod statik; pub use statik::{EagerStorage, LazyStorage, thread_local_inner}; + pub(crate) use statik::{LocalPointer, local_pointer}; } else if #[cfg(target_thread_local)] { mod native; pub use native::{EagerStorage, LazyStorage, thread_local_inner}; + pub(crate) use native::{LocalPointer, local_pointer}; } else { mod os; pub use os::{Storage, thread_local_inner}; + pub(crate) use os::{LocalPointer, local_pointer}; } } @@ -72,36 +75,47 @@ pub(crate) mod destructors { } /// This module provides a way to schedule the execution of the destructor list -/// on systems without a per-variable destructor system. -mod guard { +/// and the [runtime cleanup](crate::rt::thread_cleanup) function. Calling `enable` +/// should ensure that these functions are called at the right times. +pub(crate) mod guard { cfg_if::cfg_if! { if #[cfg(all(target_thread_local, target_vendor = "apple"))] { mod apple; - pub(super) use apple::enable; + pub(crate) use apple::enable; } else if #[cfg(target_os = "windows")] { mod windows; - pub(super) use windows::enable; + pub(crate) use windows::enable; } else if #[cfg(any( - all(target_family = "wasm", target_feature = "atomics"), + target_family = "wasm", + target_os = "uefi", + target_os = "zkvm", ))] { - pub(super) fn enable() { - // FIXME: Right now there is no concept of "thread exit", but - // this is likely going to show up at some point in the form of - // an exported symbol that the wasm runtime is going to be - // expected to call. For now we just leak everything, but if - // such a function starts to exist it will probably need to - // iterate the destructor list with this function: + pub(crate) fn enable() { + // FIXME: Right now there is no concept of "thread exit" on + // wasm, but this is likely going to show up at some point in + // the form of an exported symbol that the wasm runtime is going + // to be expected to call. For now we just leak everything, but + // if such a function starts to exist it will probably need to + // iterate the destructor list with these functions: + #[cfg(all(target_family = "wasm", target_feature = "atomics"))] #[allow(unused)] use super::destructors::run; + #[allow(unused)] + use crate::rt::thread_cleanup; } - } else if #[cfg(target_os = "hermit")] { - pub(super) fn enable() {} + } else if #[cfg(any( + target_os = "hermit", + target_os = "xous", + ))] { + // `std` is the only runtime, so it just calls the destructor functions + // itself when the time comes. + pub(crate) fn enable() {} } else if #[cfg(target_os = "solid_asp3")] { mod solid; - pub(super) use solid::enable; - } else if #[cfg(all(target_thread_local, not(target_family = "wasm")))] { + pub(crate) use solid::enable; + } else { mod key; - pub(super) use key::enable; + pub(crate) use key::enable; } } } diff --git a/library/std/src/sys/thread_local/native/mod.rs b/library/std/src/sys/thread_local/native/mod.rs index 1cc45fe892d..f498dee0899 100644 --- a/library/std/src/sys/thread_local/native/mod.rs +++ b/library/std/src/sys/thread_local/native/mod.rs @@ -29,6 +29,9 @@ //! eliminates the `Destroyed` state for these values, which can allow more niche //! optimizations to occur for the `State` enum. For `Drop` types, `()` is used. +use crate::cell::Cell; +use crate::ptr; + mod eager; mod lazy; @@ -107,3 +110,31 @@ pub macro thread_local_inner { $crate::thread::local_impl::thread_local_inner!(@key $t, $($init)*); }, } + +#[rustc_macro_transparency = "semitransparent"] +pub(crate) macro local_pointer { + () => {}, + ($vis:vis static $name:ident; $($rest:tt)*) => { + #[thread_local] + $vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new(); + $crate::sys::thread_local::local_pointer! { $($rest)* } + }, +} + +pub(crate) struct LocalPointer { + p: Cell<*mut ()>, +} + +impl LocalPointer { + pub const fn __new() -> LocalPointer { + LocalPointer { p: Cell::new(ptr::null_mut()) } + } + + pub fn get(&self) -> *mut () { + self.p.get() + } + + pub fn set(&self, p: *mut ()) { + self.p.set(p) + } +} diff --git a/library/std/src/sys/thread_local/os.rs b/library/std/src/sys/thread_local/os.rs index f09c396ef0f..26ce3322a16 100644 --- a/library/std/src/sys/thread_local/os.rs +++ b/library/std/src/sys/thread_local/os.rs @@ -1,8 +1,8 @@ -use super::abort_on_dtor_unwind; +use super::key::{Key, LazyKey, get, set}; +use super::{abort_on_dtor_unwind, guard}; use crate::cell::Cell; use crate::marker::PhantomData; use crate::ptr; -use crate::sys::thread_local::key::{Key, LazyKey, get, set}; #[doc(hidden)] #[allow_internal_unstable(thread_local_internals)] @@ -138,5 +138,35 @@ unsafe extern "C" fn destroy_value<T: 'static>(ptr: *mut u8) { drop(ptr); // SAFETY: `key` is the TLS key `ptr` was stored under. unsafe { set(key, ptr::null_mut()) }; + // Make sure that the runtime cleanup will be performed + // after the next round of TLS destruction. + guard::enable(); }); } + +#[rustc_macro_transparency = "semitransparent"] +pub(crate) macro local_pointer { + () => {}, + ($vis:vis static $name:ident; $($rest:tt)*) => { + $vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new(); + $crate::sys::thread_local::local_pointer! { $($rest)* } + }, +} + +pub(crate) struct LocalPointer { + key: LazyKey, +} + +impl LocalPointer { + pub const fn __new() -> LocalPointer { + LocalPointer { key: LazyKey::new(None) } + } + + pub fn get(&'static self) -> *mut () { + unsafe { get(self.key.force()) as *mut () } + } + + pub fn set(&'static self, p: *mut ()) { + unsafe { set(self.key.force(), p as *mut u8) } + } +} diff --git a/library/std/src/sys/thread_local/statik.rs b/library/std/src/sys/thread_local/statik.rs index a3451ab74e0..ba94caa6690 100644 --- a/library/std/src/sys/thread_local/statik.rs +++ b/library/std/src/sys/thread_local/statik.rs @@ -1,7 +1,8 @@ //! On some targets like wasm there's no threads, so no need to generate //! thread locals and we can instead just use plain statics! -use crate::cell::UnsafeCell; +use crate::cell::{Cell, UnsafeCell}; +use crate::ptr; #[doc(hidden)] #[allow_internal_unstable(thread_local_internals)] @@ -93,3 +94,33 @@ impl<T> LazyStorage<T> { // SAFETY: the target doesn't have threads. unsafe impl<T> Sync for LazyStorage<T> {} + +#[rustc_macro_transparency = "semitransparent"] +pub(crate) macro local_pointer { + () => {}, + ($vis:vis static $name:ident; $($rest:tt)*) => { + $vis static $name: $crate::sys::thread_local::LocalPointer = $crate::sys::thread_local::LocalPointer::__new(); + $crate::sys::thread_local::local_pointer! { $($rest)* } + }, +} + +pub(crate) struct LocalPointer { + p: Cell<*mut ()>, +} + +impl LocalPointer { + pub const fn __new() -> LocalPointer { + LocalPointer { p: Cell::new(ptr::null_mut()) } + } + + pub fn get(&self) -> *mut () { + self.p.get() + } + + pub fn set(&self, p: *mut ()) { + self.p.set(p) + } +} + +// SAFETY: the target doesn't have threads. +unsafe impl Sync for LocalPointer {} |
