use std::cell::{Cell, UnsafeCell};
use std::collections::LinkedList;
use std::sync::OnceLock;
use crate::core::{Domain, HzrdPtr, RetiredPtr};
use crate::stack::SharedStack;
pub static GLOBAL_CONFIG: OnceLock<Config> = OnceLock::new();
fn global_config() -> &'static Config {
GLOBAL_CONFIG.get_or_init(Config::default)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Config {
caching: bool,
bulk_size: usize,
}
impl Config {
pub fn caching(self, caching: bool) -> Self {
Self { caching, ..self }
}
pub fn bulk_size(self, bulk_size: usize) -> Self {
Self { bulk_size, ..self }
}
}
impl Default for Config {
fn default() -> Self {
Self {
caching: false,
bulk_size: 1,
}
}
}
thread_local! {
static HAZARD_POINTERS_CACHE: Cell<Vec<usize>> = const { Cell::new(Vec::new()) };
}
struct HzrdPtrs {
list: Vec<usize>,
caching: bool,
}
impl HzrdPtrs {
fn load<'t>(hzrd_ptrs: impl Iterator<Item = &'t HzrdPtr>) -> Self {
match global_config().caching {
false => Self::new(hzrd_ptrs),
true => Self::cached(hzrd_ptrs),
}
}
fn new<'t>(hzrd_ptrs: impl Iterator<Item = &'t HzrdPtr>) -> Self {
Self {
list: Vec::from_iter(hzrd_ptrs.map(HzrdPtr::get)),
caching: false,
}
}
fn cached<'t>(hzrd_ptrs: impl Iterator<Item = &'t HzrdPtr>) -> Self {
let mut hzrd_ptrs_cache: Vec<usize> = HAZARD_POINTERS_CACHE.with(|cell| cell.take());
hzrd_ptrs_cache.clear();
hzrd_ptrs_cache.extend(hzrd_ptrs.map(HzrdPtr::get));
Self {
list: hzrd_ptrs_cache,
caching: true,
}
}
fn contains(&self, addr: usize) -> bool {
self.list.contains(&addr)
}
}
impl Drop for HzrdPtrs {
fn drop(&mut self) {
if self.caching {
let list = std::mem::take(&mut self.list);
HAZARD_POINTERS_CACHE.with(|cell| cell.set(list));
}
}
}
static GLOBAL_DOMAIN: SharedDomain = SharedDomain::new();
#[derive(Clone, Copy)]
pub struct GlobalDomain;
impl GlobalDomain {
#[cfg(test)]
pub(crate) fn number_of_hzrd_ptrs(&self) -> usize {
GLOBAL_DOMAIN.number_of_hzrd_ptrs()
}
#[cfg(test)]
pub(crate) fn number_of_retired_ptrs(&self) -> usize {
GLOBAL_DOMAIN.number_of_retired_ptrs()
}
}
unsafe impl Domain for GlobalDomain {
fn hzrd_ptr(&self) -> &HzrdPtr {
GLOBAL_DOMAIN.hzrd_ptr()
}
fn just_retire(&self, ret_ptr: RetiredPtr) {
GLOBAL_DOMAIN.just_retire(ret_ptr)
}
fn reclaim(&self) -> usize {
GLOBAL_DOMAIN.reclaim()
}
}
impl std::fmt::Debug for GlobalDomain {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
GLOBAL_DOMAIN.fmt(f)
}
}
#[derive(Debug)]
pub struct SharedDomain {
hzrd_ptrs: SharedStack<HzrdPtr>,
retired_ptrs: SharedStack<RetiredPtr>,
}
impl Default for SharedDomain {
fn default() -> Self {
Self::new()
}
}
impl SharedDomain {
pub const fn new() -> Self {
Self {
hzrd_ptrs: SharedStack::new(),
retired_ptrs: SharedStack::new(),
}
}
#[cfg(test)]
pub(crate) fn number_of_hzrd_ptrs(&self) -> usize {
self.hzrd_ptrs.iter().count()
}
#[cfg(test)]
pub(crate) fn number_of_retired_ptrs(&self) -> usize {
let tooketh = unsafe { self.retired_ptrs.take() };
let size = tooketh.iter().count();
self.retired_ptrs.push_stack(tooketh);
size
}
}
unsafe impl Domain for SharedDomain {
fn hzrd_ptr(&self) -> &HzrdPtr {
match self.hzrd_ptrs.iter().find_map(|node| node.try_acquire()) {
Some(hzrd_ptr) => hzrd_ptr,
None => self.hzrd_ptrs.push_get(HzrdPtr::new()),
}
}
fn just_retire(&self, ret_ptr: RetiredPtr) {
self.retired_ptrs.push(ret_ptr);
}
fn reclaim(&self) -> usize {
let retired_ptrs = unsafe { self.retired_ptrs.take() };
let prev_size = retired_ptrs.iter().count();
if prev_size < global_config().bulk_size {
return 0;
}
let hzrd_ptrs = HzrdPtrs::load(self.hzrd_ptrs.iter());
let remaining: SharedStack<RetiredPtr> = retired_ptrs
.into_iter()
.filter(|retired_ptr| hzrd_ptrs.contains(retired_ptr.addr()))
.collect();
let new_size = remaining.iter().count();
self.retired_ptrs.push_stack(remaining);
assert!(prev_size >= new_size);
prev_size - new_size
}
}
use shared_cell::SharedCell;
mod shared_cell {
pub(crate) struct SharedCell<T>(T);
impl<T> SharedCell<T> {
pub(crate) fn new(value: T) -> Self {
Self(value)
}
pub(crate) fn get(&self) -> &T {
&self.0
}
}
}
#[derive(Debug)]
pub struct LocalDomain {
hzrd_ptrs: UnsafeCell<LinkedList<SharedCell<HzrdPtr>>>,
retired_ptrs: UnsafeCell<Vec<RetiredPtr>>,
}
impl Default for LocalDomain {
fn default() -> Self {
Self::new()
}
}
impl LocalDomain {
pub const fn new() -> Self {
Self {
hzrd_ptrs: UnsafeCell::new(LinkedList::new()),
retired_ptrs: UnsafeCell::new(Vec::new()),
}
}
#[cfg(test)]
pub(crate) fn number_of_hzrd_ptrs(&self) -> usize {
unsafe { (*self.hzrd_ptrs.get()).len() }
}
#[cfg(test)]
pub(crate) fn number_of_retired_ptrs(&self) -> usize {
unsafe { (*self.retired_ptrs.get()).len() }
}
}
unsafe impl Domain for LocalDomain {
fn hzrd_ptr(&self) -> &HzrdPtr {
{
let hzrd_ptrs = unsafe { &*self.hzrd_ptrs.get() };
if let Some(hzrd_ptr) = hzrd_ptrs.iter().find_map(|node| node.get().try_acquire()) {
return hzrd_ptr;
}
}
let hzrd_ptrs = unsafe { &mut *self.hzrd_ptrs.get() };
hzrd_ptrs.push_back(SharedCell::new(HzrdPtr::new()));
unsafe { hzrd_ptrs.back().unwrap_unchecked().get() }
}
fn just_retire(&self, ret_ptr: RetiredPtr) {
let retired_ptrs = unsafe { &mut *self.retired_ptrs.get() };
retired_ptrs.push(ret_ptr);
}
fn reclaim(&self) -> usize {
let retired_ptrs = unsafe { &mut *self.retired_ptrs.get() };
let hzrd_ptrs = unsafe { &mut *self.hzrd_ptrs.get() };
let prev_size = retired_ptrs.len();
if prev_size < global_config().bulk_size {
return 0;
}
let hzrd_ptrs = HzrdPtrs::load(hzrd_ptrs.iter().map(SharedCell::get));
retired_ptrs.retain(|p| hzrd_ptrs.contains(p.addr()));
prev_size - retired_ptrs.len()
}
}
#[cfg(test)]
mod tests {
use std::ptr::NonNull;
use super::*;
fn new_value<T>(value: T) -> NonNull<T> {
let boxed = Box::new(value);
let raw = Box::into_raw(boxed);
unsafe { NonNull::new_unchecked(raw) }
}
#[test]
fn global_domain() {
let ptr = new_value(['a', 'b', 'c', 'd']);
let domain = GlobalDomain;
let hzrd_ptr = domain.hzrd_ptr();
assert_eq!(domain.number_of_hzrd_ptrs(), 1);
unsafe { hzrd_ptr.protect(ptr.as_ptr()) };
let hzrd_ptrs = HzrdPtrs::load(GLOBAL_DOMAIN.hzrd_ptrs.iter());
assert!(hzrd_ptrs.contains(ptr.as_ptr() as usize));
{
let reclaimed = domain.retire(unsafe { RetiredPtr::new(ptr) });
assert_eq!(reclaimed, 0);
assert_eq!(domain.number_of_retired_ptrs(), 1);
}
{
let reclaimed = domain.reclaim();
assert_eq!(reclaimed, 0);
assert_eq!(domain.number_of_retired_ptrs(), 1);
}
unsafe { hzrd_ptr.reset() };
{
let reclaimed = domain.reclaim();
assert_eq!(reclaimed, 1);
assert_eq!(domain.number_of_retired_ptrs(), 0);
}
}
#[test]
fn shared_domain() {
let ptr = new_value(['a', 'b', 'c', 'd']);
let domain = SharedDomain::new();
let hzrd_ptr = domain.hzrd_ptr();
assert_eq!(domain.number_of_hzrd_ptrs(), 1);
unsafe { hzrd_ptr.protect(ptr.as_ptr()) };
let hzrd_ptrs = HzrdPtrs::load(domain.hzrd_ptrs.iter());
assert!(hzrd_ptrs.contains(ptr.as_ptr() as usize));
{
let reclaimed = domain.retire(unsafe { RetiredPtr::new(ptr) });
assert_eq!(reclaimed, 0);
assert_eq!(domain.number_of_retired_ptrs(), 1);
}
{
let reclaimed = domain.reclaim();
assert_eq!(reclaimed, 0);
assert_eq!(domain.number_of_retired_ptrs(), 1);
}
unsafe { hzrd_ptr.reset() };
{
let reclaimed = domain.reclaim();
assert_eq!(reclaimed, 1);
assert_eq!(domain.number_of_retired_ptrs(), 0);
}
}
#[test]
fn local_domain() {
let ptr = new_value(['a', 'b', 'c', 'd']);
let domain = LocalDomain::new();
let hzrd_ptr = domain.hzrd_ptr();
assert_eq!(domain.number_of_hzrd_ptrs(), 1);
unsafe { hzrd_ptr.protect(ptr.as_ptr()) };
let hzrd_ptrs = unsafe { &*domain.hzrd_ptrs.get() };
let hzrd_ptrs = HzrdPtrs::load(hzrd_ptrs.iter().map(SharedCell::get));
assert!(hzrd_ptrs.contains(ptr.as_ptr() as usize));
{
let reclaimed = domain.retire(unsafe { RetiredPtr::new(ptr) });
assert_eq!(reclaimed, 0);
assert_eq!(domain.number_of_retired_ptrs(), 1);
}
{
let reclaimed = domain.reclaim();
assert_eq!(reclaimed, 0);
assert_eq!(domain.number_of_retired_ptrs(), 1);
}
unsafe { hzrd_ptr.reset() };
{
let reclaimed = domain.reclaim();
assert_eq!(reclaimed, 1);
assert_eq!(domain.number_of_retired_ptrs(), 0);
}
}
}