mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Introduce and use Scoped TLS.
It turns out that it's problematic to embed ThreadLocalStyleContext within LayoutContext, because parameterizing the former on TElement (which we do in the next patch) infects all the traversal stuff with the trait parameters, which we don't really want. In general, it probably makes sense to use separate scoped TLS types for the separate DOM and Flow tree passes, so we can add a different ScopedTLS type for the Flow pass if we ever need it. We also reorder the |scope| and |shared| parameters in parallel.rs, because it aligns more with the order in style/parallel.rs. I did this when I was adding a TLS parameter to all these functions, which I realized we don't need for now.
This commit is contained in:
parent
8f7f62f810
commit
c5f01fe3b8
16 changed files with 212 additions and 205 deletions
|
@ -19,7 +19,6 @@ use net_traits::image_cache_thread::{ImageOrMetadataAvailable, UsePlaceholder};
|
|||
use parking_lot::RwLock;
|
||||
use servo_config::opts;
|
||||
use servo_url::ServoUrl;
|
||||
use std::borrow::Borrow;
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use std::collections::HashMap;
|
||||
use std::hash::BuildHasherDefault;
|
||||
|
@ -27,59 +26,64 @@ use std::rc::Rc;
|
|||
use std::sync::{Arc, Mutex};
|
||||
use style::context::{SharedStyleContext, ThreadLocalStyleContext};
|
||||
|
||||
pub struct ThreadLocalLayoutContext {
|
||||
/// TLS data scoped to the traversal.
|
||||
pub struct ScopedThreadLocalLayoutContext {
|
||||
pub style_context: ThreadLocalStyleContext,
|
||||
}
|
||||
|
||||
impl ScopedThreadLocalLayoutContext {
|
||||
pub fn new(shared: &SharedLayoutContext) -> Self {
|
||||
ScopedThreadLocalLayoutContext {
|
||||
style_context: ThreadLocalStyleContext::new(&shared.style_context),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// TLS data that persists across traversals.
|
||||
pub struct PersistentThreadLocalLayoutContext {
|
||||
// FontContext uses Rc all over the place and so isn't Send, which means we
|
||||
// can't use ScopedTLS for it. There's also no reason to scope it to the
|
||||
// traversal, and performance is probably better if we don't.
|
||||
pub font_context: RefCell<FontContext>,
|
||||
}
|
||||
|
||||
impl ThreadLocalLayoutContext {
|
||||
impl PersistentThreadLocalLayoutContext {
|
||||
pub fn new(shared: &SharedLayoutContext) -> Rc<Self> {
|
||||
let font_cache_thread = shared.font_cache_thread.lock().unwrap().clone();
|
||||
let local_style_data = shared.style_context.local_context_creation_data.lock().unwrap();
|
||||
|
||||
Rc::new(ThreadLocalLayoutContext {
|
||||
style_context: ThreadLocalStyleContext::new(&local_style_data),
|
||||
Rc::new(PersistentThreadLocalLayoutContext {
|
||||
font_context: RefCell::new(FontContext::new(font_cache_thread)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<ThreadLocalStyleContext> for ThreadLocalLayoutContext {
|
||||
fn borrow(&self) -> &ThreadLocalStyleContext {
|
||||
&self.style_context
|
||||
}
|
||||
}
|
||||
|
||||
impl HeapSizeOf for ThreadLocalLayoutContext {
|
||||
// FIXME(njn): measure other fields eventually.
|
||||
impl HeapSizeOf for PersistentThreadLocalLayoutContext {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
self.font_context.heap_size_of_children()
|
||||
}
|
||||
}
|
||||
|
||||
thread_local!(static LOCAL_CONTEXT_KEY: RefCell<Option<Rc<ThreadLocalLayoutContext>>> = RefCell::new(None));
|
||||
thread_local!(static LOCAL_CONTEXT_KEY: RefCell<Option<Rc<PersistentThreadLocalLayoutContext>>> = RefCell::new(None));
|
||||
|
||||
pub fn heap_size_of_local_context() -> usize {
|
||||
LOCAL_CONTEXT_KEY.with(|r| {
|
||||
r.borrow().clone().map_or(0, |context| context.heap_size_of_children())
|
||||
})
|
||||
}
|
||||
|
||||
// Keep this implementation in sync with the one in ports/geckolib/traversal.rs.
|
||||
pub fn create_or_get_local_context(shared: &SharedLayoutContext)
|
||||
-> Rc<ThreadLocalLayoutContext> {
|
||||
fn create_or_get_persistent_context(shared: &SharedLayoutContext)
|
||||
-> Rc<PersistentThreadLocalLayoutContext> {
|
||||
LOCAL_CONTEXT_KEY.with(|r| {
|
||||
let mut r = r.borrow_mut();
|
||||
if let Some(context) = r.clone() {
|
||||
context
|
||||
} else {
|
||||
let context = ThreadLocalLayoutContext::new(shared);
|
||||
let context = PersistentThreadLocalLayoutContext::new(shared);
|
||||
*r = Some(context.clone());
|
||||
context
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn heap_size_of_persistent_local_context() -> usize {
|
||||
LOCAL_CONTEXT_KEY.with(|r| {
|
||||
r.borrow().clone().map_or(0, |context| context.heap_size_of_children())
|
||||
})
|
||||
}
|
||||
|
||||
/// Layout information shared among all workers. This must be thread-safe.
|
||||
pub struct SharedLayoutContext {
|
||||
/// Bits shared by the layout and style system.
|
||||
|
@ -100,24 +104,17 @@ pub struct SharedLayoutContext {
|
|||
BuildHasherDefault<FnvHasher>>>>,
|
||||
}
|
||||
|
||||
impl Borrow<SharedStyleContext> for SharedLayoutContext {
|
||||
fn borrow(&self) -> &SharedStyleContext {
|
||||
&self.style_context
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LayoutContext<'a> {
|
||||
pub shared: &'a SharedLayoutContext,
|
||||
pub thread_local: &'a ThreadLocalLayoutContext,
|
||||
pub persistent: Rc<PersistentThreadLocalLayoutContext>,
|
||||
}
|
||||
|
||||
impl<'a> LayoutContext<'a> {
|
||||
pub fn new(shared: &'a SharedLayoutContext,
|
||||
thread_local: &'a ThreadLocalLayoutContext) -> Self
|
||||
pub fn new(shared: &'a SharedLayoutContext) -> Self
|
||||
{
|
||||
LayoutContext {
|
||||
shared: shared,
|
||||
thread_local: thread_local,
|
||||
persistent: create_or_get_persistent_context(shared),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,7 +135,7 @@ impl<'a> LayoutContext<'a> {
|
|||
|
||||
#[inline(always)]
|
||||
pub fn font_context(&self) -> RefMut<FontContext> {
|
||||
self.thread_local.font_context.borrow_mut()
|
||||
self.persistent.font_context.borrow_mut()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
|
||||
#![allow(unsafe_code)]
|
||||
|
||||
use context::{LayoutContext, SharedLayoutContext, ThreadLocalLayoutContext};
|
||||
use context::create_or_get_local_context;
|
||||
use context::{LayoutContext, SharedLayoutContext};
|
||||
use flow::{self, Flow, MutableFlowUtils, PostorderFlowTraversal, PreorderFlowTraversal};
|
||||
use flow_ref::FlowRef;
|
||||
use profile_traits::time::{self, TimerMetadata, profile};
|
||||
|
@ -51,7 +50,7 @@ pub fn borrowed_flow_to_unsafe_flow(flow: &Flow) -> UnsafeFlow {
|
|||
}
|
||||
|
||||
pub type ChunkedFlowTraversalFunction<'scope> =
|
||||
extern "Rust" fn(Box<[UnsafeFlow]>, &'scope SharedLayoutContext, &rayon::Scope<'scope>);
|
||||
extern "Rust" fn(Box<[UnsafeFlow]>, &rayon::Scope<'scope>, &'scope SharedLayoutContext);
|
||||
|
||||
pub type FlowTraversalFunction = extern "Rust" fn(UnsafeFlow, &LayoutContext);
|
||||
|
||||
|
@ -133,23 +132,22 @@ trait ParallelPostorderFlowTraversal : PostorderFlowTraversal {
|
|||
trait ParallelPreorderFlowTraversal : PreorderFlowTraversal {
|
||||
fn run_parallel<'scope>(&self,
|
||||
unsafe_flows: &[UnsafeFlow],
|
||||
layout_context: &'scope SharedLayoutContext,
|
||||
scope: &rayon::Scope<'scope>);
|
||||
scope: &rayon::Scope<'scope>,
|
||||
shared: &'scope SharedLayoutContext);
|
||||
|
||||
fn should_record_thread_ids(&self) -> bool;
|
||||
|
||||
#[inline(always)]
|
||||
fn run_parallel_helper<'scope>(&self,
|
||||
unsafe_flows: &[UnsafeFlow],
|
||||
shared: &'scope SharedLayoutContext,
|
||||
scope: &rayon::Scope<'scope>,
|
||||
shared: &'scope SharedLayoutContext,
|
||||
top_down_func: ChunkedFlowTraversalFunction<'scope>,
|
||||
bottom_up_func: FlowTraversalFunction)
|
||||
{
|
||||
let tlc = create_or_get_local_context(shared);
|
||||
let context = LayoutContext::new(&shared, &*tlc);
|
||||
|
||||
let mut discovered_child_flows = vec![];
|
||||
let context = LayoutContext::new(&shared);
|
||||
|
||||
for unsafe_flow in unsafe_flows {
|
||||
let mut had_children = false;
|
||||
unsafe {
|
||||
|
@ -187,7 +185,7 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal {
|
|||
let nodes = chunk.iter().cloned().collect::<Vec<_>>().into_boxed_slice();
|
||||
|
||||
scope.spawn(move |scope| {
|
||||
top_down_func(nodes, shared, scope);
|
||||
top_down_func(nodes, scope, shared);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -196,12 +194,12 @@ trait ParallelPreorderFlowTraversal : PreorderFlowTraversal {
|
|||
impl<'a> ParallelPreorderFlowTraversal for AssignISizes<'a> {
|
||||
fn run_parallel<'scope>(&self,
|
||||
unsafe_flows: &[UnsafeFlow],
|
||||
layout_context: &'scope SharedLayoutContext,
|
||||
scope: &rayon::Scope<'scope>)
|
||||
scope: &rayon::Scope<'scope>,
|
||||
shared: &'scope SharedLayoutContext)
|
||||
{
|
||||
self.run_parallel_helper(unsafe_flows,
|
||||
layout_context,
|
||||
scope,
|
||||
shared,
|
||||
assign_inline_sizes,
|
||||
assign_block_sizes_and_store_overflow)
|
||||
}
|
||||
|
@ -214,12 +212,12 @@ impl<'a> ParallelPreorderFlowTraversal for AssignISizes<'a> {
|
|||
impl<'a> ParallelPostorderFlowTraversal for AssignBSizes<'a> {}
|
||||
|
||||
fn assign_inline_sizes<'scope>(unsafe_flows: Box<[UnsafeFlow]>,
|
||||
shared_layout_context: &'scope SharedLayoutContext,
|
||||
scope: &rayon::Scope<'scope>) {
|
||||
scope: &rayon::Scope<'scope>,
|
||||
shared: &'scope SharedLayoutContext) {
|
||||
let assign_inline_sizes_traversal = AssignISizes {
|
||||
shared_context: &shared_layout_context.style_context,
|
||||
shared_context: &shared.style_context,
|
||||
};
|
||||
assign_inline_sizes_traversal.run_parallel(&unsafe_flows, shared_layout_context, scope)
|
||||
assign_inline_sizes_traversal.run_parallel(&unsafe_flows, scope, shared)
|
||||
}
|
||||
|
||||
fn assign_block_sizes_and_store_overflow(
|
||||
|
@ -238,8 +236,7 @@ pub fn traverse_flow_tree_preorder(
|
|||
shared: &SharedLayoutContext,
|
||||
queue: &rayon::ThreadPool) {
|
||||
if opts::get().bubble_inline_sizes_separately {
|
||||
let tlc = ThreadLocalLayoutContext::new(shared);
|
||||
let context = LayoutContext::new(shared, &*tlc);
|
||||
let context = LayoutContext::new(shared);
|
||||
let bubble_inline_sizes = BubbleISizes { layout_context: &context };
|
||||
root.traverse_postorder(&bubble_inline_sizes);
|
||||
}
|
||||
|
@ -250,7 +247,7 @@ pub fn traverse_flow_tree_preorder(
|
|||
rayon::scope(move |scope| {
|
||||
profile(time::ProfilerCategory::LayoutParallelWarmup,
|
||||
profiler_metadata, time_profiler_chan, move || {
|
||||
assign_inline_sizes(nodes, &shared, scope);
|
||||
assign_inline_sizes(nodes, scope, &shared);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -6,8 +6,7 @@
|
|||
|
||||
use app_units::Au;
|
||||
use construct::ConstructionResult;
|
||||
use context::SharedLayoutContext;
|
||||
use context::create_or_get_local_context;
|
||||
use context::{ScopedThreadLocalLayoutContext, SharedLayoutContext};
|
||||
use euclid::point::Point2D;
|
||||
use euclid::rect::Rect;
|
||||
use euclid::size::Size2D;
|
||||
|
@ -646,10 +645,10 @@ pub fn process_resolved_style_request<'a, N>(shared: &SharedLayoutContext,
|
|||
// we'd need a mechanism to prevent detect when it's stale (since we don't
|
||||
// traverse display:none subtrees during restyle).
|
||||
let display_none_root = if element.get_data().is_none() {
|
||||
let tlc = create_or_get_local_context(shared);
|
||||
let mut tlc = ScopedThreadLocalLayoutContext::new(shared);
|
||||
let context = StyleContext {
|
||||
shared: &shared.style_context,
|
||||
thread_local: &tlc.style_context,
|
||||
thread_local: &mut tlc.style_context,
|
||||
};
|
||||
|
||||
Some(style_element_in_display_none_subtree(&context, element,
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//! Implements sequential traversals over the DOM and flow trees.
|
||||
|
||||
use app_units::Au;
|
||||
use context::{LayoutContext, SharedLayoutContext, ThreadLocalLayoutContext};
|
||||
use context::{LayoutContext, SharedLayoutContext};
|
||||
use display_list_builder::DisplayListBuildState;
|
||||
use euclid::point::Point2D;
|
||||
use floats::SpeculatedFloatPlacement;
|
||||
|
@ -34,8 +34,7 @@ pub fn resolve_generated_content(root: &mut Flow, shared: &SharedLayoutContext)
|
|||
}
|
||||
}
|
||||
|
||||
let tlc = ThreadLocalLayoutContext::new(shared);
|
||||
let layout_context = LayoutContext::new(shared, &*tlc);
|
||||
let layout_context = LayoutContext::new(shared);
|
||||
let mut traversal = ResolveGeneratedContent::new(&layout_context);
|
||||
doit(root, 0, &mut traversal)
|
||||
}
|
||||
|
@ -58,8 +57,7 @@ pub fn traverse_flow_tree_preorder(root: &mut Flow,
|
|||
}
|
||||
}
|
||||
|
||||
let tlc = ThreadLocalLayoutContext::new(shared);
|
||||
let layout_context = LayoutContext::new(shared, &*tlc);
|
||||
let layout_context = LayoutContext::new(shared);
|
||||
|
||||
if opts::get().bubble_inline_sizes_separately {
|
||||
let bubble_inline_sizes = BubbleISizes { layout_context: &layout_context };
|
||||
|
|
|
@ -5,15 +5,13 @@
|
|||
//! Traversals over the DOM and flow trees, running the layout computations.
|
||||
|
||||
use construct::FlowConstructor;
|
||||
use context::{LayoutContext, SharedLayoutContext, ThreadLocalLayoutContext};
|
||||
use context::create_or_get_local_context;
|
||||
use context::{LayoutContext, ScopedThreadLocalLayoutContext, SharedLayoutContext};
|
||||
use display_list_builder::DisplayListBuildState;
|
||||
use flow::{self, PreorderFlowTraversal};
|
||||
use flow::{CAN_BE_FRAGMENTED, Flow, ImmutableFlowUtils, PostorderFlowTraversal};
|
||||
use gfx::display_list::OpaqueNode;
|
||||
use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode};
|
||||
use servo_config::opts;
|
||||
use std::rc::Rc;
|
||||
use style::atomic_refcell::AtomicRefCell;
|
||||
use style::context::{SharedStyleContext, StyleContext};
|
||||
use style::data::ElementData;
|
||||
|
@ -56,9 +54,10 @@ impl<N> DomTraversal<N> for RecalcStyleAndConstructFlows
|
|||
where N: LayoutNode + TNode,
|
||||
N::ConcreteElement: TElement
|
||||
{
|
||||
type ThreadLocalContext = ThreadLocalLayoutContext;
|
||||
type ThreadLocalContext = ScopedThreadLocalLayoutContext;
|
||||
|
||||
fn process_preorder(&self, node: N, traversal_data: &mut PerLevelTraversalData) {
|
||||
fn process_preorder(&self, traversal_data: &mut PerLevelTraversalData,
|
||||
thread_local: &mut Self::ThreadLocalContext, node: N) {
|
||||
// FIXME(pcwalton): Stop allocating here. Ideally this should just be
|
||||
// done by the HTML parser.
|
||||
node.initialize_data();
|
||||
|
@ -66,19 +65,17 @@ impl<N> DomTraversal<N> for RecalcStyleAndConstructFlows
|
|||
if !node.is_text_node() {
|
||||
let el = node.as_element().unwrap();
|
||||
let mut data = el.mutate_data().unwrap();
|
||||
let tlc = create_or_get_local_context(&self.shared);
|
||||
let context = StyleContext {
|
||||
let mut context = StyleContext {
|
||||
shared: &self.shared.style_context,
|
||||
thread_local: &tlc.style_context,
|
||||
thread_local: &mut thread_local.style_context,
|
||||
};
|
||||
recalc_style_at(self, traversal_data, &context, el, &mut data);
|
||||
recalc_style_at(self, traversal_data, &mut context, el, &mut data);
|
||||
}
|
||||
}
|
||||
|
||||
fn process_postorder(&self, node: N) {
|
||||
let tlc = create_or_get_local_context(&self.shared);
|
||||
let context = LayoutContext::new(&self.shared, &*tlc);
|
||||
construct_flows_at(&context, self.root, node);
|
||||
fn process_postorder(&self, thread_local: &mut Self::ThreadLocalContext, node: N) {
|
||||
let context = LayoutContext::new(&self.shared);
|
||||
construct_flows_at(&context, thread_local, self.root, node);
|
||||
}
|
||||
|
||||
fn text_node_needs_traversal(node: N) -> bool {
|
||||
|
@ -103,8 +100,8 @@ impl<N> DomTraversal<N> for RecalcStyleAndConstructFlows
|
|||
&self.shared.style_context
|
||||
}
|
||||
|
||||
fn create_or_get_thread_local_context(&self) -> Rc<ThreadLocalLayoutContext> {
|
||||
create_or_get_local_context(&self.shared)
|
||||
fn create_thread_local_context(&self) -> Self::ThreadLocalContext {
|
||||
ScopedThreadLocalLayoutContext::new(&self.shared)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,7 +114,9 @@ pub trait PostorderNodeMutTraversal<ConcreteThreadSafeLayoutNode: ThreadSafeLayo
|
|||
/// The flow construction traversal, which builds flows for styled nodes.
|
||||
#[inline]
|
||||
#[allow(unsafe_code)]
|
||||
fn construct_flows_at<'a, N>(context: &LayoutContext<'a>, root: OpaqueNode, node: N)
|
||||
fn construct_flows_at<'a, N>(context: &LayoutContext<'a>,
|
||||
_thread_local: &ScopedThreadLocalLayoutContext,
|
||||
root: OpaqueNode, node: N)
|
||||
where N: LayoutNode,
|
||||
{
|
||||
debug!("construct_flows_at: {:?}", node);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue