use core::num::NonZeroU64;
fast_thread_local! {
#[cfg(not(all(feature = "nightly", feature = "std")))]
static THREAD_ID: core::cell::Cell<Option<UniqueThreadId>> = core::cell::Cell::new(None);
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[must_use]
#[repr(transparent)]
pub struct UniqueThreadId(NonZeroU64);
impl UniqueThreadId {
#[inline]
pub unsafe fn from_int(x: u64) -> Self {
UniqueThreadId(unsafe { NonZeroU64::new_unchecked(x) })
}
#[cfg(feature = "unique-wrap-std")]
#[cfg_attr(feature = "nightly-docs", doc(cfg(feature = "unique-wrap-std")))]
#[inline]
pub fn from_std(id: impl Into<std::thread::ThreadId>) -> Self {
cfg_if::cfg_if! {
if #[cfg(feature="nightly")] {
unsafe { Self::from_int(id.into().as_u64().get()) }
} else {
let _ = id;
unreachable!("unique-wrap-std not possible without nightly features")
}
}
}
#[inline]
#[must_use]
pub fn to_int(&self) -> u64 {
self.0.get()
}
#[cold]
#[cfg(not(all(feature = "nightly", feature = "std")))]
fn alloc() -> UniqueThreadId {
use core::sync::atomic::Ordering;
use portable_atomic::AtomicU64;
static NEXT_ID: AtomicU64 = AtomicU64::new(1);
let id = NEXT_ID
.fetch_update(Ordering::AcqRel, Ordering::Relaxed, |old_value| {
old_value.checked_add(1)
})
.expect("id overflow");
UniqueThreadId(NonZeroU64::new(id).unwrap())
}
#[inline]
pub fn current() -> UniqueThreadId {
cfg_if::cfg_if! {
if #[cfg(all(feature = "std", feature = "nightly"))] {
UniqueThreadId(crate::StdThreadId::current().0.as_u64())
} else if #[cfg(feature = "unique-wrap-std")] {
compile_error!("The `unique-wrap-std` feature requires the `nightly` feature to be enabled")
} else {
THREAD_ID.with(|cell| {
match cell.get() {
None => {
let id = UniqueThreadId::alloc();
cell.set(Some(id));
id
}
Some(id) => id,
}
})
}
}
}
}
simple_serde_serialize!(UniqueThreadId, |this| this.to_int());
unsafe impl crate::IThreadId for UniqueThreadId {
#[inline]
fn current() -> Self {
<Self>::current()
}
}
#[cfg(feature = "bytemuck")]
#[cfg_attr(feature = "nightly-docs", doc(cfg(feature = "bytemuck")))]
unsafe impl bytemuck::ZeroableInOption for UniqueThreadId {}
#[cfg(feature = "bytemuck")]
#[cfg_attr(feature = "nightly-docs", doc(cfg(feature = "bytemuck")))]
unsafe impl bytemuck::NoUninit for UniqueThreadId {}
impl From<UniqueThreadId> for u64 {
#[inline]
fn from(value: UniqueThreadId) -> Self {
value.to_int()
}
}
#[cfg(feature = "slog")]
#[cfg_attr(feature = "nightly-docs", doc(cfg(feature = "slog")))]
impl slog::Value for UniqueThreadId {
fn serialize(&self, _record: &slog::Record, key: slog::Key, serializer: &mut dyn slog::Serializer) -> slog::Result {
serializer.emit_u64(key, self.to_int())
}
}