mod record;
use crate::sync::atomic;
use std::{cell::RefCell, fmt::Debug, sync::Arc};
use record::Record;
mod retire_node;
mod domain;
use domain::{DomainGlobal, TLDomain};
mod guard;
pub use guard::Guard;
use crate::thread_data::ThreadData;
mod global {
use std::sync::Arc;
use lazy_static::lazy_static;
use super::Domain;
lazy_static! {
static ref GLOBAL: Arc<Domain> = Arc::new(Domain::new(64));
}
pub fn get_global_domain() -> Arc<Domain> {
GLOBAL.clone()
}
}
pub use global::*;
#[derive(Clone)]
pub struct Domain {
global: Arc<DomainGlobal>,
local: Arc<ThreadData<RefCell<TLDomain>>>,
reclaim_threshold: usize,
}
impl Debug for Domain {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"LocalDomain (reclaim_threshold: {})",
self.reclaim_threshold
)
}
}
impl Domain {
pub fn new(reclaim_threshold: usize) -> Self {
Self {
global: Arc::new(DomainGlobal::new()),
local: Arc::new(ThreadData::default()),
reclaim_threshold,
}
}
fn get_local(&self) -> &RefCell<TLDomain> {
self.local.get_or(|| {
let global = self.global.clone();
RefCell::new(TLDomain::new(global, self.reclaim_threshold))
})
}
pub fn protect<T>(
&self,
atom_ptr: &atomic::AtomicPtr<T>,
load_order: atomic::Ordering,
) -> Guard<T> {
let local = self.get_local();
let mut shared = local.borrow_mut();
shared.protect(atom_ptr, load_order)
}
pub fn empty_guard<T>(&self) -> Guard<T> {
let local = self.get_local();
let mut shared = local.borrow_mut();
shared.empty_guard()
}
pub unsafe fn retire<T, F>(&self, ptr: *mut T, retire_fn: F)
where
F: Fn(*mut T) + 'static,
{
let local = self.get_local();
let mut shared = local.borrow_mut();
shared.retire_node(ptr as *mut (), move |raw_ptr| retire_fn(raw_ptr as *mut T));
}
pub fn reclaim(&self) {
let local = self.get_local();
let mut shared = local.borrow_mut();
shared.reclaim();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sync::atomic;
#[derive(Debug, Clone)]
struct DropCheck {
d_count: Arc<atomic::AtomicU64>,
}
impl DropCheck {
pub fn new() -> Self {
Self {
d_count: Arc::new(atomic::AtomicU64::new(0)),
}
}
pub fn drop_count(&self) -> u64 {
self.d_count.load(atomic::Ordering::SeqCst)
}
}
impl Drop for DropCheck {
fn drop(&mut self) {
self.d_count.fetch_add(1, atomic::Ordering::SeqCst);
}
}
#[test]
#[ignore = "Hazard-Pointers are currently not working"]
fn local_domain_protect() {
let drop_chk = DropCheck::new();
let domain = Arc::new(Domain::new(10));
let raw_ptr = Box::into_raw(Box::new(drop_chk.clone()));
let shared_ptr = atomic::AtomicPtr::new(raw_ptr);
let guard = domain.protect(&shared_ptr, atomic::Ordering::SeqCst);
assert_eq!(0, guard.drop_count());
unsafe {
domain.retire(raw_ptr, |ptr| {
let boxed: Box<DropCheck> = Box::from_raw(ptr);
drop(boxed);
});
}
domain.reclaim();
assert_eq!(0, guard.drop_count());
drop(guard);
let second_drop_chk = DropCheck::new();
let other_raw_ptr = Box::into_raw(Box::new(second_drop_chk.clone()));
shared_ptr.store(other_raw_ptr, atomic::Ordering::SeqCst);
unsafe {
domain.retire(other_raw_ptr, |ptr| {
let boxed = Box::from_raw(ptr);
drop(boxed);
});
}
domain.reclaim();
assert_eq!(1, drop_chk.drop_count());
assert_eq!(1, second_drop_chk.drop_count());
}
}
#[cfg(loom)]
mod loom_tests {
use super::*;
use loom::thread;
use std::sync::Arc;
#[test]
fn swap_remove() {
loom::model(|| {
let og_domain = Arc::new(Domain::new(0));
let initial_boxed = Box::new(13usize);
let shared_ptr = Arc::new(atomic::AtomicPtr::new(Box::into_raw(initial_boxed)));
{
let domain = og_domain.clone();
let shared = shared_ptr.clone();
thread::spawn(move || {
let previous = shared.swap(core::ptr::null_mut(), atomic::Ordering::SeqCst);
unsafe {
domain.retire(previous, |ptr| {
let _ = unsafe { Box::from_raw(ptr) };
});
}
});
}
{
let domain = og_domain.clone();
let shared = shared_ptr.clone();
thread::spawn(move || {
let mut empty_guard: Guard<usize> = domain.empty_guard();
empty_guard.protect(&shared_ptr, atomic::Ordering::SeqCst);
});
}
core::mem::forget(og_domain);
});
}
}