diff --git a/src/sync/arc_cell.rs b/src/sync/arc_cell.rs index e26d45a94..d50008916 100644 --- a/src/sync/arc_cell.rs +++ b/src/sync/arc_cell.rs @@ -5,48 +5,56 @@ use std::sync::atomic::{AtomicUsize, Ordering}; /// A type providing atomic storage and retrieval of an `Arc`. #[derive(Debug)] -pub struct ArcCell(AtomicUsize, PhantomData>); +pub struct ArcCell { + ptr: AtomicUsize, + sem: AtomicUsize, + _marker: PhantomData>, +} impl Drop for ArcCell { fn drop(&mut self) { - self.take(); + unsafe { mem::transmute::<_, Arc>(self.ptr.load(Ordering::Relaxed)); } } } impl ArcCell { /// Creates a new `ArcCell`. pub fn new(t: Arc) -> ArcCell { - ArcCell(AtomicUsize::new(unsafe { mem::transmute(t) }), PhantomData) - } - - fn take(&self) -> Arc { - loop { - match self.0.swap(0, Ordering::Acquire) { - 0 => {} - n => return unsafe { mem::transmute(n) } - } + ArcCell { + ptr: AtomicUsize::new(unsafe { mem::transmute(t) }), + sem: AtomicUsize::new(0), + _marker: PhantomData, } } - fn put(&self, t: Arc) { - debug_assert_eq!(self.0.load(Ordering::SeqCst), 0); - self.0.store(unsafe { mem::transmute(t) }, Ordering::Release); + /// Create a new `ArcCell` from the given `Arc` interior. + pub fn with_val(v: T) -> ArcCell { + ArcCell { + ptr: AtomicUsize::new(unsafe { mem::transmute(Arc::new(v)) }), + sem: AtomicUsize::new(0), + _marker: PhantomData, + } } /// Stores a new value in the `ArcCell`, returning the previous /// value. pub fn set(&self, t: Arc) -> Arc { - let old = self.take(); - self.put(t); - old + unsafe { + let t: usize = mem::transmute(t); + let old: Arc = mem::transmute(self.ptr.swap(t, Ordering::Acquire)); + while self.sem.load(Ordering::Relaxed) > 0 {} + old + } } /// Returns a copy of the value stored by the `ArcCell`. pub fn get(&self) -> Arc { - let t = self.take(); + self.sem.fetch_add(1, Ordering::Relaxed); + let t: Arc = unsafe { mem::transmute(self.ptr.load(Ordering::SeqCst)) }; // NB: correctness here depends on Arc's clone impl not panicking let out = t.clone(); - self.put(t); + self.sem.fetch_sub(1, Ordering::Relaxed); + mem::forget(t); out } }