Auto merge of #16033 - servo:per-document-locks, r=emilio

Per-document shared lock for author-origin stylesheets

Fix #16027

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16033)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-03-19 18:22:23 -07:00 committed by GitHub
commit 8d25dcb8b5
3 changed files with 117 additions and 118 deletions

View file

@ -114,7 +114,6 @@ use style::error_reporting::StdoutErrorReporter;
use style::logical_geometry::LogicalPoint; use style::logical_geometry::LogicalPoint;
use style::media_queries::{Device, MediaType}; use style::media_queries::{Device, MediaType};
use style::parser::ParserContextExtraData; use style::parser::ParserContextExtraData;
use style::servo::AUTHOR_SHARED_LOCK;
use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION, STORE_OVERFLOW}; use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION, STORE_OVERFLOW};
use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards}; use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards};
use style::stylesheets::{Origin, Stylesheet, UserAgentStylesheets}; use style::stylesheets::{Origin, Stylesheet, UserAgentStylesheets};
@ -190,6 +189,9 @@ pub struct LayoutThread {
/// The root of the flow tree. /// The root of the flow tree.
root_flow: Option<FlowRef>, root_flow: Option<FlowRef>,
/// The document-specific shared lock used for author-origin stylesheets
document_shared_lock: Option<SharedRwLock>,
/// The list of currently-running animations. /// The list of currently-running animations.
running_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>, running_animations: Arc<RwLock<HashMap<OpaqueNode, Vec<Animation>>>>,
@ -442,6 +444,7 @@ impl LayoutThread {
new_animations_receiver: new_animations_receiver, new_animations_receiver: new_animations_receiver,
outstanding_web_fonts: outstanding_web_fonts_counter, outstanding_web_fonts: outstanding_web_fonts_counter,
root_flow: None, root_flow: None,
document_shared_lock: None,
running_animations: Arc::new(RwLock::new(HashMap::new())), running_animations: Arc::new(RwLock::new(HashMap::new())),
expired_animations: Arc::new(RwLock::new(HashMap::new())), expired_animations: Arc::new(RwLock::new(HashMap::new())),
epoch: Epoch(0), epoch: Epoch(0),
@ -1020,7 +1023,9 @@ impl LayoutThread {
// Calculate the actual viewport as per DEVICE-ADAPT § 6 // Calculate the actual viewport as per DEVICE-ADAPT § 6
let author_guard = document.style_shared_lock().read(); let document_shared_lock = document.style_shared_lock();
self.document_shared_lock = Some(document_shared_lock.clone());
let author_guard = document_shared_lock.read();
let device = Device::new(MediaType::Screen, initial_viewport); let device = Device::new(MediaType::Screen, initial_viewport);
Arc::get_mut(&mut rw_data.stylist).unwrap() Arc::get_mut(&mut rw_data.stylist).unwrap()
.set_device(device, &author_guard, &data.document_stylesheets); .set_device(device, &author_guard, &data.document_stylesheets);
@ -1189,11 +1194,14 @@ impl LayoutThread {
unsafe { layout_context.style_context.stylist.rule_tree.maybe_gc(); } unsafe { layout_context.style_context.stylist.rule_tree.maybe_gc(); }
// Perform post-style recalculation layout passes. // Perform post-style recalculation layout passes.
self.perform_post_style_recalc_layout_passes(&data.reflow_info, if let Some(mut root_flow) = self.root_flow.clone() {
Some(&data.query_type), self.perform_post_style_recalc_layout_passes(&mut root_flow,
Some(&document), &data.reflow_info,
&mut rw_data, Some(&data.query_type),
&mut layout_context); Some(&document),
&mut rw_data,
&mut layout_context);
}
self.respond_to_query_if_necessary(&data.query_type, self.respond_to_query_if_necessary(&data.query_type,
&mut *rw_data, &mut *rw_data,
@ -1346,123 +1354,125 @@ impl LayoutThread {
println!("**** pipeline={}\tForDisplay\tSpecial\tAnimationTick", self.id); println!("**** pipeline={}\tForDisplay\tSpecial\tAnimationTick", self.id);
} }
let reflow_info = Reflow {
goal: ReflowGoal::ForDisplay,
page_clip_rect: max_rect(),
};
let author_guard = AUTHOR_SHARED_LOCK.read();
let ua_or_user_guard = UA_STYLESHEETS.shared_lock.read();
let guards = StylesheetGuards {
author: &author_guard,
ua_or_user: &ua_or_user_guard,
};
let mut layout_context = self.build_layout_context(guards, &*rw_data, false);
if let Some(mut root_flow) = self.root_flow.clone() { if let Some(mut root_flow) = self.root_flow.clone() {
// Perform an abbreviated style recalc that operates without access to the DOM. let reflow_info = Reflow {
let animations = self.running_animations.read(); goal: ReflowGoal::ForDisplay,
profile(time::ProfilerCategory::LayoutStyleRecalc, page_clip_rect: max_rect(),
self.profiler_metadata(), };
self.time_profiler_chan.clone(),
|| { // Unwrap here should not panic since self.root_flow is only ever set to Some(_)
animation::recalc_style_for_animations(&layout_context, // in handle_reflow() where self.document_shared_lock is as well.
FlowRef::deref_mut(&mut root_flow), let author_shared_lock = self.document_shared_lock.clone().unwrap();
&animations) let author_guard = author_shared_lock.read();
}); let ua_or_user_guard = UA_STYLESHEETS.shared_lock.read();
let guards = StylesheetGuards {
author: &author_guard,
ua_or_user: &ua_or_user_guard,
};
let mut layout_context = self.build_layout_context(guards, &*rw_data, false);
{
// Perform an abbreviated style recalc that operates without access to the DOM.
let animations = self.running_animations.read();
profile(time::ProfilerCategory::LayoutStyleRecalc,
self.profiler_metadata(),
self.time_profiler_chan.clone(),
|| {
animation::recalc_style_for_animations(
&layout_context, FlowRef::deref_mut(&mut root_flow), &animations)
});
}
self.perform_post_style_recalc_layout_passes(&mut root_flow,
&reflow_info,
None,
None,
&mut *rw_data,
&mut layout_context);
assert!(layout_context.pending_images.is_none());
} }
self.perform_post_style_recalc_layout_passes(&reflow_info,
None,
None,
&mut *rw_data,
&mut layout_context);
assert!(layout_context.pending_images.is_none());
} }
fn perform_post_style_recalc_layout_passes(&mut self, fn perform_post_style_recalc_layout_passes(&mut self,
root_flow: &mut FlowRef,
data: &Reflow, data: &Reflow,
query_type: Option<&ReflowQueryType>, query_type: Option<&ReflowQueryType>,
document: Option<&ServoLayoutDocument>, document: Option<&ServoLayoutDocument>,
rw_data: &mut LayoutThreadData, rw_data: &mut LayoutThreadData,
context: &mut LayoutContext) { context: &mut LayoutContext) {
if let Some(mut root_flow) = self.root_flow.clone() { // Kick off animations if any were triggered, expire completed ones.
// Kick off animations if any were triggered, expire completed ones. animation::update_animation_state(&self.constellation_chan,
animation::update_animation_state(&self.constellation_chan, &self.script_chan,
&self.script_chan, &mut *self.running_animations.write(),
&mut *self.running_animations.write(), &mut *self.expired_animations.write(),
&mut *self.expired_animations.write(), &self.new_animations_receiver,
&self.new_animations_receiver, self.id,
self.id, &self.timer);
&self.timer);
profile(time::ProfilerCategory::LayoutRestyleDamagePropagation, profile(time::ProfilerCategory::LayoutRestyleDamagePropagation,
self.profiler_metadata(),
self.time_profiler_chan.clone(),
|| {
// Call `compute_layout_damage` even in non-incremental mode, because it sets flags
// that are needed in both incremental and non-incremental traversals.
let damage = FlowRef::deref_mut(root_flow).compute_layout_damage();
if opts::get().nonincremental_layout || damage.contains(REFLOW_ENTIRE_DOCUMENT) {
FlowRef::deref_mut(root_flow).reflow_entire_document()
}
});
if opts::get().trace_layout {
layout_debug::begin_trace(root_flow.clone());
}
// Resolve generated content.
profile(time::ProfilerCategory::LayoutGeneratedContent,
self.profiler_metadata(),
self.time_profiler_chan.clone(),
|| sequential::resolve_generated_content(FlowRef::deref_mut(root_flow), &context));
// Guess float placement.
profile(time::ProfilerCategory::LayoutFloatPlacementSpeculation,
self.profiler_metadata(),
self.time_profiler_chan.clone(),
|| sequential::guess_float_placement(FlowRef::deref_mut(root_flow)));
// Perform the primary layout passes over the flow tree to compute the locations of all
// the boxes.
if flow::base(&**root_flow).restyle_damage.intersects(REFLOW | REFLOW_OUT_OF_FLOW) {
profile(time::ProfilerCategory::LayoutMain,
self.profiler_metadata(), self.profiler_metadata(),
self.time_profiler_chan.clone(), self.time_profiler_chan.clone(),
|| { || {
// Call `compute_layout_damage` even in non-incremental mode, because it sets flags let profiler_metadata = self.profiler_metadata();
// that are needed in both incremental and non-incremental traversals.
let damage = FlowRef::deref_mut(&mut root_flow).compute_layout_damage();
if opts::get().nonincremental_layout || damage.contains(REFLOW_ENTIRE_DOCUMENT) { if let (true, Some(traversal)) = (self.parallel_flag, self.parallel_traversal.as_mut()) {
FlowRef::deref_mut(&mut root_flow).reflow_entire_document() // Parallel mode.
LayoutThread::solve_constraints_parallel(traversal,
FlowRef::deref_mut(root_flow),
profiler_metadata,
self.time_profiler_chan.clone(),
&*context);
} else {
//Sequential mode
LayoutThread::solve_constraints(FlowRef::deref_mut(root_flow), &context)
} }
}); });
if opts::get().trace_layout {
layout_debug::begin_trace(root_flow.clone());
}
// Resolve generated content.
profile(time::ProfilerCategory::LayoutGeneratedContent,
self.profiler_metadata(),
self.time_profiler_chan.clone(),
|| sequential::resolve_generated_content(FlowRef::deref_mut(&mut root_flow), &context));
// Guess float placement.
profile(time::ProfilerCategory::LayoutFloatPlacementSpeculation,
self.profiler_metadata(),
self.time_profiler_chan.clone(),
|| sequential::guess_float_placement(FlowRef::deref_mut(&mut root_flow)));
// Perform the primary layout passes over the flow tree to compute the locations of all
// the boxes.
if flow::base(&*root_flow).restyle_damage.intersects(REFLOW | REFLOW_OUT_OF_FLOW) {
profile(time::ProfilerCategory::LayoutMain,
self.profiler_metadata(),
self.time_profiler_chan.clone(),
|| {
let profiler_metadata = self.profiler_metadata();
if let (true, Some(traversal)) = (self.parallel_flag, self.parallel_traversal.as_mut()) {
// Parallel mode.
LayoutThread::solve_constraints_parallel(traversal,
FlowRef::deref_mut(&mut root_flow),
profiler_metadata,
self.time_profiler_chan.clone(),
&*context);
} else {
//Sequential mode
LayoutThread::solve_constraints(FlowRef::deref_mut(&mut root_flow), &context)
}
});
}
profile(time::ProfilerCategory::LayoutStoreOverflow,
self.profiler_metadata(),
self.time_profiler_chan.clone(),
|| {
sequential::store_overflow(context,
FlowRef::deref_mut(&mut root_flow) as &mut Flow);
});
self.perform_post_main_layout_passes(data,
query_type,
document,
rw_data,
context);
} }
profile(time::ProfilerCategory::LayoutStoreOverflow,
self.profiler_metadata(),
self.time_profiler_chan.clone(),
|| {
sequential::store_overflow(context,
FlowRef::deref_mut(root_flow) as &mut Flow);
});
self.perform_post_main_layout_passes(data,
query_type,
document,
rw_data,
context);
} }
fn perform_post_main_layout_passes(&mut self, fn perform_post_main_layout_passes(&mut self,

View file

@ -134,7 +134,6 @@ use style::attr::AttrValue;
use style::context::{QuirksMode, ReflowGoal}; use style::context::{QuirksMode, ReflowGoal};
use style::restyle_hints::{RestyleHint, RESTYLE_STYLE_ATTRIBUTE}; use style::restyle_hints::{RestyleHint, RESTYLE_STYLE_ATTRIBUTE};
use style::selector_parser::{RestyleDamage, Snapshot}; use style::selector_parser::{RestyleDamage, Snapshot};
use style::servo::AUTHOR_SHARED_LOCK;
use style::shared_lock::SharedRwLock as StyleSharedRwLock; use style::shared_lock::SharedRwLock as StyleSharedRwLock;
use style::str::{HTML_SPACE_CHARACTERS, split_html_space_chars, str_join}; use style::str::{HTML_SPACE_CHARACTERS, split_html_space_chars, str_join};
use style::stylesheets::Stylesheet; use style::stylesheets::Stylesheet;
@ -2132,7 +2131,7 @@ impl Document {
scripts: Default::default(), scripts: Default::default(),
anchors: Default::default(), anchors: Default::default(),
applets: Default::default(), applets: Default::default(),
style_shared_lock: AUTHOR_SHARED_LOCK.clone(), style_shared_lock: StyleSharedRwLock::new(),
stylesheets: DOMRefCell::new(None), stylesheets: DOMRefCell::new(None),
stylesheets_changed_since_reflow: Cell::new(false), stylesheets_changed_since_reflow: Cell::new(false),
stylesheet_list: MutNullableJS::new(None), stylesheet_list: MutNullableJS::new(None),

View file

@ -9,13 +9,3 @@
pub mod media_queries; pub mod media_queries;
pub mod restyle_damage; pub mod restyle_damage;
pub mod selector_parser; pub mod selector_parser;
use shared_lock::SharedRwLock;
lazy_static! {
/// Per-process shared lock for author-origin stylesheets
///
/// FIXME: make it per-document or per-pipeline instead:
/// https://github.com/servo/servo/issues/16027
pub static ref AUTHOR_SHARED_LOCK: SharedRwLock = SharedRwLock::new();
}