summaryrefslogtreecommitdiffstats
path: root/library/std/src/sys
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--library/std/src/sync/remutex.rs (renamed from library/std/src/sys_common/remutex.rs)0
-rw-r--r--library/std/src/sys/itron/thread.rs27
-rw-r--r--library/std/src/sys/sgx/mod.rs1
-rw-r--r--library/std/src/sys/sgx/thread.rs21
-rw-r--r--library/std/src/sys/sgx/thread_parking.rs23
-rw-r--r--library/std/src/sys/unix/android.rs4
-rw-r--r--library/std/src/sys/unix/fs.rs154
-rw-r--r--library/std/src/sys/unix/kernel_copy.rs8
-rw-r--r--library/std/src/sys/unix/locks/pthread_condvar.rs93
-rw-r--r--library/std/src/sys/unix/mod.rs23
-rw-r--r--library/std/src/sys/unix/net.rs2
-rw-r--r--library/std/src/sys/unix/pipe.rs4
-rw-r--r--library/std/src/sys/unix/process/process_common.rs67
-rw-r--r--library/std/src/sys/unix/process/process_fuchsia.rs15
-rw-r--r--library/std/src/sys/unix/process/process_unix.rs20
-rw-r--r--library/std/src/sys/unix/process/process_unix/tests.rs8
-rw-r--r--library/std/src/sys/unix/process/process_unsupported.rs4
-rw-r--r--library/std/src/sys/unix/process/process_vxworks.rs11
-rw-r--r--library/std/src/sys/unix/stack_overflow.rs7
-rw-r--r--library/std/src/sys/unix/thread.rs46
-rw-r--r--library/std/src/sys/unix/thread_parker/mod.rs32
-rw-r--r--library/std/src/sys/unix/thread_parker/netbsd.rs113
-rw-r--r--library/std/src/sys/unix/thread_parking/darwin.rs (renamed from library/std/src/sys/unix/thread_parker/darwin.rs)2
-rw-r--r--library/std/src/sys/unix/thread_parking/mod.rs32
-rw-r--r--library/std/src/sys/unix/thread_parking/netbsd.rs52
-rw-r--r--library/std/src/sys/unix/thread_parking/pthread.rs (renamed from library/std/src/sys/unix/thread_parker/pthread.rs)12
-rw-r--r--library/std/src/sys/unix/time.rs3
-rw-r--r--library/std/src/sys/unix/weak.rs53
-rw-r--r--library/std/src/sys/unsupported/mod.rs1
-rw-r--r--library/std/src/sys/unsupported/once.rs89
-rw-r--r--library/std/src/sys/unsupported/pipe.rs4
-rw-r--r--library/std/src/sys/unsupported/process.rs4
-rw-r--r--library/std/src/sys/wasi/mod.rs2
-rw-r--r--library/std/src/sys/wasm/mod.rs2
-rw-r--r--library/std/src/sys/windows/c.rs13
-rw-r--r--library/std/src/sys/windows/mod.rs2
-rw-r--r--library/std/src/sys/windows/os.rs2
-rw-r--r--library/std/src/sys/windows/pipe.rs6
-rw-r--r--library/std/src/sys/windows/process.rs5
-rw-r--r--library/std/src/sys/windows/rand.rs121
-rw-r--r--library/std/src/sys/windows/thread.rs2
-rw-r--r--library/std/src/sys/windows/thread_parker.rs253
-rw-r--r--library/std/src/sys/windows/thread_parking.rs253
-rw-r--r--library/std/src/sys_common/backtrace.rs6
-rw-r--r--library/std/src/sys_common/io.rs3
-rw-r--r--library/std/src/sys_common/mod.rs3
-rw-r--r--library/std/src/sys_common/once/mod.rs27
-rw-r--r--library/std/src/sys_common/once/queue.rs (renamed from library/std/src/sys_common/once/generic.rs)0
-rw-r--r--library/std/src/sys_common/process.rs42
-rw-r--r--library/std/src/sys_common/remutex/tests.rs60
-rw-r--r--library/std/src/sys_common/thread_local_key.rs25
-rw-r--r--library/std/src/sys_common/thread_parker/generic.rs125
-rw-r--r--library/std/src/sys_common/thread_parker/mod.rs23
-rw-r--r--library/std/src/sys_common/thread_parking/futex.rs (renamed from library/std/src/sys_common/thread_parker/futex.rs)2
-rw-r--r--library/std/src/sys_common/thread_parking/generic.rs125
-rw-r--r--library/std/src/sys_common/thread_parking/id.rs108
-rw-r--r--library/std/src/sys_common/thread_parking/mod.rs29
-rw-r--r--library/std/src/sys_common/thread_parking/wait_flag.rs (renamed from library/std/src/sys_common/thread_parker/wait_flag.rs)2
58 files changed, 1151 insertions, 1025 deletions
diff --git a/library/std/src/sys_common/remutex.rs b/library/std/src/sync/remutex.rs
index 4c054da64..4c054da64 100644
--- a/library/std/src/sys_common/remutex.rs
+++ b/library/std/src/sync/remutex.rs
diff --git a/library/std/src/sys/itron/thread.rs b/library/std/src/sys/itron/thread.rs
index c2b366808..19350b83f 100644
--- a/library/std/src/sys/itron/thread.rs
+++ b/library/std/src/sys/itron/thread.rs
@@ -119,7 +119,7 @@ impl Thread {
let old_lifecycle = inner
.lifecycle
- .swap(LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE, Ordering::Release);
+ .swap(LIFECYCLE_EXITED_OR_FINISHED_OR_JOIN_FINALIZE, Ordering::AcqRel);
match old_lifecycle {
LIFECYCLE_DETACHED => {
@@ -129,9 +129,9 @@ impl Thread {
// In this case, `*p_inner`'s ownership has been moved to
// us, and we are responsible for dropping it. The acquire
- // ordering is not necessary because the parent thread made
- // no memory access needing synchronization since the call
- // to `acre_tsk`.
+ // ordering ensures that the swap operation that wrote
+ // `LIFECYCLE_DETACHED` happens-before `Box::from_raw(
+ // p_inner)`.
// Safety: See above.
let _ = unsafe { Box::from_raw(p_inner) };
@@ -151,6 +151,9 @@ impl Thread {
// Since the parent might drop `*inner` and terminate us as
// soon as it sees `JOIN_FINALIZE`, the release ordering
// must be used in the above `swap` call.
+ //
+ // To make the task referred to by `parent_tid` visible, we
+ // must use the acquire ordering in the above `swap` call.
// [JOINING → JOIN_FINALIZE]
// Wake up the parent task.
@@ -218,11 +221,15 @@ impl Thread {
let current_task = current_task as usize;
- match inner.lifecycle.swap(current_task, Ordering::Acquire) {
+ match inner.lifecycle.swap(current_task, Ordering::AcqRel) {
LIFECYCLE_INIT => {
// [INIT → JOINING]
// The child task will transition the state to `JOIN_FINALIZE`
// and wake us up.
+ //
+ // To make the task referred to by `current_task` visible from
+ // the child task's point of view, we must use the release
+ // ordering in the above `swap` call.
loop {
expect_success_aborting(unsafe { abi::slp_tsk() }, &"slp_tsk");
// To synchronize with the child task's memory accesses to
@@ -267,15 +274,15 @@ impl Drop for Thread {
let inner = unsafe { self.p_inner.as_ref() };
// Detach the thread.
- match inner.lifecycle.swap(LIFECYCLE_DETACHED_OR_JOINED, Ordering::Acquire) {
+ match inner.lifecycle.swap(LIFECYCLE_DETACHED_OR_JOINED, Ordering::AcqRel) {
LIFECYCLE_INIT => {
// [INIT → DETACHED]
// When the time comes, the child will figure out that no
// one will ever join it.
// The ownership of `*p_inner` is moved to the child thread.
- // However, the release ordering is not necessary because we
- // made no memory access needing synchronization since the call
- // to `acre_tsk`.
+ // The release ordering ensures that the above swap operation on
+ // `lifecycle` happens-before the child thread's
+ // `Box::from_raw(p_inner)`.
}
LIFECYCLE_FINISHED => {
// [FINISHED → JOINED]
@@ -287,7 +294,7 @@ impl Drop for Thread {
// Terminate and delete the task
// Safety: `self.task` still represents a task we own (because
// this method or `join_inner` is called only once for
- // each `Thread`). The task indicated that it's safe to
+ // each `Thread`). The task indicated that it's safe to
// delete by entering the `FINISHED` state.
unsafe { terminate_and_delete_task(self.task) };
diff --git a/library/std/src/sys/sgx/mod.rs b/library/std/src/sys/sgx/mod.rs
index 01e4ffe3d..9865a945b 100644
--- a/library/std/src/sys/sgx/mod.rs
+++ b/library/std/src/sys/sgx/mod.rs
@@ -34,6 +34,7 @@ pub mod process;
pub mod stdio;
pub mod thread;
pub mod thread_local_key;
+pub mod thread_parking;
pub mod time;
mod condvar;
diff --git a/library/std/src/sys/sgx/thread.rs b/library/std/src/sys/sgx/thread.rs
index d745a6196..1608b8cb6 100644
--- a/library/std/src/sys/sgx/thread.rs
+++ b/library/std/src/sys/sgx/thread.rs
@@ -65,39 +65,36 @@ mod task_queue {
/// execution. The signal is sent once all TLS destructors have finished at
/// which point no new thread locals should be created.
pub mod wait_notify {
- use super::super::waitqueue::{SpinMutex, WaitQueue, WaitVariable};
+ use crate::pin::Pin;
use crate::sync::Arc;
+ use crate::sys_common::thread_parking::Parker;
- pub struct Notifier(Arc<SpinMutex<WaitVariable<bool>>>);
+ pub struct Notifier(Arc<Parker>);
impl Notifier {
/// Notify the waiter. The waiter is either notified right away (if
/// currently blocked in `Waiter::wait()`) or later when it calls the
/// `Waiter::wait()` method.
pub fn notify(self) {
- let mut guard = self.0.lock();
- *guard.lock_var_mut() = true;
- let _ = WaitQueue::notify_one(guard);
+ Pin::new(&*self.0).unpark()
}
}
- pub struct Waiter(Arc<SpinMutex<WaitVariable<bool>>>);
+ pub struct Waiter(Arc<Parker>);
impl Waiter {
/// Wait for a notification. If `Notifier::notify()` has already been
/// called, this will return immediately, otherwise the current thread
/// is blocked until notified.
pub fn wait(self) {
- let guard = self.0.lock();
- if *guard.lock_var() {
- return;
- }
- WaitQueue::wait(guard, || {});
+ // SAFETY:
+ // This is only ever called on one thread.
+ unsafe { Pin::new(&*self.0).park() }
}
}
pub fn new() -> (Notifier, Waiter) {
- let inner = Arc::new(SpinMutex::new(WaitVariable::new(false)));
+ let inner = Arc::new(Parker::new());
(Notifier(inner.clone()), Waiter(inner))
}
}
diff --git a/library/std/src/sys/sgx/thread_parking.rs b/library/std/src/sys/sgx/thread_parking.rs
new file mode 100644
index 000000000..0006cd4f1
--- /dev/null
+++ b/library/std/src/sys/sgx/thread_parking.rs
@@ -0,0 +1,23 @@
+use super::abi::usercalls;
+use crate::io::ErrorKind;
+use crate::time::Duration;
+use fortanix_sgx_abi::{EV_UNPARK, WAIT_INDEFINITE};
+
+pub type ThreadId = fortanix_sgx_abi::Tcs;
+
+pub use super::abi::thread::current;
+
+pub fn park(_hint: usize) {
+ usercalls::wait(EV_UNPARK, WAIT_INDEFINITE).unwrap();
+}
+
+pub fn park_timeout(dur: Duration, _hint: usize) {
+ let timeout = u128::min(dur.as_nanos(), WAIT_INDEFINITE as u128 - 1) as u64;
+ if let Err(e) = usercalls::wait(EV_UNPARK, timeout) {
+ assert!(matches!(e.kind(), ErrorKind::TimedOut | ErrorKind::WouldBlock))
+ }
+}
+
+pub fn unpark(tid: ThreadId, _hint: usize) {
+ let _ = usercalls::send(EV_UNPARK, Some(tid));
+}
diff --git a/library/std/src/sys/unix/android.rs b/library/std/src/sys/unix/android.rs
index 73ff10ab8..0f704994f 100644
--- a/library/std/src/sys/unix/android.rs
+++ b/library/std/src/sys/unix/android.rs
@@ -1,7 +1,7 @@
//! Android ABI-compatibility module
//!
-//! The ABI of Android has changed quite a bit over time, and libstd attempts to
-//! be both forwards and backwards compatible as much as possible. We want to
+//! The ABI of Android has changed quite a bit over time, and std attempts to be
+//! both forwards and backwards compatible as much as possible. We want to
//! always work with the most recent version of Android, but we also want to
//! work with older versions of Android for whenever projects need to.
//!
diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs
index 37a49f2d7..8e1f35d6c 100644
--- a/library/std/src/sys/unix/fs.rs
+++ b/library/std/src/sys/unix/fs.rs
@@ -149,12 +149,13 @@ cfg_has_statx! {{
) -> Option<io::Result<FileAttr>> {
use crate::sync::atomic::{AtomicU8, Ordering};
- // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`
- // We store the availability in global to avoid unnecessary syscalls.
- // 0: Unknown
- // 1: Not available
- // 2: Available
- static STATX_STATE: AtomicU8 = AtomicU8::new(0);
+ // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`.
+ // We check for it on first failure and remember availability to avoid having to
+ // do it again.
+ #[repr(u8)]
+ enum STATX_STATE{ Unknown = 0, Present, Unavailable }
+ static STATX_SAVED_STATE: AtomicU8 = AtomicU8::new(STATX_STATE::Unknown as u8);
+
syscall! {
fn statx(
fd: c_int,
@@ -165,31 +166,44 @@ cfg_has_statx! {{
) -> c_int
}
- match STATX_STATE.load(Ordering::Relaxed) {
- 0 => {
- // It is a trick to call `statx` with null pointers to check if the syscall
- // is available. According to the manual, it is expected to fail with EFAULT.
- // We do this mainly for performance, since it is nearly hundreds times
- // faster than a normal successful call.
- let err = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut()))
- .err()
- .and_then(|e| e.raw_os_error());
- // We don't check `err == Some(libc::ENOSYS)` because the syscall may be limited
- // and returns `EPERM`. Listing all possible errors seems not a good idea.
- // See: https://github.com/rust-lang/rust/issues/65662
- if err != Some(libc::EFAULT) {
- STATX_STATE.store(1, Ordering::Relaxed);
- return None;
- }
- STATX_STATE.store(2, Ordering::Relaxed);
- }
- 1 => return None,
- _ => {}
+ if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Unavailable as u8 {
+ return None;
}
let mut buf: libc::statx = mem::zeroed();
if let Err(err) = cvt(statx(fd, path, flags, mask, &mut buf)) {
- return Some(Err(err));
+ if STATX_SAVED_STATE.load(Ordering::Relaxed) == STATX_STATE::Present as u8 {
+ return Some(Err(err));
+ }
+
+ // Availability not checked yet.
+ //
+ // First try the cheap way.
+ if err.raw_os_error() == Some(libc::ENOSYS) {
+ STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed);
+ return None;
+ }
+
+ // Error other than `ENOSYS` is not a good enough indicator -- it is
+ // known that `EPERM` can be returned as a result of using seccomp to
+ // block the syscall.
+ // Availability is checked by performing a call which expects `EFAULT`
+ // if the syscall is usable.
+ // See: https://github.com/rust-lang/rust/issues/65662
+ // FIXME this can probably just do the call if `EPERM` was received, but
+ // previous iteration of the code checked it for all errors and for now
+ // this is retained.
+ // FIXME what about transient conditions like `ENOMEM`?
+ let err2 = cvt(statx(0, ptr::null(), 0, libc::STATX_ALL, ptr::null_mut()))
+ .err()
+ .and_then(|e| e.raw_os_error());
+ if err2 == Some(libc::EFAULT) {
+ STATX_SAVED_STATE.store(STATX_STATE::Present as u8, Ordering::Relaxed);
+ return Some(Err(err));
+ } else {
+ STATX_SAVED_STATE.store(STATX_STATE::Unavailable as u8, Ordering::Relaxed);
+ return None;
+ }
}
// We cannot fill `stat64` exhaustively because of private padding fields.
@@ -243,17 +257,15 @@ struct InnerReadDir {
pub struct ReadDir {
inner: Arc<InnerReadDir>,
- #[cfg(not(any(
- target_os = "android",
- target_os = "linux",
- target_os = "solaris",
- target_os = "illumos",
- target_os = "fuchsia",
- target_os = "redox",
- )))]
end_of_stream: bool,
}
+impl ReadDir {
+ fn new(inner: InnerReadDir) -> Self {
+ Self { inner: Arc::new(inner), end_of_stream: false }
+ }
+}
+
struct Dir(*mut libc::DIR);
unsafe impl Send for Dir {}
@@ -332,11 +344,23 @@ pub struct FileTimes {
modified: Option<SystemTime>,
}
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+#[derive(Copy, Clone, Eq, Debug)]
pub struct FileType {
mode: mode_t,
}
+impl PartialEq for FileType {
+ fn eq(&self, other: &Self) -> bool {
+ self.masked() == other.masked()
+ }
+}
+
+impl core::hash::Hash for FileType {
+ fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
+ self.masked().hash(state);
+ }
+}
+
#[derive(Debug)]
pub struct DirBuilder {
mode: mode_t,
@@ -548,7 +572,11 @@ impl FileType {
}
pub fn is(&self, mode: mode_t) -> bool {
- self.mode & libc::S_IFMT == mode
+ self.masked() == mode
+ }
+
+ fn masked(&self) -> mode_t {
+ self.mode & libc::S_IFMT
}
}
@@ -578,18 +606,26 @@ impl Iterator for ReadDir {
target_os = "illumos"
))]
fn next(&mut self) -> Option<io::Result<DirEntry>> {
+ if self.end_of_stream {
+ return None;
+ }
+
unsafe {
loop {
// As of POSIX.1-2017, readdir() is not required to be thread safe; only
// readdir_r() is. However, readdir_r() cannot correctly handle platforms
- // with unlimited or variable NAME_MAX. Many modern platforms guarantee
+ // with unlimited or variable NAME_MAX. Many modern platforms guarantee
// thread safety for readdir() as long an individual DIR* is not accessed
// concurrently, which is sufficient for Rust.
super::os::set_errno(0);
let entry_ptr = readdir64(self.inner.dirp.0);
if entry_ptr.is_null() {
- // null can mean either the end is reached or an error occurred.
- // So we had to clear errno beforehand to check for an error now.
+ // We either encountered an error, or reached the end. Either way,
+ // the next call to next() should return None.
+ self.end_of_stream = true;
+
+ // To distinguish between errors and end-of-directory, we had to clear
+ // errno beforehand to check for an error now.
return match super::os::errno() {
0 => None,
e => Some(Err(Error::from_raw_os_error(e))),
@@ -1347,18 +1383,7 @@ pub fn readdir(path: &Path) -> io::Result<ReadDir> {
} else {
let root = path.to_path_buf();
let inner = InnerReadDir { dirp: Dir(ptr), root };
- Ok(ReadDir {
- inner: Arc::new(inner),
- #[cfg(not(any(
- target_os = "android",
- target_os = "linux",
- target_os = "solaris",
- target_os = "illumos",
- target_os = "fuchsia",
- target_os = "redox",
- )))]
- end_of_stream: false,
- })
+ Ok(ReadDir::new(inner))
}
}
@@ -1739,12 +1764,16 @@ mod remove_dir_impl {
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd};
use crate::os::unix::prelude::{OwnedFd, RawFd};
use crate::path::{Path, PathBuf};
- use crate::sync::Arc;
use crate::sys::common::small_c_string::run_path_with_cstr;
use crate::sys::{cvt, cvt_r};
- #[cfg(not(all(target_os = "macos", not(target_arch = "aarch64")),))]
+ #[cfg(not(any(
+ all(target_os = "linux", target_env = "gnu"),
+ all(target_os = "macos", not(target_arch = "aarch64"))
+ )))]
use libc::{fdopendir, openat, unlinkat};
+ #[cfg(all(target_os = "linux", target_env = "gnu"))]
+ use libc::{fdopendir, openat64 as openat, unlinkat};
#[cfg(all(target_os = "macos", not(target_arch = "aarch64")))]
use macos_weak::{fdopendir, openat, unlinkat};
@@ -1811,21 +1840,8 @@ mod remove_dir_impl {
// a valid root is not needed because we do not call any functions involving the full path
// of the DirEntrys.
let dummy_root = PathBuf::new();
- Ok((
- ReadDir {
- inner: Arc::new(InnerReadDir { dirp, root: dummy_root }),
- #[cfg(not(any(
- target_os = "android",
- target_os = "linux",
- target_os = "solaris",
- target_os = "illumos",
- target_os = "fuchsia",
- target_os = "redox",
- )))]
- end_of_stream: false,
- },
- new_parent_fd,
- ))
+ let inner = InnerReadDir { dirp, root: dummy_root };
+ Ok((ReadDir::new(inner), new_parent_fd))
}
#[cfg(any(
diff --git a/library/std/src/sys/unix/kernel_copy.rs b/library/std/src/sys/unix/kernel_copy.rs
index 94546ca09..73b9bef7e 100644
--- a/library/std/src/sys/unix/kernel_copy.rs
+++ b/library/std/src/sys/unix/kernel_copy.rs
@@ -61,6 +61,10 @@ use crate::ptr;
use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use crate::sys::cvt;
use crate::sys::weak::syscall;
+#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
+use libc::sendfile as sendfile64;
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
+use libc::sendfile64;
use libc::{EBADF, EINVAL, ENOSYS, EOPNOTSUPP, EOVERFLOW, EPERM, EXDEV};
#[cfg(test)]
@@ -583,7 +587,7 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) ->
// - copy_file_range file is immutable or syscall is blocked by seccomp¹ (EPERM)
// - copy_file_range cannot be used with pipes or device nodes (EINVAL)
// - the writer fd was opened with O_APPEND (EBADF²)
- // and no bytes were written successfully yet. (All these errnos should
+ // and no bytes were written successfully yet. (All these errnos should
// not be returned if something was already written, but they happen in
// the wild, see #91152.)
//
@@ -647,7 +651,7 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) ->
let result = match mode {
SpliceMode::Sendfile => {
- cvt(unsafe { libc::sendfile(writer, reader, ptr::null_mut(), chunk_size) })
+ cvt(unsafe { sendfile64(writer, reader, ptr::null_mut(), chunk_size) })
}
SpliceMode::Splice => cvt(unsafe {
splice(reader, ptr::null_mut(), writer, ptr::null_mut(), chunk_size, 0)
diff --git a/library/std/src/sys/unix/locks/pthread_condvar.rs b/library/std/src/sys/unix/locks/pthread_condvar.rs
index 1ddb09905..6be1abc2b 100644
--- a/library/std/src/sys/unix/locks/pthread_condvar.rs
+++ b/library/std/src/sys/unix/locks/pthread_condvar.rs
@@ -2,6 +2,7 @@ use crate::cell::UnsafeCell;
use crate::ptr;
use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed};
use crate::sys::locks::{pthread_mutex, Mutex};
+use crate::sys::time::TIMESPEC_MAX;
use crate::sys_common::lazy_box::{LazyBox, LazyInit};
use crate::time::Duration;
@@ -12,13 +13,6 @@ pub struct Condvar {
mutex: AtomicPtr<libc::pthread_mutex_t>,
}
-const TIMESPEC_MAX: libc::timespec =
- libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
-
-fn saturating_cast_to_time_t(value: u64) -> libc::time_t {
- if value > <libc::time_t>::MAX as u64 { <libc::time_t>::MAX } else { value as libc::time_t }
-}
-
#[inline]
fn raw(c: &Condvar) -> *mut libc::pthread_cond_t {
c.inner.0.get()
@@ -133,26 +127,15 @@ impl Condvar {
target_os = "horizon"
)))]
pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
- use crate::mem;
+ use crate::sys::time::Timespec;
let mutex = pthread_mutex::raw(mutex);
self.verify(mutex);
- let mut now: libc::timespec = mem::zeroed();
- let r = libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut now);
- assert_eq!(r, 0);
-
- // Nanosecond calculations can't overflow because both values are below 1e9.
- let nsec = dur.subsec_nanos() + now.tv_nsec as u32;
-
- let sec = saturating_cast_to_time_t(dur.as_secs())
- .checked_add((nsec / 1_000_000_000) as libc::time_t)
- .and_then(|s| s.checked_add(now.tv_sec));
- let nsec = nsec % 1_000_000_000;
-
- let timeout =
- sec.map(|s| libc::timespec { tv_sec: s, tv_nsec: nsec as _ }).unwrap_or(TIMESPEC_MAX);
-
+ let timeout = Timespec::now(libc::CLOCK_MONOTONIC)
+ .checked_add_duration(&dur)
+ .and_then(|t| t.to_timespec())
+ .unwrap_or(TIMESPEC_MAX);
let r = libc::pthread_cond_timedwait(raw(self), mutex, &timeout);
assert!(r == libc::ETIMEDOUT || r == 0);
r == 0
@@ -169,57 +152,41 @@ impl Condvar {
target_os = "espidf",
target_os = "horizon"
))]
- pub unsafe fn wait_timeout(&self, mutex: &Mutex, mut dur: Duration) -> bool {
+ pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool {
+ use crate::sys::time::SystemTime;
use crate::time::Instant;
let mutex = pthread_mutex::raw(mutex);
self.verify(mutex);
- // 1000 years
- let max_dur = Duration::from_secs(1000 * 365 * 86400);
-
- if dur > max_dur {
- // OSX implementation of `pthread_cond_timedwait` is buggy
- // with super long durations. When duration is greater than
- // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait`
- // in macOS Sierra return error 316.
- //
- // This program demonstrates the issue:
- // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c
- //
- // To work around this issue, and possible bugs of other OSes, timeout
- // is clamped to 1000 years, which is allowable per the API of `wait_timeout`
- // because of spurious wakeups.
-
- dur = max_dur;
- }
-
- // First, figure out what time it currently is, in both system and
- // stable time. pthread_cond_timedwait uses system time, but we want to
- // report timeout based on stable time.
- let mut sys_now = libc::timeval { tv_sec: 0, tv_usec: 0 };
- let stable_now = Instant::now();
- let r = libc::gettimeofday(&mut sys_now, ptr::null_mut());
- assert_eq!(r, 0, "unexpected error: {:?}", crate::io::Error::last_os_error());
-
- let nsec = dur.subsec_nanos() as libc::c_long + (sys_now.tv_usec * 1000) as libc::c_long;
- let extra = (nsec / 1_000_000_000) as libc::time_t;
- let nsec = nsec % 1_000_000_000;
- let seconds = saturating_cast_to_time_t(dur.as_secs());
-
- let timeout = sys_now
- .tv_sec
- .checked_add(extra)
- .and_then(|s| s.checked_add(seconds))
- .map(|s| libc::timespec { tv_sec: s, tv_nsec: nsec })
+ // OSX implementation of `pthread_cond_timedwait` is buggy
+ // with super long durations. When duration is greater than
+ // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait`
+ // in macOS Sierra returns error 316.
+ //
+ // This program demonstrates the issue:
+ // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c
+ //
+ // To work around this issue, and possible bugs of other OSes, timeout
+ // is clamped to 1000 years, which is allowable per the API of `wait_timeout`
+ // because of spurious wakeups.
+ let dur = Duration::min(dur, Duration::from_secs(1000 * 365 * 86400));
+
+ // pthread_cond_timedwait uses system time, but we want to report timeout
+ // based on stable time.
+ let now = Instant::now();
+
+ let timeout = SystemTime::now()
+ .t
+ .checked_add_duration(&dur)
+ .and_then(|t| t.to_timespec())
.unwrap_or(TIMESPEC_MAX);
- // And wait!
let r = libc::pthread_cond_timedwait(raw(self), mutex, &timeout);
debug_assert!(r == libc::ETIMEDOUT || r == 0);
// ETIMEDOUT is not a totally reliable method of determining timeout due
// to clock shifts, so do the check ourselves
- stable_now.elapsed() < dur
+ now.elapsed() < dur
}
}
diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs
index 9055a011c..30a96be14 100644
--- a/library/std/src/sys/unix/mod.rs
+++ b/library/std/src/sys/unix/mod.rs
@@ -40,7 +40,7 @@ pub mod stdio;
pub mod thread;
pub mod thread_local_dtor;
pub mod thread_local_key;
-pub mod thread_parker;
+pub mod thread_parking;
pub mod time;
#[cfg(target_os = "espidf")]
@@ -95,6 +95,10 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
)))]
'poll: {
use crate::sys::os::errno;
+ #[cfg(not(all(target_os = "linux", target_env = "gnu")))]
+ use libc::open as open64;
+ #[cfg(all(target_os = "linux", target_env = "gnu"))]
+ use libc::open64;
let pfds: &mut [_] = &mut [
libc::pollfd { fd: 0, events: 0, revents: 0 },
libc::pollfd { fd: 1, events: 0, revents: 0 },
@@ -116,7 +120,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
if pfd.revents & libc::POLLNVAL == 0 {
continue;
}
- if libc::open("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 {
+ if open64("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 {
// If the stream is closed but we failed to reopen it, abort the
// process. Otherwise we wouldn't preserve the safety of
// operations on the corresponding Rust object Stdin, Stdout, or
@@ -139,9 +143,13 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
)))]
{
use crate::sys::os::errno;
+ #[cfg(not(all(target_os = "linux", target_env = "gnu")))]
+ use libc::open as open64;
+ #[cfg(all(target_os = "linux", target_env = "gnu"))]
+ use libc::open64;
for fd in 0..3 {
if libc::fcntl(fd, libc::F_GETFD) == -1 && errno() == libc::EBADF {
- if libc::open("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 {
+ if open64("/dev/null\0".as_ptr().cast(), libc::O_RDWR, 0) == -1 {
// If the stream is closed but we failed to reopen it, abort the
// process. Otherwise we wouldn't preserve the safety of
// operations on the corresponding Rust object Stdin, Stdout, or
@@ -156,7 +164,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
unsafe fn reset_sigpipe(#[allow(unused_variables)] sigpipe: u8) {
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))]
{
- // We don't want to add this as a public type to libstd, nor do we
+ // We don't want to add this as a public type to std, nor do we
// want to `include!` a file from the compiler (which would break
// Miri and xargo for example), so we choose to duplicate these
// constants from `compiler/rustc_session/src/config/sigpipe.rs`.
@@ -176,12 +184,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
sigpipe::SIG_DFL => (true, Some(libc::SIG_DFL)),
_ => unreachable!(),
};
- // The bootstrap compiler doesn't know about sigpipe::DEFAULT, and always passes in
- // SIG_IGN. This causes some tests to fail because they expect SIGPIPE to be reset to
- // default on process spawning (which doesn't happen if #[unix_sigpipe] is specified).
- // Since we can't differentiate between the cases here, treat SIG_IGN as DEFAULT
- // unconditionally.
- if sigpipe_attr_specified && !(cfg!(bootstrap) && sigpipe == sigpipe::SIG_IGN) {
+ if sigpipe_attr_specified {
UNIX_SIGPIPE_ATTR_SPECIFIED.store(true, crate::sync::atomic::Ordering::Relaxed);
}
if let Some(handler) = handler {
diff --git a/library/std/src/sys/unix/net.rs b/library/std/src/sys/unix/net.rs
index b84bf8f92..c86f80972 100644
--- a/library/std/src/sys/unix/net.rs
+++ b/library/std/src/sys/unix/net.rs
@@ -512,7 +512,7 @@ impl FromRawFd for Socket {
// A workaround for this bug is to call the res_init libc function, to clear
// the cached configs. Unfortunately, while we believe glibc's implementation
// of res_init is thread-safe, we know that other implementations are not
-// (https://github.com/rust-lang/rust/issues/43592). Code here in libstd could
+// (https://github.com/rust-lang/rust/issues/43592). Code here in std could
// try to synchronize its res_init calls with a Mutex, but that wouldn't
// protect programs that call into libc in other ways. So instead of calling
// res_init unconditionally, we call it only when we detect we're linking
diff --git a/library/std/src/sys/unix/pipe.rs b/library/std/src/sys/unix/pipe.rs
index a56c275c9..a744d0ab6 100644
--- a/library/std/src/sys/unix/pipe.rs
+++ b/library/std/src/sys/unix/pipe.rs
@@ -58,6 +58,10 @@ impl AnonPipe {
self.0.is_read_vectored()
}
+ pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
+ self.0.read_to_end(buf)
+ }
+
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
self.0.write(buf)
}
diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs
index 848adca78..afd03d79c 100644
--- a/library/std/src/sys/unix/process/process_common.rs
+++ b/library/std/src/sys/unix/process/process_common.rs
@@ -144,6 +144,7 @@ pub enum ChildStdio {
Null,
}
+#[derive(Debug)]
pub enum Stdio {
Inherit,
Null,
@@ -510,16 +511,68 @@ impl ChildStdio {
}
impl fmt::Debug for Command {
+ // show all attributes but `self.closures` which does not implement `Debug`
+ // and `self.argv` which is not useful for debugging
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if self.program != self.args[0] {
- write!(f, "[{:?}] ", self.program)?;
- }
- write!(f, "{:?}", self.args[0])?;
+ if f.alternate() {
+ let mut debug_command = f.debug_struct("Command");
+ debug_command.field("program", &self.program).field("args", &self.args);
+ if !self.env.is_unchanged() {
+ debug_command.field("env", &self.env);
+ }
+
+ if self.cwd.is_some() {
+ debug_command.field("cwd", &self.cwd);
+ }
+ if self.uid.is_some() {
+ debug_command.field("uid", &self.uid);
+ }
+ if self.gid.is_some() {
+ debug_command.field("gid", &self.gid);
+ }
+
+ if self.groups.is_some() {
+ debug_command.field("groups", &self.groups);
+ }
+
+ if self.stdin.is_some() {
+ debug_command.field("stdin", &self.stdin);
+ }
+ if self.stdout.is_some() {
+ debug_command.field("stdout", &self.stdout);
+ }
+ if self.stderr.is_some() {
+ debug_command.field("stderr", &self.stderr);
+ }
+ if self.pgroup.is_some() {
+ debug_command.field("pgroup", &self.pgroup);
+ }
+
+ #[cfg(target_os = "linux")]
+ {
+ debug_command.field("create_pidfd", &self.create_pidfd);
+ }
- for arg in &self.args[1..] {
- write!(f, " {:?}", arg)?;
+ debug_command.finish()
+ } else {
+ if let Some(ref cwd) = self.cwd {
+ write!(f, "cd {cwd:?} && ")?;
+ }
+ for (key, value_opt) in self.get_envs() {
+ if let Some(value) = value_opt {
+ write!(f, "{}={value:?} ", key.to_string_lossy())?;
+ }
+ }
+ if self.program != self.args[0] {
+ write!(f, "[{:?}] ", self.program)?;
+ }
+ write!(f, "{:?}", self.args[0])?;
+
+ for arg in &self.args[1..] {
+ write!(f, " {:?}", arg)?;
+ }
+ Ok(())
}
- Ok(())
}
}
diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs
index 66ea3db20..d4c7e58b3 100644
--- a/library/std/src/sys/unix/process/process_fuchsia.rs
+++ b/library/std/src/sys/unix/process/process_fuchsia.rs
@@ -35,6 +35,11 @@ impl Command {
Ok((Process { handle: Handle::new(process_handle) }, ours))
}
+ pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+ let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?;
+ crate::sys_common::process::wait_with_output(proc, pipes)
+ }
+
pub fn exec(&mut self, default: Stdio) -> io::Error {
if self.saw_nul() {
return io::const_io_error!(
@@ -257,7 +262,7 @@ impl ExitStatus {
// available on Fuchsia.
//
// It does not appear that Fuchsia is Unix-like enough to implement ExitStatus (or indeed many
- // other things from std::os::unix) properly. This veneer is always going to be a bodge. So
+ // other things from std::os::unix) properly. This veneer is always going to be a bodge. So
// while I don't know if these implementations are actually correct, I think they will do for
// now at least.
pub fn core_dumped(&self) -> bool {
@@ -272,9 +277,9 @@ impl ExitStatus {
pub fn into_raw(&self) -> c_int {
// We don't know what someone who calls into_raw() will do with this value, but it should
- // have the conventional Unix representation. Despite the fact that this is not
+ // have the conventional Unix representation. Despite the fact that this is not
// standardised in SuS or POSIX, all Unix systems encode the signal and exit status the
- // same way. (Ie the WIFEXITED, WEXITSTATUS etc. macros have identical behaviour on every
+ // same way. (Ie the WIFEXITED, WEXITSTATUS etc. macros have identical behaviour on every
// Unix.)
//
// The caller of `std::os::unix::into_raw` is probably wanting a Unix exit status, and may
@@ -282,14 +287,14 @@ impl ExitStatus {
// different Unix variant.
//
// The other view would be to say that the caller on Fuchsia ought to know that `into_raw`
- // will give a raw Fuchsia status (whatever that is - I don't know, personally). That is
+ // will give a raw Fuchsia status (whatever that is - I don't know, personally). That is
// not possible here because we must return a c_int because that's what Unix (including
// SuS and POSIX) say a wait status is, but Fuchsia apparently uses a u64, so it won't
// necessarily fit.
//
// It seems to me that the right answer would be to provide std::os::fuchsia with its
// own ExitStatusExt, rather that trying to provide a not very convincing imitation of
- // Unix. Ie, std::os::unix::process:ExitStatusExt ought not to exist on Fuchsia. But
+ // Unix. Ie, std::os::unix::process:ExitStatusExt ought not to exist on Fuchsia. But
// fixing this up that is beyond the scope of my efforts now.
let exit_status_as_if_unix: u8 = self.0.try_into().expect("Fuchsia process return code bigger than 8 bits, but std::os::unix::ExitStatusExt::into_raw() was called to try to convert the value into a traditional Unix-style wait status, which cannot represent values greater than 255.");
let wait_status_as_if_unix = (exit_status_as_if_unix as c_int) << 8;
diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
index 56a805cef..3bc17b775 100644
--- a/library/std/src/sys/unix/process/process_unix.rs
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -66,14 +66,15 @@ impl Command {
//
// Note that as soon as we're done with the fork there's no need to hold
// a lock any more because the parent won't do anything and the child is
- // in its own process. Thus the parent drops the lock guard while the child
- // forgets it to avoid unlocking it on a new thread, which would be invalid.
+ // in its own process. Thus the parent drops the lock guard immediately.
+ // The child calls `mem::forget` to leak the lock, which is crucial because
+ // releasing a lock is not async-signal-safe.
let env_lock = sys::os::env_read_lock();
let (pid, pidfd) = unsafe { self.do_fork()? };
if pid == 0 {
crate::panic::always_abort();
- mem::forget(env_lock);
+ mem::forget(env_lock); // avoid non-async-signal-safe unlocking
drop(input);
let Err(err) = unsafe { self.do_exec(theirs, envp.as_ref()) };
let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
@@ -132,6 +133,11 @@ impl Command {
}
}
+ pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+ let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?;
+ crate::sys_common::process::wait_with_output(proc, pipes)
+ }
+
// Attempts to fork the process. If successful, returns Ok((0, -1))
// in the child, and Ok((child_pid, -1)) in the parent.
#[cfg(not(target_os = "linux"))]
@@ -660,11 +666,11 @@ impl ExitStatus {
}
pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
- // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
+ // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
// true on all actual versions of Unix, is widely assumed, and is specified in SuS
- // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html . If it is not
+ // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not
// true for a platform pretending to be Unix, the tests (our doctests, and also
- // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
+ // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
match NonZero_c_int::try_from(self.0) {
/* was nonzero */ Ok(failure) => Err(ExitStatusError(failure)),
/* was zero, couldn't convert */ Err(_) => Ok(()),
@@ -740,6 +746,8 @@ fn signal_string(signal: i32) -> &'static str {
libc::SIGWINCH => " (SIGWINCH)",
#[cfg(not(target_os = "haiku"))]
libc::SIGIO => " (SIGIO)",
+ #[cfg(target_os = "haiku")]
+ libc::SIGPOLL => " (SIGPOLL)",
libc::SIGSYS => " (SIGSYS)",
// For information on Linux signals, run `man 7 signal`
#[cfg(all(
diff --git a/library/std/src/sys/unix/process/process_unix/tests.rs b/library/std/src/sys/unix/process/process_unix/tests.rs
index e0e2d478f..e5e1f956b 100644
--- a/library/std/src/sys/unix/process/process_unix/tests.rs
+++ b/library/std/src/sys/unix/process/process_unix/tests.rs
@@ -3,7 +3,7 @@ use crate::panic::catch_unwind;
use crate::process::Command;
// Many of the other aspects of this situation, including heap alloc concurrency
-// safety etc., are tested in src/test/ui/process/process-panic-after-fork.rs
+// safety etc., are tested in tests/ui/process/process-panic-after-fork.rs
#[test]
fn exitstatus_display_tests() {
@@ -19,17 +19,17 @@ fn exitstatus_display_tests() {
t(0x00000, "exit status: 0");
t(0x0ff00, "exit status: 255");
- // On MacOS, 0x0137f is WIFCONTINUED, not WIFSTOPPED. Probably *BSD is similar.
+ // On MacOS, 0x0137f is WIFCONTINUED, not WIFSTOPPED. Probably *BSD is similar.
// https://github.com/rust-lang/rust/pull/82749#issuecomment-790525956
// The purpose of this test is to test our string formatting, not our understanding of the wait
- // status magic numbers. So restrict these to Linux.
+ // status magic numbers. So restrict these to Linux.
if cfg!(target_os = "linux") {
t(0x0137f, "stopped (not terminated) by signal: 19 (SIGSTOP)");
t(0x0ffff, "continued (WIFCONTINUED)");
}
// Testing "unrecognised wait status" is hard because the wait.h macros typically
- // assume that the value came from wait and isn't mad. With the glibc I have here
+ // assume that the value came from wait and isn't mad. With the glibc I have here
// this works:
if cfg!(all(target_os = "linux", target_env = "gnu")) {
t(0x000ff, "unrecognised wait status: 255 0xff");
diff --git a/library/std/src/sys/unix/process/process_unsupported.rs b/library/std/src/sys/unix/process/process_unsupported.rs
index 72f9f3f9c..f28ca58d0 100644
--- a/library/std/src/sys/unix/process/process_unsupported.rs
+++ b/library/std/src/sys/unix/process/process_unsupported.rs
@@ -20,6 +20,10 @@ impl Command {
unsupported()
}
+ pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+ unsupported()
+ }
+
pub fn exec(&mut self, _default: Stdio) -> io::Error {
unsupported_err()
}
diff --git a/library/std/src/sys/unix/process/process_vxworks.rs b/library/std/src/sys/unix/process/process_vxworks.rs
index 200ef6719..569a4b149 100644
--- a/library/std/src/sys/unix/process/process_vxworks.rs
+++ b/library/std/src/sys/unix/process/process_vxworks.rs
@@ -108,6 +108,11 @@ impl Command {
}
}
+ pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+ let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?;
+ crate::sys_common::process::wait_with_output(proc, pipes)
+ }
+
pub fn exec(&mut self, default: Stdio) -> io::Error {
let ret = Command::spawn(self, default, false);
match ret {
@@ -190,11 +195,11 @@ impl ExitStatus {
}
pub fn exit_ok(&self) -> Result<(), ExitStatusError> {
- // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
+ // This assumes that WIFEXITED(status) && WEXITSTATUS==0 corresponds to status==0. This is
// true on all actual versions of Unix, is widely assumed, and is specified in SuS
- // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html . If it is not
+ // https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html. If it is not
// true for a platform pretending to be Unix, the tests (our doctests, and also
- // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
+ // procsss_unix/tests.rs) will spot it. `ExitStatusError::code` assumes this too.
match NonZero_c_int::try_from(self.0) {
Ok(failure) => Err(ExitStatusError(failure)),
Err(_) => Ok(()),
diff --git a/library/std/src/sys/unix/stack_overflow.rs b/library/std/src/sys/unix/stack_overflow.rs
index 75a5c0f92..b59d4ba26 100644
--- a/library/std/src/sys/unix/stack_overflow.rs
+++ b/library/std/src/sys/unix/stack_overflow.rs
@@ -45,7 +45,10 @@ mod imp {
use crate::thread;
use libc::MAP_FAILED;
- use libc::{mmap, munmap};
+ #[cfg(not(all(target_os = "linux", target_env = "gnu")))]
+ use libc::{mmap as mmap64, munmap};
+ #[cfg(all(target_os = "linux", target_env = "gnu"))]
+ use libc::{mmap64, munmap};
use libc::{sigaction, sighandler_t, SA_ONSTACK, SA_SIGINFO, SIGBUS, SIG_DFL};
use libc::{sigaltstack, SIGSTKSZ, SS_DISABLE};
use libc::{MAP_ANON, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE, SIGSEGV};
@@ -135,7 +138,7 @@ mod imp {
#[cfg(not(any(target_os = "openbsd", target_os = "netbsd", target_os = "linux",)))]
let flags = MAP_PRIVATE | MAP_ANON;
let stackp =
- mmap(ptr::null_mut(), SIGSTKSZ + page_size(), PROT_READ | PROT_WRITE, flags, -1, 0);
+ mmap64(ptr::null_mut(), SIGSTKSZ + page_size(), PROT_READ | PROT_WRITE, flags, -1, 0);
if stackp == MAP_FAILED {
panic!("failed to allocate an alternative stack: {}", io::Error::last_os_error());
}
diff --git a/library/std/src/sys/unix/thread.rs b/library/std/src/sys/unix/thread.rs
index c1d30dd9d..2a1830d06 100644
--- a/library/std/src/sys/unix/thread.rs
+++ b/library/std/src/sys/unix/thread.rs
@@ -73,7 +73,7 @@ impl Thread {
n => {
assert_eq!(n, libc::EINVAL);
// EINVAL means |stack_size| is either too small or not a
- // multiple of the system page size. Because it's definitely
+ // multiple of the system page size. Because it's definitely
// >= PTHREAD_STACK_MIN, it must be an alignment issue.
// Round up to the nearest page and try again.
let page_size = os::page_size();
@@ -136,7 +136,7 @@ impl Thread {
unsafe {
// Available since glibc 2.12, musl 1.1.16, and uClibc 1.0.20.
- let name = truncate_cstr(name, TASK_COMM_LEN);
+ let name = truncate_cstr::<{ TASK_COMM_LEN }>(name);
let res = libc::pthread_setname_np(libc::pthread_self(), name.as_ptr());
// We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
debug_assert_eq!(res, 0);
@@ -153,7 +153,7 @@ impl Thread {
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos"))]
pub fn set_name(name: &CStr) {
unsafe {
- let name = truncate_cstr(name, libc::MAXTHREADNAMESIZE);
+ let name = truncate_cstr::<{ libc::MAXTHREADNAMESIZE }>(name);
let res = libc::pthread_setname_np(name.as_ptr());
// We have no good way of propagating errors here, but in debug-builds let's check that this actually worked.
debug_assert_eq!(res, 0);
@@ -285,17 +285,12 @@ impl Drop for Thread {
}
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "watchos"))]
-fn truncate_cstr(cstr: &CStr, max_with_nul: usize) -> crate::borrow::Cow<'_, CStr> {
- use crate::{borrow::Cow, ffi::CString};
-
- if cstr.to_bytes_with_nul().len() > max_with_nul {
- let bytes = cstr.to_bytes()[..max_with_nul - 1].to_vec();
- // SAFETY: the non-nul bytes came straight from a CStr.
- // (CString will add the terminating nul.)
- Cow::Owned(unsafe { CString::from_vec_unchecked(bytes) })
- } else {
- Cow::Borrowed(cstr)
+fn truncate_cstr<const MAX_WITH_NUL: usize>(cstr: &CStr) -> [libc::c_char; MAX_WITH_NUL] {
+ let mut result = [0; MAX_WITH_NUL];
+ for (src, dst) in cstr.to_bytes().iter().zip(&mut result[..MAX_WITH_NUL - 1]) {
+ *dst = *src as libc::c_char;
}
+ result
}
pub fn available_parallelism() -> io::Result<NonZeroUsize> {
@@ -510,7 +505,7 @@ mod cgroups {
let limit = raw_quota.next()?;
let period = raw_quota.next()?;
match (limit.parse::<usize>(), period.parse::<usize>()) {
- (Ok(limit), Ok(period)) => {
+ (Ok(limit), Ok(period)) if period > 0 => {
quota = quota.min(limit / period);
}
_ => {}
@@ -570,7 +565,7 @@ mod cgroups {
let period = parse_file("cpu.cfs_period_us");
match (limit, period) {
- (Some(limit), Some(period)) => quota = quota.min(limit / period),
+ (Some(limit), Some(period)) if period > 0 => quota = quota.min(limit / period),
_ => {}
}
@@ -658,7 +653,10 @@ pub mod guard {
))]
#[cfg_attr(test, allow(dead_code))]
pub mod guard {
- use libc::{mmap, mprotect};
+ #[cfg(not(all(target_os = "linux", target_env = "gnu")))]
+ use libc::{mmap as mmap64, mprotect};
+ #[cfg(all(target_os = "linux", target_env = "gnu"))]
+ use libc::{mmap64, mprotect};
use libc::{MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_PRIVATE, PROT_NONE, PROT_READ, PROT_WRITE};
use crate::io;
@@ -757,10 +755,10 @@ pub mod guard {
if cfg!(all(target_os = "linux", not(target_env = "musl"))) {
// Linux doesn't allocate the whole stack right away, and
// the kernel has its own stack-guard mechanism to fault
- // when growing too close to an existing mapping. If we map
+ // when growing too close to an existing mapping. If we map
// our own guard, then the kernel starts enforcing a rather
// large gap above that, rendering much of the possible
- // stack space useless. See #43052.
+ // stack space useless. See #43052.
//
// Instead, we'll just note where we expect rlimit to start
// faulting, so our handler can report "stack overflow", and
@@ -776,14 +774,14 @@ pub mod guard {
None
} else if cfg!(target_os = "freebsd") {
// FreeBSD's stack autogrows, and optionally includes a guard page
- // at the bottom. If we try to remap the bottom of the stack
- // ourselves, FreeBSD's guard page moves upwards. So we'll just use
+ // at the bottom. If we try to remap the bottom of the stack
+ // ourselves, FreeBSD's guard page moves upwards. So we'll just use
// the builtin guard page.
let stackptr = get_stack_start_aligned()?;
let guardaddr = stackptr.addr();
// Technically the number of guard pages is tunable and controlled
// by the security.bsd.stack_guard_page sysctl, but there are
- // few reasons to change it from the default. The default value has
+ // few reasons to change it from the default. The default value has
// been 1 ever since FreeBSD 11.1 and 10.4.
const GUARD_PAGES: usize = 1;
let guard = guardaddr..guardaddr + GUARD_PAGES * page_size;
@@ -808,7 +806,7 @@ pub mod guard {
// read/write permissions and only then mprotect() it to
// no permissions at all. See issue #50313.
let stackptr = get_stack_start_aligned()?;
- let result = mmap(
+ let result = mmap64(
stackptr,
page_size,
PROT_READ | PROT_WRITE,
@@ -879,9 +877,9 @@ pub mod guard {
} else if cfg!(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))
{
// glibc used to include the guard area within the stack, as noted in the BUGS
- // section of `man pthread_attr_getguardsize`. This has been corrected starting
+ // section of `man pthread_attr_getguardsize`. This has been corrected starting
// with glibc 2.27, and in some distro backports, so the guard is now placed at the
- // end (below) the stack. There's no easy way for us to know which we have at
+ // end (below) the stack. There's no easy way for us to know which we have at
// runtime, so we'll just match any fault in the range right above or below the
// stack base to call that fault a stack overflow.
Some(stackaddr - guardsize..stackaddr + guardsize)
diff --git a/library/std/src/sys/unix/thread_parker/mod.rs b/library/std/src/sys/unix/thread_parker/mod.rs
deleted file mode 100644
index 35f1e68a8..000000000
--- a/library/std/src/sys/unix/thread_parker/mod.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-//! Thread parking on systems without futex support.
-
-#![cfg(not(any(
- target_os = "linux",
- target_os = "android",
- all(target_os = "emscripten", target_feature = "atomics"),
- target_os = "freebsd",
- target_os = "openbsd",
- target_os = "dragonfly",
- target_os = "fuchsia",
-)))]
-
-cfg_if::cfg_if! {
- if #[cfg(all(
- any(
- target_os = "macos",
- target_os = "ios",
- target_os = "watchos",
- target_os = "tvos",
- ),
- not(miri),
- ))] {
- mod darwin;
- pub use darwin::Parker;
- } else if #[cfg(target_os = "netbsd")] {
- mod netbsd;
- pub use netbsd::Parker;
- } else {
- mod pthread;
- pub use pthread::Parker;
- }
-}
diff --git a/library/std/src/sys/unix/thread_parker/netbsd.rs b/library/std/src/sys/unix/thread_parker/netbsd.rs
deleted file mode 100644
index 7657605b5..000000000
--- a/library/std/src/sys/unix/thread_parker/netbsd.rs
+++ /dev/null
@@ -1,113 +0,0 @@
-use crate::ffi::{c_int, c_void};
-use crate::pin::Pin;
-use crate::ptr::{null, null_mut};
-use crate::sync::atomic::{
- AtomicU64,
- Ordering::{Acquire, Relaxed, Release},
-};
-use crate::time::Duration;
-use libc::{_lwp_self, clockid_t, lwpid_t, time_t, timespec, CLOCK_MONOTONIC};
-
-extern "C" {
- fn ___lwp_park60(
- clock_id: clockid_t,
- flags: c_int,
- ts: *mut timespec,
- unpark: lwpid_t,
- hint: *const c_void,
- unparkhint: *const c_void,
- ) -> c_int;
- fn _lwp_unpark(lwp: lwpid_t, hint: *const c_void) -> c_int;
-}
-
-/// The thread is not parked and the token is not available.
-///
-/// Zero cannot be a valid LWP id, since it is used as empty value for the unpark
-/// argument in _lwp_park.
-const EMPTY: u64 = 0;
-/// The token is available. Do not park anymore.
-const NOTIFIED: u64 = u64::MAX;
-
-pub struct Parker {
- /// The parker state. Contains either one of the two state values above or the LWP
- /// id of the parked thread.
- state: AtomicU64,
-}
-
-impl Parker {
- pub unsafe fn new(parker: *mut Parker) {
- parker.write(Parker { state: AtomicU64::new(EMPTY) })
- }
-
- // Does not actually need `unsafe` or `Pin`, but the pthread implementation does.
- pub unsafe fn park(self: Pin<&Self>) {
- // If the token has already been made available, we can skip
- // a bit of work, so check for it here.
- if self.state.load(Acquire) != NOTIFIED {
- let parked = _lwp_self() as u64;
- let hint = self.state.as_mut_ptr().cast();
- if self.state.compare_exchange(EMPTY, parked, Relaxed, Acquire).is_ok() {
- // Loop to guard against spurious wakeups.
- loop {
- ___lwp_park60(0, 0, null_mut(), 0, hint, null());
- if self.state.load(Acquire) == NOTIFIED {
- break;
- }
- }
- }
- }
-
- // At this point, the change to NOTIFIED has always been observed with acquire
- // ordering, so we can just use a relaxed store here (instead of a swap).
- self.state.store(EMPTY, Relaxed);
- }
-
- // Does not actually need `unsafe` or `Pin`, but the pthread implementation does.
- pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
- if self.state.load(Acquire) != NOTIFIED {
- let parked = _lwp_self() as u64;
- let hint = self.state.as_mut_ptr().cast();
- let mut timeout = timespec {
- // Saturate so that the operation will definitely time out
- // (even if it is after the heat death of the universe).
- tv_sec: dur.as_secs().try_into().ok().unwrap_or(time_t::MAX),
- tv_nsec: dur.subsec_nanos().into(),
- };
-
- if self.state.compare_exchange(EMPTY, parked, Relaxed, Acquire).is_ok() {
- // Timeout needs to be mutable since it is modified on NetBSD 9.0 and
- // above.
- ___lwp_park60(CLOCK_MONOTONIC, 0, &mut timeout, 0, hint, null());
- // Use a swap to get acquire ordering even if the token was set after
- // the timeout occurred.
- self.state.swap(EMPTY, Acquire);
- return;
- }
- }
-
- self.state.store(EMPTY, Relaxed);
- }
-
- // Does not actually need `Pin`, but the pthread implementation does.
- pub fn unpark(self: Pin<&Self>) {
- let state = self.state.swap(NOTIFIED, Release);
- if !matches!(state, EMPTY | NOTIFIED) {
- let lwp = state as lwpid_t;
- let hint = self.state.as_mut_ptr().cast();
-
- // If the parking thread terminated and did not actually park, this will
- // probably return an error, which is OK. In the worst case, another
- // thread has received the same LWP id. It will then receive a spurious
- // wakeup, but those are allowable per the API contract. The same reasoning
- // applies if a timeout occurred before this call, but the state was not
- // yet reset.
-
- // SAFETY:
- // The syscall has no invariants to hold. Only unsafe because it is an
- // extern function.
- unsafe {
- _lwp_unpark(lwp, hint);
- }
- }
- }
-}
diff --git a/library/std/src/sys/unix/thread_parker/darwin.rs b/library/std/src/sys/unix/thread_parking/darwin.rs
index 2f5356fe2..b709fada3 100644
--- a/library/std/src/sys/unix/thread_parker/darwin.rs
+++ b/library/std/src/sys/unix/thread_parking/darwin.rs
@@ -46,7 +46,7 @@ unsafe impl Sync for Parker {}
unsafe impl Send for Parker {}
impl Parker {
- pub unsafe fn new(parker: *mut Parker) {
+ pub unsafe fn new_in_place(parker: *mut Parker) {
let semaphore = dispatch_semaphore_create(0);
assert!(
!semaphore.is_null(),
diff --git a/library/std/src/sys/unix/thread_parking/mod.rs b/library/std/src/sys/unix/thread_parking/mod.rs
new file mode 100644
index 000000000..185333c07
--- /dev/null
+++ b/library/std/src/sys/unix/thread_parking/mod.rs
@@ -0,0 +1,32 @@
+//! Thread parking on systems without futex support.
+
+#![cfg(not(any(
+ target_os = "linux",
+ target_os = "android",
+ all(target_os = "emscripten", target_feature = "atomics"),
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
+ target_os = "fuchsia",
+)))]
+
+cfg_if::cfg_if! {
+ if #[cfg(all(
+ any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "watchos",
+ target_os = "tvos",
+ ),
+ not(miri),
+ ))] {
+ mod darwin;
+ pub use darwin::Parker;
+ } else if #[cfg(target_os = "netbsd")] {
+ mod netbsd;
+ pub use netbsd::{current, park, park_timeout, unpark, ThreadId};
+ } else {
+ mod pthread;
+ pub use pthread::Parker;
+ }
+}
diff --git a/library/std/src/sys/unix/thread_parking/netbsd.rs b/library/std/src/sys/unix/thread_parking/netbsd.rs
new file mode 100644
index 000000000..3be081221
--- /dev/null
+++ b/library/std/src/sys/unix/thread_parking/netbsd.rs
@@ -0,0 +1,52 @@
+use crate::ffi::{c_int, c_void};
+use crate::ptr;
+use crate::time::Duration;
+use libc::{_lwp_self, clockid_t, lwpid_t, time_t, timespec, CLOCK_MONOTONIC};
+
+extern "C" {
+ fn ___lwp_park60(
+ clock_id: clockid_t,
+ flags: c_int,
+ ts: *mut timespec,
+ unpark: lwpid_t,
+ hint: *const c_void,
+ unparkhint: *const c_void,
+ ) -> c_int;
+ fn _lwp_unpark(lwp: lwpid_t, hint: *const c_void) -> c_int;
+}
+
+pub type ThreadId = lwpid_t;
+
+#[inline]
+pub fn current() -> ThreadId {
+ unsafe { _lwp_self() }
+}
+
+#[inline]
+pub fn park(hint: usize) {
+ unsafe {
+ ___lwp_park60(0, 0, ptr::null_mut(), 0, ptr::invalid(hint), ptr::null());
+ }
+}
+
+pub fn park_timeout(dur: Duration, hint: usize) {
+ let mut timeout = timespec {
+ // Saturate so that the operation will definitely time out
+ // (even if it is after the heat death of the universe).
+ tv_sec: dur.as_secs().try_into().ok().unwrap_or(time_t::MAX),
+ tv_nsec: dur.subsec_nanos().into(),
+ };
+
+ // Timeout needs to be mutable since it is modified on NetBSD 9.0 and
+ // above.
+ unsafe {
+ ___lwp_park60(CLOCK_MONOTONIC, 0, &mut timeout, 0, ptr::invalid(hint), ptr::null());
+ }
+}
+
+#[inline]
+pub fn unpark(tid: ThreadId, hint: usize) {
+ unsafe {
+ _lwp_unpark(tid, ptr::invalid(hint));
+ }
+}
diff --git a/library/std/src/sys/unix/thread_parker/pthread.rs b/library/std/src/sys/unix/thread_parking/pthread.rs
index 3dfc0026e..082d25e68 100644
--- a/library/std/src/sys/unix/thread_parker/pthread.rs
+++ b/library/std/src/sys/unix/thread_parking/pthread.rs
@@ -6,6 +6,7 @@ use crate::pin::Pin;
use crate::ptr::addr_of_mut;
use crate::sync::atomic::AtomicUsize;
use crate::sync::atomic::Ordering::SeqCst;
+use crate::sys::time::TIMESPEC_MAX;
use crate::time::Duration;
const EMPTY: usize = 0;
@@ -32,9 +33,6 @@ unsafe fn wait(cond: *mut libc::pthread_cond_t, lock: *mut libc::pthread_mutex_t
debug_assert_eq!(r, 0);
}
-const TIMESPEC_MAX: libc::timespec =
- libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
-
unsafe fn wait_timeout(
cond: *mut libc::pthread_cond_t,
lock: *mut libc::pthread_mutex_t,
@@ -46,7 +44,8 @@ unsafe fn wait_timeout(
target_os = "macos",
target_os = "ios",
target_os = "watchos",
- target_os = "espidf"
+ target_os = "espidf",
+ target_os = "horizon",
))]
let (now, dur) = {
use crate::cmp::min;
@@ -72,7 +71,8 @@ unsafe fn wait_timeout(
target_os = "macos",
target_os = "ios",
target_os = "watchos",
- target_os = "espidf"
+ target_os = "espidf",
+ target_os = "horizon",
)))]
let (now, dur) = {
use crate::sys::time::Timespec;
@@ -99,7 +99,7 @@ impl Parker {
///
/// # Safety
/// The constructed parker must never be moved.
- pub unsafe fn new(parker: *mut Parker) {
+ pub unsafe fn new_in_place(parker: *mut Parker) {
// Use the default mutex implementation to allow for simpler initialization.
// This could lead to undefined behaviour when deadlocking. This is avoided
// by not deadlocking. Note in particular the unlocking operation before any
diff --git a/library/std/src/sys/unix/time.rs b/library/std/src/sys/unix/time.rs
index d5abd9b58..2daad981b 100644
--- a/library/std/src/sys/unix/time.rs
+++ b/library/std/src/sys/unix/time.rs
@@ -5,6 +5,9 @@ pub use self::inner::Instant;
const NSEC_PER_SEC: u64 = 1_000_000_000;
pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
+#[allow(dead_code)] // Used for pthread condvar timeouts
+pub const TIMESPEC_MAX: libc::timespec =
+ libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
diff --git a/library/std/src/sys/unix/weak.rs b/library/std/src/sys/unix/weak.rs
index f5a4ce929..62ffee70b 100644
--- a/library/std/src/sys/unix/weak.rs
+++ b/library/std/src/sys/unix/weak.rs
@@ -1,9 +1,8 @@
//! Support for "weak linkage" to symbols on Unix
//!
-//! Some I/O operations we do in libstd require newer versions of OSes but we
-//! need to maintain binary compatibility with older releases for now. In order
-//! to use the new functionality when available we use this module for
-//! detection.
+//! Some I/O operations we do in std require newer versions of OSes but we need
+//! to maintain binary compatibility with older releases for now. In order to
+//! use the new functionality when available we use this module for detection.
//!
//! One option to use here is weak linkage, but that is unfortunately only
//! really workable with ELF. Otherwise, use dlsym to get the symbol value at
@@ -29,7 +28,7 @@ use crate::ptr;
use crate::sync::atomic::{self, AtomicPtr, Ordering};
// We can use true weak linkage on ELF targets.
-#[cfg(all(not(any(target_os = "macos", target_os = "ios")), not(bootstrap)))]
+#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub(crate) macro weak {
(fn $name:ident($($t:ty),*) -> $ret:ty) => (
let ref $name: ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
@@ -43,30 +42,14 @@ pub(crate) macro weak {
)
}
-#[cfg(all(not(any(target_os = "macos", target_os = "ios")), bootstrap))]
-pub(crate) macro weak {
- (fn $name:ident($($t:ty),*) -> $ret:ty) => (
- let ref $name: ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
- extern "C" {
- #[linkage = "extern_weak"]
- static $name: *const libc::c_void;
- }
- #[allow(unused_unsafe)]
- ExternWeak::new(unsafe { $name })
- };
- )
-}
-
// On non-ELF targets, use the dlsym approximation of weak linkage.
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub(crate) use self::dlsym as weak;
-#[cfg(not(bootstrap))]
pub(crate) struct ExternWeak<F: Copy> {
weak_ptr: Option<F>,
}
-#[cfg(not(bootstrap))]
impl<F: Copy> ExternWeak<F> {
#[inline]
pub(crate) fn new(weak_ptr: Option<F>) -> Self {
@@ -79,34 +62,6 @@ impl<F: Copy> ExternWeak<F> {
}
}
-#[cfg(bootstrap)]
-pub(crate) struct ExternWeak<F> {
- weak_ptr: *const libc::c_void,
- _marker: PhantomData<F>,
-}
-
-#[cfg(bootstrap)]
-impl<F> ExternWeak<F> {
- #[inline]
- pub(crate) fn new(weak_ptr: *const libc::c_void) -> Self {
- ExternWeak { weak_ptr, _marker: PhantomData }
- }
-}
-
-#[cfg(bootstrap)]
-impl<F> ExternWeak<F> {
- #[inline]
- pub(crate) fn get(&self) -> Option<F> {
- unsafe {
- if self.weak_ptr.is_null() {
- None
- } else {
- Some(mem::transmute_copy::<*const libc::c_void, F>(&self.weak_ptr))
- }
- }
- }
-}
-
pub(crate) macro dlsym {
(fn $name:ident($($t:ty),*) -> $ret:ty) => (
dlsym!(fn $name($($t),*) -> $ret, stringify!($name));
diff --git a/library/std/src/sys/unsupported/mod.rs b/library/std/src/sys/unsupported/mod.rs
index 7bf6d40b7..15b22c620 100644
--- a/library/std/src/sys/unsupported/mod.rs
+++ b/library/std/src/sys/unsupported/mod.rs
@@ -9,6 +9,7 @@ pub mod fs;
pub mod io;
pub mod locks;
pub mod net;
+pub mod once;
pub mod os;
#[path = "../unix/os_str.rs"]
pub mod os_str;
diff --git a/library/std/src/sys/unsupported/once.rs b/library/std/src/sys/unsupported/once.rs
new file mode 100644
index 000000000..b4bb4975f
--- /dev/null
+++ b/library/std/src/sys/unsupported/once.rs
@@ -0,0 +1,89 @@
+use crate::cell::Cell;
+use crate::sync as public;
+
+pub struct Once {
+ state: Cell<State>,
+}
+
+pub struct OnceState {
+ poisoned: bool,
+ set_state_to: Cell<State>,
+}
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+enum State {
+ Incomplete,
+ Poisoned,
+ Running,
+ Complete,
+}
+
+struct CompletionGuard<'a> {
+ state: &'a Cell<State>,
+ set_state_on_drop_to: State,
+}
+
+impl<'a> Drop for CompletionGuard<'a> {
+ fn drop(&mut self) {
+ self.state.set(self.set_state_on_drop_to);
+ }
+}
+
+// Safety: threads are not supported on this platform.
+unsafe impl Sync for Once {}
+
+impl Once {
+ #[inline]
+ #[rustc_const_stable(feature = "const_once_new", since = "1.32.0")]
+ pub const fn new() -> Once {
+ Once { state: Cell::new(State::Incomplete) }
+ }
+
+ #[inline]
+ pub fn is_completed(&self) -> bool {
+ self.state.get() == State::Complete
+ }
+
+ #[cold]
+ #[track_caller]
+ pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) {
+ let state = self.state.get();
+ match state {
+ State::Poisoned if !ignore_poisoning => {
+ // Panic to propagate the poison.
+ panic!("Once instance has previously been poisoned");
+ }
+ State::Incomplete | State::Poisoned => {
+ self.state.set(State::Running);
+ // `guard` will set the new state on drop.
+ let mut guard =
+ CompletionGuard { state: &self.state, set_state_on_drop_to: State::Poisoned };
+ // Run the function, letting it know if we're poisoned or not.
+ let f_state = public::OnceState {
+ inner: OnceState {
+ poisoned: state == State::Poisoned,
+ set_state_to: Cell::new(State::Complete),
+ },
+ };
+ f(&f_state);
+ guard.set_state_on_drop_to = f_state.inner.set_state_to.get();
+ }
+ State::Running => {
+ panic!("one-time initialization may not be performed recursively");
+ }
+ State::Complete => {}
+ }
+ }
+}
+
+impl OnceState {
+ #[inline]
+ pub fn is_poisoned(&self) -> bool {
+ self.poisoned
+ }
+
+ #[inline]
+ pub fn poison(&self) {
+ self.set_state_to.set(State::Poisoned)
+ }
+}
diff --git a/library/std/src/sys/unsupported/pipe.rs b/library/std/src/sys/unsupported/pipe.rs
index 25514c232..0bba673b4 100644
--- a/library/std/src/sys/unsupported/pipe.rs
+++ b/library/std/src/sys/unsupported/pipe.rs
@@ -15,6 +15,10 @@ impl AnonPipe {
self.0
}
+ pub fn read_to_end(&self, _buf: &mut Vec<u8>) -> io::Result<usize> {
+ self.0
+ }
+
pub fn write(&self, _buf: &[u8]) -> io::Result<usize> {
self.0
}
diff --git a/library/std/src/sys/unsupported/process.rs b/library/std/src/sys/unsupported/process.rs
index 633f17c05..a494f2d6b 100644
--- a/library/std/src/sys/unsupported/process.rs
+++ b/library/std/src/sys/unsupported/process.rs
@@ -75,6 +75,10 @@ impl Command {
) -> io::Result<(Process, StdioPipes)> {
unsupported()
}
+
+ pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+ unsupported()
+ }
}
impl From<AnonPipe> for Stdio {
diff --git a/library/std/src/sys/wasi/mod.rs b/library/std/src/sys/wasi/mod.rs
index c8c47763a..1dc3f2b20 100644
--- a/library/std/src/sys/wasi/mod.rs
+++ b/library/std/src/sys/wasi/mod.rs
@@ -32,6 +32,8 @@ pub mod io;
#[path = "../unsupported/locks/mod.rs"]
pub mod locks;
pub mod net;
+#[path = "../unsupported/once.rs"]
+pub mod once;
pub mod os;
#[path = "../unix/os_str.rs"]
pub mod os_str;
diff --git a/library/std/src/sys/wasm/mod.rs b/library/std/src/sys/wasm/mod.rs
index d68c3e5f1..77ebe3c4a 100644
--- a/library/std/src/sys/wasm/mod.rs
+++ b/library/std/src/sys/wasm/mod.rs
@@ -66,6 +66,8 @@ cfg_if::cfg_if! {
} else {
#[path = "../unsupported/locks/mod.rs"]
pub mod locks;
+ #[path = "../unsupported/once.rs"]
+ pub mod once;
#[path = "../unsupported/thread.rs"]
pub mod thread;
}
diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs
index 81461de4f..f58dcf128 100644
--- a/library/std/src/sys/windows/c.rs
+++ b/library/std/src/sys/windows/c.rs
@@ -295,8 +295,6 @@ pub fn nt_success(status: NTSTATUS) -> bool {
status >= 0
}
-// "RNG\0"
-pub const BCRYPT_RNG_ALGORITHM: &[u16] = &[b'R' as u16, b'N' as u16, b'G' as u16, 0];
pub const BCRYPT_USE_SYSTEM_PREFERRED_RNG: DWORD = 0x00000002;
#[repr(C)]
@@ -834,6 +832,10 @@ if #[cfg(not(target_vendor = "uwp"))] {
#[link(name = "advapi32")]
extern "system" {
+ // Forbidden when targeting UWP
+ #[link_name = "SystemFunction036"]
+ pub fn RtlGenRandom(RandomBuffer: *mut u8, RandomBufferLength: ULONG) -> BOOLEAN;
+
// Allowed but unused by UWP
pub fn OpenProcessToken(
ProcessHandle: HANDLE,
@@ -1258,13 +1260,6 @@ extern "system" {
cbBuffer: ULONG,
dwFlags: ULONG,
) -> NTSTATUS;
- pub fn BCryptOpenAlgorithmProvider(
- phalgorithm: *mut BCRYPT_ALG_HANDLE,
- pszAlgId: LPCWSTR,
- pszimplementation: LPCWSTR,
- dwflags: ULONG,
- ) -> NTSTATUS;
- pub fn BCryptCloseAlgorithmProvider(hAlgorithm: BCRYPT_ALG_HANDLE, dwFlags: ULONG) -> NTSTATUS;
}
// Functions that aren't available on every version of Windows that we support,
diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs
index e67411e16..77359abe4 100644
--- a/library/std/src/sys/windows/mod.rs
+++ b/library/std/src/sys/windows/mod.rs
@@ -33,7 +33,7 @@ pub mod stdio;
pub mod thread;
pub mod thread_local_dtor;
pub mod thread_local_key;
-pub mod thread_parker;
+pub mod thread_parking;
pub mod time;
cfg_if::cfg_if! {
if #[cfg(not(target_vendor = "uwp"))] {
diff --git a/library/std/src/sys/windows/os.rs b/library/std/src/sys/windows/os.rs
index 352337ba3..d7adeb266 100644
--- a/library/std/src/sys/windows/os.rs
+++ b/library/std/src/sys/windows/os.rs
@@ -157,7 +157,7 @@ impl<'a> Iterator for SplitPaths<'a> {
// Double quotes are used as a way of introducing literal semicolons
// (since c:\some;dir is a valid Windows path). Double quotes are not
// themselves permitted in path names, so there is no way to escape a
- // double quote. Quoted regions can appear in arbitrary locations, so
+ // double quote. Quoted regions can appear in arbitrary locations, so
//
// c:\foo;c:\som"e;di"r;c:\bar
//
diff --git a/library/std/src/sys/windows/pipe.rs b/library/std/src/sys/windows/pipe.rs
index 9f26acc45..7b25edaa5 100644
--- a/library/std/src/sys/windows/pipe.rs
+++ b/library/std/src/sys/windows/pipe.rs
@@ -1,7 +1,7 @@
use crate::os::windows::prelude::*;
use crate::ffi::OsStr;
-use crate::io::{self, IoSlice, IoSliceMut};
+use crate::io::{self, IoSlice, IoSliceMut, Read};
use crate::mem;
use crate::path::Path;
use crate::ptr;
@@ -261,6 +261,10 @@ impl AnonPipe {
self.inner.is_read_vectored()
}
+ pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
+ self.handle().read_to_end(buf)
+ }
+
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
unsafe {
let len = crate::cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD;
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index 31e9b34fb..10bc949e1 100644
--- a/library/std/src/sys/windows/process.rs
+++ b/library/std/src/sys/windows/process.rs
@@ -351,6 +351,11 @@ impl Command {
))
}
}
+
+ pub fn output(&mut self) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+ let (proc, pipes) = self.spawn(Stdio::MakePipe, false)?;
+ crate::sys_common::process::wait_with_output(proc, pipes)
+ }
}
impl fmt::Debug for Command {
diff --git a/library/std/src/sys/windows/rand.rs b/library/std/src/sys/windows/rand.rs
index b5a49489d..cdf37cfe9 100644
--- a/library/std/src/sys/windows/rand.rs
+++ b/library/std/src/sys/windows/rand.rs
@@ -1,106 +1,39 @@
-//! # Random key generation
-//!
-//! This module wraps the RNG provided by the OS. There are a few different
-//! ways to interface with the OS RNG so it's worth exploring each of the options.
-//! Note that at the time of writing these all go through the (undocumented)
-//! `bcryptPrimitives.dll` but they use different route to get there.
-//!
-//! Originally we were using [`RtlGenRandom`], however that function is
-//! deprecated and warns it "may be altered or unavailable in subsequent versions".
-//!
-//! So we switched to [`BCryptGenRandom`] with the `BCRYPT_USE_SYSTEM_PREFERRED_RNG`
-//! flag to query and find the system configured RNG. However, this change caused a small
-//! but significant number of users to experience panics caused by a failure of
-//! this function. See [#94098].
-//!
-//! The current version falls back to using `BCryptOpenAlgorithmProvider` if
-//! `BCRYPT_USE_SYSTEM_PREFERRED_RNG` fails for any reason.
-//!
-//! [#94098]: https://github.com/rust-lang/rust/issues/94098
-//! [`RtlGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom
-//! [`BCryptGenRandom`]: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
+use crate::io;
use crate::mem;
use crate::ptr;
use crate::sys::c;
-/// Generates high quality secure random keys for use by [`HashMap`].
-///
-/// This is used to seed the default [`RandomState`].
-///
-/// [`HashMap`]: crate::collections::HashMap
-/// [`RandomState`]: crate::collections::hash_map::RandomState
pub fn hashmap_random_keys() -> (u64, u64) {
- Rng::SYSTEM.gen_random_keys().unwrap_or_else(fallback_rng)
+ let mut v = (0, 0);
+ let ret = unsafe {
+ c::BCryptGenRandom(
+ ptr::null_mut(),
+ &mut v as *mut _ as *mut u8,
+ mem::size_of_val(&v) as c::ULONG,
+ c::BCRYPT_USE_SYSTEM_PREFERRED_RNG,
+ )
+ };
+ if c::nt_success(ret) { v } else { fallback_rng() }
}
-struct Rng {
- algorithm: c::BCRYPT_ALG_HANDLE,
- flags: u32,
-}
-impl Rng {
- const SYSTEM: Self = unsafe { Self::new(ptr::null_mut(), c::BCRYPT_USE_SYSTEM_PREFERRED_RNG) };
-
- /// Create the RNG from an existing algorithm handle.
- ///
- /// # Safety
- ///
- /// The handle must either be null or a valid algorithm handle.
- const unsafe fn new(algorithm: c::BCRYPT_ALG_HANDLE, flags: u32) -> Self {
- Self { algorithm, flags }
- }
-
- /// Open a handle to the RNG algorithm.
- fn open() -> Result<Self, c::NTSTATUS> {
- use crate::sync::atomic::AtomicPtr;
- use crate::sync::atomic::Ordering::{Acquire, Release};
-
- // An atomic is used so we don't need to reopen the handle every time.
- static HANDLE: AtomicPtr<crate::ffi::c_void> = AtomicPtr::new(ptr::null_mut());
-
- let mut handle = HANDLE.load(Acquire);
- if handle.is_null() {
- let status = unsafe {
- c::BCryptOpenAlgorithmProvider(
- &mut handle,
- c::BCRYPT_RNG_ALGORITHM.as_ptr(),
- ptr::null(),
- 0,
- )
- };
- if c::nt_success(status) {
- // If another thread opens a handle first then use that handle instead.
- let result = HANDLE.compare_exchange(ptr::null_mut(), handle, Release, Acquire);
- if let Err(previous_handle) = result {
- // Close our handle and return the previous one.
- unsafe { c::BCryptCloseAlgorithmProvider(handle, 0) };
- handle = previous_handle;
- }
- Ok(unsafe { Self::new(handle, 0) })
- } else {
- Err(status)
- }
- } else {
- Ok(unsafe { Self::new(handle, 0) })
- }
- }
+/// Generate random numbers using the fallback RNG function (RtlGenRandom)
+///
+/// This is necessary because of a failure to load the SysWOW64 variant of the
+/// bcryptprimitives.dll library from code that lives in bcrypt.dll
+/// See <https://bugzilla.mozilla.org/show_bug.cgi?id=1788004#c9>
+#[cfg(not(target_vendor = "uwp"))]
+#[inline(never)]
+fn fallback_rng() -> (u64, u64) {
+ let mut v = (0, 0);
+ let ret =
+ unsafe { c::RtlGenRandom(&mut v as *mut _ as *mut u8, mem::size_of_val(&v) as c::ULONG) };
- fn gen_random_keys(self) -> Result<(u64, u64), c::NTSTATUS> {
- let mut v = (0, 0);
- let status = unsafe {
- let size = mem::size_of_val(&v).try_into().unwrap();
- c::BCryptGenRandom(self.algorithm, ptr::addr_of_mut!(v).cast(), size, self.flags)
- };
- if c::nt_success(status) { Ok(v) } else { Err(status) }
- }
+ if ret != 0 { v } else { panic!("fallback RNG broken: {}", io::Error::last_os_error()) }
}
-/// Generate random numbers using the fallback RNG function
+/// We can't use RtlGenRandom with UWP, so there is no fallback
+#[cfg(target_vendor = "uwp")]
#[inline(never)]
-fn fallback_rng(rng_status: c::NTSTATUS) -> (u64, u64) {
- match Rng::open().and_then(|rng| rng.gen_random_keys()) {
- Ok(keys) => keys,
- Err(status) => {
- panic!("RNG broken: {rng_status:#x}, fallback RNG broken: {status:#x}")
- }
- }
+fn fallback_rng() -> (u64, u64) {
+ panic!("fallback RNG broken: RtlGenRandom() not supported on UWP");
}
diff --git a/library/std/src/sys/windows/thread.rs b/library/std/src/sys/windows/thread.rs
index c5c9e97e6..1cb576c95 100644
--- a/library/std/src/sys/windows/thread.rs
+++ b/library/std/src/sys/windows/thread.rs
@@ -26,7 +26,7 @@ impl Thread {
// FIXME On UNIX, we guard against stack sizes that are too small but
// that's because pthreads enforces that stacks are at least
- // PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's
+ // PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's
// just that below a certain threshold you can't do anything useful.
// That threshold is application and architecture-specific, however.
let ret = c::CreateThread(
diff --git a/library/std/src/sys/windows/thread_parker.rs b/library/std/src/sys/windows/thread_parker.rs
deleted file mode 100644
index 2f7ae863b..000000000
--- a/library/std/src/sys/windows/thread_parker.rs
+++ /dev/null
@@ -1,253 +0,0 @@
-// Thread parker implementation for Windows.
-//
-// This uses WaitOnAddress and WakeByAddressSingle if available (Windows 8+).
-// This modern API is exactly the same as the futex syscalls the Linux thread
-// parker uses. When These APIs are available, the implementation of this
-// thread parker matches the Linux thread parker exactly.
-//
-// However, when the modern API is not available, this implementation falls
-// back to NT Keyed Events, which are similar, but have some important
-// differences. These are available since Windows XP.
-//
-// WaitOnAddress first checks the state of the thread parker to make sure it no
-// WakeByAddressSingle calls can be missed between updating the parker state
-// and calling the function.
-//
-// NtWaitForKeyedEvent does not have this option, and unconditionally blocks
-// without checking the parker state first. Instead, NtReleaseKeyedEvent
-// (unlike WakeByAddressSingle) *blocks* until it woke up a thread waiting for
-// it by NtWaitForKeyedEvent. This way, we can be sure no events are missed,
-// but we need to be careful not to block unpark() if park_timeout() was woken
-// up by a timeout instead of unpark().
-//
-// Unlike WaitOnAddress, NtWaitForKeyedEvent/NtReleaseKeyedEvent operate on a
-// HANDLE (created with NtCreateKeyedEvent). This means that we can be sure
-// a successfully awoken park() was awoken by unpark() and not a
-// NtReleaseKeyedEvent call from some other code, as these events are not only
-// matched by the key (address of the parker (state)), but also by this HANDLE.
-// We lazily allocate this handle the first time it is needed.
-//
-// The fast path (calling park() after unpark() was already called) and the
-// possible states are the same for both implementations. This is used here to
-// make sure the fast path does not even check which API to use, but can return
-// right away, independent of the used API. Only the slow paths (which will
-// actually block/wake a thread) check which API is available and have
-// different implementations.
-//
-// Unfortunately, NT Keyed Events are an undocumented Windows API. However:
-// - This API is relatively simple with obvious behaviour, and there are
-// several (unofficial) articles documenting the details. [1]
-// - `parking_lot` has been using this API for years (on Windows versions
-// before Windows 8). [2] Many big projects extensively use parking_lot,
-// such as servo and the Rust compiler itself.
-// - It is the underlying API used by Windows SRW locks and Windows critical
-// sections. [3] [4]
-// - The source code of the implementations of Wine, ReactOs, and Windows XP
-// are available and match the expected behaviour.
-// - The main risk with an undocumented API is that it might change in the
-// future. But since we only use it for older versions of Windows, that's not
-// a problem.
-// - Even if these functions do not block or wake as we expect (which is
-// unlikely, see all previous points), this implementation would still be
-// memory safe. The NT Keyed Events API is only used to sleep/block in the
-// right place.
-//
-// [1]: http://www.locklessinc.com/articles/keyed_events/
-// [2]: https://github.com/Amanieu/parking_lot/commit/43abbc964e
-// [3]: https://docs.microsoft.com/en-us/archive/msdn-magazine/2012/november/windows-with-c-the-evolution-of-synchronization-in-windows-and-c
-// [4]: Windows Internals, Part 1, ISBN 9780735671300
-
-use crate::pin::Pin;
-use crate::ptr;
-use crate::sync::atomic::{
- AtomicI8, AtomicPtr,
- Ordering::{Acquire, Relaxed, Release},
-};
-use crate::sys::{c, dur2timeout};
-use crate::time::Duration;
-
-pub struct Parker {
- state: AtomicI8,
-}
-
-const PARKED: i8 = -1;
-const EMPTY: i8 = 0;
-const NOTIFIED: i8 = 1;
-
-// Notes about memory ordering:
-//
-// Memory ordering is only relevant for the relative ordering of operations
-// between different variables. Even Ordering::Relaxed guarantees a
-// monotonic/consistent order when looking at just a single atomic variable.
-//
-// So, since this parker is just a single atomic variable, we only need to look
-// at the ordering guarantees we need to provide to the 'outside world'.
-//
-// The only memory ordering guarantee that parking and unparking provide, is
-// that things which happened before unpark() are visible on the thread
-// returning from park() afterwards. Otherwise, it was effectively unparked
-// before unpark() was called while still consuming the 'token'.
-//
-// In other words, unpark() needs to synchronize with the part of park() that
-// consumes the token and returns.
-//
-// This is done with a release-acquire synchronization, by using
-// Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using
-// Ordering::Acquire when reading this state in park() after waking up.
-impl Parker {
- /// Construct the Windows parker. The UNIX parker implementation
- /// requires this to happen in-place.
- pub unsafe fn new(parker: *mut Parker) {
- parker.write(Self { state: AtomicI8::new(EMPTY) });
- }
-
- // Assumes this is only called by the thread that owns the Parker,
- // which means that `self.state != PARKED`. This implementation doesn't require `Pin`,
- // but other implementations do.
- pub unsafe fn park(self: Pin<&Self>) {
- // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
- // first case.
- if self.state.fetch_sub(1, Acquire) == NOTIFIED {
- return;
- }
-
- if let Some(wait_on_address) = c::WaitOnAddress::option() {
- loop {
- // Wait for something to happen, assuming it's still set to PARKED.
- wait_on_address(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, c::INFINITE);
- // Change NOTIFIED=>EMPTY but leave PARKED alone.
- if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Acquire).is_ok() {
- // Actually woken up by unpark().
- return;
- } else {
- // Spurious wake up. We loop to try again.
- }
- }
- } else {
- // Wait for unpark() to produce this event.
- c::NtWaitForKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut());
- // Set the state back to EMPTY (from either PARKED or NOTIFIED).
- // Note that we don't just write EMPTY, but use swap() to also
- // include an acquire-ordered read to synchronize with unpark()'s
- // release-ordered write.
- self.state.swap(EMPTY, Acquire);
- }
- }
-
- // Assumes this is only called by the thread that owns the Parker,
- // which means that `self.state != PARKED`. This implementation doesn't require `Pin`,
- // but other implementations do.
- pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) {
- // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
- // first case.
- if self.state.fetch_sub(1, Acquire) == NOTIFIED {
- return;
- }
-
- if let Some(wait_on_address) = c::WaitOnAddress::option() {
- // Wait for something to happen, assuming it's still set to PARKED.
- wait_on_address(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, dur2timeout(timeout));
- // Set the state back to EMPTY (from either PARKED or NOTIFIED).
- // Note that we don't just write EMPTY, but use swap() to also
- // include an acquire-ordered read to synchronize with unpark()'s
- // release-ordered write.
- if self.state.swap(EMPTY, Acquire) == NOTIFIED {
- // Actually woken up by unpark().
- } else {
- // Timeout or spurious wake up.
- // We return either way, because we can't easily tell if it was the
- // timeout or not.
- }
- } else {
- // Need to wait for unpark() using NtWaitForKeyedEvent.
- let handle = keyed_event_handle();
-
- // NtWaitForKeyedEvent uses a unit of 100ns, and uses negative
- // values to indicate a relative time on the monotonic clock.
- // This is documented here for the underlying KeWaitForSingleObject function:
- // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-kewaitforsingleobject
- let mut timeout = match i64::try_from((timeout.as_nanos() + 99) / 100) {
- Ok(t) => -t,
- Err(_) => i64::MIN,
- };
-
- // Wait for unpark() to produce this event.
- let unparked =
- c::NtWaitForKeyedEvent(handle, self.ptr(), 0, &mut timeout) == c::STATUS_SUCCESS;
-
- // Set the state back to EMPTY (from either PARKED or NOTIFIED).
- let prev_state = self.state.swap(EMPTY, Acquire);
-
- if !unparked && prev_state == NOTIFIED {
- // We were awoken by a timeout, not by unpark(), but the state
- // was set to NOTIFIED, which means we *just* missed an
- // unpark(), which is now blocked on us to wait for it.
- // Wait for it to consume the event and unblock that thread.
- c::NtWaitForKeyedEvent(handle, self.ptr(), 0, ptr::null_mut());
- }
- }
- }
-
- // This implementation doesn't require `Pin`, but other implementations do.
- pub fn unpark(self: Pin<&Self>) {
- // Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and
- // wake the thread in the first case.
- //
- // Note that even NOTIFIED=>NOTIFIED results in a write. This is on
- // purpose, to make sure every unpark() has a release-acquire ordering
- // with park().
- if self.state.swap(NOTIFIED, Release) == PARKED {
- unsafe {
- if let Some(wake_by_address_single) = c::WakeByAddressSingle::option() {
- wake_by_address_single(self.ptr());
- } else {
- // If we run NtReleaseKeyedEvent before the waiting thread runs
- // NtWaitForKeyedEvent, this (shortly) blocks until we can wake it up.
- // If the waiting thread wakes up before we run NtReleaseKeyedEvent
- // (e.g. due to a timeout), this blocks until we do wake up a thread.
- // To prevent this thread from blocking indefinitely in that case,
- // park_impl() will, after seeing the state set to NOTIFIED after
- // waking up, call NtWaitForKeyedEvent again to unblock us.
- c::NtReleaseKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut());
- }
- }
- }
- }
-
- fn ptr(&self) -> c::LPVOID {
- &self.state as *const _ as c::LPVOID
- }
-}
-
-fn keyed_event_handle() -> c::HANDLE {
- const INVALID: c::HANDLE = ptr::invalid_mut(!0);
- static HANDLE: AtomicPtr<libc::c_void> = AtomicPtr::new(INVALID);
- match HANDLE.load(Relaxed) {
- INVALID => {
- let mut handle = c::INVALID_HANDLE_VALUE;
- unsafe {
- match c::NtCreateKeyedEvent(
- &mut handle,
- c::GENERIC_READ | c::GENERIC_WRITE,
- ptr::null_mut(),
- 0,
- ) {
- c::STATUS_SUCCESS => {}
- r => panic!("Unable to create keyed event handle: error {r}"),
- }
- }
- match HANDLE.compare_exchange(INVALID, handle, Relaxed, Relaxed) {
- Ok(_) => handle,
- Err(h) => {
- // Lost the race to another thread initializing HANDLE before we did.
- // Closing our handle and using theirs instead.
- unsafe {
- c::CloseHandle(handle);
- }
- h
- }
- }
- }
- handle => handle,
- }
-}
diff --git a/library/std/src/sys/windows/thread_parking.rs b/library/std/src/sys/windows/thread_parking.rs
new file mode 100644
index 000000000..5d43676ad
--- /dev/null
+++ b/library/std/src/sys/windows/thread_parking.rs
@@ -0,0 +1,253 @@
+// Thread parker implementation for Windows.
+//
+// This uses WaitOnAddress and WakeByAddressSingle if available (Windows 8+).
+// This modern API is exactly the same as the futex syscalls the Linux thread
+// parker uses. When These APIs are available, the implementation of this
+// thread parker matches the Linux thread parker exactly.
+//
+// However, when the modern API is not available, this implementation falls
+// back to NT Keyed Events, which are similar, but have some important
+// differences. These are available since Windows XP.
+//
+// WaitOnAddress first checks the state of the thread parker to make sure it no
+// WakeByAddressSingle calls can be missed between updating the parker state
+// and calling the function.
+//
+// NtWaitForKeyedEvent does not have this option, and unconditionally blocks
+// without checking the parker state first. Instead, NtReleaseKeyedEvent
+// (unlike WakeByAddressSingle) *blocks* until it woke up a thread waiting for
+// it by NtWaitForKeyedEvent. This way, we can be sure no events are missed,
+// but we need to be careful not to block unpark() if park_timeout() was woken
+// up by a timeout instead of unpark().
+//
+// Unlike WaitOnAddress, NtWaitForKeyedEvent/NtReleaseKeyedEvent operate on a
+// HANDLE (created with NtCreateKeyedEvent). This means that we can be sure
+// a successfully awoken park() was awoken by unpark() and not a
+// NtReleaseKeyedEvent call from some other code, as these events are not only
+// matched by the key (address of the parker (state)), but also by this HANDLE.
+// We lazily allocate this handle the first time it is needed.
+//
+// The fast path (calling park() after unpark() was already called) and the
+// possible states are the same for both implementations. This is used here to
+// make sure the fast path does not even check which API to use, but can return
+// right away, independent of the used API. Only the slow paths (which will
+// actually block/wake a thread) check which API is available and have
+// different implementations.
+//
+// Unfortunately, NT Keyed Events are an undocumented Windows API. However:
+// - This API is relatively simple with obvious behaviour, and there are
+// several (unofficial) articles documenting the details. [1]
+// - `parking_lot` has been using this API for years (on Windows versions
+// before Windows 8). [2] Many big projects extensively use parking_lot,
+// such as servo and the Rust compiler itself.
+// - It is the underlying API used by Windows SRW locks and Windows critical
+// sections. [3] [4]
+// - The source code of the implementations of Wine, ReactOs, and Windows XP
+// are available and match the expected behaviour.
+// - The main risk with an undocumented API is that it might change in the
+// future. But since we only use it for older versions of Windows, that's not
+// a problem.
+// - Even if these functions do not block or wake as we expect (which is
+// unlikely, see all previous points), this implementation would still be
+// memory safe. The NT Keyed Events API is only used to sleep/block in the
+// right place.
+//
+// [1]: http://www.locklessinc.com/articles/keyed_events/
+// [2]: https://github.com/Amanieu/parking_lot/commit/43abbc964e
+// [3]: https://docs.microsoft.com/en-us/archive/msdn-magazine/2012/november/windows-with-c-the-evolution-of-synchronization-in-windows-and-c
+// [4]: Windows Internals, Part 1, ISBN 9780735671300
+
+use crate::pin::Pin;
+use crate::ptr;
+use crate::sync::atomic::{
+ AtomicI8, AtomicPtr,
+ Ordering::{Acquire, Relaxed, Release},
+};
+use crate::sys::{c, dur2timeout};
+use crate::time::Duration;
+
+pub struct Parker {
+ state: AtomicI8,
+}
+
+const PARKED: i8 = -1;
+const EMPTY: i8 = 0;
+const NOTIFIED: i8 = 1;
+
+// Notes about memory ordering:
+//
+// Memory ordering is only relevant for the relative ordering of operations
+// between different variables. Even Ordering::Relaxed guarantees a
+// monotonic/consistent order when looking at just a single atomic variable.
+//
+// So, since this parker is just a single atomic variable, we only need to look
+// at the ordering guarantees we need to provide to the 'outside world'.
+//
+// The only memory ordering guarantee that parking and unparking provide, is
+// that things which happened before unpark() are visible on the thread
+// returning from park() afterwards. Otherwise, it was effectively unparked
+// before unpark() was called while still consuming the 'token'.
+//
+// In other words, unpark() needs to synchronize with the part of park() that
+// consumes the token and returns.
+//
+// This is done with a release-acquire synchronization, by using
+// Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using
+// Ordering::Acquire when reading this state in park() after waking up.
+impl Parker {
+ /// Construct the Windows parker. The UNIX parker implementation
+ /// requires this to happen in-place.
+ pub unsafe fn new_in_place(parker: *mut Parker) {
+ parker.write(Self { state: AtomicI8::new(EMPTY) });
+ }
+
+ // Assumes this is only called by the thread that owns the Parker,
+ // which means that `self.state != PARKED`. This implementation doesn't require `Pin`,
+ // but other implementations do.
+ pub unsafe fn park(self: Pin<&Self>) {
+ // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
+ // first case.
+ if self.state.fetch_sub(1, Acquire) == NOTIFIED {
+ return;
+ }
+
+ if let Some(wait_on_address) = c::WaitOnAddress::option() {
+ loop {
+ // Wait for something to happen, assuming it's still set to PARKED.
+ wait_on_address(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, c::INFINITE);
+ // Change NOTIFIED=>EMPTY but leave PARKED alone.
+ if self.state.compare_exchange(NOTIFIED, EMPTY, Acquire, Acquire).is_ok() {
+ // Actually woken up by unpark().
+ return;
+ } else {
+ // Spurious wake up. We loop to try again.
+ }
+ }
+ } else {
+ // Wait for unpark() to produce this event.
+ c::NtWaitForKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut());
+ // Set the state back to EMPTY (from either PARKED or NOTIFIED).
+ // Note that we don't just write EMPTY, but use swap() to also
+ // include an acquire-ordered read to synchronize with unpark()'s
+ // release-ordered write.
+ self.state.swap(EMPTY, Acquire);
+ }
+ }
+
+ // Assumes this is only called by the thread that owns the Parker,
+ // which means that `self.state != PARKED`. This implementation doesn't require `Pin`,
+ // but other implementations do.
+ pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) {
+ // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
+ // first case.
+ if self.state.fetch_sub(1, Acquire) == NOTIFIED {
+ return;
+ }
+
+ if let Some(wait_on_address) = c::WaitOnAddress::option() {
+ // Wait for something to happen, assuming it's still set to PARKED.
+ wait_on_address(self.ptr(), &PARKED as *const _ as c::LPVOID, 1, dur2timeout(timeout));
+ // Set the state back to EMPTY (from either PARKED or NOTIFIED).
+ // Note that we don't just write EMPTY, but use swap() to also
+ // include an acquire-ordered read to synchronize with unpark()'s
+ // release-ordered write.
+ if self.state.swap(EMPTY, Acquire) == NOTIFIED {
+ // Actually woken up by unpark().
+ } else {
+ // Timeout or spurious wake up.
+ // We return either way, because we can't easily tell if it was the
+ // timeout or not.
+ }
+ } else {
+ // Need to wait for unpark() using NtWaitForKeyedEvent.
+ let handle = keyed_event_handle();
+
+ // NtWaitForKeyedEvent uses a unit of 100ns, and uses negative
+ // values to indicate a relative time on the monotonic clock.
+ // This is documented here for the underlying KeWaitForSingleObject function:
+ // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-kewaitforsingleobject
+ let mut timeout = match i64::try_from((timeout.as_nanos() + 99) / 100) {
+ Ok(t) => -t,
+ Err(_) => i64::MIN,
+ };
+
+ // Wait for unpark() to produce this event.
+ let unparked =
+ c::NtWaitForKeyedEvent(handle, self.ptr(), 0, &mut timeout) == c::STATUS_SUCCESS;
+
+ // Set the state back to EMPTY (from either PARKED or NOTIFIED).
+ let prev_state = self.state.swap(EMPTY, Acquire);
+
+ if !unparked && prev_state == NOTIFIED {
+ // We were awoken by a timeout, not by unpark(), but the state
+ // was set to NOTIFIED, which means we *just* missed an
+ // unpark(), which is now blocked on us to wait for it.
+ // Wait for it to consume the event and unblock that thread.
+ c::NtWaitForKeyedEvent(handle, self.ptr(), 0, ptr::null_mut());
+ }
+ }
+ }
+
+ // This implementation doesn't require `Pin`, but other implementations do.
+ pub fn unpark(self: Pin<&Self>) {
+ // Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and
+ // wake the thread in the first case.
+ //
+ // Note that even NOTIFIED=>NOTIFIED results in a write. This is on
+ // purpose, to make sure every unpark() has a release-acquire ordering
+ // with park().
+ if self.state.swap(NOTIFIED, Release) == PARKED {
+ unsafe {
+ if let Some(wake_by_address_single) = c::WakeByAddressSingle::option() {
+ wake_by_address_single(self.ptr());
+ } else {
+ // If we run NtReleaseKeyedEvent before the waiting thread runs
+ // NtWaitForKeyedEvent, this (shortly) blocks until we can wake it up.
+ // If the waiting thread wakes up before we run NtReleaseKeyedEvent
+ // (e.g. due to a timeout), this blocks until we do wake up a thread.
+ // To prevent this thread from blocking indefinitely in that case,
+ // park_impl() will, after seeing the state set to NOTIFIED after
+ // waking up, call NtWaitForKeyedEvent again to unblock us.
+ c::NtReleaseKeyedEvent(keyed_event_handle(), self.ptr(), 0, ptr::null_mut());
+ }
+ }
+ }
+ }
+
+ fn ptr(&self) -> c::LPVOID {
+ &self.state as *const _ as c::LPVOID
+ }
+}
+
+fn keyed_event_handle() -> c::HANDLE {
+ const INVALID: c::HANDLE = ptr::invalid_mut(!0);
+ static HANDLE: AtomicPtr<libc::c_void> = AtomicPtr::new(INVALID);
+ match HANDLE.load(Relaxed) {
+ INVALID => {
+ let mut handle = c::INVALID_HANDLE_VALUE;
+ unsafe {
+ match c::NtCreateKeyedEvent(
+ &mut handle,
+ c::GENERIC_READ | c::GENERIC_WRITE,
+ ptr::null_mut(),
+ 0,
+ ) {
+ c::STATUS_SUCCESS => {}
+ r => panic!("Unable to create keyed event handle: error {r}"),
+ }
+ }
+ match HANDLE.compare_exchange(INVALID, handle, Relaxed, Relaxed) {
+ Ok(_) => handle,
+ Err(h) => {
+ // Lost the race to another thread initializing HANDLE before we did.
+ // Closing our handle and using theirs instead.
+ unsafe {
+ c::CloseHandle(handle);
+ }
+ h
+ }
+ }
+ }
+ handle => handle,
+ }
+}
diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs
index 8807077cb..f1d804ef4 100644
--- a/library/std/src/sys_common/backtrace.rs
+++ b/library/std/src/sys_common/backtrace.rs
@@ -20,7 +20,7 @@ pub fn lock() -> impl Drop {
/// Prints the current backtrace.
pub fn print(w: &mut dyn Write, format: PrintFmt) -> io::Result<()> {
// There are issues currently linking libbacktrace into tests, and in
- // general during libstd's own unit tests we're not testing this path. In
+ // general during std's own unit tests we're not testing this path. In
// test mode immediately return here to optimize away any references to the
// libbacktrace symbols
if cfg!(test) {
@@ -111,7 +111,7 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::
}
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
-/// this is only inline(never) when backtraces in libstd are enabled, otherwise
+/// this is only inline(never) when backtraces in std are enabled, otherwise
/// it's fine to optimize away.
#[cfg_attr(feature = "backtrace", inline(never))]
pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
@@ -127,7 +127,7 @@ where
}
/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
-/// this is only inline(never) when backtraces in libstd are enabled, otherwise
+/// this is only inline(never) when backtraces in std are enabled, otherwise
/// it's fine to optimize away.
#[cfg_attr(feature = "backtrace", inline(never))]
pub fn __rust_end_short_backtrace<F, T>(f: F) -> T
diff --git a/library/std/src/sys_common/io.rs b/library/std/src/sys_common/io.rs
index d1e9fed41..4a42ff3c6 100644
--- a/library/std/src/sys_common/io.rs
+++ b/library/std/src/sys_common/io.rs
@@ -39,9 +39,10 @@ pub mod test {
}
}
+ #[track_caller] // for `test_rng`
pub fn tmpdir() -> TempDir {
let p = env::temp_dir();
- let mut r = rand::thread_rng();
+ let mut r = crate::test_helpers::test_rng();
let ret = p.join(&format!("rust-{}", r.next_u32()));
fs::create_dir(&ret).unwrap();
TempDir(ret)
diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs
index b1987aa0f..6b24b0e9a 100644
--- a/library/std/src/sys_common/mod.rs
+++ b/library/std/src/sys_common/mod.rs
@@ -27,11 +27,10 @@ pub mod lazy_box;
pub mod memchr;
pub mod once;
pub mod process;
-pub mod remutex;
pub mod thread;
pub mod thread_info;
pub mod thread_local_dtor;
-pub mod thread_parker;
+pub mod thread_parking;
pub mod wstr;
pub mod wtf8;
diff --git a/library/std/src/sys_common/once/mod.rs b/library/std/src/sys_common/once/mod.rs
index 8742e68cc..359697d83 100644
--- a/library/std/src/sys_common/once/mod.rs
+++ b/library/std/src/sys_common/once/mod.rs
@@ -6,22 +6,6 @@
// As a result, we end up implementing it ourselves in the standard library.
// This also gives us the opportunity to optimize the implementation a bit which
// should help the fast path on call sites.
-//
-// So to recap, the guarantees of a Once are that it will call the
-// initialization closure at most once, and it will never return until the one
-// that's running has finished running. This means that we need some form of
-// blocking here while the custom callback is running at the very least.
-// Additionally, we add on the restriction of **poisoning**. Whenever an
-// initialization closure panics, the Once enters a "poisoned" state which means
-// that all future calls will immediately panic as well.
-//
-// So to implement this, one might first reach for a `Mutex`, but those cannot
-// be put into a `static`. It also gets a lot harder with poisoning to figure
-// out when the mutex needs to be deallocated because it's not after the closure
-// finishes, but after the first successful closure finishes.
-//
-// All in all, this is instead implemented with atomics and lock-free
-// operations! Whee!
cfg_if::cfg_if! {
if #[cfg(any(
@@ -36,8 +20,15 @@ cfg_if::cfg_if! {
))] {
mod futex;
pub use futex::{Once, OnceState};
+ } else if #[cfg(any(
+ windows,
+ target_family = "unix",
+ all(target_vendor = "fortanix", target_env = "sgx"),
+ target_os = "solid_asp3",
+ ))] {
+ mod queue;
+ pub use queue::{Once, OnceState};
} else {
- mod generic;
- pub use generic::{Once, OnceState};
+ pub use crate::sys::once::{Once, OnceState};
}
}
diff --git a/library/std/src/sys_common/once/generic.rs b/library/std/src/sys_common/once/queue.rs
index d953a6745..d953a6745 100644
--- a/library/std/src/sys_common/once/generic.rs
+++ b/library/std/src/sys_common/once/queue.rs
diff --git a/library/std/src/sys_common/process.rs b/library/std/src/sys_common/process.rs
index 9f978789a..18883048d 100644
--- a/library/std/src/sys_common/process.rs
+++ b/library/std/src/sys_common/process.rs
@@ -4,10 +4,13 @@
use crate::collections::BTreeMap;
use crate::env;
use crate::ffi::{OsStr, OsString};
-use crate::sys::process::EnvKey;
+use crate::fmt;
+use crate::io;
+use crate::sys::pipe::read2;
+use crate::sys::process::{EnvKey, ExitStatus, Process, StdioPipes};
// Stores a set of changes to an environment
-#[derive(Clone, Debug)]
+#[derive(Clone)]
pub struct CommandEnv {
clear: bool,
saw_path: bool,
@@ -20,6 +23,14 @@ impl Default for CommandEnv {
}
}
+impl fmt::Debug for CommandEnv {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut debug_command_env = f.debug_struct("CommandEnv");
+ debug_command_env.field("clear", &self.clear).field("vars", &self.vars);
+ debug_command_env.finish()
+ }
+}
+
impl CommandEnv {
// Capture the current environment with these changes applied
pub fn capture(&self) -> BTreeMap<EnvKey, OsString> {
@@ -117,3 +128,30 @@ impl<'a> ExactSizeIterator for CommandEnvs<'a> {
self.iter.is_empty()
}
}
+
+pub fn wait_with_output(
+ mut process: Process,
+ mut pipes: StdioPipes,
+) -> io::Result<(ExitStatus, Vec<u8>, Vec<u8>)> {
+ drop(pipes.stdin.take());
+
+ let (mut stdout, mut stderr) = (Vec::new(), Vec::new());
+ match (pipes.stdout.take(), pipes.stderr.take()) {
+ (None, None) => {}
+ (Some(out), None) => {
+ let res = out.read_to_end(&mut stdout);
+ res.unwrap();
+ }
+ (None, Some(err)) => {
+ let res = err.read_to_end(&mut stderr);
+ res.unwrap();
+ }
+ (Some(out), Some(err)) => {
+ let res = read2(out, &mut stdout, err, &mut stderr);
+ res.unwrap();
+ }
+ }
+
+ let status = process.wait()?;
+ Ok((status, stdout, stderr))
+}
diff --git a/library/std/src/sys_common/remutex/tests.rs b/library/std/src/sys_common/remutex/tests.rs
deleted file mode 100644
index 8e97ce11c..000000000
--- a/library/std/src/sys_common/remutex/tests.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-use crate::cell::RefCell;
-use crate::sync::Arc;
-use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
-use crate::thread;
-
-#[test]
-fn smoke() {
- let m = ReentrantMutex::new(());
- {
- let a = m.lock();
- {
- let b = m.lock();
- {
- let c = m.lock();
- assert_eq!(*c, ());
- }
- assert_eq!(*b, ());
- }
- assert_eq!(*a, ());
- }
-}
-
-#[test]
-fn is_mutex() {
- let m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
- let m2 = m.clone();
- let lock = m.lock();
- let child = thread::spawn(move || {
- let lock = m2.lock();
- assert_eq!(*lock.borrow(), 4950);
- });
- for i in 0..100 {
- let lock = m.lock();
- *lock.borrow_mut() += i;
- }
- drop(lock);
- child.join().unwrap();
-}
-
-#[test]
-fn trylock_works() {
- let m = Arc::new(ReentrantMutex::new(()));
- let m2 = m.clone();
- let _lock = m.try_lock();
- let _lock2 = m.try_lock();
- thread::spawn(move || {
- let lock = m2.try_lock();
- assert!(lock.is_none());
- })
- .join()
- .unwrap();
- let _lock3 = m.try_lock();
-}
-
-pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell<u32>>);
-impl Drop for Answer<'_> {
- fn drop(&mut self) {
- *self.0.borrow_mut() = 42;
- }
-}
diff --git a/library/std/src/sys_common/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs
index 747579f17..2672a2a75 100644
--- a/library/std/src/sys_common/thread_local_key.rs
+++ b/library/std/src/sys_common/thread_local_key.rs
@@ -117,10 +117,14 @@ pub struct Key {
/// This value specifies no destructor by default.
pub const INIT: StaticKey = StaticKey::new(None);
+// Define a sentinel value that is unlikely to be returned
+// as a TLS key (but it may be returned).
+const KEY_SENTVAL: usize = 0;
+
impl StaticKey {
#[rustc_const_unstable(feature = "thread_local_internals", issue = "none")]
pub const fn new(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> StaticKey {
- StaticKey { key: atomic::AtomicUsize::new(0), dtor }
+ StaticKey { key: atomic::AtomicUsize::new(KEY_SENTVAL), dtor }
}
/// Gets the value associated with this TLS key
@@ -144,31 +148,36 @@ impl StaticKey {
#[inline]
unsafe fn key(&self) -> imp::Key {
match self.key.load(Ordering::Relaxed) {
- 0 => self.lazy_init() as imp::Key,
+ KEY_SENTVAL => self.lazy_init() as imp::Key,
n => n as imp::Key,
}
}
unsafe fn lazy_init(&self) -> usize {
- // POSIX allows the key created here to be 0, but the compare_exchange
- // below relies on using 0 as a sentinel value to check who won the
+ // POSIX allows the key created here to be KEY_SENTVAL, but the compare_exchange
+ // below relies on using KEY_SENTVAL as a sentinel value to check who won the
// race to set the shared TLS key. As far as I know, there is no
// guaranteed value that cannot be returned as a posix_key_create key,
// so there is no value we can initialize the inner key with to
// prove that it has not yet been set. As such, we'll continue using a
- // value of 0, but with some gyrations to make sure we have a non-0
+ // value of KEY_SENTVAL, but with some gyrations to make sure we have a non-KEY_SENTVAL
// value returned from the creation routine.
// FIXME: this is clearly a hack, and should be cleaned up.
let key1 = imp::create(self.dtor);
- let key = if key1 != 0 {
+ let key = if key1 as usize != KEY_SENTVAL {
key1
} else {
let key2 = imp::create(self.dtor);
imp::destroy(key1);
key2
};
- rtassert!(key != 0);
- match self.key.compare_exchange(0, key as usize, Ordering::SeqCst, Ordering::SeqCst) {
+ rtassert!(key as usize != KEY_SENTVAL);
+ match self.key.compare_exchange(
+ KEY_SENTVAL,
+ key as usize,
+ Ordering::SeqCst,
+ Ordering::SeqCst,
+ ) {
// The CAS succeeded, so we've created the actual key
Ok(_) => key as usize,
// If someone beat us to the punch, use their key instead
diff --git a/library/std/src/sys_common/thread_parker/generic.rs b/library/std/src/sys_common/thread_parker/generic.rs
deleted file mode 100644
index f3d8b34d3..000000000
--- a/library/std/src/sys_common/thread_parker/generic.rs
+++ /dev/null
@@ -1,125 +0,0 @@
-//! Parker implementation based on a Mutex and Condvar.
-
-use crate::pin::Pin;
-use crate::sync::atomic::AtomicUsize;
-use crate::sync::atomic::Ordering::SeqCst;
-use crate::sync::{Condvar, Mutex};
-use crate::time::Duration;
-
-const EMPTY: usize = 0;
-const PARKED: usize = 1;
-const NOTIFIED: usize = 2;
-
-pub struct Parker {
- state: AtomicUsize,
- lock: Mutex<()>,
- cvar: Condvar,
-}
-
-impl Parker {
- /// Construct the generic parker. The UNIX parker implementation
- /// requires this to happen in-place.
- pub unsafe fn new(parker: *mut Parker) {
- parker.write(Parker {
- state: AtomicUsize::new(EMPTY),
- lock: Mutex::new(()),
- cvar: Condvar::new(),
- });
- }
-
- // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
- pub unsafe fn park(self: Pin<&Self>) {
- // If we were previously notified then we consume this notification and
- // return quickly.
- if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
- return;
- }
-
- // Otherwise we need to coordinate going to sleep
- let mut m = self.lock.lock().unwrap();
- match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
- Ok(_) => {}
- Err(NOTIFIED) => {
- // We must read here, even though we know it will be `NOTIFIED`.
- // This is because `unpark` may have been called again since we read
- // `NOTIFIED` in the `compare_exchange` above. We must perform an
- // acquire operation that synchronizes with that `unpark` to observe
- // any writes it made before the call to unpark. To do that we must
- // read from the write it made to `state`.
- let old = self.state.swap(EMPTY, SeqCst);
- assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
- return;
- } // should consume this notification, so prohibit spurious wakeups in next park.
- Err(_) => panic!("inconsistent park state"),
- }
- loop {
- m = self.cvar.wait(m).unwrap();
- match self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) {
- Ok(_) => return, // got a notification
- Err(_) => {} // spurious wakeup, go back to sleep
- }
- }
- }
-
- // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
- pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
- // Like `park` above we have a fast path for an already-notified thread, and
- // afterwards we start coordinating for a sleep.
- // return quickly.
- if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
- return;
- }
- let m = self.lock.lock().unwrap();
- match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
- Ok(_) => {}
- Err(NOTIFIED) => {
- // We must read again here, see `park`.
- let old = self.state.swap(EMPTY, SeqCst);
- assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
- return;
- } // should consume this notification, so prohibit spurious wakeups in next park.
- Err(_) => panic!("inconsistent park_timeout state"),
- }
-
- // Wait with a timeout, and if we spuriously wake up or otherwise wake up
- // from a notification we just want to unconditionally set the state back to
- // empty, either consuming a notification or un-flagging ourselves as
- // parked.
- let (_m, _result) = self.cvar.wait_timeout(m, dur).unwrap();
- match self.state.swap(EMPTY, SeqCst) {
- NOTIFIED => {} // got a notification, hurray!
- PARKED => {} // no notification, alas
- n => panic!("inconsistent park_timeout state: {n}"),
- }
- }
-
- // This implementation doesn't require `Pin`, but other implementations do.
- pub fn unpark(self: Pin<&Self>) {
- // To ensure the unparked thread will observe any writes we made
- // before this call, we must perform a release operation that `park`
- // can synchronize with. To do that we must write `NOTIFIED` even if
- // `state` is already `NOTIFIED`. That is why this must be a swap
- // rather than a compare-and-swap that returns if it reads `NOTIFIED`
- // on failure.
- match self.state.swap(NOTIFIED, SeqCst) {
- EMPTY => return, // no one was waiting
- NOTIFIED => return, // already unparked
- PARKED => {} // gotta go wake someone up
- _ => panic!("inconsistent state in unpark"),
- }
-
- // There is a period between when the parked thread sets `state` to
- // `PARKED` (or last checked `state` in the case of a spurious wake
- // up) and when it actually waits on `cvar`. If we were to notify
- // during this period it would be ignored and then when the parked
- // thread went to sleep it would never wake up. Fortunately, it has
- // `lock` locked at this stage so we can acquire `lock` to wait until
- // it is ready to receive the notification.
- //
- // Releasing `lock` before the call to `notify_one` means that when the
- // parked thread wakes it doesn't get woken only to have to wait for us
- // to release `lock`.
- drop(self.lock.lock().unwrap());
- self.cvar.notify_one()
- }
-}
diff --git a/library/std/src/sys_common/thread_parker/mod.rs b/library/std/src/sys_common/thread_parker/mod.rs
deleted file mode 100644
index f86a9a555..000000000
--- a/library/std/src/sys_common/thread_parker/mod.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-cfg_if::cfg_if! {
- if #[cfg(any(
- target_os = "linux",
- target_os = "android",
- all(target_arch = "wasm32", target_feature = "atomics"),
- target_os = "freebsd",
- target_os = "openbsd",
- target_os = "dragonfly",
- target_os = "fuchsia",
- target_os = "hermit",
- ))] {
- mod futex;
- pub use futex::Parker;
- } else if #[cfg(target_os = "solid_asp3")] {
- mod wait_flag;
- pub use wait_flag::Parker;
- } else if #[cfg(any(windows, target_family = "unix"))] {
- pub use crate::sys::thread_parker::Parker;
- } else {
- mod generic;
- pub use generic::Parker;
- }
-}
diff --git a/library/std/src/sys_common/thread_parker/futex.rs b/library/std/src/sys_common/thread_parking/futex.rs
index d9e2f39e3..588e7b278 100644
--- a/library/std/src/sys_common/thread_parker/futex.rs
+++ b/library/std/src/sys_common/thread_parking/futex.rs
@@ -35,7 +35,7 @@ pub struct Parker {
impl Parker {
/// Construct the futex parker. The UNIX parker implementation
/// requires this to happen in-place.
- pub unsafe fn new(parker: *mut Parker) {
+ pub unsafe fn new_in_place(parker: *mut Parker) {
parker.write(Self { state: AtomicU32::new(EMPTY) });
}
diff --git a/library/std/src/sys_common/thread_parking/generic.rs b/library/std/src/sys_common/thread_parking/generic.rs
new file mode 100644
index 000000000..3209bffe3
--- /dev/null
+++ b/library/std/src/sys_common/thread_parking/generic.rs
@@ -0,0 +1,125 @@
+//! Parker implementation based on a Mutex and Condvar.
+
+use crate::pin::Pin;
+use crate::sync::atomic::AtomicUsize;
+use crate::sync::atomic::Ordering::SeqCst;
+use crate::sync::{Condvar, Mutex};
+use crate::time::Duration;
+
+const EMPTY: usize = 0;
+const PARKED: usize = 1;
+const NOTIFIED: usize = 2;
+
+pub struct Parker {
+ state: AtomicUsize,
+ lock: Mutex<()>,
+ cvar: Condvar,
+}
+
+impl Parker {
+ /// Construct the generic parker. The UNIX parker implementation
+ /// requires this to happen in-place.
+ pub unsafe fn new_in_place(parker: *mut Parker) {
+ parker.write(Parker {
+ state: AtomicUsize::new(EMPTY),
+ lock: Mutex::new(()),
+ cvar: Condvar::new(),
+ });
+ }
+
+ // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
+ pub unsafe fn park(self: Pin<&Self>) {
+ // If we were previously notified then we consume this notification and
+ // return quickly.
+ if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
+ return;
+ }
+
+ // Otherwise we need to coordinate going to sleep
+ let mut m = self.lock.lock().unwrap();
+ match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
+ Ok(_) => {}
+ Err(NOTIFIED) => {
+ // We must read here, even though we know it will be `NOTIFIED`.
+ // This is because `unpark` may have been called again since we read
+ // `NOTIFIED` in the `compare_exchange` above. We must perform an
+ // acquire operation that synchronizes with that `unpark` to observe
+ // any writes it made before the call to unpark. To do that we must
+ // read from the write it made to `state`.
+ let old = self.state.swap(EMPTY, SeqCst);
+ assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
+ return;
+ } // should consume this notification, so prohibit spurious wakeups in next park.
+ Err(_) => panic!("inconsistent park state"),
+ }
+ loop {
+ m = self.cvar.wait(m).unwrap();
+ match self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) {
+ Ok(_) => return, // got a notification
+ Err(_) => {} // spurious wakeup, go back to sleep
+ }
+ }
+ }
+
+ // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
+ pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
+ // Like `park` above we have a fast path for an already-notified thread, and
+ // afterwards we start coordinating for a sleep.
+ // return quickly.
+ if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
+ return;
+ }
+ let m = self.lock.lock().unwrap();
+ match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
+ Ok(_) => {}
+ Err(NOTIFIED) => {
+ // We must read again here, see `park`.
+ let old = self.state.swap(EMPTY, SeqCst);
+ assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
+ return;
+ } // should consume this notification, so prohibit spurious wakeups in next park.
+ Err(_) => panic!("inconsistent park_timeout state"),
+ }
+
+ // Wait with a timeout, and if we spuriously wake up or otherwise wake up
+ // from a notification we just want to unconditionally set the state back to
+ // empty, either consuming a notification or un-flagging ourselves as
+ // parked.
+ let (_m, _result) = self.cvar.wait_timeout(m, dur).unwrap();
+ match self.state.swap(EMPTY, SeqCst) {
+ NOTIFIED => {} // got a notification, hurray!
+ PARKED => {} // no notification, alas
+ n => panic!("inconsistent park_timeout state: {n}"),
+ }
+ }
+
+ // This implementation doesn't require `Pin`, but other implementations do.
+ pub fn unpark(self: Pin<&Self>) {
+ // To ensure the unparked thread will observe any writes we made
+ // before this call, we must perform a release operation that `park`
+ // can synchronize with. To do that we must write `NOTIFIED` even if
+ // `state` is already `NOTIFIED`. That is why this must be a swap
+ // rather than a compare-and-swap that returns if it reads `NOTIFIED`
+ // on failure.
+ match self.state.swap(NOTIFIED, SeqCst) {
+ EMPTY => return, // no one was waiting
+ NOTIFIED => return, // already unparked
+ PARKED => {} // gotta go wake someone up
+ _ => panic!("inconsistent state in unpark"),
+ }
+
+ // There is a period between when the parked thread sets `state` to
+ // `PARKED` (or last checked `state` in the case of a spurious wake
+ // up) and when it actually waits on `cvar`. If we were to notify
+ // during this period it would be ignored and then when the parked
+ // thread went to sleep it would never wake up. Fortunately, it has
+ // `lock` locked at this stage so we can acquire `lock` to wait until
+ // it is ready to receive the notification.
+ //
+ // Releasing `lock` before the call to `notify_one` means that when the
+ // parked thread wakes it doesn't get woken only to have to wait for us
+ // to release `lock`.
+ drop(self.lock.lock().unwrap());
+ self.cvar.notify_one()
+ }
+}
diff --git a/library/std/src/sys_common/thread_parking/id.rs b/library/std/src/sys_common/thread_parking/id.rs
new file mode 100644
index 000000000..e98169597
--- /dev/null
+++ b/library/std/src/sys_common/thread_parking/id.rs
@@ -0,0 +1,108 @@
+//! Thread parking using thread ids.
+//!
+//! Some platforms (notably NetBSD) have thread parking primitives whose semantics
+//! match those offered by `thread::park`, with the difference that the thread to
+//! be unparked is referenced by a platform-specific thread id. Since the thread
+//! parker is constructed before that id is known, an atomic state variable is used
+//! to manage the park state and propagate the thread id. This also avoids platform
+//! calls in the case where `unpark` is called before `park`.
+
+use crate::cell::UnsafeCell;
+use crate::pin::Pin;
+use crate::sync::atomic::{
+ fence, AtomicI8,
+ Ordering::{Acquire, Relaxed, Release},
+};
+use crate::sys::thread_parking::{current, park, park_timeout, unpark, ThreadId};
+use crate::time::Duration;
+
+pub struct Parker {
+ state: AtomicI8,
+ tid: UnsafeCell<Option<ThreadId>>,
+}
+
+const PARKED: i8 = -1;
+const EMPTY: i8 = 0;
+const NOTIFIED: i8 = 1;
+
+impl Parker {
+ pub fn new() -> Parker {
+ Parker { state: AtomicI8::new(EMPTY), tid: UnsafeCell::new(None) }
+ }
+
+ /// Create a new thread parker. UNIX requires this to happen in-place.
+ pub unsafe fn new_in_place(parker: *mut Parker) {
+ parker.write(Parker::new())
+ }
+
+ /// # Safety
+ /// * must always be called from the same thread
+ /// * must be called before the state is set to PARKED
+ unsafe fn init_tid(&self) {
+ // The field is only ever written to from this thread, so we don't need
+ // synchronization to read it here.
+ if self.tid.get().read().is_none() {
+ // Because this point is only reached once, before the state is set
+ // to PARKED for the first time, the non-atomic write here can not
+ // conflict with reads by other threads.
+ self.tid.get().write(Some(current()));
+ // Ensure that the write can be observed by all threads reading the
+ // state. Synchronizes with the acquire barrier in `unpark`.
+ fence(Release);
+ }
+ }
+
+ pub unsafe fn park(self: Pin<&Self>) {
+ self.init_tid();
+
+ // Changes NOTIFIED to EMPTY and EMPTY to PARKED.
+ let mut state = self.state.fetch_sub(1, Acquire).wrapping_sub(1);
+ if state == PARKED {
+ // Loop to guard against spurious wakeups.
+ while state == PARKED {
+ park(self.state.as_mut_ptr().addr());
+ state = self.state.load(Acquire);
+ }
+
+ // Since the state change has already been observed with acquire
+ // ordering, the state can be reset with a relaxed store instead
+ // of a swap.
+ self.state.store(EMPTY, Relaxed);
+ }
+ }
+
+ pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
+ self.init_tid();
+
+ let state = self.state.fetch_sub(1, Acquire).wrapping_sub(1);
+ if state == PARKED {
+ park_timeout(dur, self.state.as_mut_ptr().addr());
+ // Swap to ensure that we observe all state changes with acquire
+ // ordering, even if the state has been changed after the timeout
+ // occured.
+ self.state.swap(EMPTY, Acquire);
+ }
+ }
+
+ pub fn unpark(self: Pin<&Self>) {
+ let state = self.state.swap(NOTIFIED, Release);
+ if state == PARKED {
+ // Synchronize with the release fence in `init_tid` to observe the
+ // write to `tid`.
+ fence(Acquire);
+ // # Safety
+ // The thread id is initialized before the state is set to `PARKED`
+ // for the first time and is not written to from that point on
+ // (negating the need for an atomic read).
+ let tid = unsafe { self.tid.get().read().unwrap_unchecked() };
+ // It is possible that the waiting thread woke up because of a timeout
+ // and terminated before this call is made. This call then returns an
+ // error or wakes up an unrelated thread. The platform API and
+ // environment does allow this, however.
+ unpark(tid, self.state.as_mut_ptr().addr());
+ }
+ }
+}
+
+unsafe impl Send for Parker {}
+unsafe impl Sync for Parker {}
diff --git a/library/std/src/sys_common/thread_parking/mod.rs b/library/std/src/sys_common/thread_parking/mod.rs
new file mode 100644
index 000000000..0ead6633c
--- /dev/null
+++ b/library/std/src/sys_common/thread_parking/mod.rs
@@ -0,0 +1,29 @@
+cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ all(target_arch = "wasm32", target_feature = "atomics"),
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
+ target_os = "fuchsia",
+ target_os = "hermit",
+ ))] {
+ mod futex;
+ pub use futex::Parker;
+ } else if #[cfg(any(
+ target_os = "netbsd",
+ all(target_vendor = "fortanix", target_env = "sgx"),
+ ))] {
+ mod id;
+ pub use id::Parker;
+ } else if #[cfg(target_os = "solid_asp3")] {
+ mod wait_flag;
+ pub use wait_flag::Parker;
+ } else if #[cfg(any(windows, target_family = "unix"))] {
+ pub use crate::sys::thread_parking::Parker;
+ } else {
+ mod generic;
+ pub use generic::Parker;
+ }
+}
diff --git a/library/std/src/sys_common/thread_parker/wait_flag.rs b/library/std/src/sys_common/thread_parking/wait_flag.rs
index 6561c1866..d0f8899a9 100644
--- a/library/std/src/sys_common/thread_parker/wait_flag.rs
+++ b/library/std/src/sys_common/thread_parking/wait_flag.rs
@@ -41,7 +41,7 @@ pub struct Parker {
impl Parker {
/// Construct a parker for the current thread. The UNIX parker
/// implementation requires this to happen in-place.
- pub unsafe fn new(parker: *mut Parker) {
+ pub unsafe fn new_in_place(parker: *mut Parker) {
parker.write(Parker { state: AtomicI8::new(EMPTY), wait_flag: WaitFlag::new() })
}