myhandle 0.1.0

A smart pointer that allows early drop of `T`
Documentation
use std::{
    cell::UnsafeCell,
    sync::{
        Arc,
        atomic::{self, AtomicI32, Ordering},
    },
};

/// A smart pointer similar to `Arc<T>`, but allows `T` to be
/// safely dropped early, providing additional flexibility in
/// multi-threaded scenarios.
///
/// ## Semantics
///
/// This type provides two key guarantees:
///
/// 1. Once a thread has obtained access to `T`, it may safely
///    use `T` without worrying about it being freed by another
///    thread.
/// 2. Acquiring access to `T` does **not** prevent other threads
///    from initiating a drop of `T`. Instead, it creates the
///    illusion that `T` has already been dropped, while the
///    actual destruction is deferred until no thread is still
///    accessing it.
pub struct MyHandle<T> {
    value: UnsafeCell<Option<T>>,
    stack: AtomicI32,
    detached: AtomicI32,
    dropped: AtomicI32,
}

impl<T> MyHandle<T> {
    /// Attaches a `T` to be managed by `MyHandle`.
    ///
    /// The underlying `T` is dropped when the last `MyHandle` clone is dropped,
    /// or when `detach` is invoked on any handle.
    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() }
    }

    /// Releases a previously acquired lock on `T`.
    ///
    /// If `detach` has already been called and this is the last active
    /// lock, calling `put()` will trigger the actual drop of `T`.
    ///
    /// # Safety
    ///
    /// `put()` must be called exactly once for each successful `get()`
    /// call that returned `Some(_)`.
    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() };
        }
    }

    /// Locks and obtains a reference to the inner `T`.
    ///
    /// This method returns `None` if another instance of `MyHandle` has
    /// already detached the value.
    ///
    /// If `detach` is called while `T` is locked, subsequent calls to
    /// `get()` will return `None`. However, `T` will not be dropped until
    /// `put()` is called.
    ///
    /// Therefore, once this method successfully returns a reference,
    /// it is safe to access `T` until the corresponding `put()` call.
    pub fn get(&self) -> Option<&T> {
        self.get_with(0)
    }

    /// Initiates early dropping of `T`.
    ///
    /// After this method is called, all subsequent calls to `get()`
    /// will return `None`. Any existing references obtained via `get()`
    /// remain valid and may continue to safely access `T` until the
    /// corresponding `put()` calls are made.
    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() };
        }
    }
}

/// `T` might be dropped through a reference, so `MyHandle<T>` cannot be `Sync`
/// unless `T: Send`.
///
/// This prevents `&MyHandle<T>` from being safely sent to another thread
/// if `T` is not `Send`.
unsafe impl<T> Sync for MyHandle<T> where T: Sync + Send {}