diff options
Diffstat (limited to 'library/std/src/thread')
-rw-r--r-- | library/std/src/thread/local.rs | 8 | ||||
-rw-r--r-- | library/std/src/thread/mod.rs | 114 | ||||
-rw-r--r-- | library/std/src/thread/tests.rs | 22 |
3 files changed, 115 insertions, 29 deletions
diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index 8aedfc4a6..5d267891b 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -95,6 +95,7 @@ use crate::fmt; /// [loader lock]: https://docs.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-best-practices /// [`JoinHandle::join`]: crate::thread::JoinHandle::join /// [`with`]: LocalKey::with +#[cfg_attr(not(test), rustc_diagnostic_item = "LocalKey")] #[stable(feature = "rust1", since = "1.0.0")] pub struct LocalKey<T: 'static> { // This outer `LocalKey<T>` type is what's going to be stored in statics, @@ -900,7 +901,7 @@ pub mod statik { } #[doc(hidden)] -#[cfg(target_thread_local)] +#[cfg(all(target_thread_local, not(all(target_family = "wasm", not(target_feature = "atomics"))),))] pub mod fast { use super::lazy::LazyKeyInner; use crate::cell::Cell; @@ -1036,7 +1037,10 @@ pub mod fast { } #[doc(hidden)] -#[cfg(not(target_thread_local))] +#[cfg(all( + not(target_thread_local), + not(all(target_family = "wasm", not(target_feature = "atomics"))), +))] pub mod os { use super::lazy::LazyKeyInner; use crate::cell::Cell; diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index ceea6986e..05023df1b 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -150,6 +150,8 @@ #![stable(feature = "rust1", since = "1.0.0")] #![deny(unsafe_op_in_unsafe_fn)] +// Under `test`, `__FastLocalKeyInner` seems unused. +#![cfg_attr(test, allow(dead_code))] #[cfg(all(test, not(target_os = "emscripten")))] mod tests; @@ -160,7 +162,7 @@ use crate::ffi::{CStr, CString}; use crate::fmt; use crate::io; use crate::marker::PhantomData; -use crate::mem; +use crate::mem::{self, forget}; use crate::num::NonZeroU64; use crate::num::NonZeroUsize; use crate::panic; @@ -192,32 +194,40 @@ pub use scoped::{scope, Scope, ScopedJoinHandle}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::local::{AccessError, LocalKey}; -// Select the type used by the thread_local! macro to access TLS keys. There -// are three types: "static", "fast", "OS". The "OS" thread local key +// Provide the type used by the thread_local! macro to access TLS keys. This +// needs to be kept in sync with the macro itself (in `local.rs`). +// There are three types: "static", "fast", "OS". The "OS" thread local key // type is accessed via platform-specific API calls and is slow, while the "fast" // key type is accessed via code generated via LLVM, where TLS keys are set up // by the elf linker. "static" is for single-threaded platforms where a global // static is sufficient. #[unstable(feature = "libstd_thread_internals", issue = "none")] -#[cfg(target_thread_local)] #[cfg(not(test))] +#[cfg(all( + target_thread_local, + not(all(target_family = "wasm", not(target_feature = "atomics"))), +))] #[doc(hidden)] pub use self::local::fast::Key as __FastLocalKeyInner; -#[unstable(feature = "libstd_thread_internals", issue = "none")] -#[cfg(target_thread_local)] -#[cfg(test)] // when building for tests, use real std's key -pub use realstd::thread::__FastLocalKeyInner; +// when building for tests, use real std's type #[unstable(feature = "libstd_thread_internals", issue = "none")] -#[cfg(target_thread_local)] #[cfg(test)] -pub use self::local::fast::Key as __FastLocalKeyInnerUnused; // we import this anyway to silence 'unused' warnings +#[cfg(all( + target_thread_local, + not(all(target_family = "wasm", not(target_feature = "atomics"))), +))] +pub use realstd::thread::__FastLocalKeyInner; #[unstable(feature = "libstd_thread_internals", issue = "none")] +#[cfg(all( + not(target_thread_local), + not(all(target_family = "wasm", not(target_feature = "atomics"))), +))] #[doc(hidden)] -#[cfg(not(target_thread_local))] pub use self::local::os::Key as __OsLocalKeyInner; + #[unstable(feature = "libstd_thread_internals", issue = "none")] #[cfg(all(target_family = "wasm", not(target_feature = "atomics")))] #[doc(hidden)] @@ -499,6 +509,31 @@ impl Builder { let output_capture = crate::io::set_output_capture(None); crate::io::set_output_capture(output_capture.clone()); + // Pass `f` in `MaybeUninit` because actually that closure might *run longer than the lifetime of `F`*. + // See <https://github.com/rust-lang/rust/issues/101983> for more details. + // To prevent leaks we use a wrapper that drops its contents. + #[repr(transparent)] + struct MaybeDangling<T>(mem::MaybeUninit<T>); + impl<T> MaybeDangling<T> { + fn new(x: T) -> Self { + MaybeDangling(mem::MaybeUninit::new(x)) + } + fn into_inner(self) -> T { + // SAFETY: we are always initiailized. + let ret = unsafe { self.0.assume_init_read() }; + // Make sure we don't drop. + mem::forget(self); + ret + } + } + impl<T> Drop for MaybeDangling<T> { + fn drop(&mut self) { + // SAFETY: we are always initiailized. + unsafe { self.0.assume_init_drop() }; + } + } + + let f = MaybeDangling::new(f); let main = move || { if let Some(name) = their_thread.cname() { imp::Thread::set_name(name); @@ -506,6 +541,8 @@ impl Builder { crate::io::set_output_capture(output_capture); + // SAFETY: we constructed `f` initialized. + let f = f.into_inner(); // SAFETY: the stack guard passed is the one for the current thread. // This means the current thread's stack and the new thread's stack // are properly set and protected from each other. @@ -518,6 +555,12 @@ impl Builder { // same `JoinInner` as this closure meaning the mutation will be // safe (not modify it and affect a value far away). unsafe { *their_packet.result.get() = Some(try_result) }; + // Here `their_packet` gets dropped, and if this is the last `Arc` for that packet that + // will call `decrement_num_running_threads` and therefore signal that this thread is + // done. + drop(their_packet); + // Here, the lifetime `'a` and even `'scope` can end. `main` keeps running for a bit + // after that before returning itself. }; if let Some(scope_data) = &my_packet.scope { @@ -779,6 +822,8 @@ pub fn panicking() -> bool { panicking::panicking() } +/// Use [`sleep`]. +/// /// Puts the current thread to sleep for at least the specified amount of time. /// /// The thread may sleep longer than the duration specified due to scheduling @@ -849,10 +894,22 @@ pub fn sleep(dur: Duration) { imp::Thread::sleep(dur) } +/// Used to ensure that `park` and `park_timeout` do not unwind, as that can +/// cause undefined behaviour if not handled correctly (see #102398 for context). +struct PanicGuard; + +impl Drop for PanicGuard { + fn drop(&mut self) { + rtabort!("an irrecoverable error occurred while synchronizing threads") + } +} + /// 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 -/// forever, and callers should be prepared for this possibility. +/// forever, and callers should be prepared for this possibility. However, +/// it is guaranteed that this function will not panic (it may abort the +/// process if the implementation encounters some rare errors). /// /// # park and unpark /// @@ -937,10 +994,13 @@ pub fn sleep(dur: Duration) { /// [`thread::park_timeout`]: park_timeout #[stable(feature = "rust1", since = "1.0.0")] pub fn park() { + let guard = PanicGuard; // SAFETY: park_timeout is called on the parker owned by this thread. unsafe { current().inner.as_ref().parker().park(); } + // No panic occurred, do not abort. + forget(guard); } /// Use [`park_timeout`]. @@ -1001,10 +1061,13 @@ pub fn park_timeout_ms(ms: u32) { /// ``` #[stable(feature = "park_timeout", since = "1.4.0")] pub fn park_timeout(dur: Duration) { + let guard = PanicGuard; // SAFETY: park_timeout is called on the parker owned by this thread. unsafe { current().inner.as_ref().parker().park_timeout(dur); } + // No panic occurred, do not abort. + forget(guard); } //////////////////////////////////////////////////////////////////////////////// @@ -1065,24 +1128,21 @@ impl ThreadId { } } } else { - use crate::sys_common::mutex::StaticMutex; + use crate::sync::{Mutex, PoisonError}; - // It is UB to attempt to acquire this mutex reentrantly! - static GUARD: StaticMutex = StaticMutex::new(); - static mut COUNTER: u64 = 0; + static COUNTER: Mutex<u64> = Mutex::new(0); - unsafe { - let guard = GUARD.lock(); + let mut counter = COUNTER.lock().unwrap_or_else(PoisonError::into_inner); + let Some(id) = counter.checked_add(1) else { + // in case the panic handler ends up calling `ThreadId::new()`, + // avoid reentrant lock acquire. + drop(counter); + exhausted(); + }; - let Some(id) = COUNTER.checked_add(1) else { - drop(guard); // in case the panic handler ends up calling `ThreadId::new()`, avoid reentrant lock acquire. - exhausted(); - }; - - COUNTER = id; - drop(guard); - ThreadId(NonZeroU64::new(id).unwrap()) - } + *counter = id; + drop(counter); + ThreadId(NonZeroU64::new(id).unwrap()) } } } diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index 777964f04..6c9ce6fa0 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -276,6 +276,28 @@ fn test_try_panic_any_message_unit_struct() { } #[test] +fn test_park_unpark_before() { + for _ in 0..10 { + thread::current().unpark(); + thread::park(); + } +} + +#[test] +fn test_park_unpark_called_other_thread() { + for _ in 0..10 { + let th = thread::current(); + + let _guard = thread::spawn(move || { + super::sleep(Duration::from_millis(50)); + th.unpark(); + }); + + thread::park(); + } +} + +#[test] fn test_park_timeout_unpark_before() { for _ in 0..10 { thread::current().unpark(); |