mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
style: Add refcount logging to servo_arc.
Differential Revision: https://phabricator.services.mozilla.com/D32173
This commit is contained in:
parent
57868f571f
commit
9a9a4e12d5
10 changed files with 100 additions and 23 deletions
|
@ -12,6 +12,7 @@ path = "lib.rs"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
servo = ["serde"]
|
servo = ["serde"]
|
||||||
|
gecko = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nodrop = {version = "0.1.8"}
|
nodrop = {version = "0.1.8"}
|
||||||
|
|
|
@ -42,7 +42,7 @@ use std::fmt;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::iter::{ExactSizeIterator, Iterator};
|
use std::iter::{ExactSizeIterator, Iterator};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem;
|
use std::mem::{self, align_of, size_of};
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::os::raw::c_void;
|
use std::os::raw::c_void;
|
||||||
use std::process;
|
use std::process;
|
||||||
|
@ -115,7 +115,10 @@ pub struct Arc<T: ?Sized> {
|
||||||
/// Once the mutation is finished, you can call `.shareable()` and get a regular `Arc`
|
/// Once the mutation is finished, you can call `.shareable()` and get a regular `Arc`
|
||||||
/// out of it.
|
/// out of it.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// Ignore the doctest below there's no way to skip building with refcount
|
||||||
|
/// logging during doc tests (see rust-lang/rust#45599).
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
/// # use servo_arc::UniqueArc;
|
/// # use servo_arc::UniqueArc;
|
||||||
/// let data = [1, 2, 3, 4, 5];
|
/// let data = [1, 2, 3, 4, 5];
|
||||||
/// let mut x = UniqueArc::new(data);
|
/// let mut x = UniqueArc::new(data);
|
||||||
|
@ -169,18 +172,35 @@ impl<T> Arc<T> {
|
||||||
/// Construct an `Arc<T>`
|
/// Construct an `Arc<T>`
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(data: T) -> Self {
|
pub fn new(data: T) -> Self {
|
||||||
let x = Box::new(ArcInner {
|
let ptr = Box::into_raw(Box::new(ArcInner {
|
||||||
count: atomic::AtomicUsize::new(1),
|
count: atomic::AtomicUsize::new(1),
|
||||||
data,
|
data,
|
||||||
});
|
}));
|
||||||
|
|
||||||
|
#[cfg(all(feature = "gecko", debug_assertions))]
|
||||||
|
unsafe {
|
||||||
|
// FIXME(emilio): Would be so amazing to have
|
||||||
|
// std::intrinsics::type_name() around, so that we could also report
|
||||||
|
// a real size.
|
||||||
|
NS_LogCtor(ptr as *const _, b"ServoArc\0".as_ptr() as *const _, 8);
|
||||||
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
Arc {
|
Arc {
|
||||||
p: ptr::NonNull::new_unchecked(Box::into_raw(x)),
|
p: ptr::NonNull::new_unchecked(ptr),
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct an intentionally-leaked arc.
|
||||||
|
#[inline]
|
||||||
|
pub fn new_leaked(data: T) -> Self {
|
||||||
|
let arc = Self::new(data);
|
||||||
|
arc.mark_as_intentionally_leaked();
|
||||||
|
arc
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert the Arc<T> to a raw pointer, suitable for use across FFI
|
/// Convert the Arc<T> to a raw pointer, suitable for use across FFI
|
||||||
///
|
///
|
||||||
/// Note: This returns a pointer to the data T, which is offset in the allocation.
|
/// Note: This returns a pointer to the data T, which is offset in the allocation.
|
||||||
|
@ -290,9 +310,29 @@ impl<T: ?Sized> Arc<T> {
|
||||||
unsafe { &*self.ptr() }
|
unsafe { &*self.ptr() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-inlined part of `drop`. Just invokes the destructor.
|
#[inline(always)]
|
||||||
|
fn record_drop(&self) {
|
||||||
|
#[cfg(all(feature = "gecko", debug_assertions))]
|
||||||
|
unsafe {
|
||||||
|
NS_LogDtor(self.ptr() as *const _, b"ServoArc\0".as_ptr() as *const _, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Marks this `Arc` as intentionally leaked for the purposes of refcount
|
||||||
|
/// logging.
|
||||||
|
///
|
||||||
|
/// It's a logic error to call this more than once, but it's not unsafe, as
|
||||||
|
/// it'd just report negative leaks.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn mark_as_intentionally_leaked(&self) {
|
||||||
|
self.record_drop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-inlined part of `drop`. Just invokes the destructor and calls the
|
||||||
|
// refcount logging machinery if enabled.
|
||||||
#[inline(never)]
|
#[inline(never)]
|
||||||
unsafe fn drop_slow(&mut self) {
|
unsafe fn drop_slow(&mut self) {
|
||||||
|
self.record_drop();
|
||||||
let _ = Box::from_raw(self.ptr());
|
let _ = Box::from_raw(self.ptr());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,6 +348,12 @@ impl<T: ?Sized> Arc<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "gecko", debug_assertions))]
|
||||||
|
extern "C" {
|
||||||
|
fn NS_LogCtor(aPtr: *const std::os::raw::c_void, aTypeName: *const std::os::raw::c_char, aSize: u32);
|
||||||
|
fn NS_LogDtor(aPtr: *const std::os::raw::c_void, aTypeName: *const std::os::raw::c_char, aSize: u32);
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: ?Sized> Clone for Arc<T> {
|
impl<T: ?Sized> Clone for Arc<T> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
|
@ -612,7 +658,6 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
|
||||||
F: FnOnce(Layout) -> *mut u8,
|
F: FnOnce(Layout) -> *mut u8,
|
||||||
I: Iterator<Item = T> + ExactSizeIterator,
|
I: Iterator<Item = T> + ExactSizeIterator,
|
||||||
{
|
{
|
||||||
use std::mem::{align_of, size_of};
|
|
||||||
assert_ne!(size_of::<T>(), 0, "Need to think about ZST");
|
assert_ne!(size_of::<T>(), 0, "Need to think about ZST");
|
||||||
|
|
||||||
let inner_align = align_of::<ArcInner<HeaderSlice<H, [T; 0]>>>();
|
let inner_align = align_of::<ArcInner<HeaderSlice<H, [T; 0]>>>();
|
||||||
|
@ -700,6 +745,15 @@ impl<H, T> Arc<HeaderSlice<H, [T]>> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "gecko", debug_assertions))]
|
||||||
|
unsafe {
|
||||||
|
if !is_static {
|
||||||
|
// FIXME(emilio): Would be so amazing to have
|
||||||
|
// std::intrinsics::type_name() around.
|
||||||
|
NS_LogCtor(ptr as *const _, b"ServoArc\0".as_ptr() as *const _, 8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Return the fat Arc.
|
// Return the fat Arc.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
size_of::<Self>(),
|
size_of::<Self>(),
|
||||||
|
|
|
@ -19,7 +19,7 @@ thread_local! {
|
||||||
/// such that they can be reused across style traversals. StyleBloom is responsible
|
/// such that they can be reused across style traversals. StyleBloom is responsible
|
||||||
/// for ensuring that the bloom filter is zeroed when it is dropped.
|
/// for ensuring that the bloom filter is zeroed when it is dropped.
|
||||||
static BLOOM_KEY: Arc<AtomicRefCell<BloomFilter>> =
|
static BLOOM_KEY: Arc<AtomicRefCell<BloomFilter>> =
|
||||||
Arc::new(AtomicRefCell::new(BloomFilter::new()));
|
Arc::new_leaked(AtomicRefCell::new(BloomFilter::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A struct that allows us to fast-reject deep descendant selectors avoiding
|
/// A struct that allows us to fast-reject deep descendant selectors avoiding
|
||||||
|
|
|
@ -1736,7 +1736,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
PropertyDeclaration::TextAlign(SpecifiedTextAlign::MozCenterOrInherit),
|
PropertyDeclaration::TextAlign(SpecifiedTextAlign::MozCenterOrInherit),
|
||||||
Importance::Normal,
|
Importance::Normal,
|
||||||
);
|
);
|
||||||
let arc = Arc::new(global_style_data.shared_lock.wrap(pdb));
|
let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
|
||||||
ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints)
|
ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints)
|
||||||
};
|
};
|
||||||
static ref TABLE_COLOR_RULE: ApplicableDeclarationBlock = {
|
static ref TABLE_COLOR_RULE: ApplicableDeclarationBlock = {
|
||||||
|
@ -1745,7 +1745,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
PropertyDeclaration::Color(SpecifiedColor(Color::InheritFromBodyQuirk.into())),
|
PropertyDeclaration::Color(SpecifiedColor(Color::InheritFromBodyQuirk.into())),
|
||||||
Importance::Normal,
|
Importance::Normal,
|
||||||
);
|
);
|
||||||
let arc = Arc::new(global_style_data.shared_lock.wrap(pdb));
|
let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
|
||||||
ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints)
|
ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints)
|
||||||
};
|
};
|
||||||
static ref MATHML_LANG_RULE: ApplicableDeclarationBlock = {
|
static ref MATHML_LANG_RULE: ApplicableDeclarationBlock = {
|
||||||
|
@ -1754,7 +1754,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
PropertyDeclaration::XLang(SpecifiedLang(atom!("x-math"))),
|
PropertyDeclaration::XLang(SpecifiedLang(atom!("x-math"))),
|
||||||
Importance::Normal,
|
Importance::Normal,
|
||||||
);
|
);
|
||||||
let arc = Arc::new(global_style_data.shared_lock.wrap(pdb));
|
let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
|
||||||
ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints)
|
ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints)
|
||||||
};
|
};
|
||||||
static ref SVG_TEXT_DISABLE_ZOOM_RULE: ApplicableDeclarationBlock = {
|
static ref SVG_TEXT_DISABLE_ZOOM_RULE: ApplicableDeclarationBlock = {
|
||||||
|
@ -1763,7 +1763,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
PropertyDeclaration::XTextZoom(SpecifiedZoom(false)),
|
PropertyDeclaration::XTextZoom(SpecifiedZoom(false)),
|
||||||
Importance::Normal,
|
Importance::Normal,
|
||||||
);
|
);
|
||||||
let arc = Arc::new(global_style_data.shared_lock.wrap(pdb));
|
let arc = Arc::new_leaked(global_style_data.shared_lock.wrap(pdb));
|
||||||
ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints)
|
ApplicableDeclarationBlock::from_declarations(arc, ServoCascadeLevel::PresHints)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -125,7 +125,7 @@ lazy_static! {
|
||||||
};
|
};
|
||||||
/// Global style data
|
/// Global style data
|
||||||
pub static ref GLOBAL_STYLE_DATA: GlobalStyleData = GlobalStyleData {
|
pub static ref GLOBAL_STYLE_DATA: GlobalStyleData = GlobalStyleData {
|
||||||
shared_lock: SharedRwLock::new(),
|
shared_lock: SharedRwLock::new_leaked(),
|
||||||
options: StyleSystemOptions::default(),
|
options: StyleSystemOptions::default(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -755,8 +755,7 @@ unsafe impl Sync for RuleTree {}
|
||||||
unsafe impl Send for RuleTree {}
|
unsafe impl Send for RuleTree {}
|
||||||
|
|
||||||
// On Gecko builds, hook into the leak checking machinery.
|
// On Gecko builds, hook into the leak checking machinery.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(all(feature = "gecko", debug_assertions))]
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
mod gecko_leak_checking {
|
mod gecko_leak_checking {
|
||||||
use super::RuleNode;
|
use super::RuleNode;
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
@ -789,15 +788,13 @@ mod gecko_leak_checking {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn log_new(_ptr: *const RuleNode) {
|
fn log_new(_ptr: *const RuleNode) {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(all(feature = "gecko", debug_assertions))]
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
gecko_leak_checking::log_ctor(_ptr);
|
gecko_leak_checking::log_ctor(_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn log_drop(_ptr: *const RuleNode) {
|
fn log_drop(_ptr: *const RuleNode) {
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(all(feature = "gecko", debug_assertions))]
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
gecko_leak_checking::log_dtor(_ptr);
|
gecko_leak_checking::log_dtor(_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,14 @@ impl SharedRwLock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new global shared lock (gecko).
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
pub fn new_leaked() -> Self {
|
||||||
|
SharedRwLock {
|
||||||
|
cell: Some(Arc::new_leaked(AtomicRefCell::new(SomethingZeroSizedButTyped))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Create a new read-only shared lock (gecko).
|
/// Create a new read-only shared lock (gecko).
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
pub fn read_only() -> Self {
|
pub fn read_only() -> Self {
|
||||||
|
|
|
@ -485,8 +485,12 @@ type SharingCache<E> = SharingCacheBase<StyleSharingCandidate<E>>;
|
||||||
type TypelessSharingCache = SharingCacheBase<FakeCandidate>;
|
type TypelessSharingCache = SharingCacheBase<FakeCandidate>;
|
||||||
type StoredSharingCache = Arc<AtomicRefCell<TypelessSharingCache>>;
|
type StoredSharingCache = Arc<AtomicRefCell<TypelessSharingCache>>;
|
||||||
|
|
||||||
thread_local!(static SHARING_CACHE_KEY: StoredSharingCache =
|
thread_local! {
|
||||||
Arc::new(AtomicRefCell::new(TypelessSharingCache::default())));
|
// TODO(emilio): Looks like a few of these should just be Rc<RefCell<>> or
|
||||||
|
// something. No need for atomics in the thread-local code.
|
||||||
|
static SHARING_CACHE_KEY: StoredSharingCache =
|
||||||
|
Arc::new_leaked(AtomicRefCell::new(TypelessSharingCache::default()));
|
||||||
|
}
|
||||||
|
|
||||||
/// An LRU cache of the last few nodes seen, so that we can aggressively try to
|
/// An LRU cache of the last few nodes seen, so that we can aggressively try to
|
||||||
/// reuse their styles.
|
/// reuse their styles.
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub use crate::values::specified::list::MozListReversed;
|
||||||
pub use crate::values::specified::list::{QuotePair, Quotes};
|
pub use crate::values::specified::list::{QuotePair, Quotes};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref INITIAL_QUOTES: crate::ArcSlice<QuotePair> = crate::ArcSlice::from_iter(
|
static ref INITIAL_QUOTES: crate::ArcSlice<QuotePair> = crate::ArcSlice::from_iter_leaked(
|
||||||
vec![
|
vec![
|
||||||
QuotePair {
|
QuotePair {
|
||||||
opening: "\u{201c}".to_owned().into(),
|
opening: "\u{201c}".to_owned().into(),
|
||||||
|
|
|
@ -40,7 +40,7 @@ impl<T> Deref for ArcSlice<T> {
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
// ThinArc doesn't support alignments greater than align_of::<u64>.
|
// ThinArc doesn't support alignments greater than align_of::<u64>.
|
||||||
static ref EMPTY_ARC_SLICE: ArcSlice<u64> = {
|
static ref EMPTY_ARC_SLICE: ArcSlice<u64> = {
|
||||||
ArcSlice(ThinArc::from_header_and_iter(ARC_SLICE_CANARY, iter::empty()))
|
ArcSlice::from_iter_leaked(iter::empty())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +74,19 @@ impl<T> ArcSlice<T> {
|
||||||
ArcSlice(ThinArc::from_header_and_iter(ARC_SLICE_CANARY, items))
|
ArcSlice(ThinArc::from_header_and_iter(ARC_SLICE_CANARY, items))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates an Arc for a slice using the given iterator to generate the
|
||||||
|
/// slice, and marks the arc as intentionally leaked from the refcount
|
||||||
|
/// logging point of view.
|
||||||
|
#[inline]
|
||||||
|
pub fn from_iter_leaked<I>(items: I) -> Self
|
||||||
|
where
|
||||||
|
I: Iterator<Item = T> + ExactSizeIterator,
|
||||||
|
{
|
||||||
|
let thin_arc = ThinArc::from_header_and_iter(ARC_SLICE_CANARY, items);
|
||||||
|
thin_arc.with_arc(|a| a.mark_as_intentionally_leaked());
|
||||||
|
ArcSlice(thin_arc)
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a value that can be passed via FFI, and forgets this value
|
/// Creates a value that can be passed via FFI, and forgets this value
|
||||||
/// altogether.
|
/// altogether.
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue