Skip to content

Commit 894ef96

Browse files
committed
Generate ID using u64 + atomic spinlock
1 parent 6e10e29 commit 894ef96

File tree

1 file changed

+54
-14
lines changed

1 file changed

+54
-14
lines changed

src/libstd/thread/mod.rs

Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ use panic;
165165
use panicking;
166166
use str;
167167
use sync::{Mutex, Condvar, Arc};
168-
use sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
168+
use sync::atomic::{AtomicBool, Ordering};
169169
use sys::thread as imp;
170170
use sys_common::thread_info;
171171
use sys_common::util;
@@ -536,21 +536,52 @@ pub fn park_timeout(dur: Duration) {
536536
/// designated identifier.
537537
#[unstable(feature = "thread_id", issue = "21507")]
538538
#[derive(Eq, PartialEq, Copy, Clone)]
539-
pub struct ThreadId(usize);
539+
pub struct ThreadId(u64);
540540

541541
impl ThreadId {
542-
/// Returns an identifier unique to the current calling thread.
543-
#[unstable(feature = "thread_id", issue = "21507")]
544-
pub fn current() -> ThreadId {
545-
static THREAD_ID_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;
546-
#[thread_local] static mut THREAD_ID: ThreadId = ThreadId(0);
542+
// Generate a new unique thread ID. Since this function is called every
543+
// time a thread is created, this is optimized to generate unique values
544+
// as quickly as possible.
545+
fn new() -> ThreadId {
546+
// 64-bit operations are not atomic on all systems, so use an atomic
547+
// flag as a guard around a 64-bit global counter. The window for
548+
// contention on the counter is rather narrow since the general case
549+
// should be compiled down to three instructions between locking and
550+
// unlocking the guard. Since contention on the guard is low, use a
551+
// spinlock that optimizes for the fast path of the guard being
552+
// unlocked.
553+
static GUARD: AtomicBool = AtomicBool::new(false);
554+
static mut COUNTER: u64 = 0;
555+
556+
// Get exclusive access to the counter.
557+
while GUARD.compare_exchange_weak(
558+
false,
559+
true,
560+
Ordering::Acquire,
561+
Ordering::Relaxed
562+
).is_err() {
563+
// Give up the rest of our thread quantum if another thread is
564+
// using the counter. This is the slow_er_ path.
565+
yield_now();
566+
}
547567

548-
unsafe {
549-
if THREAD_ID.0 == 0 {
550-
THREAD_ID.0 = 1 + THREAD_ID_COUNT.fetch_add(1, Ordering::SeqCst);
568+
// We have exclusive access to the counter, so use it fast and get out.
569+
let id = unsafe {
570+
// If we somehow use up all our bits, panic so that we're not
571+
// covering up subtle bugs of IDs being reused.
572+
if COUNTER == ::u64::MAX {
573+
panic!("failed to generate unique thread ID: bitspace exhausted");
551574
}
552-
THREAD_ID
553-
}
575+
576+
let id = COUNTER;
577+
COUNTER += 1;
578+
id
579+
};
580+
581+
// Unlock the guard.
582+
GUARD.store(false, Ordering::Release);
583+
584+
ThreadId(id)
554585
}
555586
}
556587

@@ -561,6 +592,7 @@ impl ThreadId {
561592
/// The internal representation of a `Thread` handle
562593
struct Inner {
563594
name: Option<CString>, // Guaranteed to be UTF-8
595+
id: ThreadId,
564596
lock: Mutex<bool>, // true when there is a buffered unpark
565597
cvar: Condvar,
566598
}
@@ -581,6 +613,7 @@ impl Thread {
581613
Thread {
582614
inner: Arc::new(Inner {
583615
name: cname,
616+
id: ThreadId::new(),
584617
lock: Mutex::new(false),
585618
cvar: Condvar::new(),
586619
})
@@ -599,6 +632,12 @@ impl Thread {
599632
}
600633
}
601634

635+
/// Gets the thread's unique identifier.
636+
#[unstable(feature = "thread_id", issue = "21507")]
637+
pub fn id(&self) -> ThreadId {
638+
self.inner.id
639+
}
640+
602641
/// Gets the thread's name.
603642
///
604643
/// # Examples
@@ -1009,12 +1048,13 @@ mod tests {
10091048

10101049
#[test]
10111050
fn test_thread_id_equal() {
1012-
assert!(thread::ThreadId::current() == thread::ThreadId::current());
1051+
assert!(thread::current().id() == thread::current().id());
10131052
}
10141053

10151054
#[test]
10161055
fn test_thread_id_not_equal() {
1017-
assert!(thread::ThreadId::current() != thread::spawn(|| thread::ThreadId::current()).join().unwrap());
1056+
let spawned_id = thread::spawn(|| thread::current().id()).join().unwrap();
1057+
assert!(thread::current().id() != spawned_id);
10181058
}
10191059

10201060
// NOTE: the corresponding test for stderr is in run-pass/thread-stderr, due

0 commit comments

Comments
 (0)