use std::cell::{Cell, LazyCell};
use std::fmt::{self, Debug};
use std::marker::PhantomData;
use sealed::Sealed;
mod sealed {
use super::ThreadKey;
pub trait Sealed {}
impl Sealed for ThreadKey {}
impl Sealed for &mut ThreadKey {}
}
thread_local! {
static KEY: LazyCell<KeyCell> = LazyCell::new(KeyCell::default);
}
pub struct ThreadKey {
phantom: PhantomData<*const ()>, }
pub unsafe trait Keyable: Sealed {}
unsafe impl Keyable for ThreadKey {}
unsafe impl Keyable for &mut ThreadKey {}
unsafe impl Sync for ThreadKey {}
#[mutants::skip]
#[cfg(not(tarpaulin_include))]
impl Debug for ThreadKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ThreadKey")
}
}
impl Drop for ThreadKey {
fn drop(&mut self) {
unsafe { KEY.with(|key| key.force_unlock()) }
}
}
impl ThreadKey {
#[must_use]
pub fn get() -> Option<Self> {
KEY.with(|key| {
key.try_lock().then_some(Self {
phantom: PhantomData,
})
})
}
}
#[derive(Default)]
struct KeyCell {
is_locked: Cell<bool>,
}
impl KeyCell {
#[must_use]
pub fn try_lock(&self) -> bool {
!self.is_locked.replace(true)
}
pub unsafe fn force_unlock(&self) {
self.is_locked.set(false);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn thread_key_returns_some_on_first_call() {
assert!(ThreadKey::get().is_some());
}
#[test]
fn thread_key_returns_none_on_second_call() {
let key = ThreadKey::get();
assert!(ThreadKey::get().is_none());
drop(key);
}
#[test]
fn dropping_thread_key_allows_reobtaining() {
drop(ThreadKey::get());
assert!(ThreadKey::get().is_some())
}
}