use owned_alloc::OwnedAlloc;
use std::{
fmt,
marker::PhantomData,
ptr::null_mut,
sync::atomic::{AtomicPtr, AtomicUsize, Ordering::*},
};
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct ThreadId {
bits: usize,
_non_tsafe: PhantomData<*mut ()>,
}
impl ThreadId {
#[inline]
pub fn current() -> Self {
ID.with(|id| Self { bits: id.bits, _non_tsafe: PhantomData })
}
pub(super) fn bits(self) -> usize {
self.bits
}
}
impl Default for ThreadId {
fn default() -> Self {
Self::current()
}
}
impl fmt::Debug for ThreadId {
fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
write!(fmtr, "ThreadId({:?})", self.bits)
}
}
static ID_COUNTER: AtomicUsize = AtomicUsize::new(1);
static ID_LIST: Node =
Node { free: AtomicUsize::new(0), next: AtomicPtr::new(null_mut()) };
static ID_LIST_BACK: AtomicPtr<Node> =
AtomicPtr::new(&ID_LIST as *const _ as *mut _);
thread_local! {
static ID: IdGuard = IdGuard::new();
}
struct IdGuard {
bits: usize,
node: &'static Node,
}
impl IdGuard {
fn new() -> Self {
let back_then = ID_LIST_BACK.load(Acquire);
let mut node = &ID_LIST;
loop {
let bits = node.free.swap(usize::max_value(), Relaxed);
if bits != usize::max_value() {
break Self { node, bits };
}
let next = node.next.load(Acquire);
if next.is_null() || node as *const _ == back_then {
break Self::create_node();
}
node = unsafe { &*next };
}
}
fn create_node() -> Self {
let new = Node {
free: AtomicUsize::new(usize::max_value()),
next: AtomicPtr::new(null_mut()),
};
let alloc = OwnedAlloc::new(new);
let nnptr = alloc.into_raw();
let prev = ID_LIST_BACK.swap(nnptr.as_ptr(), AcqRel);
let bits = ID_COUNTER.fetch_add(1, Relaxed);
let node = unsafe {
(*prev).next.store(nnptr.as_ptr(), Release);
&*nnptr.as_ptr()
};
Self { node, bits }
}
}
impl Drop for IdGuard {
fn drop(&mut self) {
self.node.free.store(self.bits, Relaxed);
}
}
struct Node {
free: AtomicUsize,
next: AtomicPtr<Node>,
}