Bug 1549559 - Introduce ArcSlice, a small wrapper over ThinArc but without an explicit header. r=heycam
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 09 May 2019 10:53:50 +0000
changeset 532023 dc57611c4e71768dc1f0e226e4b891634ec71b78
parent 532022 34b343ca6c2a44e1fd4402a0d7eb247cc2c6df2e
child 532024 51a7edbe0a9e8f63e45d59d6a9a18bc0e700a199
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1549559
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1549559 - Introduce ArcSlice, a small wrapper over ThinArc but without an explicit header. r=heycam We could make the header PhantomData or something, but then we wouldn't be able to bind to C++, since C++ doesn't have ZSTs. So add a canary instead to add a runtime check of stuff being sane. Differential Revision: https://phabricator.services.mozilla.com/D30133
servo/components/servo_arc/lib.rs
servo/components/style/lib.rs
servo/components/style/values/animated/mod.rs
servo/components/style_traits/arc_slice.rs
servo/components/style_traits/lib.rs
servo/components/style_traits/specified_value_info.rs
--- a/servo/components/servo_arc/lib.rs
+++ b/servo/components/servo_arc/lib.rs
@@ -779,16 +779,23 @@ type HeaderSliceWithLength<H, T> = Heade
 /// `ThinArc` solves this by storing the length in the allocation itself,
 /// via `HeaderSliceWithLength`.
 #[repr(C)]
 pub struct ThinArc<H, T> {
     ptr: ptr::NonNull<ArcInner<HeaderSliceWithLength<H, [T; 0]>>>,
     phantom: PhantomData<(H, T)>,
 }
 
+
+impl<H: fmt::Debug, T: fmt::Debug> fmt::Debug for ThinArc<H, T> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Debug::fmt(self.deref(), f)
+    }
+}
+
 unsafe impl<H: Sync + Send, T: Sync + Send> Send for ThinArc<H, T> {}
 unsafe impl<H: Sync + Send, T: Sync + Send> Sync for ThinArc<H, T> {}
 
 // Synthesize a fat pointer from a thin pointer.
 //
 // See the comment around the analogous operation in from_header_and_iter.
 fn thin_to_thick<H, T>(
     thin: *mut ArcInner<HeaderSliceWithLength<H, [T; 0]>>,
@@ -851,27 +858,31 @@ impl<H, T> ThinArc<H, T> {
     {
         let header = HeaderWithLength::new(header, items.len());
         Arc::into_thin(Arc::from_header_and_iter_alloc(
             alloc, header, items, /* is_static = */ true,
         ))
     }
 
     /// Returns the address on the heap of the ThinArc itself -- not the T
-    /// within it -- for memory reporting.
-    ///
+    /// within it -- for memory reporting, and bindings.
+    #[inline]
+    pub fn ptr(&self) -> *const c_void {
+        self.ptr.as_ptr() as *const ArcInner<T> as *const c_void
+    }
+
     /// If this is a static ThinArc, this returns null.
     #[inline]
     pub fn heap_ptr(&self) -> *const c_void {
         let is_static =
             ThinArc::with_arc(self, |a| a.inner().count.load(Relaxed) == STATIC_REFCOUNT);
         if is_static {
             ptr::null()
         } else {
-            self.ptr.as_ptr() as *const ArcInner<T> as *const c_void
+            self.ptr()
         }
     }
 }
 
 impl<H, T> Deref for ThinArc<H, T> {
     type Target = HeaderSliceWithLength<H, [T]>;
 
     #[inline]
--- a/servo/components/style/lib.rs
+++ b/servo/components/style/lib.rs
@@ -183,16 +183,17 @@ pub use crate::gecko_string_cache::Names
 pub use html5ever::LocalName;
 #[cfg(feature = "servo")]
 pub use html5ever::Namespace;
 #[cfg(feature = "servo")]
 pub use html5ever::Prefix;
 #[cfg(feature = "servo")]
 pub use servo_atoms::Atom;
 
+pub use style_traits::arc_slice::ArcSlice;
 pub use style_traits::owned_slice::OwnedSlice;
 
 /// The CSS properties supported by the style system.
 /// Generated from the properties.mako.rs template by build.rs
 #[macro_use]
 #[allow(unsafe_code)]
 #[deny(missing_docs)]
 pub mod properties {
--- a/servo/components/style/values/animated/mod.rs
+++ b/servo/components/style/values/animated/mod.rs
@@ -457,8 +457,22 @@ where
     fn to_animated_zero(&self) -> Result<Self, ()> {
         let v = self
             .iter()
             .map(|v| v.to_animated_zero())
             .collect::<Result<Vec<_>, _>>()?;
         Ok(v.into_boxed_slice())
     }
 }
+
+impl<T> ToAnimatedZero for crate::ArcSlice<T>
+where
+    T: ToAnimatedZero,
+{
+    #[inline]
+    fn to_animated_zero(&self) -> Result<Self, ()> {
+        let v = self
+            .iter()
+            .map(|v| v.to_animated_zero())
+            .collect::<Result<Vec<_>, _>>()?;
+        Ok(crate::ArcSlice::from_iter(v.into_iter()))
+    }
+}
new file mode 100644
--- /dev/null
+++ b/servo/components/style_traits/arc_slice.rs
@@ -0,0 +1,66 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+//! A thin atomically-reference-counted slice.
+
+use servo_arc::ThinArc;
+use std::mem;
+use std::ops::Deref;
+use std::ptr::NonNull;
+
+/// A canary that we stash in ArcSlices.
+///
+/// Given we cannot use a zero-sized-type for the header, since well, C++
+/// doesn't have zsts, and we want to use cbindgen for this type, we may as well
+/// assert some sanity at runtime.
+const ARC_SLICE_CANARY: u32 = 0xf3f3f3f3;
+
+/// A wrapper type for a refcounted slice using ThinArc.
+///
+/// cbindgen:derive-eq=false
+/// cbindgen:derive-neq=false
+#[repr(C)]
+#[derive(Debug, Clone, PartialEq, Eq, ToShmem)]
+pub struct ArcSlice<T>(#[shmem(field_bound)] ThinArc<u32, T>);
+
+impl<T> Deref for ArcSlice<T> {
+    type Target = [T];
+
+    #[inline]
+    fn deref(&self) -> &Self::Target {
+        debug_assert_eq!(self.0.header.header, ARC_SLICE_CANARY);
+        &self.0.slice
+    }
+}
+
+/// The inner pointer of an ArcSlice<T>, to be sent via FFI.
+/// The type of the pointer is a bit of a lie, we just want to preserve the type
+/// but these pointers cannot be constructed outside of this crate, so we're
+/// good.
+#[repr(C)]
+pub struct ForgottenArcSlicePtr<T>(NonNull<T>);
+
+impl<T> ArcSlice<T> {
+    /// Creates an Arc for a slice using the given iterator to generate the
+    /// slice.
+    #[inline]
+    pub fn from_iter<I>(items: I) -> Self
+    where
+        I: Iterator<Item = T> + ExactSizeIterator,
+    {
+        ArcSlice(ThinArc::from_header_and_iter(ARC_SLICE_CANARY, items))
+    }
+
+    /// Creates a value that can be passed via FFI, and forgets this value
+    /// altogether.
+    #[inline]
+    #[allow(unsafe_code)]
+    pub fn forget(self) -> ForgottenArcSlicePtr<T> {
+        let ret = unsafe {
+            ForgottenArcSlicePtr(NonNull::new_unchecked(self.0.ptr() as *const _ as *mut _))
+        };
+        mem::forget(self);
+        ret
+    }
+}
--- a/servo/components/style_traits/lib.rs
+++ b/servo/components/style_traits/lib.rs
@@ -79,16 +79,17 @@ impl PinchZoomFactor {
 pub enum CSSPixel {}
 
 // In summary, the hierarchy of pixel units and the factors to convert from one to the next:
 //
 // DevicePixel
 //   / hidpi_ratio => DeviceIndependentPixel
 //     / desktop_zoom => CSSPixel
 
+pub mod arc_slice;
 pub mod specified_value_info;
 #[macro_use]
 pub mod values;
 #[macro_use]
 pub mod viewport;
 pub mod owned_slice;
 
 pub use crate::specified_value_info::{CssType, KeywordsCollectFn, SpecifiedValueInfo};
--- a/servo/components/style_traits/specified_value_info.rs
+++ b/servo/components/style_traits/specified_value_info.rs
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 //! Value information for devtools.
 
+use crate::arc_slice::ArcSlice;
 use servo_arc::Arc;
 use std::ops::Range;
 use std::sync::Arc as StdArc;
 
 /// Type of value that a property supports. This is used by Gecko's
 /// devtools to make sense about value it parses, and types listed
 /// here should match InspectorPropertyType in InspectorUtils.webidl.
 ///
@@ -111,16 +112,17 @@ macro_rules! impl_generic_specified_valu
             }
         }
     };
 }
 impl_generic_specified_value_info!(Option<T>);
 impl_generic_specified_value_info!(Vec<T>);
 impl_generic_specified_value_info!(Arc<T>);
 impl_generic_specified_value_info!(StdArc<T>);
+impl_generic_specified_value_info!(ArcSlice<T>);
 impl_generic_specified_value_info!(Range<Idx>);
 
 impl<T1, T2> SpecifiedValueInfo for (T1, T2)
 where
     T1: SpecifiedValueInfo,
     T2: SpecifiedValueInfo,
 {
     const SUPPORTED_TYPES: u8 = T1::SUPPORTED_TYPES | T2::SUPPORTED_TYPES;