#[cfg(not(any(test, feature = "std")))]
use alloc::boxed::Box;
use core::iter::FusedIterator;
use core::mem;
use core::ptr::NonNull;
use core::sync::atomic::{
self,
Ordering::{self, Acquire, Relaxed, Release, SeqCst},
};
use reclaim::align::CacheAligned;
use reclaim::leak::Owned;
use crate::hazard::{Hazard, FREE, THREAD_RESERVED};
use crate::sanitize::{RELEASE_FAIL, RELEASE_SUCCESS};
type Atomic<T> = reclaim::leak::Atomic<T, reclaim::typenum::U0>;
type Shared<'g, T> = reclaim::leak::Shared<'g, T, reclaim::typenum::U0>;
#[derive(Debug, Default)]
pub(crate) struct HazardList {
head: Atomic<HazardNode>,
}
impl HazardList {
#[inline]
pub const fn new() -> Self {
Self { head: Atomic::null() }
}
#[inline]
pub fn iter(&self) -> Iter {
Iter {
current: self.head.load_shared(Acquire),
}
}
#[inline]
pub fn get_hazard(&self, protect: Option<NonNull<()>>) -> &Hazard {
let (ptr, order) = match protect {
Some(protect) => (protect.as_ptr(), SeqCst),
None => (THREAD_RESERVED, Release),
};
self.get_hazard_for(ptr, order)
}
#[inline]
fn get_hazard_for(&self, ptr: *mut (), order: Ordering) -> &Hazard {
let mut prev = &self.head;
let mut curr = prev.load_shared(Acquire);
while let Some(node) = curr.map(Shared::into_ref) {
if node.hazard().protected.load(Relaxed) == FREE {
let prev = node.hazard.protected.compare_and_swap(FREE, ptr, order);
if prev == FREE {
return node.hazard();
}
}
prev = node.next();
curr = node.next().load_shared(Acquire);
}
self.insert_back(prev, ptr)
}
#[inline]
fn insert_back(&self, mut tail: &Atomic<HazardNode>, ptr: *mut ()) -> &Hazard {
let node = unsafe {
Owned::leak_shared(Owned::new(HazardNode {
hazard: CacheAligned(Hazard::new(ptr)),
next: CacheAligned(Atomic::null()),
}))
};
loop {
match tail.compare_exchange_weak(Shared::none(), node, RELEASE_SUCCESS, RELEASE_FAIL) {
Ok(_) => return &*Shared::into_ref(node).hazard,
Err(fail) => {
atomic::fence(Acquire);
if let Some(node) = fail.loaded {
tail = unsafe { &node.deref_unprotected().next };
}
}
}
}
}
}
impl Drop for HazardList {
#[inline]
fn drop(&mut self) {
let mut curr = self.head.take();
while let Some(mut owned) = curr {
curr = owned.next.take();
mem::drop(owned);
}
}
}
#[derive(Debug)]
pub(crate) struct Iter<'a> {
current: Option<Shared<'a, HazardNode>>,
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a Hazard;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.current.take().map(|node| {
let node = Shared::into_ref(node);
self.current = node.next.load_shared(Acquire);
&*node.hazard
})
}
}
impl<'a> FusedIterator for Iter<'a> {}
#[derive(Debug)]
struct HazardNode {
hazard: CacheAligned<Hazard>,
next: CacheAligned<Atomic<HazardNode>>,
}
impl HazardNode {
#[inline]
fn hazard(&self) -> &Hazard {
&*self.hazard
}
#[inline]
fn next(&self) -> &Atomic<HazardNode> {
&*self.next
}
}
#[cfg(test)]
mod tests {
use std::ptr::NonNull;
use std::sync::atomic::Ordering;
use super::HazardList;
#[test]
fn insert_one() {
let ptr = NonNull::new(0xDEAD_BEEF as *mut ()).unwrap();
let list = HazardList::new();
let hazard = list.get_hazard(Some(ptr));
assert_eq!(hazard.protected.load(Ordering::Relaxed), 0xDEAD_BEEF as *mut ());
}
#[test]
fn iter() {
let ptr = NonNull::new(0xDEAD_BEEF as *mut ()).unwrap();
let list = HazardList::new();
let _ = list.get_hazard(Some(ptr));
let _ = list.get_hazard(Some(ptr));
let _ = list.get_hazard(Some(ptr));
assert!(list
.iter()
.fuse()
.all(|hazard| hazard.protected.load(Ordering::Relaxed) == ptr.as_ptr()));
}
}