mod list;
use core::ptr::NonNull;
use core::sync::atomic::{AtomicPtr, Ordering};
pub(crate) use self::list::HazardList;
const FREE: *mut () = 0 as *mut ();
const THREAD_RESERVED: *mut () = 1 as *mut ();
#[derive(Debug)]
pub struct Hazard {
protected: AtomicPtr<()>,
}
impl Hazard {
#[inline]
pub fn set_free(&self, order: Ordering) {
self.protected.store(FREE, order);
}
#[inline]
pub fn set_thread_reserved(&self, order: Ordering) {
self.protected.store(THREAD_RESERVED, order);
}
#[inline]
pub fn protected(&self, order: Ordering) -> Option<Protected> {
match self.protected.load(order) {
FREE | THREAD_RESERVED => None,
ptr => Some(Protected(unsafe { NonNull::new_unchecked(ptr) })),
}
}
#[inline]
pub fn set_protected(&self, protect: NonNull<()>, order: Ordering) {
assert_eq!(order, Ordering::SeqCst, "must only be called with `SeqCst`");
self.protected.store(protect.as_ptr(), order);
}
#[inline]
fn new(ptr: *mut ()) -> Self {
debug_assert_ne!(ptr, FREE);
Self { protected: AtomicPtr::new(ptr) }
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct Protected(NonNull<()>);
impl Protected {
#[inline]
pub fn address(self) -> usize {
self.0.as_ptr() as usize
}
#[inline]
pub fn into_inner(self) -> NonNull<()> {
self.0
}
}
#[cfg(test)]
mod tests {
use std::ptr::NonNull;
use std::sync::atomic::Ordering;
use super::*;
#[test]
fn protect_hazard() {
let ptr = NonNull::from(&1);
let hazard = Hazard::new(ptr.cast().as_ptr());
assert_eq!(ptr.as_ptr() as usize, hazard.protected(Ordering::Relaxed).unwrap().address());
hazard.set_free(Ordering::Relaxed);
assert_eq!(None, hazard.protected(Ordering::Relaxed));
assert_eq!(FREE, hazard.protected.load(Ordering::Relaxed));
hazard.set_thread_reserved(Ordering::Relaxed);
assert_eq!(None, hazard.protected(Ordering::Relaxed));
assert_eq!(THREAD_RESERVED, hazard.protected.load(Ordering::Relaxed));
hazard.set_protected(ptr.cast(), Ordering::SeqCst);
assert_eq!(ptr.as_ptr() as usize, hazard.protected(Ordering::Relaxed).unwrap().address());
}
}