style: Gracefully handle errors creating shared memory UA style sheets.

We still panic in a debug build, so that developers can notice when they
need to add a new static atom after modifying UA sheets.

We also add telemetry to note when this happens, add an app note to a
crash report, in case any crash later on occurs, and re-up the existing,
expired shared memory sheet telemetry probes so we can look at them
again.

Differential Revision: https://phabricator.services.mozilla.com/D73188
This commit is contained in:
Cameron McCormack 2020-05-11 00:11:45 +00:00 committed by Emilio Cobos Álvarez
parent 709c52fefb
commit 6bae0b99ca
10 changed files with 184 additions and 122 deletions

View file

@ -85,9 +85,22 @@ pub fn add_predicate(where_clause: &mut Option<syn::WhereClause>, pred: WherePre
.push(pred); .push(pred);
} }
pub fn fmap_match<F>(input: &DeriveInput, bind_style: BindStyle, mut f: F) -> TokenStream pub fn fmap_match<F>(input: &DeriveInput, bind_style: BindStyle, f: F) -> TokenStream
where where
F: FnMut(BindingInfo) -> TokenStream, F: FnMut(&BindingInfo) -> TokenStream,
{
fmap2_match(input, bind_style, f, |_| None)
}
pub fn fmap2_match<F, G>(
input: &DeriveInput,
bind_style: BindStyle,
mut f: F,
mut g: G,
) -> TokenStream
where
F: FnMut(&BindingInfo) -> TokenStream,
G: FnMut(&BindingInfo) -> Option<TokenStream>,
{ {
let mut s = synstructure::Structure::new(input); let mut s = synstructure::Structure::new(input);
s.variants_mut().iter_mut().for_each(|v| { s.variants_mut().iter_mut().for_each(|v| {
@ -95,12 +108,20 @@ where
}); });
s.each_variant(|variant| { s.each_variant(|variant| {
let (mapped, mapped_fields) = value(variant, "mapped"); let (mapped, mapped_fields) = value(variant, "mapped");
let fields_pairs = variant.bindings().iter().zip(mapped_fields); let fields_pairs = variant.bindings().iter().zip(mapped_fields.iter());
let mut computations = quote!(); let mut computations = quote!();
computations.append_all(fields_pairs.map(|(field, mapped_field)| { computations.append_all(fields_pairs.map(|(field, mapped_field)| {
let expr = f(field.clone()); let expr = f(field);
quote! { let #mapped_field = #expr; } quote! { let #mapped_field = #expr; }
})); }));
computations.append_all(
mapped_fields
.iter()
.map(|mapped_field| match g(mapped_field) {
Some(expr) => quote! { let #mapped_field = #expr; },
None => quote!(),
}),
);
computations.append_all(mapped); computations.append_all(mapped);
Some(computations) Some(computations)
}) })

View file

@ -18,7 +18,7 @@ use std::fmt::{self, Write};
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
use std::sync::RwLock; use std::sync::RwLock;
use style_traits::{CssWriter, ParseError, ToCss}; use style_traits::{CssWriter, ParseError, ToCss};
use to_shmem::{SharedMemoryBuilder, ToShmem}; use to_shmem::{self, SharedMemoryBuilder, ToShmem};
/// A CSS url() value for gecko. /// A CSS url() value for gecko.
#[css(function = "url")] #[css(function = "url")]
@ -241,11 +241,11 @@ impl LoadDataSource {
} }
impl ToShmem for LoadDataSource { impl ToShmem for LoadDataSource {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
ManuallyDrop::new(match self { Ok(ManuallyDrop::new(match self {
LoadDataSource::Owned(..) => LoadDataSource::Lazy, LoadDataSource::Owned(..) => LoadDataSource::Lazy,
LoadDataSource::Lazy => LoadDataSource::Lazy, LoadDataSource::Lazy => LoadDataSource::Lazy,
}) }))
} }
} }

View file

@ -3,7 +3,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#![allow(unsafe_code)] #![allow(unsafe_code)]
// This is needed for the constants in atom_macro.rs, because we have some // This is needed for the constants in atom_macro.rs, because we have some
// atoms whose names differ only by case, e.g. datetime and dateTime. // atoms whose names differ only by case, e.g. datetime and dateTime.
#![allow(non_upper_case_globals)] #![allow(non_upper_case_globals)]
@ -30,7 +29,7 @@ use std::num::NonZeroUsize;
use std::ops::Deref; use std::ops::Deref;
use std::{slice, str}; use std::{slice, str};
use style_traits::SpecifiedValueInfo; use style_traits::SpecifiedValueInfo;
use to_shmem::{SharedMemoryBuilder, ToShmem}; use to_shmem::{self, SharedMemoryBuilder, ToShmem};
#[macro_use] #[macro_use]
#[allow(improper_ctypes, non_camel_case_types, missing_docs)] #[allow(improper_ctypes, non_camel_case_types, missing_docs)]
@ -132,14 +131,15 @@ impl Borrow<WeakAtom> for Atom {
} }
impl ToShmem for Atom { impl ToShmem for Atom {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
assert!( if !self.is_static() {
self.is_static(), return Err(format!(
"ToShmem failed for Atom: must be a static atom: {}", "ToShmem failed for Atom: must be a static atom: {}",
self self
); ));
}
ManuallyDrop::new(Atom(self.0)) Ok(ManuallyDrop::new(Atom(self.0)))
} }
} }

View file

@ -258,20 +258,20 @@ impl<T> Locked<T> {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
impl<T: ToShmem> ToShmem for Locked<T> { impl<T: ToShmem> ToShmem for Locked<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
let guard = self.shared_lock.read(); let guard = self.shared_lock.read();
ManuallyDrop::new(Locked { Ok(ManuallyDrop::new(Locked {
shared_lock: SharedRwLock::read_only(), shared_lock: SharedRwLock::read_only(),
data: UnsafeCell::new(ManuallyDrop::into_inner( data: UnsafeCell::new(ManuallyDrop::into_inner(
self.read_with(&guard).to_shmem(builder), self.read_with(&guard).to_shmem(builder)?,
)), )),
}) }))
} }
} }
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
impl<T: ToShmem> ToShmem for Locked<T> { impl<T: ToShmem> ToShmem for Locked<T> {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
panic!("ToShmem not supported in Servo currently") panic!("ToShmem not supported in Servo currently")
} }
} }

View file

@ -15,9 +15,8 @@ use crate::stylesheets::{CssRule, Origin, StylesheetInDocument};
use crate::values::CssUrl; use crate::values::CssUrl;
use cssparser::SourceLocation; use cssparser::SourceLocation;
use std::fmt::{self, Write}; use std::fmt::{self, Write};
use std::mem::ManuallyDrop;
use style_traits::{CssWriter, ToCss}; use style_traits::{CssWriter, ToCss};
use to_shmem::{SharedMemoryBuilder, ToShmem}; use to_shmem::{self, SharedMemoryBuilder, ToShmem};
/// With asynchronous stylesheet parsing, we can't synchronously create a /// With asynchronous stylesheet parsing, we can't synchronously create a
/// GeckoStyleSheet. So we use this placeholder instead. /// GeckoStyleSheet. So we use this placeholder instead.
@ -184,8 +183,10 @@ pub struct ImportRule {
} }
impl ToShmem for ImportRule { impl ToShmem for ImportRule {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
panic!("ToShmem failed for ImportRule: cannot handle imported style sheets") Err(String::from(
"ToShmem failed for ImportRule: cannot handle imported style sheets",
))
} }
} }

View file

@ -40,7 +40,7 @@ use std::fmt;
use std::mem::{self, ManuallyDrop}; use std::mem::{self, ManuallyDrop};
use style_traits::ParsingMode; use style_traits::ParsingMode;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use to_shmem::{SharedMemoryBuilder, ToShmem}; use to_shmem::{self, SharedMemoryBuilder, ToShmem};
pub use self::counter_style_rule::CounterStyleRule; pub use self::counter_style_rule::CounterStyleRule;
pub use self::document_rule::DocumentRule; pub use self::document_rule::DocumentRule;
@ -117,20 +117,25 @@ impl Drop for UrlExtraData {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
impl ToShmem for UrlExtraData { impl ToShmem for UrlExtraData {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
if self.0 & 1 == 0 { if self.0 & 1 == 0 {
let shared_extra_datas = unsafe { &structs::URLExtraData_sShared }; let shared_extra_datas = unsafe { &structs::URLExtraData_sShared };
let self_ptr = self.as_ref() as *const _ as *mut _; let self_ptr = self.as_ref() as *const _ as *mut _;
let sheet_id = shared_extra_datas let sheet_id = shared_extra_datas
.iter() .iter()
.position(|r| r.mRawPtr == self_ptr) .position(|r| r.mRawPtr == self_ptr);
.expect( let sheet_id = match sheet_id {
"ToShmem failed for UrlExtraData: expected sheet's URLExtraData to be in \ Some(id) => id,
URLExtraData::sShared", None => {
); return Err(String::from(
ManuallyDrop::new(UrlExtraData((sheet_id << 1) | 1)) "ToShmem failed for UrlExtraData: expected sheet's URLExtraData to be in \
URLExtraData::sShared",
));
},
};
Ok(ManuallyDrop::new(UrlExtraData((sheet_id << 1) | 1)))
} else { } else {
ManuallyDrop::new(UrlExtraData(self.0)) Ok(ManuallyDrop::new(UrlExtraData(self.0)))
} }
} }
} }

View file

@ -32,7 +32,7 @@ use std::mem::{self, ManuallyDrop};
use std::slice; use std::slice;
use style_traits::{CssWriter, ParseError, ToCss}; use style_traits::{CssWriter, ParseError, ToCss};
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use to_shmem::{SharedMemoryBuilder, ToShmem}; use to_shmem::{self, SharedMemoryBuilder, ToShmem};
pub use crate::values::computed::Length as MozScriptMinSize; pub use crate::values::computed::Length as MozScriptMinSize;
pub use crate::values::specified::font::{FontSynthesis, MozScriptSizeMultiplier}; pub use crate::values::specified::font::{FontSynthesis, MozScriptSizeMultiplier};
@ -466,19 +466,20 @@ pub enum FontFamilyList {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
impl ToShmem for FontFamilyList { impl ToShmem for FontFamilyList {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
// In practice, the only SharedFontList objects we create from shared // In practice, the only SharedFontList objects we create from shared
// style sheets are ones with a single generic entry. // style sheets are ones with a single generic entry.
ManuallyDrop::new(match *self { Ok(ManuallyDrop::new(match *self {
FontFamilyList::SharedFontList(ref r) => { FontFamilyList::SharedFontList(ref r) => {
assert!( if !(r.mNames.len() == 1 && r.mNames[0].mName.mRawPtr.is_null()) {
r.mNames.len() == 1 && r.mNames[0].mName.mRawPtr.is_null(), return Err(String::from(
"ToShmem failed for FontFamilyList: cannot handle non-generic families", "ToShmem failed for FontFamilyList: cannot handle non-generic families",
); ));
}
FontFamilyList::Generic(r.mNames[0].mGeneric) FontFamilyList::Generic(r.mNames[0].mGeneric)
}, },
FontFamilyList::Generic(t) => FontFamilyList::Generic(t), FontFamilyList::Generic(t) => FontFamilyList::Generic(t),
}) }))
} }
} }

View file

@ -13,7 +13,7 @@ use std::marker::PhantomData;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::ptr::NonNull; use std::ptr::NonNull;
use std::{fmt, iter, mem, slice}; use std::{fmt, iter, mem, slice};
use to_shmem::{SharedMemoryBuilder, ToShmem}; use to_shmem::{self, SharedMemoryBuilder, ToShmem};
/// A struct that basically replaces a `Box<[T]>`, but which cbindgen can /// A struct that basically replaces a `Box<[T]>`, but which cbindgen can
/// understand. /// understand.
@ -159,10 +159,10 @@ impl<T: MallocSizeOf + Sized> MallocSizeOf for OwnedSlice<T> {
} }
impl<T: ToShmem + Sized> ToShmem for OwnedSlice<T> { impl<T: ToShmem + Sized> ToShmem for OwnedSlice<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> mem::ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
unsafe { unsafe {
let dest = to_shmem::to_shmem_slice(self.iter(), builder); let dest = to_shmem::to_shmem_slice(self.iter(), builder)?;
mem::ManuallyDrop::new(Self::from(Box::from_raw(dest))) Ok(mem::ManuallyDrop::new(Self::from(Box::from_raw(dest))))
} }
} }
} }

View file

@ -42,6 +42,11 @@ use std::slice;
use std::str; use std::str;
use thin_slice::ThinBoxedSlice; use thin_slice::ThinBoxedSlice;
/// Result type for ToShmem::to_shmem.
///
/// The String is an error message describing why the call failed.
pub type Result<T> = std::result::Result<ManuallyDrop<T>, String>;
// Various pointer arithmetic functions in this file can be replaced with // Various pointer arithmetic functions in this file can be replaced with
// functions on `Layout` once they have stabilized: // functions on `Layout` once they have stabilized:
// //
@ -114,13 +119,13 @@ impl SharedMemoryBuilder {
/// a shared memory buffer by calling ToShmem::to_shmem on it. /// a shared memory buffer by calling ToShmem::to_shmem on it.
/// ///
/// Panics if there is insufficient space in the buffer. /// Panics if there is insufficient space in the buffer.
pub fn write<T: ToShmem>(&mut self, value: &T) -> *mut T { pub fn write<T: ToShmem>(&mut self, value: &T) -> std::result::Result<*mut T, String> {
// Reserve space for the value. // Reserve space for the value.
let dest: *mut T = self.alloc_value(); let dest: *mut T = self.alloc_value();
// Make a clone of the value with all of its heap allocations // Make a clone of the value with all of its heap allocations
// placed in the shared memory buffer. // placed in the shared memory buffer.
let value = value.to_shmem(self); let value = value.to_shmem(self)?;
unsafe { unsafe {
// Copy the value into the buffer. // Copy the value into the buffer.
@ -128,7 +133,7 @@ impl SharedMemoryBuilder {
} }
// Return a pointer to the shared value. // Return a pointer to the shared value.
dest Ok(dest)
} }
/// Reserves space in the shared memory buffer to fit a value of type T, /// Reserves space in the shared memory buffer to fit a value of type T,
@ -190,7 +195,10 @@ pub trait ToShmem: Sized {
/// ///
/// The return type is wrapped in ManuallyDrop to make it harder to /// The return type is wrapped in ManuallyDrop to make it harder to
/// accidentally invoke the destructor of the value that is produced. /// accidentally invoke the destructor of the value that is produced.
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self>; ///
/// Returns a Result so that we can gracefully recover from unexpected
/// content.
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self>;
} }
#[macro_export] #[macro_export]
@ -201,8 +209,8 @@ macro_rules! impl_trivial_to_shmem {
fn to_shmem( fn to_shmem(
&self, &self,
_builder: &mut $crate::SharedMemoryBuilder, _builder: &mut $crate::SharedMemoryBuilder,
) -> ::std::mem::ManuallyDrop<Self> { ) -> $crate::Result<Self> {
::std::mem::ManuallyDrop::new(*self) $crate::Result::Ok(::std::mem::ManuallyDrop::new(*self))
} }
} }
)* )*
@ -231,58 +239,60 @@ impl_trivial_to_shmem!(cssparser::SourceLocation);
impl_trivial_to_shmem!(cssparser::TokenSerializationType); impl_trivial_to_shmem!(cssparser::TokenSerializationType);
impl<T> ToShmem for PhantomData<T> { impl<T> ToShmem for PhantomData<T> {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
ManuallyDrop::new(*self) Ok(ManuallyDrop::new(*self))
} }
} }
impl<T: ToShmem> ToShmem for Range<T> { impl<T: ToShmem> ToShmem for Range<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
ManuallyDrop::new(Range { Ok(ManuallyDrop::new(Range {
start: ManuallyDrop::into_inner(self.start.to_shmem(builder)), start: ManuallyDrop::into_inner(self.start.to_shmem(builder)?),
end: ManuallyDrop::into_inner(self.end.to_shmem(builder)), end: ManuallyDrop::into_inner(self.end.to_shmem(builder)?),
}) }))
} }
} }
impl ToShmem for cssparser::UnicodeRange { impl ToShmem for cssparser::UnicodeRange {
fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> {
ManuallyDrop::new(cssparser::UnicodeRange { Ok(ManuallyDrop::new(cssparser::UnicodeRange {
start: self.start, start: self.start,
end: self.end, end: self.end,
}) }))
} }
} }
impl<T: ToShmem, U: ToShmem> ToShmem for (T, U) { impl<T: ToShmem, U: ToShmem> ToShmem for (T, U) {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
ManuallyDrop::new(( Ok(ManuallyDrop::new((
ManuallyDrop::into_inner(self.0.to_shmem(builder)), ManuallyDrop::into_inner(self.0.to_shmem(builder)?),
ManuallyDrop::into_inner(self.1.to_shmem(builder)), ManuallyDrop::into_inner(self.1.to_shmem(builder)?),
)) )))
} }
} }
impl<T: ToShmem> ToShmem for Wrapping<T> { impl<T: ToShmem> ToShmem for Wrapping<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
ManuallyDrop::new(Wrapping(ManuallyDrop::into_inner(self.0.to_shmem(builder)))) Ok(ManuallyDrop::new(Wrapping(ManuallyDrop::into_inner(
self.0.to_shmem(builder)?,
))))
} }
} }
impl<T: ToShmem> ToShmem for Box<T> { impl<T: ToShmem> ToShmem for Box<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
// Reserve space for the boxed value. // Reserve space for the boxed value.
let dest: *mut T = builder.alloc_value(); let dest: *mut T = builder.alloc_value();
// Make a clone of the boxed value with all of its heap allocations // Make a clone of the boxed value with all of its heap allocations
// placed in the shared memory buffer. // placed in the shared memory buffer.
let value = (**self).to_shmem(builder); let value = (**self).to_shmem(builder)?;
unsafe { unsafe {
// Copy the value into the buffer. // Copy the value into the buffer.
ptr::write(dest, ManuallyDrop::into_inner(value)); ptr::write(dest, ManuallyDrop::into_inner(value));
ManuallyDrop::new(Box::from_raw(dest)) Ok(ManuallyDrop::new(Box::from_raw(dest)))
} }
} }
} }
@ -293,7 +303,7 @@ unsafe fn to_shmem_slice_ptr<'a, T, I>(
src: I, src: I,
dest: *mut T, dest: *mut T,
builder: &mut SharedMemoryBuilder, builder: &mut SharedMemoryBuilder,
) -> *mut [T] ) -> std::result::Result<*mut [T], String>
where where
T: 'a + ToShmem, T: 'a + ToShmem,
I: ExactSizeIterator<Item = &'a T>, I: ExactSizeIterator<Item = &'a T>,
@ -303,15 +313,18 @@ where
// Make a clone of each element from the iterator with its own heap // Make a clone of each element from the iterator with its own heap
// allocations placed in the buffer, and copy that clone into the buffer. // allocations placed in the buffer, and copy that clone into the buffer.
for (src, dest) in src.zip(dest.iter_mut()) { for (src, dest) in src.zip(dest.iter_mut()) {
ptr::write(dest, ManuallyDrop::into_inner(src.to_shmem(builder))); ptr::write(dest, ManuallyDrop::into_inner(src.to_shmem(builder)?));
} }
dest Ok(dest)
} }
/// Writes all the items in `src` into a slice in the shared memory buffer and /// Writes all the items in `src` into a slice in the shared memory buffer and
/// returns a pointer to the slice. /// returns a pointer to the slice.
pub unsafe fn to_shmem_slice<'a, T, I>(src: I, builder: &mut SharedMemoryBuilder) -> *mut [T] pub unsafe fn to_shmem_slice<'a, T, I>(
src: I,
builder: &mut SharedMemoryBuilder,
) -> std::result::Result<*mut [T], String>
where where
T: 'a + ToShmem, T: 'a + ToShmem,
I: ExactSizeIterator<Item = &'a T>, I: ExactSizeIterator<Item = &'a T>,
@ -321,16 +334,16 @@ where
} }
impl<T: ToShmem> ToShmem for Box<[T]> { impl<T: ToShmem> ToShmem for Box<[T]> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
unsafe { unsafe {
let dest = to_shmem_slice(self.iter(), builder); let dest = to_shmem_slice(self.iter(), builder)?;
ManuallyDrop::new(Box::from_raw(dest)) Ok(ManuallyDrop::new(Box::from_raw(dest)))
} }
} }
} }
impl<T: ToShmem> ToShmem for ThinBoxedSlice<T> { impl<T: ToShmem> ToShmem for ThinBoxedSlice<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
// We could support this if we needed but in practice we will never // We could support this if we needed but in practice we will never
// need to handle such big ThinBoxedSlices. // need to handle such big ThinBoxedSlices.
assert!( assert!(
@ -340,14 +353,14 @@ impl<T: ToShmem> ToShmem for ThinBoxedSlice<T> {
); );
unsafe { unsafe {
let dest = to_shmem_slice(self.iter(), builder); let dest = to_shmem_slice(self.iter(), builder)?;
ManuallyDrop::new(ThinBoxedSlice::from_raw(dest)) Ok(ManuallyDrop::new(ThinBoxedSlice::from_raw(dest)))
} }
} }
} }
impl ToShmem for Box<str> { impl ToShmem for Box<str> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
// Reserve space for the string bytes. // Reserve space for the string bytes.
let dest: *mut u8 = builder.alloc_array(self.len()); let dest: *mut u8 = builder.alloc_array(self.len());
@ -355,15 +368,15 @@ impl ToShmem for Box<str> {
// Copy the value into the buffer. // Copy the value into the buffer.
ptr::copy(self.as_ptr(), dest, self.len()); ptr::copy(self.as_ptr(), dest, self.len());
ManuallyDrop::new(Box::from_raw(str::from_utf8_unchecked_mut( Ok(ManuallyDrop::new(Box::from_raw(
slice::from_raw_parts_mut(dest, self.len()), str::from_utf8_unchecked_mut(slice::from_raw_parts_mut(dest, self.len())),
))) )))
} }
} }
} }
impl ToShmem for String { impl ToShmem for String {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
// Reserve space for the string bytes. // Reserve space for the string bytes.
let dest: *mut u8 = builder.alloc_array(self.len()); let dest: *mut u8 = builder.alloc_array(self.len());
@ -371,13 +384,17 @@ impl ToShmem for String {
// Copy the value into the buffer. // Copy the value into the buffer.
ptr::copy(self.as_ptr(), dest, self.len()); ptr::copy(self.as_ptr(), dest, self.len());
ManuallyDrop::new(String::from_raw_parts(dest, self.len(), self.len())) Ok(ManuallyDrop::new(String::from_raw_parts(
dest,
self.len(),
self.len(),
)))
} }
} }
} }
impl ToShmem for CString { impl ToShmem for CString {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
let len = self.as_bytes_with_nul().len(); let len = self.as_bytes_with_nul().len();
// Reserve space for the string bytes. // Reserve space for the string bytes.
@ -387,53 +404,55 @@ impl ToShmem for CString {
// Copy the value into the buffer. // Copy the value into the buffer.
ptr::copy(self.as_ptr(), dest, len); ptr::copy(self.as_ptr(), dest, len);
ManuallyDrop::new(CString::from_raw(dest)) Ok(ManuallyDrop::new(CString::from_raw(dest)))
} }
} }
} }
impl<T: ToShmem> ToShmem for Vec<T> { impl<T: ToShmem> ToShmem for Vec<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
unsafe { unsafe {
let dest = to_shmem_slice(self.iter(), builder) as *mut T; let dest = to_shmem_slice(self.iter(), builder)? as *mut T;
let dest_vec = Vec::from_raw_parts(dest, self.len(), self.len()); let dest_vec = Vec::from_raw_parts(dest, self.len(), self.len());
ManuallyDrop::new(dest_vec) Ok(ManuallyDrop::new(dest_vec))
} }
} }
} }
impl<T: ToShmem, A: Array<Item = T>> ToShmem for SmallVec<A> { impl<T: ToShmem, A: Array<Item = T>> ToShmem for SmallVec<A> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
let dest_vec = unsafe { let dest_vec = unsafe {
if self.spilled() { if self.spilled() {
// Place the items in a separate allocation in the shared memory // Place the items in a separate allocation in the shared memory
// buffer. // buffer.
let dest = to_shmem_slice(self.iter(), builder) as *mut T; let dest = to_shmem_slice(self.iter(), builder)? as *mut T;
SmallVec::from_raw_parts(dest, self.len(), self.len()) SmallVec::from_raw_parts(dest, self.len(), self.len())
} else { } else {
// Place the items inline. // Place the items inline.
let mut s = SmallVec::new(); let mut s = SmallVec::new();
to_shmem_slice_ptr(self.iter(), s.as_mut_ptr(), builder); to_shmem_slice_ptr(self.iter(), s.as_mut_ptr(), builder)?;
s.set_len(self.len()); s.set_len(self.len());
s s
} }
}; };
ManuallyDrop::new(dest_vec) Ok(ManuallyDrop::new(dest_vec))
} }
} }
impl<T: ToShmem> ToShmem for Option<T> { impl<T: ToShmem> ToShmem for Option<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
ManuallyDrop::new( let v = match self {
self.as_ref() Some(v) => Some(ManuallyDrop::into_inner(v.to_shmem(builder)?)),
.map(|v| ManuallyDrop::into_inner(v.to_shmem(builder))), None => None,
) };
Ok(ManuallyDrop::new(v))
} }
} }
impl<T: 'static + ToShmem> ToShmem for Arc<T> { impl<T: 'static + ToShmem> ToShmem for Arc<T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
// Assert that we don't encounter any shared references to values we // Assert that we don't encounter any shared references to values we
// don't expect. Those we expect are those noted by calling // don't expect. Those we expect are those noted by calling
// add_allowed_duplication_type, and should be types where we're fine // add_allowed_duplication_type, and should be types where we're fine
@ -453,7 +472,7 @@ impl<T: 'static + ToShmem> ToShmem for Arc<T> {
// Make a clone of the Arc-owned value with all of its heap allocations // Make a clone of the Arc-owned value with all of its heap allocations
// placed in the shared memory buffer. // placed in the shared memory buffer.
let value = (**self).to_shmem(builder); let value = (**self).to_shmem(builder)?;
// Create a new Arc with the shared value and have it place its // Create a new Arc with the shared value and have it place its
// ArcInner in the shared memory buffer. // ArcInner in the shared memory buffer.
@ -466,13 +485,13 @@ impl<T: 'static + ToShmem> ToShmem for Arc<T> {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
builder.shared_values.insert(self.heap_ptr()); builder.shared_values.insert(self.heap_ptr());
ManuallyDrop::new(static_arc) Ok(ManuallyDrop::new(static_arc))
} }
} }
} }
impl<H: 'static + ToShmem, T: 'static + ToShmem> ToShmem for ThinArc<H, T> { impl<H: 'static + ToShmem, T: 'static + ToShmem> ToShmem for ThinArc<H, T> {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
// We don't currently have any shared ThinArc values in stylesheets, // We don't currently have any shared ThinArc values in stylesheets,
// so don't support them for now. // so don't support them for now.
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@ -484,8 +503,11 @@ impl<H: 'static + ToShmem, T: 'static + ToShmem> ToShmem for ThinArc<H, T> {
// Make a clone of the Arc-owned header and slice values with all of // Make a clone of the Arc-owned header and slice values with all of
// their heap allocations placed in the shared memory buffer. // their heap allocations placed in the shared memory buffer.
let header = self.header.header.to_shmem(builder); let header = self.header.header.to_shmem(builder)?;
let values: Vec<ManuallyDrop<T>> = self.slice.iter().map(|v| v.to_shmem(builder)).collect(); let mut values = Vec::with_capacity(self.slice.len());
for v in self.slice.iter() {
values.push(v.to_shmem(builder)?);
}
// Create a new ThinArc with the shared value and have it place // Create a new ThinArc with the shared value and have it place
// its ArcInner in the shared memory buffer. // its ArcInner in the shared memory buffer.
@ -499,13 +521,13 @@ impl<H: 'static + ToShmem, T: 'static + ToShmem> ToShmem for ThinArc<H, T> {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
builder.shared_values.insert(self.heap_ptr()); builder.shared_values.insert(self.heap_ptr());
ManuallyDrop::new(static_arc) Ok(ManuallyDrop::new(static_arc))
} }
} }
} }
impl ToShmem for SmallBitVec { impl ToShmem for SmallBitVec {
fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> {
let storage = match self.clone().into_storage() { let storage = match self.clone().into_storage() {
InternalStorage::Spilled(vs) => { InternalStorage::Spilled(vs) => {
// Reserve space for the boxed slice values. // Reserve space for the boxed slice values.
@ -524,13 +546,15 @@ impl ToShmem for SmallBitVec {
}, },
InternalStorage::Inline(x) => InternalStorage::Inline(x), InternalStorage::Inline(x) => InternalStorage::Inline(x),
}; };
ManuallyDrop::new(unsafe { SmallBitVec::from_storage(storage) }) Ok(ManuallyDrop::new(unsafe {
SmallBitVec::from_storage(storage)
}))
} }
} }
#[cfg(feature = "string_cache")] #[cfg(feature = "string_cache")]
impl<Static: string_cache::StaticAtomSet> ToShmem for string_cache::Atom<Static> { impl<Static: string_cache::StaticAtomSet> ToShmem for string_cache::Atom<Static> {
fn to_shmem(&self, _: &mut SharedMemoryBuilder) -> ManuallyDrop<Self> { fn to_shmem(&self, _: &mut SharedMemoryBuilder) -> Result<Self> {
// NOTE(emilio): In practice, this can be implemented trivially if // NOTE(emilio): In practice, this can be implemented trivially if
// string_cache could expose the implementation detail of static atoms // string_cache could expose the implementation detail of static atoms
// being an index into the static table (and panicking in the // being an index into the static table (and panicking in the

View file

@ -27,13 +27,23 @@ pub fn derive(mut input: syn::DeriveInput) -> TokenStream {
input.generics.where_clause = where_clause; input.generics.where_clause = where_clause;
let match_body = cg::fmap_match(&input, BindStyle::Ref, |binding| { // Do all of the `to_shmem()?` calls before the `ManuallyDrop::into_inner()`
quote! { // calls, so that we don't drop a value in the shared memory buffer if one
::std::mem::ManuallyDrop::into_inner( // of the `to_shmem`s fails.
::to_shmem::ToShmem::to_shmem(#binding, builder) let match_body = cg::fmap2_match(
) &input,
} BindStyle::Ref,
}); |binding| {
quote! {
::to_shmem::ToShmem::to_shmem(#binding, builder)?
}
},
|binding| {
Some(quote! {
::std::mem::ManuallyDrop::into_inner(#binding)
})
},
);
let name = &input.ident; let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
@ -44,12 +54,12 @@ pub fn derive(mut input: syn::DeriveInput) -> TokenStream {
fn to_shmem( fn to_shmem(
&self, &self,
builder: &mut ::to_shmem::SharedMemoryBuilder, builder: &mut ::to_shmem::SharedMemoryBuilder,
) -> ::std::mem::ManuallyDrop<Self> { ) -> ::to_shmem::Result<Self> {
::std::mem::ManuallyDrop::new( Ok(::std::mem::ManuallyDrop::new(
match *self { match *self {
#match_body #match_body
} }
) ))
} }
} }
} }