/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! Trait for cloning data into a shared memory buffer. //! //! This module contains the SharedMemoryBuilder type and ToShmem trait. //! //! We put them here (and not in style_traits) so that we can derive ToShmem //! from the selectors and style crates.
use std::alloc::Layout; use std::collections::HashSet; use std::ffi::CString; use std::isize; use std::marker::PhantomData; use std::mem::{self, ManuallyDrop}; use std::num::Wrapping; use std::ops::Range; use std::os::raw::c_char; use std::ptr::{self, NonNull}; use std::slice; use std::str;
/// Result type for ToShmem::to_shmem. /// /// The String is an error message describing why the call failed. pubtype Result<T> = std::result::Result<ManuallyDrop<T>, String>;
/// A builder object that transforms and copies values into a fixed size buffer. pubstruct SharedMemoryBuilder { /// The buffer into which values will be copied.
buffer: *mut u8, /// The size of the buffer.
capacity: usize, /// The current position in the buffer, where the next value will be written /// at.
index: usize, /// Pointers to every shareable value that we store in the shared memory /// buffer. We use this to assert against encountering the same value /// twice, e.g. through another Arc reference, so that we don't /// inadvertently store duplicate copies of values. #[cfg(all(debug_assertions, feature = "servo_arc"))]
shared_values: HashSet<*const std::os::raw::c_void>,
}
/// Amount of padding needed after `size` bytes to ensure that the following /// address will satisfy `align`. fn padding_needed_for(size: usize, align: usize) -> usize {
padded_size(size, align).wrapping_sub(size)
}
/// Rounds up `size` so that the following address will satisfy `align`. fn padded_size(size: usize, align: usize) -> usize {
size.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1)
}
impl SharedMemoryBuilder { /// Creates a new SharedMemoryBuilder using the specified buffer. pubunsafefn new(buffer: *mut u8, capacity: usize) -> SharedMemoryBuilder {
SharedMemoryBuilder {
buffer,
capacity,
index: 0, #[cfg(all(debug_assertions, feature = "servo_arc"))]
shared_values: HashSet::new(),
}
}
/// Returns the number of bytes currently used in the buffer. #[inline] pubfn len(&self) -> usize { self.index
}
/// Writes a value into the shared memory buffer and returns a pointer to /// it in the buffer. /// /// The value is cloned and converted into a form suitable for placing into /// a shared memory buffer by calling ToShmem::to_shmem on it. /// /// Panics if there is insufficient space in the buffer. pubfn write<T: ToShmem>(&mutself, value: &T) -> std::result::Result<*mut T, String> { // Reserve space for the value. let dest: *mut T = self.alloc_value();
// Make a clone of the value with all of its heap allocations // placed in the shared memory buffer. let value = value.to_shmem(self)?;
unsafe { // Copy the value into the buffer.
ptr::write(dest, ManuallyDrop::into_inner(value));
}
// Return a pointer to the shared value.
Ok(dest)
}
/// Reserves space in the shared memory buffer to fit a value of type T, /// and returns a pointer to that reserved space. /// /// Panics if there is insufficient space in the buffer. pubfn alloc_value<T>(&mutself) -> *mut T { self.alloc(Layout::new::<T>())
}
/// Reserves space in the shared memory buffer to fit an array of values of /// type T, and returns a pointer to that reserved space. /// /// Panics if there is insufficient space in the buffer. pubfn alloc_array<T>(&mutself, len: usize) -> *mut T { if len == 0 { return NonNull::dangling().as_ptr();
}
let size = mem::size_of::<T>(); let align = mem::align_of::<T>();
/// Reserves space in the shared memory buffer that conforms to the /// specified layout, and returns a pointer to that reserved space. /// /// Panics if there is insufficient space in the buffer. pubfn alloc<T>(&mutself, layout: Layout) -> *mut T { // Amount of padding to align the value. // // The addition can't overflow, since self.index <= self.capacity, and // for us to have successfully allocated the buffer, `buffer + capacity` // can't overflow. let padding = padding_needed_for(self.buffer as usize + self.index, layout.align());
// Reserve space for the padding. let start = self.index.checked_add(padding).unwrap();
assert!(start <= std::isize::MAX as usize); // for the cast below
// Reserve space for the value. let end = start.checked_add(layout.size()).unwrap();
assert!(end <= self.capacity);
self.index = end; unsafe { self.buffer.add(start) as *mut T }
}
}
/// A type that can be copied into a SharedMemoryBuilder. pubtrait ToShmem: Sized { /// Clones this value into a form suitable for writing into a /// SharedMemoryBuilder. /// /// If this value owns any heap allocations, they should be written into /// `builder` so that the return value of this function can point to the /// copy in the shared memory buffer. /// /// The return type is wrapped in ManuallyDrop to make it harder to /// accidentally invoke the destructor of the value that is produced. /// /// Returns a Result so that we can gracefully recover from unexpected /// content. fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self>;
}
impl<T: ToShmem> ToShmem forBox<T> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> { // Reserve space for the boxed value. let dest: *mut T = builder.alloc_value();
// Make a clone of the boxed value with all of its heap allocations // placed in the shared memory buffer. let value = (**self).to_shmem(builder)?;
unsafe { // Copy the value into the buffer.
ptr::write(dest, ManuallyDrop::into_inner(value));
Ok(ManuallyDrop::new(Box::from_raw(dest)))
}
}
}
/// Converts all the items in `src` into shared memory form, writes them into /// the specified buffer, and returns a pointer to the slice. unsafefn to_shmem_slice_ptr<'a, T, I>(
src: I,
dest: *mut T,
builder: &mut SharedMemoryBuilder,
) -> std::result::Result<*mut [T], String> where
T: 'a + ToShmem,
I: ExactSizeIterator<Item = &'a T>,
{ let dest = slice::from_raw_parts_mut(dest, src.len());
// 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. for (src, dest) in src.zip(dest.iter_mut()) {
ptr::write(dest, ManuallyDrop::into_inner(src.to_shmem(builder)?));
}
Ok(dest)
}
/// Writes all the items in `src` into a slice in the shared memory buffer and /// returns a pointer to the slice. pubunsafefn to_shmem_slice<'a, T, I>(
src: I,
builder: &mut SharedMemoryBuilder,
) -> std::result::Result<*mut [T], String> where
T: 'a + ToShmem,
I: ExactSizeIterator<Item = &'a T>,
{ let dest = builder.alloc_array(src.len());
to_shmem_slice_ptr(src, dest, builder)
}
impl<T: ToShmem> ToShmem for Vec<T> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> { unsafe { let dest = to_shmem_slice(self.iter(), builder)? as *mut T; let dest_vec = Vec::from_raw_parts(dest, self.len(), self.len());
Ok(ManuallyDrop::new(dest_vec))
}
}
}
impl<T: ToShmem, S> ToShmem for HashSet<T, S> where Self: Default,
{ fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> Result<Self> { if !self.is_empty() { return Err(format!( "ToShmem failed for HashSet: We only support empty sets \
(we don't expect custom properties in UA sheets, they're observable by content)",
));
}
Ok(ManuallyDrop::new(Self::default()))
}
}
impl<T: ToShmem> ToShmem for Option<T> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> { let v = matchself {
Some(v) => Some(ManuallyDrop::into_inner(v.to_shmem(builder)?)),
None => None,
};
Ok(ManuallyDrop::new(v))
}
}
#[cfg(feature = "smallvec")] impl<T: ToShmem, A: smallvec::Array<Item = T>> ToShmem for smallvec::SmallVec<A> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> { let dest_vec = unsafe { ifself.spilled() { // Place the items in a separate allocation in the shared memory // buffer. let dest = to_shmem_slice(self.iter(), builder)? as *mut T; Self::from_raw_parts(dest, self.len(), self.len())
} else { // Place the items inline. letmut s = Self::new();
to_shmem_slice_ptr(self.iter(), s.as_mut_ptr(), builder)?;
s.set_len(self.len());
s
}
};
Ok(ManuallyDrop::new(dest_vec))
}
}
#[cfg(feature = "servo_arc")] impl<A: 'static, B: 'static> ToShmem for servo_arc::ArcUnion<A, B> where
servo_arc::Arc<A>: ToShmem,
servo_arc::Arc<B>: ToShmem,
{ fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> { use servo_arc::ArcUnionBorrow;
Ok(ManuallyDrop::new(matchself.borrow() {
ArcUnionBorrow::First(first) => Self::from_first(ManuallyDrop::into_inner(
first.with_arc(|a| a.to_shmem(builder))?,
)),
ArcUnionBorrow::Second(second) => Self::from_second(ManuallyDrop::into_inner(
second.with_arc(|a| a.to_shmem(builder))?,
)),
}))
}
} #[cfg(feature = "servo_arc")] impl<T: ToShmem> ToShmem for servo_arc::Arc<T> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> { // Assert that we don't encounter any shared references to values we // don't expect. #[cfg(debug_assertions)]
assert!(
!builder.shared_values.contains(&self.heap_ptr()), "ToShmem failed for Arc<{}>: encountered a value with multiple \
references.",
std::any::type_name::<T>()
);
// Make a clone of the Arc-owned value with all of its heap allocations // placed in the shared memory buffer. let value = (**self).to_shmem(builder)?;
// Create a new Arc with the shared value and have it place its // ArcInner in the shared memory buffer. unsafe { let static_arc = Self::new_static(
|layout| builder.alloc(layout),
ManuallyDrop::into_inner(value),
);
Ok(ManuallyDrop::new(static_arc))
}
}
} #[cfg(feature = "servo_arc")] impl<H: ToShmem, T: ToShmem> ToShmem for servo_arc::Arc<servo_arc::HeaderSlice<H, T>> { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> { // We don't currently have any shared ThinArc values in stylesheets, // so don't support them for now. #[cfg(debug_assertions)]
assert!(
!builder.shared_values.contains(&self.heap_ptr()), "ToShmem failed for ThinArc<T>: encountered a value with multiple references, which \
is not currently supported",
);
// Make a clone of the Arc-owned header and slice values with all of // their heap allocations placed in the shared memory buffer. let header = self.header.to_shmem(builder)?; letmut values = Vec::with_capacity(self.len()); for v inself.slice().iter() {
values.push(v.to_shmem(builder)?);
}
// Create a new ThinArc with the shared value and have it place // its ArcInner in the shared memory buffer. let len = values.len(); let static_arc = Self::from_header_and_iter_alloc(
|layout| builder.alloc(layout),
ManuallyDrop::into_inner(header),
values.into_iter().map(ManuallyDrop::into_inner),
len, /* is_static = */ true,
);
// NOTE: We need to do the work of allocating the header in shared memory even if the // length is zero, because an empty ThinVec, even though it doesn't allocate, references // static memory which will not be mapped to other processes, see bug 1841011. let len = self.len();
// nsTArrayHeader size. // FIXME: Would be nice not to hard-code this, but in practice thin-vec crate also relies // on this. let header_size = 2 * mem::size_of::<u32>(); let header_align = mem::size_of::<u32>();
let item_size = mem::size_of::<T>(); let item_align = mem::align_of::<T>();
// We don't need to support underalignment for now, this could be supported if needed.
assert!(item_align >= header_align);
let shmem_header_ptr = builder.alloc::<u8>(layout); let shmem_data_ptr = unsafe { shmem_header_ptr.add(header_size + header_padding) };
let data_ptr = self.as_ptr() as *const T as *const u8; let header_ptr = unsafe { data_ptr.sub(header_size + header_padding) };
unsafe { // Copy the header. Note this might copy a wrong capacity, but it doesn't matter, // because shared memory ptrs are immutable anyways, and we can't relocate.
ptr::copy(header_ptr, shmem_header_ptr, header_size); // ToShmem + copy the contents into the shared buffer.
to_shmem_slice_ptr(self.iter(), shmem_data_ptr as *mut T, builder)?; // Return the new ThinVec, which is just a pointer to the shared memory buffer. let shmem_thinvec: Self = mem::transmute(shmem_header_ptr);
// Sanity-check that the ptr and length match.
debug_assert_eq!(shmem_thinvec.as_ptr(), shmem_data_ptr as *const T);
debug_assert_eq!(shmem_thinvec.len(), len);
Ok(ManuallyDrop::new(shmem_thinvec))
}
}
}
#[cfg(feature = "smallbitvec")] impl ToShmem for smallbitvec::SmallBitVec { fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> Result<Self> { use smallbitvec::InternalStorage;
let storage = matchself.clone().into_storage() {
InternalStorage::Spilled(vs) => { // Reserve space for the boxed slice values. let len = vs.len(); let dest: *mut usize = builder.alloc_array(len);
unsafe { // Copy the value into the buffer. let src = vs.as_ptr() as *const usize;
ptr::copy(src, dest, len);
#[cfg(feature = "string_cache")] impl<Static: string_cache::StaticAtomSet> ToShmem for string_cache::Atom<Static> { fn to_shmem(&self, _: &mut SharedMemoryBuilder) -> Result<Self> { // NOTE(emilio): In practice, this can be implemented trivially if // string_cache could expose the implementation detail of static atoms // being an index into the static table (and panicking in the // non-static, non-inline cases).
unimplemented!( "If servo wants to share stylesheets across processes, \
then ToShmem for Atom needs to be implemented"
)
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.