aboutsummaryrefslogtreecommitdiffstats
path: root/components/servo_arc/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/servo_arc/lib.rs')
-rw-r--r--components/servo_arc/lib.rs123
1 files changed, 98 insertions, 25 deletions
diff --git a/components/servo_arc/lib.rs b/components/servo_arc/lib.rs
index f4a929e23f8..f2f9fa00602 100644
--- a/components/servo_arc/lib.rs
+++ b/components/servo_arc/lib.rs
@@ -21,7 +21,7 @@
//!
//! [1]: https://bugzilla.mozilla.org/show_bug.cgi?id=1360883
-// The semantics of `Arc` are alread documented in the Rust docs, so we don't
+// The semantics of `Arc` are already documented in the Rust docs, so we don't
// duplicate those here.
#![allow(missing_docs)]
@@ -86,10 +86,14 @@ const STATIC_REFCOUNT: usize = usize::MAX;
/// See the documentation for [`Arc`] in the standard library. Unlike the
/// standard library `Arc`, this `Arc` does not support weak reference counting.
///
+/// See the discussion in https://github.com/rust-lang/rust/pull/60594 for the
+/// usage of PhantomData.
+///
/// [`Arc`]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html
#[repr(C)]
pub struct Arc<T: ?Sized> {
p: ptr::NonNull<ArcInner<T>>,
+ phantom: PhantomData<T>,
}
/// An `Arc` that is known to be uniquely owned
@@ -164,11 +168,12 @@ impl<T> Arc<T> {
pub fn new(data: T) -> Self {
let x = Box::new(ArcInner {
count: atomic::AtomicUsize::new(1),
- data: data,
+ data,
});
unsafe {
Arc {
p: ptr::NonNull::new_unchecked(Box::into_raw(x)),
+ phantom: PhantomData,
}
}
}
@@ -198,6 +203,7 @@ impl<T> Arc<T> {
let ptr = (ptr as *const u8).offset(-offset_of!(ArcInner<T>, data));
Arc {
p: ptr::NonNull::new_unchecked(ptr as *mut ArcInner<T>),
+ phantom: PhantomData,
}
}
@@ -224,6 +230,7 @@ impl<T> Arc<T> {
Arc {
p: ptr::NonNull::new_unchecked(ptr),
+ phantom: PhantomData,
}
}
@@ -301,6 +308,9 @@ impl<T: ?Sized> Arc<T> {
impl<T: ?Sized> Clone for Arc<T> {
#[inline]
fn clone(&self) -> Self {
+ // NOTE(emilio): If you change anything here, make sure that the
+ // implementation in layout/style/ServoStyleConstsInlines.h matches!
+ //
// Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since
// `count` never changes between STATIC_REFCOUNT and other values.
if self.inner().count.load(Relaxed) != STATIC_REFCOUNT {
@@ -334,6 +344,7 @@ impl<T: ?Sized> Clone for Arc<T> {
unsafe {
Arc {
p: ptr::NonNull::new_unchecked(self.ptr()),
+ phantom: PhantomData,
}
}
}
@@ -408,6 +419,9 @@ impl<T: ?Sized> Arc<T> {
impl<T: ?Sized> Drop for Arc<T> {
#[inline]
fn drop(&mut self) {
+ // NOTE(emilio): If you change anything here, make sure that the
+ // implementation in layout/style/ServoStyleConstsInlines.h matches!
+ //
// Using a relaxed ordering to check for STATIC_REFCOUNT is safe, since
// `count` never changes between STATIC_REFCOUNT and other values.
if self.inner().count.load(Relaxed) == STATIC_REFCOUNT {
@@ -563,6 +577,7 @@ impl<T: Serialize> Serialize for Arc<T> {
/// Structure to allow Arc-managing some fixed-sized data and a variably-sized
/// slice in a single allocation.
#[derive(Debug, Eq, PartialEq, PartialOrd)]
+#[repr(C)]
pub struct HeaderSlice<H, T: ?Sized> {
/// The fixed-sized data.
pub header: H,
@@ -594,7 +609,7 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
F: FnOnce(Layout) -> *mut u8,
I: Iterator<Item = T> + ExactSizeIterator,
{
- use std::mem::size_of;
+ use std::mem::{align_of, size_of};
assert_ne!(size_of::<T>(), 0, "Need to think about ZST");
// Compute the required size for the allocation.
@@ -602,7 +617,7 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
let size = {
// First, determine the alignment of a hypothetical pointer to a
// HeaderSlice.
- let fake_slice_ptr_align: usize = mem::align_of::<ArcInner<HeaderSlice<H, [T; 1]>>>();
+ let fake_slice_ptr_align: usize = mem::align_of::<ArcInner<HeaderSlice<H, [T; 0]>>>();
// Next, synthesize a totally garbage (but properly aligned) pointer
// to a sequence of T.
@@ -659,23 +674,28 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
};
ptr::write(&mut ((*ptr).count), count);
ptr::write(&mut ((*ptr).data.header), header);
- let mut current: *mut T = &mut (*ptr).data.slice[0];
- for _ in 0..num_items {
- ptr::write(
- current,
- items
- .next()
- .expect("ExactSizeIterator over-reported length"),
+ if num_items != 0 {
+ let mut current: *mut T = &mut (*ptr).data.slice[0];
+ for _ in 0..num_items {
+ ptr::write(
+ current,
+ items
+ .next()
+ .expect("ExactSizeIterator over-reported length"),
+ );
+ current = current.offset(1);
+ }
+ // We should have consumed the buffer exactly, maybe accounting
+ // for some padding from the alignment.
+ debug_assert!(
+ (buffer.offset(size as isize) as usize - current as *mut u8 as usize) <
+ align_of::<Self>()
);
- current = current.offset(1);
}
assert!(
items.next().is_none(),
"ExactSizeIterator under-reported length"
);
-
- // We should have consumed the buffer exactly.
- debug_assert_eq!(current as *mut u8, buffer.offset(size as isize));
}
// Return the fat Arc.
@@ -687,6 +707,7 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
unsafe {
Arc {
p: ptr::NonNull::new_unchecked(ptr),
+ phantom: PhantomData,
}
}
}
@@ -732,6 +753,7 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
/// Header data with an inline length. Consumers that use HeaderWithLength as the
/// Header type in HeaderSlice can take advantage of ThinArc.
#[derive(Debug, Eq, PartialEq, PartialOrd)]
+#[repr(C)]
pub struct HeaderWithLength<H> {
/// The fixed-sized data.
pub header: H,
@@ -763,11 +785,20 @@ type HeaderSliceWithLength<H, T> = HeaderSlice<HeaderWithLength<H>, T>;
/// have a thin pointer instead, perhaps for FFI compatibility
/// or space efficiency.
///
+/// Note that we use `[T; 0]` in order to have the right alignment for `T`.
+///
/// `ThinArc` solves this by storing the length in the allocation itself,
/// via `HeaderSliceWithLength`.
#[repr(C)]
pub struct ThinArc<H, T> {
- ptr: *mut ArcInner<HeaderSliceWithLength<H, [T; 1]>>,
+ 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> {}
@@ -777,7 +808,7 @@ unsafe impl<H: Sync + Send, T: Sync + Send> Sync for ThinArc<H, T> {}
//
// See the comment around the analogous operation in from_header_and_iter.
fn thin_to_thick<H, T>(
- thin: *mut ArcInner<HeaderSliceWithLength<H, [T; 1]>>,
+ thin: *mut ArcInner<HeaderSliceWithLength<H, [T; 0]>>,
) -> *mut ArcInner<HeaderSliceWithLength<H, [T]>> {
let len = unsafe { (*thin).data.header.length };
let fake_slice: *mut [T] = unsafe { slice::from_raw_parts_mut(thin as *mut T, len) };
@@ -796,7 +827,8 @@ impl<H, T> ThinArc<H, T> {
// Synthesize transient Arc, which never touches the refcount of the ArcInner.
let transient = unsafe {
NoDrop::new(Arc {
- p: ptr::NonNull::new_unchecked(thin_to_thick(self.ptr)),
+ p: ptr::NonNull::new_unchecked(thin_to_thick(self.ptr.as_ptr())),
+ phantom: PhantomData,
})
};
@@ -841,8 +873,12 @@ impl<H, T> ThinArc<H, T> {
}
/// 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 {
@@ -851,7 +887,7 @@ impl<H, T> ThinArc<H, T> {
if is_static {
ptr::null()
} else {
- self.ptr as *const ArcInner<T> as *const c_void
+ self.ptr()
}
}
}
@@ -861,7 +897,7 @@ impl<H, T> Deref for ThinArc<H, T> {
#[inline]
fn deref(&self) -> &Self::Target {
- unsafe { &(*thin_to_thick(self.ptr)).data }
+ unsafe { &(*thin_to_thick(self.ptr.as_ptr())).data }
}
}
@@ -875,7 +911,10 @@ impl<H, T> Clone for ThinArc<H, T> {
impl<H, T> Drop for ThinArc<H, T> {
#[inline]
fn drop(&mut self) {
- let _ = Arc::from_thin(ThinArc { ptr: self.ptr });
+ let _ = Arc::from_thin(ThinArc {
+ ptr: self.ptr,
+ phantom: PhantomData,
+ });
}
}
@@ -893,7 +932,12 @@ impl<H, T> Arc<HeaderSliceWithLength<H, [T]>> {
mem::forget(a);
let thin_ptr = fat_ptr as *mut [usize] as *mut usize;
ThinArc {
- ptr: thin_ptr as *mut ArcInner<HeaderSliceWithLength<H, [T; 1]>>,
+ ptr: unsafe {
+ ptr::NonNull::new_unchecked(
+ thin_ptr as *mut ArcInner<HeaderSliceWithLength<H, [T; 0]>>,
+ )
+ },
+ phantom: PhantomData,
}
}
@@ -901,11 +945,12 @@ impl<H, T> Arc<HeaderSliceWithLength<H, [T]>> {
/// is not modified.
#[inline]
pub fn from_thin(a: ThinArc<H, T>) -> Self {
- let ptr = thin_to_thick(a.ptr);
+ let ptr = thin_to_thick(a.ptr.as_ptr());
mem::forget(a);
unsafe {
Arc {
p: ptr::NonNull::new_unchecked(ptr),
+ phantom: PhantomData,
}
}
}
@@ -1306,6 +1351,34 @@ mod tests {
}
#[test]
+ fn empty_thin() {
+ let header = HeaderWithLength::new(100u32, 0);
+ let x = Arc::from_header_and_iter(header, std::iter::empty::<i32>());
+ let y = Arc::into_thin(x.clone());
+ assert_eq!(y.header.header, 100);
+ assert!(y.slice.is_empty());
+ assert_eq!(x.header.header, 100);
+ assert!(x.slice.is_empty());
+ }
+
+ #[test]
+ fn thin_assert_padding() {
+ #[derive(Clone, Default)]
+ #[repr(C)]
+ struct Padded {
+ i: u16,
+ }
+
+ // The header will have more alignment than `Padded`
+ let header = HeaderWithLength::new(0i32, 2);
+ let items = vec![Padded { i: 0xdead }, Padded { i: 0xbeef }];
+ let a = ThinArc::from_header_and_iter(header, items.into_iter());
+ assert_eq!(a.slice.len(), 2);
+ assert_eq!(a.slice[0].i, 0xdead);
+ assert_eq!(a.slice[1].i, 0xbeef);
+ }
+
+ #[test]
fn slices_and_thin() {
let mut canary = atomic::AtomicUsize::new(0);
let c = Canary(&mut canary as *mut atomic::AtomicUsize);