std/thread/
id.rs

1use crate::num::NonZero;
2use crate::sync::atomic::{Atomic, Ordering};
3
4/// A unique identifier for a running thread.
5///
6/// A `ThreadId` is an opaque object that uniquely identifies each thread
7/// created during the lifetime of a process. `ThreadId`s are guaranteed not to
8/// be reused, even when a thread terminates. `ThreadId`s are under the control
9/// of Rust's standard library and there may not be any relationship between
10/// `ThreadId` and the underlying platform's notion of a thread identifier --
11/// the two concepts cannot, therefore, be used interchangeably. A `ThreadId`
12/// can be retrieved from the [`id`] method on a [`Thread`].
13///
14/// # Examples
15///
16/// ```
17/// use std::thread;
18///
19/// let other_thread = thread::spawn(|| {
20///     thread::current().id()
21/// });
22///
23/// let other_thread_id = other_thread.join().unwrap();
24/// assert!(thread::current().id() != other_thread_id);
25/// ```
26///
27/// [`Thread`]: super::Thread
28/// [`id`]: super::Thread::id
29#[stable(feature = "thread_id", since = "1.19.0")]
30#[derive(Eq, PartialEq, Clone, Copy, Hash, Debug)]
31pub struct ThreadId(NonZero<u64>);
32
33impl ThreadId {
34    // Generate a new unique thread ID.
35    pub(crate) fn new() -> ThreadId {
36        #[cold]
37        fn exhausted() -> ! {
38            panic!("failed to generate unique thread ID: bitspace exhausted")
39        }
40
41        cfg_select! {
42            target_has_atomic = "64" => {
43                use crate::sync::atomic::AtomicU64;
44
45                static COUNTER: Atomic<u64> = AtomicU64::new(0);
46
47                let mut last = COUNTER.load(Ordering::Relaxed);
48                loop {
49                    let Some(id) = last.checked_add(1) else {
50                        exhausted();
51                    };
52
53                    match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) {
54                        Ok(_) => return ThreadId(NonZero::new(id).unwrap()),
55                        Err(id) => last = id,
56                    }
57                }
58            }
59            _ => {
60                use crate::cell::SyncUnsafeCell;
61                use crate::hint::spin_loop;
62                use crate::sync::atomic::AtomicBool;
63                use crate::thread::yield_now;
64
65                // If we don't have a 64-bit atomic we use a small spinlock. We don't use Mutex
66                // here as we might be trying to get the current thread id in the global allocator,
67                // and on some platforms Mutex requires allocation.
68                static COUNTER_LOCKED: Atomic<bool> = AtomicBool::new(false);
69                static COUNTER: SyncUnsafeCell<u64> = SyncUnsafeCell::new(0);
70
71                // Acquire lock.
72                let mut spin = 0;
73                while COUNTER_LOCKED.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() {
74                    if spin <= 3 {
75                        for _ in 0..(1 << spin) {
76                            spin_loop();
77                        }
78                    } else {
79                        yield_now();
80                    }
81                    spin += 1;
82                }
83
84                // SAFETY: we have an exclusive lock on the counter.
85                unsafe {
86                    if let Some(id) = (*COUNTER.get()).checked_add(1) {
87                        *COUNTER.get() = id;
88                        COUNTER_LOCKED.store(false, Ordering::Release);
89                        ThreadId(NonZero::new(id).unwrap())
90                    } else {
91                        COUNTER_LOCKED.store(false, Ordering::Release);
92                        exhausted()
93                    }
94                }
95            }
96        }
97    }
98
99    #[cfg(any(not(target_thread_local), target_has_atomic = "64"))]
100    pub(super) fn from_u64(v: u64) -> Option<ThreadId> {
101        NonZero::new(v).map(ThreadId)
102    }
103
104    /// This returns a numeric identifier for the thread identified by this
105    /// `ThreadId`.
106    ///
107    /// As noted in the documentation for the type itself, it is essentially an
108    /// opaque ID, but is guaranteed to be unique for each thread. The returned
109    /// value is entirely opaque -- only equality testing is stable. Note that
110    /// it is not guaranteed which values new threads will return, and this may
111    /// change across Rust versions.
112    #[must_use]
113    #[unstable(feature = "thread_id_value", issue = "67939")]
114    pub fn as_u64(&self) -> NonZero<u64> {
115        self.0
116    }
117}