use std::{
cell::UnsafeCell,
sync::{
Arc,
atomic::{self, AtomicI32, Ordering},
},
};
pub struct MyHandle<T> {
value: UnsafeCell<Option<T>>,
stack: AtomicI32,
detached: AtomicI32,
dropped: AtomicI32,
}
impl<T> MyHandle<T> {
pub fn attach(v: T) -> Arc<MyHandle<T>> {
Arc::new(MyHandle {
stack: AtomicI32::new(1),
detached: AtomicI32::new(0),
value: UnsafeCell::new(Some(v)),
dropped: AtomicI32::new(0),
})
}
fn get_with(&self, detach: i32) -> Option<&T> {
self.stack.fetch_add(1, Ordering::Relaxed);
let r = self
.detached
.compare_exchange(0, detach, Ordering::Release, Ordering::Relaxed);
if r.is_err() {
unsafe { self.put() };
return None;
};
unsafe { (*self.value.get()).as_ref() }
}
pub unsafe fn put(&self) {
if self.stack.fetch_sub(1, Ordering::Relaxed) == 1
&& self.dropped.swap(1, Ordering::Relaxed) != 1
{
unsafe { (*self.value.get()).take() };
}
}
pub fn get(&self) -> Option<&T> {
self.get_with(0)
}
pub fn dettach(&self) {
if self.get_with(1).is_some() {
self.stack.fetch_sub(1, Ordering::Relaxed);
unsafe { self.put() };
}
}
}
impl<T> Drop for MyHandle<T> {
fn drop(&mut self) {
atomic::fence(Ordering::Acquire);
if self.detached.load(Ordering::Relaxed) == 0 {
unsafe { self.put() };
}
}
}
unsafe impl<T> Sync for MyHandle<T> where T: Sync + Send {}