use std::ops::Deref;
use std::ptr::{addr_of, NonNull};
use std::rc::Rc;
use std::sync::atomic::Ordering::*;
use std::sync::atomic::{AtomicPtr, AtomicUsize};
use std::sync::Arc;
#[derive(Debug, Clone, Copy)]
pub enum Action {
Reset,
Release,
}
pub struct ReadHandle<'hzrd, T> {
value: &'hzrd T,
hzrd_ptr: &'hzrd HzrdPtr,
action: Action,
}
impl<'hzrd, T> ReadHandle<'hzrd, T> {
pub unsafe fn read_unchecked(
value: &'hzrd AtomicPtr<T>,
hzrd_ptr: &'hzrd HzrdPtr,
action: Action,
) -> Self {
let mut ptr = value.load(SeqCst);
loop {
unsafe { hzrd_ptr.protect(ptr) };
let new_ptr = value.load(SeqCst);
if ptr == new_ptr {
break;
} else {
ptr = new_ptr;
}
}
std::sync::atomic::fence(SeqCst);
let value = unsafe { &*ptr };
Self {
value,
hzrd_ptr,
action,
}
}
}
impl<T> Deref for ReadHandle<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.value
}
}
impl<T> Drop for ReadHandle<'_, T> {
fn drop(&mut self) {
match self.action {
Action::Reset => unsafe { self.hzrd_ptr.reset() },
Action::Release => unsafe { self.hzrd_ptr.release() },
}
}
}
pub unsafe trait Domain {
fn hzrd_ptr(&self) -> &HzrdPtr;
fn just_retire(&self, ret_ptr: RetiredPtr);
fn reclaim(&self) -> usize;
fn retire(&self, ret_ptr: RetiredPtr) -> usize {
self.just_retire(ret_ptr);
self.reclaim()
}
}
macro_rules! deref_impl {
($($sig:tt)+) => {
unsafe impl $($sig)+ {
fn hzrd_ptr(&self) -> &HzrdPtr {
(**self).hzrd_ptr()
}
fn just_retire(&self, ret_ptr: RetiredPtr) {
(**self).just_retire(ret_ptr);
}
fn reclaim(&self) -> usize {
(**self).reclaim()
}
}
};
}
deref_impl!(<D: Domain> Domain for &D);
deref_impl!(<D: Domain> Domain for Rc<D>);
deref_impl!(<D: Domain> Domain for Arc<D>);
fn dummy_addr() -> usize {
static DUMMY: u8 = 0;
addr_of!(DUMMY) as usize
}
pub struct HzrdPtr(AtomicUsize);
impl HzrdPtr {
pub fn new() -> Self {
HzrdPtr(AtomicUsize::new(dummy_addr()))
}
pub fn get(&self) -> usize {
self.0.load(SeqCst)
}
pub fn try_acquire(&self) -> Option<&Self> {
match self.0.compare_exchange(0, dummy_addr(), SeqCst, Relaxed) {
Ok(_) => Some(self),
Err(_) => None,
}
}
pub unsafe fn protect<T>(&self, ptr: *mut T) {
debug_assert!(!ptr.is_null());
self.0.store(ptr as usize, SeqCst);
}
pub unsafe fn reset(&self) {
self.0.store(dummy_addr(), SeqCst);
}
pub unsafe fn release(&self) {
self.0.store(0, SeqCst);
}
}
impl Default for HzrdPtr {
fn default() -> Self {
Self::new()
}
}
impl std::fmt::Debug for HzrdPtr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "HzrdPtr({:#X})", self.0.load(Relaxed))
}
}
unsafe impl Send for HzrdPtr {}
unsafe impl Sync for HzrdPtr {}
trait Delete {}
impl<T> Delete for T {}
pub struct RetiredPtr {
ptr: NonNull<dyn Delete>,
}
impl RetiredPtr {
pub unsafe fn new<T: 'static>(ptr: NonNull<T>) -> Self {
RetiredPtr { ptr }
}
pub fn addr(&self) -> usize {
self.ptr.as_ptr() as *mut () as usize
}
}
impl Drop for RetiredPtr {
fn drop(&mut self) {
let _: Box<dyn Delete> = unsafe { Box::from_raw(self.ptr.as_ptr()) };
}
}
impl std::fmt::Debug for RetiredPtr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "RetiredPtr({:#X})", self.addr())
}
}
unsafe impl Send for RetiredPtr {}
unsafe impl Sync for RetiredPtr {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hzrd_ptr() {
let mut value = String::from("Danger!");
let hzrd_ptr = HzrdPtr::new();
unsafe { hzrd_ptr.protect(&mut value) };
unsafe { hzrd_ptr.reset() };
unsafe { hzrd_ptr.protect(&mut value) };
unsafe { hzrd_ptr.release() };
unsafe { hzrd_ptr.protect(&mut value) };
}
#[test]
fn retired_ptr() {
let object = vec![String::from("Hello"), String::from("World")];
let ptr = NonNull::from(Box::leak(Box::new(object)));
let retired = unsafe { RetiredPtr::new(ptr) };
drop(retired);
}
}