use std::{
cell::UnsafeCell,
ops::Deref,
sync::{
Arc,
atomic::{self, AtomicI32, Ordering},
},
};
pub struct MyHandle<T> {
value: UnsafeCell<Option<T>>,
stack: AtomicI32,
detached: AtomicI32,
dropped: AtomicI32,
}
pub struct MyHandleGuard<'a, T>((&'a MyHandle<T>, &'a T));
impl<'a, T> Drop for MyHandleGuard<'a, T> {
fn drop(&mut self) {
self.0.0.put();
}
}
impl<'a, T> Deref for MyHandleGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.1
}
}
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<MyHandleGuard<'_, T>> {
self.stack.fetch_add(1, Ordering::Relaxed);
let r = self
.detached
.compare_exchange(0, detach, Ordering::AcqRel, Ordering::Relaxed);
if r.is_err() {
self.put();
return None;
};
unsafe {
(*self.value.get())
.as_ref()
.and_then(|x| Some(MyHandleGuard((self, x))))
}
}
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<MyHandleGuard<'_, T>> {
self.get_with(0)
}
pub fn dettach(&self) {
if let Some(g) = self.get_with(1) {
self.stack.fetch_sub(1, Ordering::Relaxed);
drop(g);
}
}
}
impl<T> Drop for MyHandle<T> {
fn drop(&mut self) {
atomic::fence(Ordering::Acquire);
if self.detached.load(Ordering::Relaxed) == 0 {
self.put();
}
}
}
unsafe impl<T> Sync for MyHandle<T> where T: Sync + Send {}