Preview
Open Original
I've published a smart pointer to crate.io, based on our previous discussion (https://www.reddit.com/r/rust/comments/1pipj05/atomic_memory_ordering_confusion_can_atomic/). I think it might be okay now, but who knows.
Since the code is quite short, I'll post it here for convenience.
Edit: for bugs found by Consistent_Milk4660, I modified the code as following:
use std::{ cell::UnsafeCell, ops::Deref, sync::{ Arc, atomic::{self, AtomicI32, Ordering}, }, }; /// A smart pointer similar to `Arc<T>`, but allows `T` to be /// safely dropped early, providi...I've published a smart pointer to crate.io, based on our previous discussion (https://www.reddit.com/r/rust/comments/1pipj05/atomic_memory_ordering_confusion_can_atomic/). I think it might be okay now, but who knows.
Since the code is quite short, I'll post it here for convenience.
Edit: for bugs found by Consistent_Milk4660, I modified the code as following:
use std::{ cell::UnsafeCell, ops::Deref, 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, } /// The RAII guard for accessing T 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> { /// 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<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() }; } } /// 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<MyHandleGuard<'_, 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 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(); } } } /// `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 {} Similar Posts
Loading similar posts...