diff --git a/components/servo_arc/lib.rs b/components/servo_arc/lib.rs index 5dc261eefb6..ff4074767f8 100644 --- a/components/servo_arc/lib.rs +++ b/components/servo_arc/lib.rs @@ -31,6 +31,7 @@ use heapsize::HeapSizeOf; use nodrop::NoDrop; #[cfg(feature = "servo")] use serde::{Deserialize, Serialize}; +use stable_deref_trait::{CloneStableDeref, StableDeref}; use std::{isize, usize}; use std::borrow; use std::cmp::Ordering; @@ -433,6 +434,9 @@ impl AsRef for Arc { } } +unsafe impl StableDeref for Arc {} +unsafe impl CloneStableDeref for Arc {} + // This is what the HeapSize crate does for regular arc, but is questionably // sound. See https://github.com/servo/heapsize/issues/37 #[cfg(feature = "servo")] diff --git a/components/style/bloom.rs b/components/style/bloom.rs index 34b6b608962..d6d05c2ed20 100644 --- a/components/style/bloom.rs +++ b/components/style/bloom.rs @@ -7,9 +7,18 @@ #![deny(missing_docs)] +use atomic_refcell::{AtomicRefMut, AtomicRefCell}; use dom::{SendElement, TElement}; +use owning_ref::OwningHandle; use selectors::bloom::BloomFilter; use smallvec::SmallVec; +use stylearc::Arc; + +/// Bloom filters are large allocations, so we store them in thread-local storage +/// such that they can be reused across style traversals. StyleBloom is responsible +/// for ensuring that the bloom filter is zeroed when it is dropped. +thread_local!(static BLOOM_KEY: Arc> = + Arc::new(AtomicRefCell::new(BloomFilter::new()))); /// A struct that allows us to fast-reject deep descendant selectors avoiding /// selector-matching. @@ -43,8 +52,11 @@ use smallvec::SmallVec; /// immutable during a restyle. /// pub struct StyleBloom { - /// The bloom filter per se. - filter: Box, + /// A handle to the bloom filter from the thread upon which this StyleBloom + /// was created. We use AtomicRefCell so that this is all |Send|, which allows + /// StyleBloom to live in ThreadLocalStyleContext, which is dropped from the + /// parent thread. + filter: OwningHandle>, AtomicRefMut<'static, BloomFilter>>, /// The stack of elements that this bloom filter contains, along with the /// number of hashes pushed for each element. @@ -101,11 +113,23 @@ fn each_relevant_element_hash(element: E, mut f: F) }); } +impl Drop for StyleBloom { + fn drop(&mut self) { + // Leave the reusable bloom filter in a zeroed state. + self.clear(); + } +} + impl StyleBloom { - /// Create an empty `StyleBloom`. + /// Create an empty `StyleBloom`. Because StyleBloom acquires the thread- + /// local filter buffer, creating multiple live StyleBloom instances at + /// the same time on the same thread will panic. pub fn new() -> Self { + let bloom_arc = BLOOM_KEY.with(|b| b.clone()); + let filter = OwningHandle::new_with_fn(bloom_arc, |x| unsafe { x.as_ref() }.unwrap().borrow_mut()); + debug_assert!(filter.is_zeroed(), "Forgot to zero the bloom filter last time"); StyleBloom { - filter: Box::new(BloomFilter::new()), + filter: filter, elements: Default::default(), pushed_hashes: Default::default(), }