diff --git a/library/std/src/thread/current.rs b/library/std/src/thread/current.rs index 508e35cefe88f..30fb3e0d3e937 100644 --- a/library/std/src/thread/current.rs +++ b/library/std/src/thread/current.rs @@ -108,7 +108,7 @@ pub(super) mod id { get().unwrap_or_else( #[cold] || { - let id = ThreadId::new(); + let id = ThreadId::desperate_new(); id::set(id); id }, diff --git a/library/std/src/thread/id.rs b/library/std/src/thread/id.rs index ba7024327881b..b21745e11fa2a 100644 --- a/library/std/src/thread/id.rs +++ b/library/std/src/thread/id.rs @@ -31,11 +31,15 @@ use crate::sync::atomic::{Atomic, Ordering}; pub struct ThreadId(NonZero); impl ThreadId { - // Generate a new unique thread ID. - pub(crate) fn new() -> ThreadId { + /// Generates a new unique thread ID, and does not rely on the global + /// allocator. + /// + /// This should only be used in contexts where a new `ThreadId` is + /// desperately required, but the global allocator might not be available. + pub(super) fn desperate_new() -> ThreadId { #[cold] fn exhausted() -> ! { - panic!("failed to generate unique thread ID: bitspace exhausted") + rtabort!("failed to generate unique thread ID: bitspace exhausted") } cfg_select! { @@ -96,6 +100,25 @@ impl ThreadId { } } + /// Generates a new unique thread ID. + pub(crate) fn new() -> ThreadId { + cfg_select! { + target_has_atomic = "64" => { + // The generation is implemented lock-free anyway. + ThreadId::desperate_new() + } + _ => { + // Synchronize the id generation – `Mutex` has better + // synchronization behaviour than the spinlock used in + // `desperate_new`, but might need allocation. + use crate::sync::nonpoison::Mutex; + static LOCK: Mutex<()> = Mutex::new(()); + let _guard = LOCK.lock(); + ThreadId::desperate_new() + } + } + } + #[cfg(any(not(target_thread_local), target_has_atomic = "64"))] pub(super) fn from_u64(v: u64) -> Option { NonZero::new(v).map(ThreadId)