about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorStephen M. Coakley <me@stephencoakley.com>2016-10-05 11:34:25 -0500
committerStephen M. Coakley <me@stephencoakley.com>2016-10-05 11:34:25 -0500
commit894ef966c631facd13cd0d021acca43e37c8510e (patch)
treefb94586493ea4948ce49790566421296323dec8e /src/libstd
parent6e10e29a9702ef2fb5b1cedced4e2a72a1a81b69 (diff)
downloadrust-894ef966c631facd13cd0d021acca43e37c8510e.tar.gz
rust-894ef966c631facd13cd0d021acca43e37c8510e.zip
Generate ID using u64 + atomic spinlock
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/thread/mod.rs68
1 files changed, 54 insertions, 14 deletions
diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs
index ea20c93fc26..a20c2c5002b 100644
--- a/src/libstd/thread/mod.rs
+++ b/src/libstd/thread/mod.rs
@@ -165,7 +165,7 @@ use panic;
 use panicking;
 use str;
 use sync::{Mutex, Condvar, Arc};
-use sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
+use sync::atomic::{AtomicBool, Ordering};
 use sys::thread as imp;
 use sys_common::thread_info;
 use sys_common::util;
@@ -536,21 +536,52 @@ pub fn park_timeout(dur: Duration) {
 /// designated identifier.
 #[unstable(feature = "thread_id", issue = "21507")]
 #[derive(Eq, PartialEq, Copy, Clone)]
-pub struct ThreadId(usize);
+pub struct ThreadId(u64);
 
 impl ThreadId {
-    /// Returns an identifier unique to the current calling thread.
-    #[unstable(feature = "thread_id", issue = "21507")]
-    pub fn current() -> ThreadId {
-        static THREAD_ID_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;
-        #[thread_local] static mut THREAD_ID: ThreadId = ThreadId(0);
+    // Generate a new unique thread ID. Since this function is called every
+    // time a thread is created, this is optimized to generate unique values
+    // as quickly as possible.
+    fn new() -> ThreadId {
+        // 64-bit operations are not atomic on all systems, so use an atomic
+        // flag as a guard around a 64-bit global counter. The window for
+        // contention on the counter is rather narrow since the general case
+        // should be compiled down to three instructions between locking and
+        // unlocking the guard. Since contention on the guard is low, use a
+        // spinlock that optimizes for the fast path of the guard being
+        // unlocked.
+        static GUARD: AtomicBool = AtomicBool::new(false);
+        static mut COUNTER: u64 = 0;
+
+        // Get exclusive access to the counter.
+        while GUARD.compare_exchange_weak(
+            false,
+            true,
+            Ordering::Acquire,
+            Ordering::Relaxed
+        ).is_err() {
+            // Give up the rest of our thread quantum if another thread is
+            // using the counter. This is the slow_er_ path.
+            yield_now();
+        }
 
-        unsafe {
-            if THREAD_ID.0 == 0 {
-                THREAD_ID.0 = 1 + THREAD_ID_COUNT.fetch_add(1, Ordering::SeqCst);
+        // We have exclusive access to the counter, so use it fast and get out.
+        let id = unsafe {
+            // If we somehow use up all our bits, panic so that we're not
+            // covering up subtle bugs of IDs being reused.
+            if COUNTER == ::u64::MAX {
+                panic!("failed to generate unique thread ID: bitspace exhausted");
             }
-            THREAD_ID
-        }
+
+            let id = COUNTER;
+            COUNTER += 1;
+            id
+        };
+
+        // Unlock the guard.
+        GUARD.store(false, Ordering::Release);
+
+        ThreadId(id)
     }
 }
 
@@ -561,6 +592,7 @@ impl ThreadId {
 /// The internal representation of a `Thread` handle
 struct Inner {
     name: Option<CString>,      // Guaranteed to be UTF-8
+    id: ThreadId,
     lock: Mutex<bool>,          // true when there is a buffered unpark
     cvar: Condvar,
 }
@@ -581,6 +613,7 @@ impl Thread {
         Thread {
             inner: Arc::new(Inner {
                 name: cname,
+                id: ThreadId::new(),
                 lock: Mutex::new(false),
                 cvar: Condvar::new(),
             })
@@ -599,6 +632,12 @@ impl Thread {
         }
     }
 
+    /// Gets the thread's unique identifier.
+    #[unstable(feature = "thread_id", issue = "21507")]
+    pub fn id(&self) -> ThreadId {
+        self.inner.id
+    }
+
     /// Gets the thread's name.
     ///
     /// # Examples
@@ -1009,12 +1048,13 @@ mod tests {
 
     #[test]
     fn test_thread_id_equal() {
-        assert!(thread::ThreadId::current() == thread::ThreadId::current());
+        assert!(thread::current().id() == thread::current().id());
     }
 
     #[test]
     fn test_thread_id_not_equal() {
-        assert!(thread::ThreadId::current() != thread::spawn(|| thread::ThreadId::current()).join().unwrap());
+        let spawned_id = thread::spawn(|| thread::current().id()).join().unwrap();
+        assert!(thread::current().id() != spawned_id);
     }
 
     // NOTE: the corresponding test for stderr is in run-pass/thread-stderr, due