diff --git a/src/threads/threads.rs b/src/threads/threads.rs index 50f0ff9d1..897d0004b 100644 --- a/src/threads/threads.rs +++ b/src/threads/threads.rs @@ -6,10 +6,14 @@ use std::sync::{Arc, Mutex, OnceLock}; use std::time::Duration; +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct ThreadId(usize); + +impl FloggableDebug for ThreadId {} impl FloggableDebug for std::thread::ThreadId {} /// The thread id of the main thread, as set by [`init()`] at startup. -static MAIN_THREAD_ID: OnceLock = OnceLock::new(); +static MAIN_THREAD_ID: OnceLock = OnceLock::new(); /// Used to bypass thread assertions when testing. const THREAD_ASSERTS_CFG_FOR_TESTING: bool = cfg!(test); /// This allows us to notice when we've forked. @@ -26,6 +30,7 @@ impl FloggableDebug for std::thread::ThreadId {} pub fn init() { MAIN_THREAD_ID .set(thread_id()) + .map_err(|_| ()) .expect("threads::init() must only be called once (at startup)!"); extern "C" fn child_post_fork() { @@ -38,7 +43,7 @@ extern "C" fn child_post_fork() { } #[inline(always)] -fn main_thread_id() -> usize { +fn main_thread_id() -> ThreadId { #[cold] fn init_not_called() -> ! { panic!("threads::init() was not called at startup!"); @@ -59,19 +64,19 @@ fn init_not_called() -> ! { /// We use our own implementation because Rust's own `Thread::id()` allocates via `Arc`, is fairly /// slow, and uses a `Mutex` on 32-bit platforms (or anywhere without an atomic 64-bit CAS). #[inline(always)] -fn thread_id() -> usize { +fn thread_id() -> ThreadId { static THREAD_COUNTER: AtomicUsize = AtomicUsize::new(1); // It would be faster and much nicer to use #[thread_local] here, but that's nightly only. // This is still faster than going through Thread::thread_id(); it's something like 15ns // for each `Thread::thread_id()` call vs 1-2 ns with `#[thread_local]` and 2-4ns with // `thread_local!`. thread_local! { - static THREAD_ID: usize = THREAD_COUNTER.fetch_add(1, Ordering::Relaxed); + static THREAD_ID: ThreadId = ThreadId(THREAD_COUNTER.fetch_add(1, Ordering::Relaxed)); } let id = THREAD_ID.with(|id| *id); // This assertion is only here to reduce hair loss in case someone runs into a known linker bug; // as it's not here to catch logic errors in our own code, it can be elided in release mode. - debug_assert_ne!(id, 0, "TLS storage not initialized!"); + debug_assert_ne!(id, ThreadId(0), "TLS storage not initialized!"); id }