about summary refs log tree commit diff
path: root/library/std/src/thread
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-10-11 11:03:12 +0000
committerbors <bors@rust-lang.org>2022-10-11 11:03:12 +0000
commitbb93450ec4af83c20e9ba6c8e575aca55423001a (patch)
tree1f8ec522088447d95945bf12ac8b5fcadc34f95c /library/std/src/thread
parent1e926f06528ecb2503f026e2fd53cb735d487b10 (diff)
parent3011538b80a52aedb2d7374798ffa35ea742d475 (diff)
downloadrust-bb93450ec4af83c20e9ba6c8e575aca55423001a.tar.gz
rust-bb93450ec4af83c20e9ba6c8e575aca55423001a.zip
Auto merge of #102915 - JohnTitor:rollup-5ht99y1, r=JohnTitor
Rollup of 7 pull requests

Successful merges:

 - #102258 (Remove unused variable in float formatting.)
 - #102277 (Consistently write `RwLock`)
 - #102412 (Never panic in `thread::park` and `thread::park_timeout`)
 - #102589 (scoped threads: pass closure through MaybeUninit to avoid invalid dangling references)
 - #102625 (fix backtrace small typo)
 - #102859 (Move lifetime resolution module to rustc_hir_analysis.)
 - #102898 (rustdoc: remove unneeded `<div>` wrapper from sidebar DOM)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'library/std/src/thread')
-rw-r--r--library/std/src/thread/mod.rs55
1 files changed, 53 insertions, 2 deletions
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 2de7da3793f..55110c44b6e 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -160,7 +160,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;
@@ -499,6 +499,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 +531,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 +545,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 {
@@ -851,10 +884,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
 ///
@@ -939,10 +984,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`].
@@ -1003,10 +1051,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);
 }
 
 ////////////////////////////////////////////////////////////////////////////////