use crate::owned_alloc::OwnedAlloc;
use core::{
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, Relaxed);
if bits != usize::MAX {
break Self { node, bits };
}
let next = node.next.load(Acquire);
if next.is_null() || std::ptr::eq(node, back_then) {
break Self::create_node();
}
node = unsafe { &*next };
}
}
fn create_node() -> Self {
let new = Node {
free: AtomicUsize::new(usize::MAX),
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>,
}