diff options
Diffstat (limited to 'library/std/src')
40 files changed, 798 insertions, 413 deletions
diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index ba158511f64..dd760062380 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -133,7 +133,7 @@ pub struct System; impl System { #[inline] - fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>, AllocErr> { + fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>, AllocError> { match layout.size() { 0 => Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0)), // SAFETY: `layout` is non-zero in size, @@ -143,7 +143,7 @@ impl System { } else { GlobalAlloc::alloc(self, layout) }; - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; Ok(NonNull::slice_from_raw_parts(ptr, size)) }, } @@ -157,7 +157,7 @@ impl System { old_layout: Layout, new_layout: Layout, zeroed: bool, - ) -> Result<NonNull<[u8]>, AllocErr> { + ) -> Result<NonNull<[u8]>, AllocError> { debug_assert!( new_layout.size() >= old_layout.size(), "`new_layout.size()` must be greater than or equal to `old_layout.size()`" @@ -175,7 +175,7 @@ impl System { intrinsics::assume(new_size >= old_layout.size()); let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size); - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; if zeroed { raw_ptr.add(old_size).write_bytes(0, new_size - old_size); } @@ -202,12 +202,12 @@ impl System { #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl AllocRef for System { #[inline] - fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> { + fn alloc(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> { self.alloc_impl(layout, false) } #[inline] - fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> { + fn alloc_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> { self.alloc_impl(layout, true) } @@ -226,7 +226,7 @@ unsafe impl AllocRef for System { ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, - ) -> Result<NonNull<[u8]>, AllocErr> { + ) -> Result<NonNull<[u8]>, AllocError> { // SAFETY: all conditions must be upheld by the caller unsafe { self.grow_impl(ptr, old_layout, new_layout, false) } } @@ -237,7 +237,7 @@ unsafe impl AllocRef for System { ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, - ) -> Result<NonNull<[u8]>, AllocErr> { + ) -> Result<NonNull<[u8]>, AllocError> { // SAFETY: all conditions must be upheld by the caller unsafe { self.grow_impl(ptr, old_layout, new_layout, true) } } @@ -248,7 +248,7 @@ unsafe impl AllocRef for System { ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, - ) -> Result<NonNull<[u8]>, AllocErr> { + ) -> Result<NonNull<[u8]>, AllocError> { debug_assert!( new_layout.size() <= old_layout.size(), "`new_layout.size()` must be smaller than or equal to `old_layout.size()`" @@ -267,7 +267,7 @@ unsafe impl AllocRef for System { intrinsics::assume(new_size <= old_layout.size()); let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size); - let ptr = NonNull::new(raw_ptr).ok_or(AllocErr)?; + let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; Ok(NonNull::slice_from_raw_parts(ptr, new_size)) }, diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs index 61d71d55d65..4424a4c1992 100644 --- a/library/std/src/collections/hash/map.rs +++ b/library/std/src/collections/hash/map.rs @@ -1102,6 +1102,16 @@ where /// documentation for more. /// /// [`iter`]: HashMap::iter +/// +/// # Example +/// +/// ``` +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter = map.iter(); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, K: 'a, V: 'a> { base: base::Iter<'a, K, V>, @@ -1129,6 +1139,16 @@ impl<K: Debug, V: Debug> fmt::Debug for Iter<'_, K, V> { /// documentation for more. /// /// [`iter_mut`]: HashMap::iter_mut +/// +/// # Example +/// +/// ``` +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter = map.iter_mut(); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct IterMut<'a, K: 'a, V: 'a> { base: base::IterMut<'a, K, V>, @@ -1148,6 +1168,16 @@ impl<'a, K, V> IterMut<'a, K, V> { /// (provided by the `IntoIterator` trait). See its documentation for more. /// /// [`into_iter`]: IntoIterator::into_iter +/// +/// # Example +/// +/// ``` +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter = map.into_iter(); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct IntoIter<K, V> { base: base::IntoIter<K, V>, @@ -1167,6 +1197,16 @@ impl<K, V> IntoIter<K, V> { /// documentation for more. /// /// [`keys`]: HashMap::keys +/// +/// # Example +/// +/// ``` +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter_keys = map.keys(); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct Keys<'a, K: 'a, V: 'a> { inner: Iter<'a, K, V>, @@ -1194,6 +1234,16 @@ impl<K: Debug, V> fmt::Debug for Keys<'_, K, V> { /// documentation for more. /// /// [`values`]: HashMap::values +/// +/// # Example +/// +/// ``` +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter_values = map.values(); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct Values<'a, K: 'a, V: 'a> { inner: Iter<'a, K, V>, @@ -1221,6 +1271,16 @@ impl<K, V: Debug> fmt::Debug for Values<'_, K, V> { /// documentation for more. /// /// [`drain`]: HashMap::drain +/// +/// # Example +/// +/// ``` +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter = map.drain(); +/// ``` #[stable(feature = "drain", since = "1.6.0")] pub struct Drain<'a, K: 'a, V: 'a> { base: base::Drain<'a, K, V>, @@ -1239,6 +1299,18 @@ impl<'a, K, V> Drain<'a, K, V> { /// This `struct` is created by the [`drain_filter`] method on [`HashMap`]. /// /// [`drain_filter`]: HashMap::drain_filter +/// +/// # Example +/// +/// ``` +/// #![feature(hash_drain_filter)] +/// +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter = map.drain_filter(|_k, v| *v % 2 == 0); +/// ``` #[unstable(feature = "hash_drain_filter", issue = "59618")] pub struct DrainFilter<'a, K, V, F> where @@ -1253,6 +1325,16 @@ where /// documentation for more. /// /// [`values_mut`]: HashMap::values_mut +/// +/// # Example +/// +/// ``` +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter_values = map.values_mut(); +/// ``` #[stable(feature = "map_values_mut", since = "1.10.0")] pub struct ValuesMut<'a, K: 'a, V: 'a> { inner: IterMut<'a, K, V>, @@ -1264,6 +1346,18 @@ pub struct ValuesMut<'a, K: 'a, V: 'a> { /// See its documentation for more. /// /// [`into_keys`]: HashMap::into_keys +/// +/// # Example +/// +/// ``` +/// #![feature(map_into_keys_values)] +/// +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter_keys = map.into_keys(); +/// ``` #[unstable(feature = "map_into_keys_values", issue = "75294")] pub struct IntoKeys<K, V> { inner: IntoIter<K, V>, @@ -1275,6 +1369,18 @@ pub struct IntoKeys<K, V> { /// See its documentation for more. /// /// [`into_values`]: HashMap::into_values +/// +/// # Example +/// +/// ``` +/// #![feature(map_into_keys_values)] +/// +/// use std::collections::HashMap; +/// +/// let mut map = HashMap::new(); +/// map.insert("a", 1); +/// let iter_keys = map.into_values(); +/// ``` #[unstable(feature = "map_into_keys_values", issue = "75294")] pub struct IntoValues<K, V> { inner: IntoIter<K, V>, @@ -1285,7 +1391,6 @@ pub struct IntoValues<K, V> { /// See the [`HashMap::raw_entry_mut`] docs for usage examples. /// /// [`HashMap::raw_entry_mut`]: HashMap::raw_entry_mut - #[unstable(feature = "hash_raw_entry", issue = "56167")] pub struct RawEntryBuilderMut<'a, K: 'a, V: 'a, S: 'a> { map: &'a mut HashMap<K, V, S>, @@ -2731,11 +2836,10 @@ impl DefaultHasher { #[stable(feature = "hashmap_default_hasher", since = "1.13.0")] impl Default for DefaultHasher { - // FIXME: here should link `new` to [DefaultHasher::new], but it occurs intra-doc link - // resolution failure when re-exporting libstd items. When #56922 fixed, - // link `new` to [DefaultHasher::new] again. - /// Creates a new `DefaultHasher` using `new`. + /// Creates a new `DefaultHasher` using [`new`]. /// See its documentation for more. + /// + /// [`new`]: DefaultHasher::new fn default() -> DefaultHasher { DefaultHasher::new() } diff --git a/library/std/src/error.rs b/library/std/src/error.rs index ee25311d3b7..5771ca758af 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -19,7 +19,7 @@ mod tests; use core::array; use core::convert::Infallible; -use crate::alloc::{AllocErr, LayoutErr}; +use crate::alloc::{AllocError, LayoutErr}; use crate::any::TypeId; use crate::backtrace::Backtrace; use crate::borrow::Cow; @@ -387,7 +387,7 @@ impl Error for ! {} reason = "the precise API and guarantees it provides may be tweaked.", issue = "32838" )] -impl Error for AllocErr {} +impl Error for AllocError {} #[stable(feature = "alloc_layout", since = "1.28.0")] impl Error for LayoutErr {} diff --git a/library/std/src/io/lazy.rs b/library/std/src/io/lazy.rs deleted file mode 100644 index 1968d498bbe..00000000000 --- a/library/std/src/io/lazy.rs +++ /dev/null @@ -1,63 +0,0 @@ -use crate::cell::Cell; -use crate::ptr; -use crate::sync::Arc; -use crate::sys_common; -use crate::sys_common::mutex::Mutex; - -pub struct Lazy<T> { - // We never call `lock.init()`, so it is UB to attempt to acquire this mutex reentrantly! - lock: Mutex, - ptr: Cell<*mut Arc<T>>, -} - -#[inline] -const fn done<T>() -> *mut Arc<T> { - 1_usize as *mut _ -} - -unsafe impl<T> Sync for Lazy<T> {} - -impl<T> Lazy<T> { - pub const fn new() -> Lazy<T> { - Lazy { lock: Mutex::new(), ptr: Cell::new(ptr::null_mut()) } - } -} - -impl<T: Send + Sync + 'static> Lazy<T> { - /// Safety: `init` must not call `get` on the variable that is being - /// initialized. - pub unsafe fn get(&'static self, init: fn() -> Arc<T>) -> Option<Arc<T>> { - let _guard = self.lock.lock(); - let ptr = self.ptr.get(); - if ptr.is_null() { - Some(self.init(init)) - } else if ptr == done() { - None - } else { - Some((*ptr).clone()) - } - } - - // Must only be called with `lock` held - unsafe fn init(&'static self, init: fn() -> Arc<T>) -> Arc<T> { - // If we successfully register an at exit handler, then we cache the - // `Arc` allocation in our own internal box (it will get deallocated by - // the at exit handler). Otherwise we just return the freshly allocated - // `Arc`. - let registered = sys_common::at_exit(move || { - let ptr = { - let _guard = self.lock.lock(); - self.ptr.replace(done()) - }; - drop(Box::from_raw(ptr)) - }); - // This could reentrantly call `init` again, which is a problem - // because our `lock` allows reentrancy! - // That's why `get` is unsafe and requires the caller to ensure no reentrancy happens. - let ret = init(); - if registered.is_ok() { - self.ptr.set(Box::into_raw(Box::new(ret.clone()))); - } - ret - } -} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index adea8a804e3..d9d03807819 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -285,7 +285,6 @@ mod buffered; mod cursor; mod error; mod impls; -mod lazy; pub mod prelude; mod stdio; mod util; diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 71ed2d84cad..61ccc6f13c8 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -7,10 +7,11 @@ use crate::io::prelude::*; use crate::cell::RefCell; use crate::fmt; -use crate::io::lazy::Lazy; use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter}; -use crate::sync::{Arc, Mutex, MutexGuard, Once}; +use crate::lazy::SyncOnceCell; +use crate::sync::{Mutex, MutexGuard}; use crate::sys::stdio; +use crate::sys_common; use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; use crate::thread::LocalKey; @@ -217,7 +218,7 @@ fn handle_ebadf<T>(r: io::Result<T>, default: T) -> io::Result<T> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct Stdin { - inner: Arc<Mutex<BufReader<StdinRaw>>>, + inner: &'static Mutex<BufReader<StdinRaw>>, } /// A locked reference to the `Stdin` handle. @@ -292,15 +293,11 @@ pub struct StdinLock<'a> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn stdin() -> Stdin { - static INSTANCE: Lazy<Mutex<BufReader<StdinRaw>>> = Lazy::new(); - return Stdin { - inner: unsafe { INSTANCE.get(stdin_init).expect("cannot access stdin during shutdown") }, - }; - - fn stdin_init() -> Arc<Mutex<BufReader<StdinRaw>>> { - // This must not reentrantly access `INSTANCE` - let stdin = stdin_raw(); - Arc::new(Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, stdin))) + static INSTANCE: SyncOnceCell<Mutex<BufReader<StdinRaw>>> = SyncOnceCell::new(); + Stdin { + inner: INSTANCE.get_or_init(|| { + Mutex::new(BufReader::with_capacity(stdio::STDIN_BUF_SIZE, stdin_raw())) + }), } } @@ -476,7 +473,7 @@ pub struct Stdout { // FIXME: this should be LineWriter or BufWriter depending on the state of // stdout (tty or not). Note that if this is not line buffered it // should also flush-on-panic or some form of flush-on-abort. - inner: Arc<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>>, + inner: &'static ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>, } /// A locked reference to the `Stdout` handle. @@ -534,19 +531,27 @@ pub struct StdoutLock<'a> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn stdout() -> Stdout { - static INSTANCE: Lazy<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = Lazy::new(); - return Stdout { - inner: unsafe { INSTANCE.get(stdout_init).expect("cannot access stdout during shutdown") }, - }; - - fn stdout_init() -> Arc<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> { - // This must not reentrantly access `INSTANCE` - let stdout = stdout_raw(); - unsafe { - let ret = Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout)))); - ret.init(); - ret - } + static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = + SyncOnceCell::new(); + Stdout { + inner: INSTANCE.get_or_init(|| unsafe { + let _ = sys_common::at_exit(|| { + if let Some(instance) = INSTANCE.get() { + // Flush the data and disable buffering during shutdown + // by replacing the line writer by one with zero + // buffering capacity. + // We use try_lock() instead of lock(), because someone + // might have leaked a StdoutLock, which would + // otherwise cause a deadlock here. + if let Some(lock) = instance.try_lock() { + *lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw()); + } + } + }); + let r = ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))); + r.init(); + r + }), } } @@ -741,16 +746,15 @@ pub fn stderr() -> Stderr { // // This has the added benefit of allowing `stderr` to be usable during // process shutdown as well! - static INSTANCE: ReentrantMutex<RefCell<StderrRaw>> = - unsafe { ReentrantMutex::new(RefCell::new(stderr_raw())) }; - - // When accessing stderr we need one-time initialization of the reentrant - // mutex. Afterwards we can just always use the now-filled-in `INSTANCE` value. - static INIT: Once = Once::new(); - INIT.call_once(|| unsafe { - INSTANCE.init(); - }); - Stderr { inner: &INSTANCE } + static INSTANCE: SyncOnceCell<ReentrantMutex<RefCell<StderrRaw>>> = SyncOnceCell::new(); + + Stderr { + inner: INSTANCE.get_or_init(|| unsafe { + let r = ReentrantMutex::new(RefCell::new(stderr_raw())); + r.init(); + r + }), + } } impl Stderr { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index d03428dd082..e343eef9112 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -239,6 +239,7 @@ #![cfg_attr(not(bootstrap), feature(const_fn_floating_point_arithmetic))] #![feature(const_fn_transmute)] #![feature(const_fn)] +#![cfg_attr(not(bootstrap), feature(const_fn_fn_ptr_basics))] #![feature(const_ip)] #![feature(const_ipv6)] #![feature(const_raw_ptr_deref)] diff --git a/library/std/src/os/linux/raw.rs b/library/std/src/os/linux/raw.rs index 4ff3a6e5789..617c4098aa9 100644 --- a/library/std/src/os/linux/raw.rs +++ b/library/std/src/os/linux/raw.rs @@ -29,6 +29,7 @@ pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; target_arch = "x86", target_arch = "le32", target_arch = "powerpc", + target_arch = "sparc", target_arch = "arm", target_arch = "asmjs", target_arch = "wasm32" diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 18d9c2f11b5..4281867314c 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -411,3 +411,6 @@ pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> { pub fn resume_unwind(payload: Box<dyn Any + Send>) -> ! { panicking::rust_panic_without_hook(payload) } + +#[cfg(test)] +mod tests; diff --git a/library/std/src/panic/tests.rs b/library/std/src/panic/tests.rs new file mode 100644 index 00000000000..b37d74011cc --- /dev/null +++ b/library/std/src/panic/tests.rs @@ -0,0 +1,56 @@ +#![allow(dead_code)] + +use crate::cell::RefCell; +use crate::panic::{AssertUnwindSafe, UnwindSafe}; +use crate::rc::Rc; +use crate::sync::{Arc, Mutex, RwLock}; + +struct Foo { + a: i32, +} + +fn assert<T: UnwindSafe + ?Sized>() {} + +#[test] +fn panic_safety_traits() { + assert::<i32>(); + assert::<&i32>(); + assert::<*mut i32>(); + assert::<*const i32>(); + assert::<usize>(); + assert::<str>(); + assert::<&str>(); + assert::<Foo>(); + assert::<&Foo>(); + assert::<Vec<i32>>(); + assert::<String>(); + assert::<RefCell<i32>>(); + assert::<Box<i32>>(); + assert::<Mutex<i32>>(); + assert::<RwLock<i32>>(); + assert::<&Mutex<i32>>(); + assert::<&RwLock<i32>>(); + assert::<Rc<i32>>(); + assert::<Arc<i32>>(); + assert::<Box<[u8]>>(); + + { + trait Trait: UnwindSafe {} + assert::<Box<dyn Trait>>(); + } + + fn bar<T>() { + assert::<Mutex<T>>(); + assert::<RwLock<T>>(); + } + + fn baz<T: UnwindSafe>() { + assert::<Box<T>>(); + assert::<Vec<T>>(); + assert::<RefCell<T>>(); + assert::<AssertUnwindSafe<T>>(); + assert::<&AssertUnwindSafe<T>>(); + assert::<Rc<AssertUnwindSafe<T>>>(); + assert::<Arc<AssertUnwindSafe<T>>>(); + } +} diff --git a/library/std/src/path.rs b/library/std/src/path.rs index b83c1e9628d..6fa73042a30 100644 --- a/library/std/src/path.rs +++ b/library/std/src/path.rs @@ -1838,7 +1838,7 @@ impl Path { // FIXME: Allow Redox prefixes self.has_root() || has_redox_scheme(self.as_u8_slice()) } else { - self.has_root() && (cfg!(unix) || self.prefix().is_some()) + self.has_root() && (cfg!(any(unix, target_os = "wasi")) || self.prefix().is_some()) } } diff --git a/library/std/src/sync/condvar.rs b/library/std/src/sync/condvar.rs index 7e2155dae6f..1376d8ebe8f 100644 --- a/library/std/src/sync/condvar.rs +++ b/library/std/src/sync/condvar.rs @@ -553,8 +553,8 @@ impl Condvar { unsafe { self.inner.notify_all() } } - fn verify(&self, mutex: &sys_mutex::Mutex) { - let addr = mutex as *const _ as usize; + fn verify(&self, mutex: &sys_mutex::MovableMutex) { + let addr = mutex.raw() as *const _ as usize; match self.mutex.compare_and_swap(0, addr, Ordering::SeqCst) { // If we got out 0, then we have successfully bound the mutex to // this cvar. diff --git a/library/std/src/sync/mutex.rs b/library/std/src/sync/mutex.rs index a1703c731d4..e8f5a6f4294 100644 --- a/library/std/src/sync/mutex.rs +++ b/library/std/src/sync/mutex.rs @@ -166,12 +166,7 @@ use crate::sys_common::poison::{self, LockResult, TryLockError, TryLockResult}; #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "mutex_type")] pub struct Mutex<T: ?Sized> { - // Note that this mutex is in a *box*, not inlined into the struct itself. - // Once a native mutex has been used once, its address can never change (it - // can't be moved). This mutex type can be safely moved at any time, so to - // ensure that the native mutex is used correctly we box the inner mutex to - // give it a constant address. - inner: Box<sys::Mutex>, + inner: sys::MovableMutex, poison: poison::Flag, data: UnsafeCell<T>, } @@ -218,15 +213,11 @@ impl<T> Mutex<T> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn new(t: T) -> Mutex<T> { - let mut m = Mutex { - inner: box sys::Mutex::new(), + Mutex { + inner: sys::MovableMutex::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t), - }; - unsafe { - m.inner.init(); } - m } } @@ -378,7 +369,6 @@ impl<T: ?Sized> Mutex<T> { (ptr::read(inner), ptr::read(poison), ptr::read(data)) }; mem::forget(self); - inner.destroy(); // Keep in sync with the `Drop` impl. drop(inner); poison::map_result(poison.borrow(), |_| data.into_inner()) @@ -411,18 +401,6 @@ impl<T: ?Sized> Mutex<T> { } } -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<#[may_dangle] T: ?Sized> Drop for Mutex<T> { - fn drop(&mut self) { - // This is actually safe b/c we know that there is no further usage of - // this mutex (it's up to the user to arrange for a mutex to get - // dropped, that's not our job) - // - // IMPORTANT: This code must be kept in sync with `Mutex::into_inner`. - unsafe { self.inner.destroy() } - } -} - #[stable(feature = "mutex_from", since = "1.24.0")] impl<T> From<T> for Mutex<T> { /// Creates a new mutex in an unlocked state ready for use. @@ -509,7 +487,7 @@ impl<T: ?Sized + fmt::Display> fmt::Display for MutexGuard<'_, T> { } } -pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { +pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::MovableMutex { &guard.lock.inner } diff --git a/library/std/src/sys/hermit/args.rs b/library/std/src/sys/hermit/args.rs index 72c1b8511ca..77272939272 100644 --- a/library/std/src/sys/hermit/args.rs +++ b/library/std/src/sys/hermit/args.rs @@ -57,11 +57,11 @@ mod imp { use crate::ptr; use crate::sys_common::os_str_bytes::*; - use crate::sys_common::mutex::Mutex; + use crate::sys_common::mutex::StaticMutex; static mut ARGC: isize = 0; static mut ARGV: *const *const u8 = ptr::null(); - static LOCK: Mutex = Mutex::new(); + static LOCK: StaticMutex = StaticMutex::new(); pub unsafe fn init(argc: isize, argv: *const *const u8) { let _guard = LOCK.lock(); diff --git a/library/std/src/sys/sgx/abi/mem.rs b/library/std/src/sys/sgx/abi/mem.rs index 57fd7efdd49..ffa234fccfe 100644 --- a/library/std/src/sys/sgx/abi/mem.rs +++ b/library/std/src/sys/sgx/abi/mem.rs @@ -21,8 +21,15 @@ extern "C" { #[inline(always)] #[unstable(feature = "sgx_platform", issue = "56975")] pub fn image_base() -> u64 { - let base; - unsafe { llvm_asm!("lea IMAGE_BASE(%rip),$0":"=r"(base)) }; + let base: u64; + unsafe { + asm!( + "lea IMAGE_BASE(%rip), {}", + lateout(reg) base, + // NOTE(#76738): ATT syntax is used to support LLVM 8 and 9. + options(att_syntax, nostack, preserves_flags, nomem, pure), + ) + }; base } diff --git a/library/std/src/sys/sgx/ext/arch.rs b/library/std/src/sys/sgx/ext/arch.rs index 0c97a87e2e4..7488e7e5dc9 100644 --- a/library/std/src/sys/sgx/ext/arch.rs +++ b/library/std/src/sys/sgx/ext/arch.rs @@ -31,13 +31,13 @@ pub fn egetkey(request: &Align512<[u8; 512]>) -> Result<Align16<[u8; 16]>, u32> let mut out = MaybeUninit::uninit(); let error; - llvm_asm!( - "enclu" - : "={eax}"(error) - : "{eax}"(ENCLU_EGETKEY), - "{rbx}"(request), - "{rcx}"(out.as_mut_ptr()) - : "flags" + asm!( + "enclu", + inlateout("eax") ENCLU_EGETKEY => error, + in("rbx") request, + in("rcx") out.as_mut_ptr(), + // NOTE(#76738): ATT syntax is used to support LLVM 8 and 9. + options(att_syntax, nostack), ); match error { @@ -60,13 +60,14 @@ pub fn ereport( unsafe { let mut report = MaybeUninit::uninit(); - llvm_asm!( - "enclu" - : /* no output registers */ - : "{eax}"(ENCLU_EREPORT), - "{rbx}"(targetinfo), - "{rcx}"(reportdata), - "{rdx}"(report.as_mut_ptr()) + asm!( + "enclu", + in("eax") ENCLU_EREPORT, + in("rbx") targetinfo, + in("rcx") reportdata, + in("rdx") report.as_mut_ptr(), + // NOTE(#76738): ATT syntax is used to support LLVM 8 and 9. + options(att_syntax, preserves_flags, nostack), ); report.assume_init() diff --git a/library/std/src/sys/unix/args.rs b/library/std/src/sys/unix/args.rs index 9bc44a59482..f7c3f163718 100644 --- a/library/std/src/sys/unix/args.rs +++ b/library/std/src/sys/unix/args.rs @@ -80,13 +80,13 @@ mod imp { use crate::ptr; use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering}; - use crate::sys_common::mutex::Mutex; + use crate::sys_common::mutex::StaticMutex; static ARGC: AtomicIsize = AtomicIsize::new(0); static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut()); // We never call `ENV_LOCK.init()`, so it is UB to attempt to // acquire this mutex reentrantly! - static LOCK: Mutex = Mutex::new(); + static LOCK: StaticMutex = StaticMutex::new(); unsafe fn really_init(argc: isize, argv: *const *const u8) { let _guard = LOCK.lock(); diff --git a/library/std/src/sys/unix/ext/io.rs b/library/std/src/sys/unix/ext/io.rs index ec7a32b675c..fbea1aa9f2a 100644 --- a/library/std/src/sys/unix/ext/io.rs +++ b/library/std/src/sys/unix/ext/io.rs @@ -62,6 +62,25 @@ pub trait IntoRawFd { fn into_raw_fd(self) -> RawFd; } +#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] +impl AsRawFd for RawFd { + fn as_raw_fd(&self) -> RawFd { + *self + } +} +#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] +impl IntoRawFd for RawFd { + fn into_raw_fd(self) -> RawFd { + self + } +} +#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] +impl FromRawFd for RawFd { + unsafe fn from_raw_fd(fd: RawFd) -> RawFd { + fd + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl AsRawFd for fs::File { fn as_raw_fd(&self) -> RawFd { diff --git a/library/std/src/sys/unix/futex.rs b/library/std/src/sys/unix/futex.rs new file mode 100644 index 00000000000..e6f0c48c59b --- /dev/null +++ b/library/std/src/sys/unix/futex.rs @@ -0,0 +1,37 @@ +#![cfg(any(target_os = "linux", target_os = "android"))] + +use crate::convert::TryInto; +use crate::ptr::null; +use crate::sync::atomic::AtomicI32; +use crate::time::Duration; + +pub fn futex_wait(futex: &AtomicI32, expected: i32, timeout: Option<Duration>) { + let timespec = timeout.and_then(|d| { + Some(libc::timespec { + // Sleep forever if the timeout is longer than fits in a timespec. + tv_sec: d.as_secs().try_into().ok()?, + // This conversion never truncates, as subsec_nanos is always <1e9. + tv_nsec: d.subsec_nanos() as _, + }) + }); + unsafe { + libc::syscall( + libc::SYS_futex, + futex as *const AtomicI32, + libc::FUTEX_WAIT | libc::FUTEX_PRIVATE_FLAG, + expected, + timespec.as_ref().map_or(null(), |d| d as *const libc::timespec), + ); + } +} + +pub fn futex_wake(futex: &AtomicI32) { + unsafe { + libc::syscall( + libc::SYS_futex, + futex as *const AtomicI32, + libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG, + 1, + ); + } +} diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index eddf00d3979..776f4f18ecf 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -49,6 +49,7 @@ pub mod env; pub mod ext; pub mod fd; pub mod fs; +pub mod futex; pub mod io; #[cfg(target_os = "l4re")] mod l4re; @@ -75,6 +76,13 @@ pub use crate::sys_common::os_str_bytes as os_str; #[cfg(not(test))] pub fn init() { + // The standard streams might be closed on application startup. To prevent + // std::io::{stdin, stdout,stderr} objects from using other unrelated file + // resources opened later, we reopen standards streams when they are closed. + unsafe { + sanitize_standard_fds(); + } + // By default, some platforms will send a *signal* when an EPIPE error // would otherwise be delivered. This runtime doesn't install a SIGPIPE // handler, causing it to kill the program, which isn't exactly what we @@ -86,6 +94,62 @@ pub fn init() { reset_sigpipe(); } + cfg_if::cfg_if! { + if #[cfg(miri)] { + // The standard fds are always available in Miri. + unsafe fn sanitize_standard_fds() {} + } else if #[cfg(not(any( + target_os = "emscripten", + target_os = "fuchsia", + // The poll on Darwin doesn't set POLLNVAL for closed fds. + target_os = "macos", + target_os = "ios", + target_os = "redox", + )))] { + // In the case when all file descriptors are open, the poll has been + // observed to perform better than fcntl (on GNU/Linux). + unsafe fn sanitize_standard_fds() { + use crate::sys::os::errno; + let pfds: &mut [_] = &mut [ + libc::pollfd { fd: 0, events: 0, revents: 0 }, + libc::pollfd { fd: 1, events: 0, revents: 0 }, + libc::pollfd { fd: 2, events: 0, revents: 0 }, + ]; + while libc::poll(pfds.as_mut_ptr(), 3, 0) == -1 { + if errno() == libc::EINTR { + continue; + } + libc::abort(); + } + for pfd in pfds { + if pfd.revents & libc::POLLNVAL == 0 { + continue; + } + if libc::open("/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 + // Stderr. + libc::abort(); + } + } + } + } else if #[cfg(any(target_os = "macos", target_os = "ios", target_os = "redox"))] { + unsafe fn sanitize_standard_fds() { + use crate::sys::os::errno; + 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 { + libc::abort(); + } + } + } + } + } else { + unsafe fn sanitize_standard_fds() {} + } + } + #[cfg(not(any(target_os = "emscripten", target_os = "fuchsia")))] unsafe fn reset_sigpipe() { assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR); diff --git a/library/std/src/sys/unix/os.rs b/library/std/src/sys/unix/os.rs index 4aa61fc5bf6..c9f9ed01e12 100644 --- a/library/std/src/sys/unix/os.rs +++ b/library/std/src/sys/unix/os.rs @@ -21,7 +21,7 @@ use crate::slice; use crate::str; use crate::sys::cvt; use crate::sys::fd; -use crate::sys_common::mutex::{Mutex, MutexGuard}; +use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard}; use crate::vec; use libc::{c_char, c_int, c_void}; @@ -470,10 +470,9 @@ pub unsafe fn environ() -> *mut *const *const c_char { &mut environ } -pub unsafe fn env_lock() -> MutexGuard<'static> { - // We never call `ENV_LOCK.init()`, so it is UB to attempt to - // acquire this mutex reentrantly! - static ENV_LOCK: Mutex = Mutex::new(); +pub unsafe fn env_lock() -> StaticMutexGuard<'static> { + // It is UB to attempt to acquire this mutex reentrantly! + static ENV_LOCK: StaticMutex = StaticMutex::new(); ENV_LOCK.lock() } diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 50f5e78cf2a..ea7ea6f067c 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -67,7 +67,7 @@ impl Command { // pipe I/O up to PIPE_BUF bytes should be atomic, and then // we want to be sure we *don't* run at_exit destructors as // we're being torn down regardless - assert!(output.write(&bytes).is_ok()); + rtassert!(output.write(&bytes).is_ok()); libc::_exit(1) } n => n, diff --git a/library/std/src/sys/vxworks/args.rs b/library/std/src/sys/vxworks/args.rs index adff6c489bb..30cf7a707c7 100644 --- a/library/std/src/sys/vxworks/args.rs +++ b/library/std/src/sys/vxworks/args.rs @@ -57,11 +57,11 @@ mod imp { use crate::marker::PhantomData; use crate::ptr; - use crate::sys_common::mutex::Mutex; + use crate::sys_common::mutex::StaticMutex; static mut ARGC: isize = 0; static mut ARGV: *const *const u8 = ptr::null(); - static LOCK: Mutex = Mutex::new(); + static LOCK: StaticMutex = StaticMutex::new(); pub unsafe fn init(argc: isize, argv: *const *const u8) { let _guard = LOCK.lock(); diff --git a/library/std/src/sys/vxworks/ext/io.rs b/library/std/src/sys/vxworks/ext/io.rs index 25c6e26d96e..8b5a2d12af7 100644 --- a/library/std/src/sys/vxworks/ext/io.rs +++ b/library/std/src/sys/vxworks/ext/io.rs @@ -63,6 +63,25 @@ pub trait IntoRawFd { fn into_raw_fd(self) -> RawFd; } +#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] +impl AsRawFd for RawFd { + fn as_raw_fd(&self) -> RawFd { + *self + } +} +#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] +impl IntoRawFd for RawFd { + fn into_raw_fd(self) -> RawFd { + self + } +} +#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] +impl FromRawFd for RawFd { + unsafe fn from_raw_fd(fd: RawFd) -> RawFd { + fd + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl AsRawFd for fs::File { fn as_raw_fd(&self) -> RawFd { diff --git a/library/std/src/sys/vxworks/os.rs b/library/std/src/sys/vxworks/os.rs index 1fadf716135..08394a8d29d 100644 --- a/library/std/src/sys/vxworks/os.rs +++ b/library/std/src/sys/vxworks/os.rs @@ -10,7 +10,7 @@ use crate::path::{self, Path, PathBuf}; use crate::slice; use crate::str; use crate::sys::cvt; -use crate::sys_common::mutex::{Mutex, MutexGuard}; +use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard}; use libc::{self, c_char /*,c_void */, c_int}; /*use sys::fd; this one is probably important */ use crate::vec; @@ -212,10 +212,9 @@ pub unsafe fn environ() -> *mut *const *const c_char { &mut environ } -pub unsafe fn env_lock() -> MutexGuard<'static> { - // We never call `ENV_LOCK.init()`, so it is UB to attempt to - // acquire this mutex reentrantly! - static ENV_LOCK: Mutex = Mutex::new(); +pub unsafe fn env_lock() -> StaticMutexGuard<'static> { + // It is UB to attempt to acquire this mutex reentrantly! + static ENV_LOCK: StaticMutex = StaticMutex::new(); ENV_LOCK.lock() } diff --git a/library/std/src/sys/wasi/ext/io.rs b/library/std/src/sys/wasi/ext/io.rs index 4e8fa65eb20..661214e8f4c 100644 --- a/library/std/src/sys/wasi/ext/io.rs +++ b/library/std/src/sys/wasi/ext/io.rs @@ -52,6 +52,25 @@ pub trait IntoRawFd { fn into_raw_fd(self) -> RawFd; } +#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] +impl AsRawFd for RawFd { + fn as_raw_fd(&self) -> RawFd { + *self + } +} +#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] +impl IntoRawFd for RawFd { + fn into_raw_fd(self) -> RawFd { + self + } +} +#[stable(feature = "raw_fd_reflexive_traits", since = "1.48.0")] +impl FromRawFd for RawFd { + unsafe fn from_raw_fd(fd: RawFd) -> RawFd { + fd + } +} + impl AsRawFd for net::TcpStream { fn as_raw_fd(&self) -> RawFd { self.as_inner().fd().as_raw() diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index f440442ca30..559c4dc9c7c 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -1032,7 +1032,7 @@ extern "system" { // Functions that aren't available on every version of Windows that we support, // but we still use them and just provide some form of a fallback implementation. compat_fn! { - kernel32: + "kernel32": pub fn CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR, _lpTargetFileName: LPCWSTR, diff --git a/library/std/src/sys/windows/compat.rs b/library/std/src/sys/windows/compat.rs index d6d433f9d08..3f25f05e1b9 100644 --- a/library/std/src/sys/windows/compat.rs +++ b/library/std/src/sys/windows/compat.rs @@ -12,7 +12,6 @@ //! function is available but afterwards it's just a load and a jump. use crate::ffi::CString; -use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sys::c; pub fn lookup(module: &str, symbol: &str) -> Option<usize> { @@ -28,45 +27,69 @@ pub fn lookup(module: &str, symbol: &str) -> Option<usize> { } } -pub fn store_func(ptr: &AtomicUsize, module: &str, symbol: &str, fallback: usize) -> usize { - let value = lookup(module, symbol).unwrap_or(fallback); - ptr.store(value, Ordering::SeqCst); - value -} - macro_rules! compat_fn { - ($module:ident: $( + ($module:literal: $( $(#[$meta:meta])* - pub fn $symbol:ident($($argname:ident: $argtype:ty),*) - -> $rettype:ty { - $($body:expr);* - } + pub fn $symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $body:block )*) => ($( - #[allow(unused_variables)] $(#[$meta])* - pub unsafe fn $symbol($($argname: $argtype),*) -> $rettype { + pub mod $symbol { + use super::*; use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::mem; + type F = unsafe extern "system" fn($($argtype),*) -> $rettype; static PTR: AtomicUsize = AtomicUsize::new(0); + #[allow(unused_variables)] + unsafe extern "system" fn fallback($($argname: $argtype),*) -> $rettype $body + + /// This address is stored in `PTR` to incidate an unavailable API. + /// + /// This way, call() will end up calling fallback() if it is unavailable. + /// + /// This is a `static` to avoid rustc duplicating `fn fallback()` + /// into both load() and is_available(), which would break + /// is_available()'s comparison. By using the same static variable + /// in both places, they'll refer to the same (copy of the) + /// function. + /// + /// LLVM merging the address of fallback with other functions + /// (because of unnamed_addr) is fine, since it's only compared to + /// an address from GetProcAddress from an external dll. + static FALLBACK: F = fallback; + + #[cold] fn load() -> usize { - crate::sys::compat::store_func(&PTR, - stringify!($module), - stringify!($symbol), - fallback as usize) + // There is no locking here. It's okay if this is executed by multiple threads in + // parallel. `lookup` will result in the same value, and it's okay if they overwrite + // eachothers result as long as they do so atomically. We don't need any guarantees + // about memory ordering, as this involves just a single atomic variable which is + // not used to protect or order anything else. + let addr = crate::sys::compat::lookup($module, stringify!($symbol)) + .unwrap_or(FALLBACK as usize); + PTR.store(addr, Ordering::Relaxed); + addr } - unsafe extern "system" fn fallback($($argname: $argtype),*) - -> $rettype { - $($body);* + + fn addr() -> usize { + match PTR.load(Ordering::Relaxed) { + 0 => load(), + addr => addr, + } + } + + #[allow(dead_code)] + pub fn is_available() -> bool { + addr() != FALLBACK as usize } - let addr = match PTR.load(Ordering::SeqCst) { - 0 => load(), - n => n, - }; - mem::transmute::<usize, F>(addr)($($argname),*) + pub unsafe fn call($($argname: $argtype),*) -> $rettype { + mem::transmute::<usize, F>(addr())($($argname),*) + } } + + pub use $symbol::call as $symbol; )*) } diff --git a/library/std/src/sys/windows/mutex.rs b/library/std/src/sys/windows/mutex.rs index 1e09b95c872..e2aaca59fe2 100644 --- a/library/std/src/sys/windows/mutex.rs +++ b/library/std/src/sys/windows/mutex.rs @@ -23,7 +23,6 @@ use crate::cell::{Cell, UnsafeCell}; use crate::mem::{self, MaybeUninit}; use crate::sync::atomic::{AtomicUsize, Ordering}; use crate::sys::c; -use crate::sys::compat; pub struct Mutex { // This is either directly an SRWLOCK (if supported), or a Box<Inner> otherwise. @@ -40,8 +39,8 @@ struct Inner { #[derive(Clone, Copy)] enum Kind { - SRWLock = 1, - CriticalSection = 2, + SRWLock, + CriticalSection, } #[inline] @@ -130,21 +129,7 @@ impl Mutex { } fn kind() -> Kind { - static KIND: AtomicUsize = AtomicUsize::new(0); - - let val = KIND.load(Ordering::SeqCst); - if val == Kind::SRWLock as usize { - return Kind::SRWLock; - } else if val == Kind::CriticalSection as usize { - return Kind::CriticalSection; - } - - let ret = match compat::lookup("kernel32", "AcquireSRWLockExclusive") { - None => Kind::CriticalSection, - Some(..) => Kind::SRWLock, - }; - KIND.store(ret as usize, Ordering::SeqCst); - ret + if c::AcquireSRWLockExclusive::is_available() { Kind::SRWLock } else { Kind::CriticalSection } } pub struct ReentrantMutex { diff --git a/library/std/src/sys_common/alloc.rs b/library/std/src/sys_common/alloc.rs index f22476be325..6c1bc0d839a 100644 --- a/library/std/src/sys_common/alloc.rs +++ b/library/std/src/sys_common/alloc.rs @@ -12,6 +12,7 @@ use crate::ptr; target_arch = "mips", target_arch = "powerpc", target_arch = "powerpc64", + target_arch = "sparc", target_arch = "asmjs", target_arch = "wasm32", target_arch = "hexagon", diff --git a/library/std/src/sys_common/at_exit_imp.rs b/library/std/src/sys_common/at_exit_imp.rs index 6b799db856e..90d5d3a7898 100644 --- a/library/std/src/sys_common/at_exit_imp.rs +++ b/library/std/src/sys_common/at_exit_imp.rs @@ -4,7 +4,7 @@ use crate::mem; use crate::ptr; -use crate::sys_common::mutex::Mutex; +use crate::sys_common::mutex::StaticMutex; type Queue = Vec<Box<dyn FnOnce()>>; @@ -12,9 +12,8 @@ type Queue = Vec<Box<dyn FnOnce()>>; // on poisoning and this module needs to operate at a lower level than requiring // the thread infrastructure to be in place (useful on the borders of // initialization/destruction). -// We never call `LOCK.init()`, so it is UB to attempt to -// acquire this mutex reentrantly! -static LOCK: Mutex = Mutex::new(); +// It is UB to attempt to acquire this mutex reentrantly! +static LOCK: StaticMutex = StaticMutex::new(); static mut QUEUE: *mut Queue = ptr::null_mut(); const DONE: *mut Queue = 1_usize as *mut _; diff --git a/library/std/src/sys_common/condvar.rs b/library/std/src/sys_common/condvar.rs index f9611bc6f7b..a48d301f812 100644 --- a/library/std/src/sys_common/condvar.rs +++ b/library/std/src/sys_common/condvar.rs @@ -1,5 +1,5 @@ use crate::sys::condvar as imp; -use crate::sys_common::mutex::{self, Mutex}; +use crate::sys_common::mutex::MovableMutex; use crate::time::Duration; /// An OS-based condition variable. @@ -46,8 +46,8 @@ impl Condvar { /// Behavior is also undefined if more than one mutex is used concurrently /// on this condition variable. #[inline] - pub unsafe fn wait(&self, mutex: &Mutex) { - self.0.wait(mutex::raw(mutex)) + pub unsafe fn wait(&self, mutex: &MovableMutex) { + self.0.wait(mutex.raw()) } /// Waits for a signal on the specified mutex with a timeout duration @@ -57,8 +57,8 @@ impl Condvar { /// Behavior is also undefined if more than one mutex is used concurrently /// on this condition variable. #[inline] - pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { - self.0.wait_timeout(mutex::raw(mutex), dur) + pub unsafe fn wait_timeout(&self, mutex: &MovableMutex, dur: Duration) -> bool { + self.0.wait_timeout(mutex.raw(), dur) } /// Deallocates all resources associated with this condition variable. diff --git a/library/std/src/sys_common/mod.rs b/library/std/src/sys_common/mod.rs index 28cdfefb12a..234b257aa92 100644 --- a/library/std/src/sys_common/mod.rs +++ b/library/std/src/sys_common/mod.rs @@ -66,6 +66,7 @@ pub mod thread; pub mod thread_info; pub mod thread_local_dtor; pub mod thread_local_key; +pub mod thread_parker; pub mod util; pub mod wtf8; diff --git a/library/std/src/sys_common/mutex.rs b/library/std/src/sys_common/mutex.rs index e66d8994147..93ec7d89bc5 100644 --- a/library/std/src/sys_common/mutex.rs +++ b/library/std/src/sys_common/mutex.rs @@ -1,101 +1,106 @@ use crate::sys::mutex as imp; -/// An OS-based mutual exclusion lock. +/// An OS-based mutual exclusion lock, meant for use in static variables. +/// +/// This mutex has a const constructor ([`StaticMutex::new`]), does not +/// implement `Drop` to cleanup resources, and causes UB when moved or used +/// reentrantly. +/// +/// This mutex does not implement poisoning. /// -/// This is the thinnest cross-platform wrapper around OS mutexes. All usage of -/// this mutex is unsafe and it is recommended to instead use the safe wrapper -/// at the top level of the crate instead of this type. -pub struct Mutex(imp::Mutex); +/// This is a wrapper around `imp::Mutex` that does *not* call `init()` and +/// `destroy()`. +pub struct StaticMutex(imp::Mutex); -unsafe impl Sync for Mutex {} +unsafe impl Sync for StaticMutex {} -impl Mutex { +impl StaticMutex { /// Creates a new mutex for use. /// /// Behavior is undefined if the mutex is moved after it is /// first used with any of the functions below. - /// Also, until `init` is called, behavior is undefined if this - /// mutex is ever used reentrantly, i.e., `raw_lock` or `try_lock` - /// are called by the thread currently holding the lock. + /// Also, the behavior is undefined if this mutex is ever used reentrantly, + /// i.e., `lock` is called by the thread currently holding the lock. #[rustc_const_stable(feature = "const_sys_mutex_new", since = "1.0.0")] - pub const fn new() -> Mutex { - Mutex(imp::Mutex::new()) + pub const fn new() -> Self { + Self(imp::Mutex::new()) } - /// Prepare the mutex for use. + /// Calls raw_lock() and then returns an RAII guard to guarantee the mutex + /// will be unlocked. /// - /// This should be called once the mutex is at a stable memory address. - /// If called, this must be the very first thing that happens to the mutex. - /// Calling it in parallel with or after any operation (including another - /// `init()`) is undefined behavior. + /// It is undefined behaviour to call this function while locked, or if the + /// mutex has been moved since the last time this was called. #[inline] - pub unsafe fn init(&mut self) { - self.0.init() + pub unsafe fn lock(&self) -> StaticMutexGuard<'_> { + self.0.lock(); + StaticMutexGuard(&self.0) } +} - /// Locks the mutex blocking the current thread until it is available. - /// - /// Behavior is undefined if the mutex has been moved between this and any - /// previous function call. +#[must_use] +pub struct StaticMutexGuard<'a>(&'a imp::Mutex); + +impl Drop for StaticMutexGuard<'_> { #[inline] - pub unsafe fn raw_lock(&self) { - self.0.lock() + fn drop(&mut self) { + unsafe { + self.0.unlock(); + } } +} - /// Calls raw_lock() and then returns an RAII guard to guarantee the mutex - /// will be unlocked. +/// An OS-based mutual exclusion lock. +/// +/// This mutex does *not* have a const constructor, cleans up its resources in +/// its `Drop` implementation, may safely be moved (when not borrowed), and +/// does not cause UB when used reentrantly. +/// +/// This mutex does not implement poisoning. +/// +/// This is a wrapper around `Box<imp::Mutex>`, to allow the object to be moved +/// without moving the raw mutex. +pub struct MovableMutex(Box<imp::Mutex>); + +unsafe impl Sync for MovableMutex {} + +impl MovableMutex { + /// Creates a new mutex. + pub fn new() -> Self { + let mut mutex = box imp::Mutex::new(); + unsafe { mutex.init() }; + Self(mutex) + } + + pub(crate) fn raw(&self) -> &imp::Mutex { + &self.0 + } + + /// Locks the mutex blocking the current thread until it is available. #[inline] - pub unsafe fn lock(&self) -> MutexGuard<'_> { - self.raw_lock(); - MutexGuard(&self.0) + pub fn raw_lock(&self) { + unsafe { self.0.lock() } } /// Attempts to lock the mutex without blocking, returning whether it was /// successfully acquired or not. - /// - /// Behavior is undefined if the mutex has been moved between this and any - /// previous function call. #[inline] - pub unsafe fn try_lock(&self) -> bool { - self.0.try_lock() + pub fn try_lock(&self) -> bool { + unsafe { self.0.try_lock() } } /// Unlocks the mutex. /// /// Behavior is undefined if the current thread does not actually hold the /// mutex. - /// - /// Consider switching from the pair of raw_lock() and raw_unlock() to - /// lock() whenever possible. #[inline] pub unsafe fn raw_unlock(&self) { self.0.unlock() } - - /// Deallocates all resources associated with this mutex. - /// - /// Behavior is undefined if there are current or will be future users of - /// this mutex. - #[inline] - pub unsafe fn destroy(&self) { - self.0.destroy() - } } -// not meant to be exported to the outside world, just the containing module -pub fn raw(mutex: &Mutex) -> &imp::Mutex { - &mutex.0 -} - -#[must_use] -/// A simple RAII utility for the above Mutex without the poisoning semantics. -pub struct MutexGuard<'a>(&'a imp::Mutex); - -impl Drop for MutexGuard<'_> { - #[inline] +impl Drop for MovableMutex { fn drop(&mut self) { - unsafe { - self.0.unlock(); - } + unsafe { self.0.destroy() }; } } diff --git a/library/std/src/sys_common/thread_local_key.rs b/library/std/src/sys_common/thread_local_key.rs index 676eadd1fac..dbcb7b36265 100644 --- a/library/std/src/sys_common/thread_local_key.rs +++ b/library/std/src/sys_common/thread_local_key.rs @@ -53,7 +53,7 @@ mod tests; use crate::sync::atomic::{self, AtomicUsize, Ordering}; use crate::sys::thread_local_key as imp; -use crate::sys_common::mutex::Mutex; +use crate::sys_common::mutex::StaticMutex; /// A type for TLS keys that are statically allocated. /// @@ -157,7 +157,7 @@ impl StaticKey { if imp::requires_synchronized_create() { // We never call `INIT_LOCK.init()`, so it is UB to attempt to // acquire this mutex reentrantly! - static INIT_LOCK: Mutex = Mutex::new(); + static INIT_LOCK: StaticMutex = StaticMutex::new(); let _guard = INIT_LOCK.lock(); let mut key = self.key.load(Ordering::SeqCst); if key == 0 { diff --git a/library/std/src/sys_common/thread_parker/futex.rs b/library/std/src/sys_common/thread_parker/futex.rs new file mode 100644 index 00000000000..a5d4927dcc5 --- /dev/null +++ b/library/std/src/sys_common/thread_parker/futex.rs @@ -0,0 +1,93 @@ +use crate::sync::atomic::AtomicI32; +use crate::sync::atomic::Ordering::{Acquire, Release}; +use crate::sys::futex::{futex_wait, futex_wake}; +use crate::time::Duration; + +const PARKED: i32 = -1; +const EMPTY: i32 = 0; +const NOTIFIED: i32 = 1; + +pub struct Parker { + state: AtomicI32, +} + +// 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 checking for this state in park(). +impl Parker { + #[inline] + pub const fn new() -> Self { + Parker { state: AtomicI32::new(EMPTY) } + } + + // Assumes this is only called by the thread that owns the Parker, + // which means that `self.state != PARKED`. + pub unsafe fn park(&self) { + // Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the + // first case. + if self.state.fetch_sub(1, Acquire) == NOTIFIED { + return; + } + loop { + // Wait for something to happen, assuming it's still set to PARKED. + futex_wait(&self.state, PARKED, None); + // Change NOTIFIED=>EMPTY and return in that case. + if self.state.compare_and_swap(NOTIFIED, EMPTY, Acquire) == NOTIFIED { + return; + } else { + // Spurious wake up. We loop to try again. + } + } + } + + // Assumes this is only called by the thread that owns the Parker, + // which means that `self.state != PARKED`. + pub unsafe fn park_timeout(&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; + } + // Wait for something to happen, assuming it's still set to PARKED. + futex_wait(&self.state, PARKED, Some(timeout)); + // This is not just a store, because we need to establish a + // release-acquire ordering with unpark(). + if self.state.swap(EMPTY, Acquire) == NOTIFIED { + // Woke up because of unpark(). + } else { + // Timeout or spurious wake up. + // We return either way, because we can't easily tell if it was the + // timeout or not. + } + } + + #[inline] + pub fn unpark(&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 { + futex_wake(&self.state); + } + } +} diff --git a/library/std/src/sys_common/thread_parker/generic.rs b/library/std/src/sys_common/thread_parker/generic.rs new file mode 100644 index 00000000000..14cfa958e5e --- /dev/null +++ b/library/std/src/sys_common/thread_parker/generic.rs @@ -0,0 +1,119 @@ +//! Parker implementaiton based on a Mutex and Condvar. + +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 { + pub fn new() -> Self { + Parker { state: AtomicUsize::new(EMPTY), lock: Mutex::new(()), cvar: Condvar::new() } + } + + // This implementaiton doesn't require `unsafe`, but other implementations + // may assume this is only called by the thread that owns the Parker. + pub unsafe fn park(&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 implementaiton doesn't require `unsafe`, but other implementations + // may assume this is only called by the thread that owns the Parker. + pub unsafe fn park_timeout(&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), + } + } + + pub fn unpark(&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 new file mode 100644 index 00000000000..23c17c8e2cf --- /dev/null +++ b/library/std/src/sys_common/thread_parker/mod.rs @@ -0,0 +1,9 @@ +cfg_if::cfg_if! { + if #[cfg(any(target_os = "linux", target_os = "android"))] { + mod futex; + pub use futex::Parker; + } else { + mod generic; + pub use generic::Parker; + } +} diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 8c353e2484e..087175bb92a 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -159,13 +159,12 @@ use crate::num::NonZeroU64; use crate::panic; use crate::panicking; use crate::str; -use crate::sync::atomic::AtomicUsize; -use crate::sync::atomic::Ordering::SeqCst; -use crate::sync::{Arc, Condvar, Mutex}; +use crate::sync::Arc; use crate::sys::thread as imp; use crate::sys_common::mutex; use crate::sys_common::thread; use crate::sys_common::thread_info; +use crate::sys_common::thread_parker::Parker; use crate::sys_common::{AsInner, IntoInner}; use crate::time::Duration; @@ -667,6 +666,8 @@ pub fn current() -> Thread { /// /// [`channel`]: crate::sync::mpsc /// [`join`]: JoinHandle::join +/// [`Condvar`]: crate::sync::Condvar +/// [`Mutex`]: crate::sync::Mutex #[stable(feature = "rust1", since = "1.0.0")] pub fn yield_now() { imp::Thread::yield_now() @@ -712,6 +713,8 @@ pub fn yield_now() { /// panic!() /// } /// ``` +/// +/// [Mutex]: crate::sync::Mutex #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn panicking() -> bool { @@ -779,11 +782,6 @@ pub fn sleep(dur: Duration) { imp::Thread::sleep(dur) } -// constants for park/unpark -const EMPTY: usize = 0; -const PARKED: usize = 1; -const NOTIFIED: usize = 2; - /// Blocks unless or until the current thread's token is made available. /// /// A call to `park` does not guarantee that the thread will remain parked @@ -870,45 +868,11 @@ const NOTIFIED: usize = 2; /// /// [`unpark`]: Thread::unpark /// [`thread::park_timeout`]: park_timeout -// -// The implementation currently uses the trivial strategy of a Mutex+Condvar -// with wakeup flag, which does not actually allow spurious wakeups. In the -// future, this will be implemented in a more efficient way, perhaps along the lines of -// http://cr.openjdk.java.net/~stefank/6989984.1/raw_files/new/src/os/linux/vm/os_linux.cpp -// or futuxes, and in either case may allow spurious wakeups. #[stable(feature = "rust1", since = "1.0.0")] pub fn park() { - let thread = current(); - - // If we were previously notified then we consume this notification and - // return quickly. - if thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() { - return; - } - - // Otherwise we need to coordinate going to sleep - let mut m = thread.inner.lock.lock().unwrap(); - match thread.inner.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 = thread.inner.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 = thread.inner.cvar.wait(m).unwrap(); - match thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) { - Ok(_) => return, // got a notification - Err(_) => {} // spurious wakeup, go back to sleep - } + // SAFETY: park_timeout is called on the parker owned by this thread. + unsafe { + current().inner.parker.park(); } } @@ -970,35 +934,9 @@ pub fn park_timeout_ms(ms: u32) { /// ``` #[stable(feature = "park_timeout", since = "1.4.0")] pub fn park_timeout(dur: Duration) { - let thread = current(); - - // Like `park` above we have a fast path for an already-notified thread, and - // afterwards we start coordinating for a sleep. - // return quickly. - if thread.inner.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() { - return; - } - let m = thread.inner.lock.lock().unwrap(); - match thread.inner.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) { - Ok(_) => {} - Err(NOTIFIED) => { - // We must read again here, see `park`. - let old = thread.inner.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) = thread.inner.cvar.wait_timeout(m, dur).unwrap(); - match thread.inner.state.swap(EMPTY, SeqCst) { - NOTIFIED => {} // got a notification, hurray! - PARKED => {} // no notification, alas - n => panic!("inconsistent park_timeout state: {}", n), + // SAFETY: park_timeout is called on the parker owned by this thread. + unsafe { + current().inner.parker.park_timeout(dur); } } @@ -1034,9 +972,8 @@ pub struct ThreadId(NonZeroU64); impl ThreadId { // Generate a new unique thread ID. fn new() -> ThreadId { - // We never call `GUARD.init()`, so it is UB to attempt to - // acquire this mutex reentrantly! - static GUARD: mutex::Mutex = mutex::Mutex::new(); + // It is UB to attempt to acquire this mutex reentrantly! + static GUARD: mutex::StaticMutex = mutex::StaticMutex::new(); static mut COUNTER: u64 = 1; unsafe { @@ -1077,11 +1014,7 @@ impl ThreadId { struct Inner { name: Option<CString>, // Guaranteed to be UTF-8 id: ThreadId, - - // state for thread park/unpark - state: AtomicUsize, - lock: Mutex<()>, - cvar: Condvar, + parker: Parker, } #[derive(Clone)] @@ -1115,13 +1048,7 @@ impl Thread { let cname = name.map(|n| CString::new(n).expect("thread name may not contain interior null bytes")); Thread { - inner: Arc::new(Inner { - name: cname, - id: ThreadId::new(), - state: AtomicUsize::new(EMPTY), - lock: Mutex::new(()), - cvar: Condvar::new(), - }), + inner: Arc::new(Inner { name: cname, id: ThreadId::new(), parker: Parker::new() }), } } @@ -1156,33 +1083,9 @@ impl Thread { /// parked_thread.join().unwrap(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn unpark(&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.inner.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.inner.lock.lock().unwrap()); - self.inner.cvar.notify_one() + self.inner.parker.unpark(); } /// Gets the thread's unique identifier. diff --git a/library/std/src/time.rs b/library/std/src/time.rs index 18e38c6299b..e7df3841147 100644 --- a/library/std/src/time.rs +++ b/library/std/src/time.rs @@ -20,7 +20,7 @@ use crate::error::Error; use crate::fmt; use crate::ops::{Add, AddAssign, Sub, SubAssign}; use crate::sys::time; -use crate::sys_common::mutex::Mutex; +use crate::sys_common::mutex::StaticMutex; use crate::sys_common::FromInner; #[stable(feature = "time", since = "1.3.0")] @@ -243,7 +243,7 @@ impl Instant { return Instant(os_now); } - static LOCK: Mutex = Mutex::new(); + static LOCK: StaticMutex = StaticMutex::new(); static mut LAST_NOW: time::Instant = time::Instant::zero(); unsafe { let _lock = LOCK.lock(); |
