diff --git a/components/servo_arc/lib.rs b/components/servo_arc/lib.rs index ff4074767f8..dacd0cebd2a 100644 --- a/components/servo_arc/lib.rs +++ b/components/servo_arc/lib.rs @@ -637,6 +637,8 @@ impl ThinArc { let result = f(&transient); // Forget the transient Arc to leave the refcount untouched. + // XXXManishearth this can be removed when unions stabilize, + // since then NoDrop becomes zero overhead mem::forget(transient); // Forward the result. @@ -700,6 +702,95 @@ impl PartialEq for ThinArc impl Eq for ThinArc {} +/// An Arc, except it holds a pointer to the T instead of to the +/// entire ArcInner. +/// +/// ```text +/// Arc RawOffsetArc +/// | | +/// v v +/// --------------------- +/// | RefCount | T (data) | [ArcInner] +/// --------------------- +/// ``` +/// +/// This means that this is a direct pointer to +/// its contained data (and can be read from by both C++ and Rust), +/// but we can also convert it to a "regular" Arc by removing the offset +pub struct RawOffsetArc { + ptr: NonZeroPtrMut, +} + +unsafe impl Send for RawOffsetArc {} +unsafe impl Sync for RawOffsetArc {} + +impl Deref for RawOffsetArc { + type Target = T; + fn deref(&self) -> &Self::Target { + unsafe { &*self.ptr.ptr() } + } +} + +impl Clone for RawOffsetArc { + fn clone(&self) -> Self { + RawOffsetArc::with_arc(self, |a| Arc::into_raw_offset(a.clone())) + } +} + +impl Drop for RawOffsetArc { + fn drop(&mut self) { + let _ = Arc::from_raw_offset(RawOffsetArc { ptr: self.ptr.clone() }); + } +} + + +impl fmt::Debug for RawOffsetArc { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl RawOffsetArc { + /// Temporarily converts |self| into a bonafide Arc and exposes it to the + /// provided callback. The refcount is not modified. + #[inline(always)] + pub fn with_arc(&self, f: F) -> U + where F: FnOnce(&Arc) -> U + { + // Synthesize transient Arc, which never touches the refcount of the ArcInner. + let transient = unsafe { NoDrop::new(Arc::from_raw(self.ptr.ptr())) }; + + // Expose the transient Arc to the callback, which may clone it if it wants. + let result = f(&transient); + + // Forget the transient Arc to leave the refcount untouched. + // XXXManishearth this can be removed when unions stabilize, + // since then NoDrop becomes zero overhead + mem::forget(transient); + + // Forward the result. + result + } +} + +impl Arc { + /// Converts an Arc into a RawOffsetArc. This consumes the Arc, so the refcount + /// is not modified. + pub fn into_raw_offset(a: Self) -> RawOffsetArc { + RawOffsetArc { + ptr: NonZeroPtrMut::new(Arc::into_raw(a) as *mut T), + } + } + + /// Converts a RawOffsetArc into an Arc. This consumes the RawOffsetArc, so the refcount + /// is not modified. + pub fn from_raw_offset(a: RawOffsetArc) -> Self { + let ptr = a.ptr.ptr(); + mem::forget(a); + unsafe { Arc::from_raw(ptr) } + } +} + #[cfg(test)] mod tests { use std::clone::Clone;