Rip out the generic abstractions around ThreadLocalStyleContext.

MozReview-Commit-ID: 5WTLuk323Ac
This commit is contained in:
Bobby Holley 2017-07-05 14:19:37 -07:00
parent 296a215e54
commit 3330653dc8
7 changed files with 52 additions and 102 deletions

View file

@ -18,39 +18,12 @@ use script_layout_interface::{PendingImage, PendingImageState};
use script_traits::PaintWorkletExecutor; use script_traits::PaintWorkletExecutor;
use script_traits::UntrustedNodeAddress; use script_traits::UntrustedNodeAddress;
use servo_url::ServoUrl; use servo_url::ServoUrl;
use std::borrow::{Borrow, BorrowMut};
use std::cell::{RefCell, RefMut}; use std::cell::{RefCell, RefMut};
use std::collections::HashMap; use std::collections::HashMap;
use std::hash::BuildHasherDefault; use std::hash::BuildHasherDefault;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
use style::context::{SharedStyleContext, ThreadLocalStyleContext}; use style::context::SharedStyleContext;
use style::dom::TElement;
/// TLS data scoped to the traversal.
pub struct ScopedThreadLocalLayoutContext<E: TElement> {
pub style_context: ThreadLocalStyleContext<E>,
}
impl<E: TElement> ScopedThreadLocalLayoutContext<E> {
pub fn new(context: &LayoutContext) -> Self {
ScopedThreadLocalLayoutContext {
style_context: ThreadLocalStyleContext::new(&context.style_context),
}
}
}
impl<E: TElement> Borrow<ThreadLocalStyleContext<E>> for ScopedThreadLocalLayoutContext<E> {
fn borrow(&self) -> &ThreadLocalStyleContext<E> {
&self.style_context
}
}
impl<E: TElement> BorrowMut<ThreadLocalStyleContext<E>> for ScopedThreadLocalLayoutContext<E> {
fn borrow_mut(&mut self) -> &mut ThreadLocalStyleContext<E> {
&mut self.style_context
}
}
thread_local!(static FONT_CONTEXT_KEY: RefCell<Option<FontContext>> = RefCell::new(None)); thread_local!(static FONT_CONTEXT_KEY: RefCell<Option<FontContext>> = RefCell::new(None));

View file

@ -6,7 +6,7 @@
use atomic_refcell::AtomicRefCell; use atomic_refcell::AtomicRefCell;
use construct::FlowConstructor; use construct::FlowConstructor;
use context::{LayoutContext, ScopedThreadLocalLayoutContext}; use context::LayoutContext;
use display_list_builder::DisplayListBuildState; use display_list_builder::DisplayListBuildState;
use flow::{self, PreorderFlowTraversal}; use flow::{self, PreorderFlowTraversal};
use flow::{CAN_BE_FRAGMENTED, Flow, ImmutableFlowUtils, PostorderFlowTraversal}; use flow::{CAN_BE_FRAGMENTED, Flow, ImmutableFlowUtils, PostorderFlowTraversal};
@ -55,10 +55,8 @@ impl<'a, E> DomTraversal<E> for RecalcStyleAndConstructFlows<'a>
E::ConcreteNode: LayoutNode, E::ConcreteNode: LayoutNode,
E::FontMetricsProvider: Send, E::FontMetricsProvider: Send,
{ {
type ThreadLocalContext = ScopedThreadLocalLayoutContext<E>;
fn process_preorder(&self, traversal_data: &PerLevelTraversalData, fn process_preorder(&self, traversal_data: &PerLevelTraversalData,
thread_local: &mut Self::ThreadLocalContext, node: E::ConcreteNode) { context: &mut StyleContext<E>, node: E::ConcreteNode) {
// FIXME(pcwalton): Stop allocating here. Ideally this should just be // FIXME(pcwalton): Stop allocating here. Ideally this should just be
// done by the HTML parser. // done by the HTML parser.
node.initialize_data(); node.initialize_data();
@ -66,16 +64,12 @@ impl<'a, E> DomTraversal<E> for RecalcStyleAndConstructFlows<'a>
if !node.is_text_node() { if !node.is_text_node() {
let el = node.as_element().unwrap(); let el = node.as_element().unwrap();
let mut data = el.mutate_data().unwrap(); let mut data = el.mutate_data().unwrap();
let mut context = StyleContext { recalc_style_at(self, traversal_data, context, el, &mut data);
shared: &self.context.shared_context(),
thread_local: &mut thread_local.style_context,
};
recalc_style_at(self, traversal_data, &mut context, el, &mut data);
} }
} }
fn process_postorder(&self, thread_local: &mut Self::ThreadLocalContext, node: E::ConcreteNode) { fn process_postorder(&self, _style_context: &mut StyleContext<E>, node: E::ConcreteNode) {
construct_flows_at(&self.context, thread_local, node); construct_flows_at(&self.context, node);
} }
fn text_node_needs_traversal(node: E::ConcreteNode) -> bool { fn text_node_needs_traversal(node: E::ConcreteNode) -> bool {
@ -100,10 +94,6 @@ impl<'a, E> DomTraversal<E> for RecalcStyleAndConstructFlows<'a>
&self.context.style_context &self.context.style_context
} }
fn create_thread_local_context(&self) -> Self::ThreadLocalContext {
ScopedThreadLocalLayoutContext::new(&self.context)
}
fn is_parallel(&self) -> bool { fn is_parallel(&self) -> bool {
self.driver.is_parallel() self.driver.is_parallel()
} }
@ -118,9 +108,7 @@ pub trait PostorderNodeMutTraversal<ConcreteThreadSafeLayoutNode: ThreadSafeLayo
/// The flow construction traversal, which builds flows for styled nodes. /// The flow construction traversal, which builds flows for styled nodes.
#[inline] #[inline]
#[allow(unsafe_code)] #[allow(unsafe_code)]
fn construct_flows_at<N>(context: &LayoutContext, fn construct_flows_at<N>(context: &LayoutContext, node: N)
_thread_local: &mut ScopedThreadLocalLayoutContext<N::ConcreteElement>,
node: N)
where N: LayoutNode, where N: LayoutNode,
{ {
debug!("construct_flows_at: {:?}", node); debug!("construct_flows_at: {:?}", node);

View file

@ -296,7 +296,7 @@ pub trait TElement : Eq + PartialEq + Debug + Hash + Sized + Copy + Clone +
/// ///
/// XXXManishearth It would be better to make this a type parameter on /// XXXManishearth It would be better to make this a type parameter on
/// ThreadLocalStyleContext and StyleContext /// ThreadLocalStyleContext and StyleContext
type FontMetricsProvider: FontMetricsProvider; type FontMetricsProvider: FontMetricsProvider + Send;
/// Get this element as a node. /// Get this element as a node.
fn as_node(&self) -> Self::ConcreteNode; fn as_node(&self) -> Self::ConcreteNode;

View file

@ -5,7 +5,7 @@
//! Gecko-specific bits for the styling DOM traversal. //! Gecko-specific bits for the styling DOM traversal.
use atomic_refcell::AtomicRefCell; use atomic_refcell::AtomicRefCell;
use context::{SharedStyleContext, StyleContext, ThreadLocalStyleContext}; use context::{SharedStyleContext, StyleContext};
use data::ElementData; use data::ElementData;
use dom::{NodeInfo, TNode}; use dom::{NodeInfo, TNode};
use gecko::wrapper::{GeckoElement, GeckoNode}; use gecko::wrapper::{GeckoElement, GeckoNode};
@ -29,25 +29,19 @@ impl<'a> RecalcStyleOnly<'a> {
} }
impl<'recalc, 'le> DomTraversal<GeckoElement<'le>> for RecalcStyleOnly<'recalc> { impl<'recalc, 'le> DomTraversal<GeckoElement<'le>> for RecalcStyleOnly<'recalc> {
type ThreadLocalContext = ThreadLocalStyleContext<GeckoElement<'le>>;
fn process_preorder(&self, fn process_preorder(&self,
traversal_data: &PerLevelTraversalData, traversal_data: &PerLevelTraversalData,
thread_local: &mut Self::ThreadLocalContext, context: &mut StyleContext<GeckoElement<'le>>,
node: GeckoNode<'le>) node: GeckoNode<'le>)
{ {
if node.is_element() { if node.is_element() {
let el = node.as_element().unwrap(); let el = node.as_element().unwrap();
let mut data = unsafe { el.ensure_data() }.borrow_mut(); let mut data = unsafe { el.ensure_data() }.borrow_mut();
let mut context = StyleContext { recalc_style_at(self, traversal_data, context, el, &mut data);
shared: &self.shared,
thread_local: thread_local,
};
recalc_style_at(self, traversal_data, &mut context, el, &mut data);
} }
} }
fn process_postorder(&self, _: &mut Self::ThreadLocalContext, _: GeckoNode<'le>) { fn process_postorder(&self, _: &mut StyleContext<GeckoElement<'le>>, _: GeckoNode<'le>) {
unreachable!(); unreachable!();
} }
@ -66,10 +60,6 @@ impl<'recalc, 'le> DomTraversal<GeckoElement<'le>> for RecalcStyleOnly<'recalc>
&self.shared &self.shared
} }
fn create_thread_local_context(&self) -> Self::ThreadLocalContext {
ThreadLocalStyleContext::new(&self.shared)
}
fn is_parallel(&self) -> bool { fn is_parallel(&self) -> bool {
self.driver.is_parallel() self.driver.is_parallel()
} }

View file

@ -23,7 +23,7 @@
#![deny(missing_docs)] #![deny(missing_docs)]
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use context::TraversalStatistics; use context::{StyleContext, ThreadLocalStyleContext, TraversalStatistics};
use dom::{OpaqueNode, SendNode, TElement, TNode}; use dom::{OpaqueNode, SendNode, TElement, TNode};
use rayon; use rayon;
use scoped_tls::ScopedTLS; use scoped_tls::ScopedTLS;
@ -84,7 +84,7 @@ pub fn traverse_dom<E, D>(traversal: &D,
let traversal_data = PerLevelTraversalData { let traversal_data = PerLevelTraversalData {
current_dom_depth: depth, current_dom_depth: depth,
}; };
let tls = ScopedTLS::<D::ThreadLocalContext>::new(pool); let tls = ScopedTLS::<ThreadLocalStyleContext<E>>::new(pool);
let root = root.as_node().opaque(); let root = root.as_node().opaque();
pool.install(|| { pool.install(|| {
@ -124,11 +124,11 @@ pub fn traverse_dom<E, D>(traversal: &D,
#[inline(never)] #[inline(never)]
fn create_thread_local_context<'scope, E, D>( fn create_thread_local_context<'scope, E, D>(
traversal: &'scope D, traversal: &'scope D,
slot: &mut Option<D::ThreadLocalContext>) slot: &mut Option<ThreadLocalStyleContext<E>>)
where E: TElement + 'scope, where E: TElement + 'scope,
D: DomTraversal<E> D: DomTraversal<E>
{ {
*slot = Some(traversal.create_thread_local_context()) *slot = Some(ThreadLocalStyleContext::new(traversal.shared_context()));
} }
/// A parallel top-down DOM traversal. /// A parallel top-down DOM traversal.
@ -152,7 +152,7 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
scope: &'a rayon::Scope<'scope>, scope: &'a rayon::Scope<'scope>,
pool: &'scope rayon::ThreadPool, pool: &'scope rayon::ThreadPool,
traversal: &'scope D, traversal: &'scope D,
tls: &'scope ScopedTLS<'scope, D::ThreadLocalContext>) tls: &'scope ScopedTLS<'scope, ThreadLocalStyleContext<E>>)
where E: TElement + 'scope, where E: TElement + 'scope,
D: DomTraversal<E>, D: DomTraversal<E>,
{ {
@ -167,7 +167,11 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
// Scope the borrow of the TLS so that the borrow is dropped before // Scope the borrow of the TLS so that the borrow is dropped before
// a potential recursive call when we pass TailCall. // a potential recursive call when we pass TailCall.
let mut tlc = tls.ensure( let mut tlc = tls.ensure(
|slot: &mut Option<D::ThreadLocalContext>| create_thread_local_context(traversal, slot)); |slot: &mut Option<ThreadLocalStyleContext<E>>| create_thread_local_context(traversal, slot));
let mut context = StyleContext {
shared: traversal.shared_context(),
thread_local: &mut *tlc,
};
for n in nodes { for n in nodes {
// If the last node we processed produced children, spawn them off // If the last node we processed produced children, spawn them off
@ -199,15 +203,15 @@ fn top_down_dom<'a, 'scope, E, D>(nodes: &'a [SendNode<E::ConcreteNode>],
let node = **n; let node = **n;
let mut children_to_process = 0isize; let mut children_to_process = 0isize;
traversal.process_preorder(&traversal_data, &mut *tlc, node); traversal.process_preorder(&traversal_data, &mut context, node);
if let Some(el) = node.as_element() { if let Some(el) = node.as_element() {
traversal.traverse_children(&mut *tlc, el, |_tlc, kid| { traversal.traverse_children(&mut context, el, |_context, kid| {
children_to_process += 1; children_to_process += 1;
discovered_child_nodes.push(unsafe { SendNode::new(kid) }) discovered_child_nodes.push(unsafe { SendNode::new(kid) })
}); });
} }
traversal.handle_postorder_traversal(&mut *tlc, root, node, traversal.handle_postorder_traversal(&mut context, root, node,
children_to_process); children_to_process);
} }
} }
@ -256,7 +260,7 @@ fn traverse_nodes<'a, 'scope, E, D>(nodes: &[SendNode<E::ConcreteNode>],
scope: &'a rayon::Scope<'scope>, scope: &'a rayon::Scope<'scope>,
pool: &'scope rayon::ThreadPool, pool: &'scope rayon::ThreadPool,
traversal: &'scope D, traversal: &'scope D,
tls: &'scope ScopedTLS<'scope, D::ThreadLocalContext>) tls: &'scope ScopedTLS<'scope, ThreadLocalStyleContext<E>>)
where E: TElement + 'scope, where E: TElement + 'scope,
D: DomTraversal<E>, D: DomTraversal<E>,
{ {

View file

@ -6,8 +6,8 @@
#![deny(missing_docs)] #![deny(missing_docs)]
use context::{StyleContext, ThreadLocalStyleContext};
use dom::{TElement, TNode}; use dom::{TElement, TNode};
use std::borrow::BorrowMut;
use std::collections::VecDeque; use std::collections::VecDeque;
use time; use time;
use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken}; use traversal::{DomTraversal, PerLevelTraversalData, PreTraverseToken};
@ -28,7 +28,12 @@ pub fn traverse_dom<E, D>(traversal: &D,
debug_assert!(token.should_traverse()); debug_assert!(token.should_traverse());
let mut discovered = VecDeque::<WorkItem<E::ConcreteNode>>::with_capacity(16); let mut discovered = VecDeque::<WorkItem<E::ConcreteNode>>::with_capacity(16);
let mut tlc = traversal.create_thread_local_context(); let mut tlc = ThreadLocalStyleContext::new(traversal.shared_context());
let mut context = StyleContext {
shared: traversal.shared_context(),
thread_local: &mut tlc,
};
let root_depth = root.depth(); let root_depth = root.depth();
if token.traverse_unstyled_children_only() { if token.traverse_unstyled_children_only() {
@ -47,25 +52,24 @@ pub fn traverse_dom<E, D>(traversal: &D,
while let Some(WorkItem(node, depth)) = discovered.pop_front() { while let Some(WorkItem(node, depth)) = discovered.pop_front() {
let mut children_to_process = 0isize; let mut children_to_process = 0isize;
let traversal_data = PerLevelTraversalData { current_dom_depth: depth }; let traversal_data = PerLevelTraversalData { current_dom_depth: depth };
traversal.process_preorder(&traversal_data, &mut tlc, node); traversal.process_preorder(&traversal_data, &mut context, node);
if let Some(el) = node.as_element() { if let Some(el) = node.as_element() {
traversal.traverse_children(&mut tlc, el, |_tlc, kid| { traversal.traverse_children(&mut context, el, |_context, kid| {
children_to_process += 1; children_to_process += 1;
discovered.push_back(WorkItem(kid, depth + 1)) discovered.push_back(WorkItem(kid, depth + 1))
}); });
} }
traversal.handle_postorder_traversal(&mut tlc, root.as_node().opaque(), traversal.handle_postorder_traversal(&mut context, root.as_node().opaque(),
node, children_to_process); node, children_to_process);
} }
// Dump statistics to stdout if requested. // Dump statistics to stdout if requested.
if dump_stats { if dump_stats {
let tlsc = tlc.borrow_mut(); context.thread_local.statistics.finish(traversal, start_time.unwrap());
tlsc.statistics.finish(traversal, start_time.unwrap()); if context.thread_local.statistics.is_large_traversal() {
if tlsc.statistics.is_large_traversal() { println!("{}", context.thread_local.statistics);
println!("{}", tlsc.statistics);
} }
} }
} }

View file

@ -5,7 +5,7 @@
//! Traversing the DOM tree; the bloom filter. //! Traversing the DOM tree; the bloom filter.
use atomic_refcell::AtomicRefCell; use atomic_refcell::AtomicRefCell;
use context::{ElementCascadeInputs, StyleContext, SharedStyleContext, ThreadLocalStyleContext}; use context::{ElementCascadeInputs, StyleContext, SharedStyleContext};
use data::{ElementData, ElementStyles}; use data::{ElementData, ElementStyles};
use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode}; use dom::{DirtyDescendants, NodeInfo, OpaqueNode, TElement, TNode};
use invalidation::element::restyle_hints::{RECASCADE_SELF, RECASCADE_DESCENDANTS, RestyleHint}; use invalidation::element::restyle_hints::{RECASCADE_SELF, RECASCADE_DESCENDANTS, RestyleHint};
@ -13,7 +13,6 @@ use matching::{ChildCascadeRequirement, MatchMethods};
use sharing::{StyleSharingBehavior, StyleSharingTarget}; use sharing::{StyleSharingBehavior, StyleSharingTarget};
#[cfg(feature = "servo")] use servo_config::opts; #[cfg(feature = "servo")] use servo_config::opts;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::borrow::BorrowMut;
/// A per-traversal-level chunk of data. This is sent down by the traversal, and /// A per-traversal-level chunk of data. This is sent down by the traversal, and
/// currently only holds the dom depth for the bloom filter. /// currently only holds the dom depth for the bloom filter.
@ -138,22 +137,17 @@ fn is_servo_nonincremental_layout() -> bool {
/// A DOM Traversal trait, that is used to generically implement styling for /// A DOM Traversal trait, that is used to generically implement styling for
/// Gecko and Servo. /// Gecko and Servo.
pub trait DomTraversal<E: TElement> : Sync { pub trait DomTraversal<E: TElement> : Sync {
/// The thread-local context, used to store non-thread-safe stuff that needs
/// to be used in the traversal, and of which we use one per worker, like
/// the bloom filter, for example.
type ThreadLocalContext: Send + BorrowMut<ThreadLocalStyleContext<E>>;
/// Process `node` on the way down, before its children have been processed. /// Process `node` on the way down, before its children have been processed.
fn process_preorder(&self, fn process_preorder(&self,
data: &PerLevelTraversalData, data: &PerLevelTraversalData,
thread_local: &mut Self::ThreadLocalContext, context: &mut StyleContext<E>,
node: E::ConcreteNode); node: E::ConcreteNode);
/// Process `node` on the way up, after its children have been processed. /// Process `node` on the way up, after its children have been processed.
/// ///
/// This is only executed if `needs_postorder_traversal` returns true. /// This is only executed if `needs_postorder_traversal` returns true.
fn process_postorder(&self, fn process_postorder(&self,
thread_local: &mut Self::ThreadLocalContext, contect: &mut StyleContext<E>,
node: E::ConcreteNode); node: E::ConcreteNode);
/// Boolean that specifies whether a bottom up traversal should be /// Boolean that specifies whether a bottom up traversal should be
@ -177,7 +171,7 @@ pub trait DomTraversal<E: TElement> : Sync {
/// call durign the parallel traversal. /// call durign the parallel traversal.
fn handle_postorder_traversal( fn handle_postorder_traversal(
&self, &self,
thread_local: &mut Self::ThreadLocalContext, context: &mut StyleContext<E>,
root: OpaqueNode, root: OpaqueNode,
mut node: E::ConcreteNode, mut node: E::ConcreteNode,
children_to_process: isize children_to_process: isize
@ -190,7 +184,7 @@ pub trait DomTraversal<E: TElement> : Sync {
if children_to_process == 0 { if children_to_process == 0 {
// We are a leaf. Walk up the chain. // We are a leaf. Walk up the chain.
loop { loop {
self.process_postorder(thread_local, node); self.process_postorder(context, node);
if node.opaque() == root { if node.opaque() == root {
break; break;
} }
@ -404,7 +398,7 @@ pub trait DomTraversal<E: TElement> : Sync {
/// a parameter to keep the logs tidy. /// a parameter to keep the logs tidy.
fn should_traverse_children( fn should_traverse_children(
&self, &self,
thread_local: &mut ThreadLocalStyleContext<E>, context: &mut StyleContext<E>,
parent: E, parent: E,
parent_data: &ElementData, parent_data: &ElementData,
log: LogBehavior log: LogBehavior
@ -442,7 +436,7 @@ pub trait DomTraversal<E: TElement> : Sync {
// happens, we may just end up doing wasted work, since Gecko // happens, we may just end up doing wasted work, since Gecko
// recursively drops Servo ElementData when the XBL insertion parent of // recursively drops Servo ElementData when the XBL insertion parent of
// an Element is changed. // an Element is changed.
if cfg!(feature = "gecko") && thread_local.is_initial_style() && if cfg!(feature = "gecko") && context.thread_local.is_initial_style() &&
parent_data.styles.primary().has_moz_binding() { parent_data.styles.primary().has_moz_binding() {
if log.allow() { if log.allow() {
debug!("Parent {:?} has XBL binding, deferring traversal", debug!("Parent {:?} has XBL binding, deferring traversal",
@ -458,23 +452,23 @@ pub trait DomTraversal<E: TElement> : Sync {
/// should be enqueued for processing. /// should be enqueued for processing.
fn traverse_children<F>( fn traverse_children<F>(
&self, &self,
thread_local: &mut Self::ThreadLocalContext, context: &mut StyleContext<E>,
parent: E, parent: E,
mut f: F mut f: F
) )
where where
F: FnMut(&mut Self::ThreadLocalContext, E::ConcreteNode) F: FnMut(&mut StyleContext<E>, E::ConcreteNode)
{ {
// Check if we're allowed to traverse past this element. // Check if we're allowed to traverse past this element.
let should_traverse = let should_traverse =
self.should_traverse_children( self.should_traverse_children(
thread_local.borrow_mut(), context,
parent, parent,
&parent.borrow_data().unwrap(), &parent.borrow_data().unwrap(),
MayLog MayLog
); );
thread_local.borrow_mut().end_element(parent); context.thread_local.end_element(parent);
if !should_traverse { if !should_traverse {
return; return;
} }
@ -495,7 +489,7 @@ pub trait DomTraversal<E: TElement> : Sync {
} }
} }
} }
f(thread_local, kid); f(context, kid);
} }
} }
} }
@ -517,9 +511,6 @@ pub trait DomTraversal<E: TElement> : Sync {
/// Return the shared style context common to all worker threads. /// Return the shared style context common to all worker threads.
fn shared_context(&self) -> &SharedStyleContext; fn shared_context(&self) -> &SharedStyleContext;
/// Creates a thread-local context.
fn create_thread_local_context(&self) -> Self::ThreadLocalContext;
/// Whether we're performing a parallel traversal. /// Whether we're performing a parallel traversal.
/// ///
/// NB: We do this check on runtime. We could guarantee correctness in this /// NB: We do this check on runtime. We could guarantee correctness in this
@ -763,7 +754,7 @@ where
// Preprocess children, propagating restyle hints and handling sibling // Preprocess children, propagating restyle hints and handling sibling
// relationships. // relationships.
let should_traverse_children = traversal.should_traverse_children( let should_traverse_children = traversal.should_traverse_children(
&mut context.thread_local, context,
element, element,
&data, &data,
DontLog DontLog