#![cfg_attr(not(feature = "flag-based"), allow(dead_code))]
use std::{ops::Deref, sync::atomic::{AtomicBool, Ordering}};
pub struct AtomicLendCell<T> {
data: T,
is_alive: AtomicBool
}
impl<T> AtomicLendCell<T> {
pub fn as_ref(&self) -> &T {
&self.data
}
}
impl<T> Deref for AtomicLendCell<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<T> Drop for AtomicLendCell<T> {
fn drop(&mut self) {
self.is_alive.store(false, Ordering::Release);
#[cfg(debug_assertions)]
std::thread::yield_now();
}
}
pub struct AtomicBorrowCell<T> {
data_ptr: *const T,
owner_alive_ptr: *const AtomicBool
}
impl<T> AtomicBorrowCell<T> {
pub fn as_ref(&self) -> &T {
#[cfg(debug_assertions)]
{
let is_alive = unsafe { self.owner_alive_ptr.as_ref().unwrap() }
.load(Ordering::Acquire);
if !is_alive {
panic!("Attempting to access AtomicBorrowCell after owner was dropped");
}
}
unsafe { self.data_ptr.as_ref().unwrap() }
}
}
impl<T> Deref for AtomicBorrowCell<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
impl<T> Drop for AtomicBorrowCell<T> {
fn drop(&mut self) {
#[cfg(debug_assertions)]
{
let is_alive = unsafe { self.owner_alive_ptr.as_ref().unwrap() }
.load(Ordering::Acquire);
if !is_alive {
panic!("AtomicBorrowCell dropped after its owner was dropped");
}
}
}
}
unsafe impl<T: Sync> Send for AtomicBorrowCell<T> {}
unsafe impl<T: Sync> Sync for AtomicBorrowCell<T> {}
impl<T> AtomicLendCell<T> {
pub fn new(data: T) -> Self {
Self { data, is_alive: AtomicBool::new(true) }
}
pub fn borrow(&self) -> AtomicBorrowCell<T> {
AtomicBorrowCell {
data_ptr: (&self.data) as *const T,
owner_alive_ptr: &self.is_alive as *const AtomicBool
}
}
}
impl<'a, T> AtomicLendCell<&'a T> {
pub fn borrow_deref(&'a self) -> AtomicBorrowCell<T> {
AtomicBorrowCell {
data_ptr: self.data as *const T,
owner_alive_ptr: &self.is_alive as *const AtomicBool
}
}
}
impl<T> Clone for AtomicBorrowCell<T> {
fn clone(&self) -> Self {
AtomicBorrowCell {
data_ptr: self.data_ptr,
owner_alive_ptr: self.owner_alive_ptr
}
}
}
#[test]
fn test_epoch_borrow() {
let x = AtomicLendCell::new(4);
let xr = x.borrow();
let t1 = std::thread::spawn(move || {
let y = xr.as_ref();
println!("{:?}", y);
});
let xr = x.borrow();
let t2 = std::thread::spawn(move || {
let y = xr.as_ref();
println!("{:?}", y);
});
t1.join().unwrap();
t2.join().unwrap();
}
#[test]
fn test_epoch_safety() {
use std::sync::Arc;
let data = Arc::new(42);
let data_clone = Arc::clone(&data);
let x_opt = Some(AtomicLendCell::new(data));
let borrow = x_opt.as_ref().unwrap().borrow();
assert_eq!(**borrow, 42);
let handle = std::thread::spawn(move || {
assert_eq!(*data_clone, 42);
std::thread::sleep(std::time::Duration::from_millis(50));
});
drop(x_opt);
#[cfg(not(debug_assertions))]
{
std::thread::sleep(std::time::Duration::from_millis(10));
let _value = *borrow;
}
handle.join().unwrap();
}