use core::convert::TryInto;
use core::sync::atomic::{AtomicUsize, Ordering};
pub(crate) use super::thread_id::ThreadId;
#[derive(Debug)]
#[repr(transparent)]
pub(crate) struct AtomicOptionThreadId(core::sync::atomic::AtomicUsize);
#[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 store_if_none(
&self,
new: Option<ThreadId>,
success: Ordering,
failure: Ordering,
) -> Result<Option<ThreadId>, Option<ThreadId>> {
self.0
.compare_exchange(unwrap(None), 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 alloc::format;
#[test]
fn test_wrapping() {
let a = ThreadId::current_thread();
assert_ne!(None, wrap(unwrap(Some(a))));
assert_eq!(None, wrap(unwrap(None)));
}
#[test]
fn test_atomic() {
let tid = ThreadId::current_thread();
let a: AtomicOptionThreadId = tid.into();
let b = AtomicOptionThreadId::default();
let _ = AtomicOptionThreadId::new(None);
assert_ne!(a.load(Ordering::Relaxed), None);
assert_eq!(b.load(Ordering::Relaxed), None);
a.store(None, Ordering::Relaxed);
b.store(Some(tid), Ordering::Relaxed);
assert_eq!(a.load(Ordering::Relaxed), None);
assert_ne!(b.load(Ordering::Relaxed), None);
assert!(a
.store_if_none(Some(tid), Ordering::Relaxed, Ordering::Relaxed)
.is_ok());
assert!(a
.store_if_none(None, Ordering::Relaxed, Ordering::Relaxed)
.is_err());
assert_ne!(a.load(Ordering::Relaxed), None);
assert_ne!(b.load(Ordering::Relaxed), None);
assert_eq!(format!("{:?}", &a), format!("{:?}", &b));
}
}