about summary refs log tree commit diff
path: root/library/std/src/thread
diff options
context:
space:
mode:
author许杰友 Jieyou Xu (Joe) <39484203+jieyouxu@users.noreply.github.com>2024-11-25 00:39:03 +0800
committerGitHub <noreply@github.com>2024-11-25 00:39:03 +0800
commit31b4023e24e82c7c0ca6b9637de1b14d46d02b92 (patch)
tree6151658caf22a6b98e10c622d684094c5492fdc6 /library/std/src/thread
parentf5d18576856ef45d1e47de79889ae7db9d1afa29 (diff)
parent5a856b82f3a1bf335e0e62d92c800d8436977af1 (diff)
downloadrust-31b4023e24e82c7c0ca6b9637de1b14d46d02b92.tar.gz
rust-31b4023e24e82c7c0ca6b9637de1b14d46d02b92.zip
Rollup merge of #132730 - joboet:after_main_sync, r=Noratrieb
std: allow after-main use of synchronization primitives

By creating an unnamed thread handle when the actual one has already been destroyed, synchronization primitives using thread parking can be used even outside the Rust runtime.

This also fixes an inefficiency in the queue-based `RwLock`: if `thread::current` was not initialized yet, it will create a new handle on every parking attempt without initializing `thread::current`. The private `current_or_unnamed` function introduced here fixes this.
Diffstat (limited to 'library/std/src/thread')
-rw-r--r--library/std/src/thread/current.rs17
-rw-r--r--library/std/src/thread/mod.rs15
-rw-r--r--library/std/src/thread/scoped.rs7
3 files changed, 33 insertions, 6 deletions
diff --git a/library/std/src/thread/current.rs b/library/std/src/thread/current.rs
index e6eb90c4c30..b9b959f9894 100644
--- a/library/std/src/thread/current.rs
+++ b/library/std/src/thread/current.rs
@@ -165,6 +165,23 @@ pub(crate) fn try_current() -> Option<Thread> {
     }
 }
 
+/// Gets a handle to the thread that invokes it. If the handle stored in thread-
+/// local storage was already destroyed, this creates a new unnamed temporary
+/// handle to allow thread parking in nearly all situations.
+pub(crate) fn current_or_unnamed() -> Thread {
+    let current = CURRENT.get();
+    if current > DESTROYED {
+        unsafe {
+            let current = ManuallyDrop::new(Thread::from_raw(current));
+            (*current).clone()
+        }
+    } else if current == DESTROYED {
+        Thread::new_unnamed(id::get_or_init())
+    } else {
+        init_current(current)
+    }
+}
+
 /// Gets a handle to the thread that invokes it.
 ///
 /// # Examples
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 2c2cc58a9dd..2ff44fcd4c6 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -186,7 +186,7 @@ mod current;
 
 #[stable(feature = "rust1", since = "1.0.0")]
 pub use current::current;
-pub(crate) use current::{current_id, drop_current, set_current, try_current};
+pub(crate) use current::{current_id, current_or_unnamed, drop_current, set_current, try_current};
 
 mod spawnhook;
 
@@ -1146,9 +1146,9 @@ 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.
+    // SAFETY: park_timeout is called on a handle owned by this thread.
     unsafe {
-        current().0.parker().park_timeout(dur);
+        current().park_timeout(dur);
     }
     // No panic occurred, do not abort.
     forget(guard);
@@ -1446,6 +1446,15 @@ impl Thread {
         unsafe { self.0.parker().park() }
     }
 
+    /// Like the public [`park_timeout`], but callable on any handle. This is
+    /// used to allow parking in TLS destructors.
+    ///
+    /// # Safety
+    /// May only be called from the thread to which this handle belongs.
+    pub(crate) unsafe fn park_timeout(&self, dur: Duration) {
+        unsafe { self.0.parker().park_timeout(dur) }
+    }
+
     /// Atomically makes the handle's token available if it is not already.
     ///
     /// Every thread is equipped with some basic low-level blocking support, via
diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs
index a14446b3515..0033fc3a732 100644
--- a/library/std/src/thread/scoped.rs
+++ b/library/std/src/thread/scoped.rs
@@ -1,4 +1,4 @@
-use super::{Builder, JoinInner, Result, Thread, current, park};
+use super::{Builder, JoinInner, Result, Thread, current_or_unnamed};
 use crate::marker::PhantomData;
 use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
 use crate::sync::Arc;
@@ -140,7 +140,7 @@ where
     let scope = Scope {
         data: Arc::new(ScopeData {
             num_running_threads: AtomicUsize::new(0),
-            main_thread: current(),
+            main_thread: current_or_unnamed(),
             a_thread_panicked: AtomicBool::new(false),
         }),
         env: PhantomData,
@@ -152,7 +152,8 @@ where
 
     // Wait until all the threads are finished.
     while scope.data.num_running_threads.load(Ordering::Acquire) != 0 {
-        park();
+        // SAFETY: this is the main thread, the handle belongs to us.
+        unsafe { scope.data.main_thread.park() };
     }
 
     // Throw any panic from `f`, or the return value of `f` if no thread panicked.