Auto merge of #36341 - sagebind:thread_id, r=alexcrichton

Add ThreadId for comparing threads

This adds the capability to store and compare threads with the current calling thread via a new struct, `std:🧵: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:
- rust-lang/rust#29457
- rust-lang/rust#29448
- rust-lang/rust#29447

And this in the RFC repo: rust-lang/rfcs#1435
This commit is contained in:
bors 2016-10-10 04:04:51 -07:00 committed by GitHub
commit 6d620843f6

View File

@ -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};
@ -524,6 +525,45 @@ 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
////////////////////////////////////////////////////////////////////////////////
@ -531,6 +571,7 @@ pub fn park_timeout(dur: Duration) {
/// 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,
}
@ -551,6 +592,7 @@ impl Thread {
Thread {
inner: Arc::new(Inner {
name: cname,
id: ThreadId::new(),
lock: Mutex::new(false),
cvar: Condvar::new(),
})
@ -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
@ -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.
}