use core::convert::TryInto;
use core::num::NonZeroUsize;
use core::sync::atomic::{AtomicUsize, Ordering};
thread_local! {
static THREAD_MARKER: () = ();
}
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
#[repr(transparent)]
pub(crate) struct ThreadId(NonZeroUsize);
#[derive(Debug)]
#[repr(transparent)]
pub(crate) struct AtomicOptionThreadId(core::sync::atomic::AtomicUsize);
impl ThreadId {
#[inline(always)]
const fn new(value: NonZeroUsize) -> Self {
Self(value)
}
pub fn current_thread() -> Self {
Self::new(
THREAD_MARKER
.try_with(|x| x as *const _ as usize)
.expect("the thread's local data has already been destroyed")
.try_into()
.expect("thread id should never be zero"),
)
}
}
#[inline(always)]
fn wrap(value: usize) -> Option<ThreadId> {
match value {
0 => None,
n => Some(ThreadId::new(n.try_into().unwrap())),
}
}
#[inline(always)]
const fn unwrap(value: Option<ThreadId>) -> usize {
match value {
None => 0,
Some(id) => id.0.get(),
}
}
impl AtomicOptionThreadId {
#[inline]
pub const fn new(id: Option<ThreadId>) -> Self {
Self(AtomicUsize::new(unwrap(id)))
}
#[inline]
pub fn load(&self, order: Ordering) -> Option<ThreadId> {
wrap(self.0.load(order))
}
#[inline]
pub fn store(&self, val: Option<ThreadId>, order: Ordering) {
self.0.store(unwrap(val), order);
}
#[inline]
pub fn compare_exchange(
&self,
current: Option<ThreadId>,
new: Option<ThreadId>,
success: Ordering,
failure: Ordering,
) -> Result<Option<ThreadId>, Option<ThreadId>> {
self.0
.compare_exchange(unwrap(current), unwrap(new), success, failure)
.map(wrap)
.map_err(wrap)
}
}
impl Default for AtomicOptionThreadId {
#[inline]
fn default() -> Self {
Self::new(None)
}
}
impl From<ThreadId> for AtomicOptionThreadId {
#[inline]
fn from(id: ThreadId) -> Self {
Self::new(Some(id))
}
}
impl From<Option<ThreadId>> for AtomicOptionThreadId {
#[inline]
fn from(id: Option<ThreadId>) -> Self {
Self::new(id)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread;
#[test]
fn test_eq() {
let a = ThreadId::current_thread();
let b = ThreadId::current_thread();
assert_eq!(a, b);
}
#[test]
fn test_ne() {
let a = ThreadId::current_thread();
let b = thread::spawn(move || ThreadId::current_thread())
.join()
.unwrap();
assert_ne!(a, b);
}
}