Skip to content

Commit

Permalink
Auto merge of #36341 - sagebind:thread_id, r=alexcrichton
Browse files Browse the repository at this point in the history
Add ThreadId for comparing threads

This adds the capability to store and compare threads with the current calling thread via a new struct, `std::thread::ThreadId`. Addresses the need outlined in issue #21507.

This avoids the need to add any special checks to the existing thread structs and does not rely on the system to provide an identifier for a thread, since it seems that this approach is unreliable and undesirable. Instead, this simply uses a lazily-created, thread-local `usize` whose value is copied from a global atomic counter. The code should be simple enough that it should be as much reliable as the `#[thread_local]` attribute it uses (however much that is).

`ThreadId`s can be compared directly for equality and have copy semantics.

Also see these other attempts:
- #29457
- #29448
- #29447

And this in the RFC repo: rust-lang/rfcs#1435
  • Loading branch information
bors committed Oct 10, 2016
2 parents a7bfb1a + 032bffa commit 6d62084
Showing 1 changed file with 59 additions and 0 deletions.
59 changes: 59 additions & 0 deletions src/libstd/thread/mod.rs
Expand Up @@ -166,6 +166,7 @@ use panicking;
use str;
use sync::{Mutex, Condvar, Arc};
use sys::thread as imp;
use sys_common::mutex;
use sys_common::thread_info;
use sys_common::util;
use sys_common::{AsInner, IntoInner};
Expand Down Expand Up @@ -524,13 +525,53 @@ pub fn park_timeout(dur: Duration) {
*guard = false;
}

////////////////////////////////////////////////////////////////////////////////
// ThreadId
////////////////////////////////////////////////////////////////////////////////

/// A unique identifier for a running thread.
///
/// A `ThreadId` is an opaque object that has a unique value for each thread
/// that creates one. `ThreadId`s do not correspond to a thread's system-
/// designated identifier.
#[unstable(feature = "thread_id", issue = "21507")]
#[derive(Eq, PartialEq, Copy, Clone)]
pub struct ThreadId(u64);

impl ThreadId {
// Generate a new unique thread ID.
fn new() -> ThreadId {
static GUARD: mutex::Mutex = mutex::Mutex::new();
static mut COUNTER: u64 = 0;

unsafe {
GUARD.lock();

// 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 {
GUARD.unlock();
panic!("failed to generate unique thread ID: bitspace exhausted");
}

let id = COUNTER;
COUNTER += 1;

GUARD.unlock();

ThreadId(id)
}
}
}

////////////////////////////////////////////////////////////////////////////////
// Thread
////////////////////////////////////////////////////////////////////////////////

/// 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,
}
Expand All @@ -551,6 +592,7 @@ impl Thread {
Thread {
inner: Arc::new(Inner {
name: cname,
id: ThreadId::new(),
lock: Mutex::new(false),
cvar: Condvar::new(),
})
Expand All @@ -569,6 +611,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
Expand Down Expand Up @@ -977,6 +1025,17 @@ mod tests {
thread::sleep(Duration::from_millis(2));
}

#[test]
fn test_thread_id_equal() {
assert!(thread::current().id() == thread::current().id());
}

#[test]
fn test_thread_id_not_equal() {
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
// to the test harness apparently interfering with stderr configuration.
}

0 comments on commit 6d62084

Please sign in to comment.