auto merge of #468 : jdm/servo/investigate, r=metajack

By the power vested in me through the agency of gdb, massif, and valgrind, here are some changes that fix various crashes that occur when shutting down at arbitrary points, along with some honest-to-goodness shutdown leaks reported by valgrind, and the layout cycles that were causing 1gb/s memory explosions when running test_hammer_layout.html.
This commit is contained in:
bors-servo 2013-05-22 12:57:28 -07:00
commit 943139b397
15 changed files with 155 additions and 27 deletions

View file

@ -177,6 +177,10 @@ pub impl FontGroup {
} }
} }
fn teardown(&mut self) {
self.fonts = ~[];
}
fn create_textrun(&self, text: ~str) -> TextRun { fn create_textrun(&self, text: ~str) -> TextRun {
assert!(self.fonts.len() > 0); assert!(self.fonts.len() > 0);
@ -283,6 +287,11 @@ pub impl Font {
return result; return result;
} }
fn teardown(&mut self) {
self.shaper = None;
self.azure_font = None;
}
// TODO: this should return a borrowed pointer, but I can't figure // TODO: this should return a borrowed pointer, but I can't figure
// out why borrowck doesn't like my implementation. // out why borrowck doesn't like my implementation.

View file

@ -132,7 +132,6 @@ impl FontFamily {
/// In the common case, each FontFamily will have a singleton FontEntry, or it will have the /// In the common case, each FontFamily will have a singleton FontEntry, or it will have the
/// standard four faces: Normal, Bold, Italic, BoldItalic. /// standard four faces: Normal, Bold, Italic, BoldItalic.
pub struct FontEntry { pub struct FontEntry {
family: @mut FontFamily,
face_name: ~str, face_name: ~str,
priv weight: CSSFontWeight, priv weight: CSSFontWeight,
priv italic: bool, priv italic: bool,
@ -141,9 +140,8 @@ pub struct FontEntry {
} }
impl FontEntry { impl FontEntry {
pub fn new(family: @mut FontFamily, handle: FontHandle) -> FontEntry { pub fn new(handle: FontHandle) -> FontEntry {
FontEntry { FontEntry {
family: family,
face_name: handle.face_name(), face_name: handle.face_name(),
weight: handle.boldness(), weight: handle.boldness(),
italic: handle.is_italic(), italic: handle.is_italic(),

View file

@ -53,6 +53,7 @@ pub struct FontHandle {
// if the font is created using FT_Memory_Face. // if the font is created using FT_Memory_Face.
source: FontSource, source: FontSource,
face: FT_Face, face: FT_Face,
handle: FontContextHandle
} }
#[unsafe_destructor] #[unsafe_destructor]
@ -81,7 +82,14 @@ impl FontHandleMethods for FontHandle {
// and moving buf into the struct ctor, but cant' move out of // and moving buf into the struct ctor, but cant' move out of
// captured binding. // captured binding.
return match face_result { return match face_result {
Ok(face) => Ok(FontHandle { face: face, source: FontSourceMem(buf) }), Ok(face) => {
let handle = FontHandle {
face: face,
source: FontSourceMem(buf),
handle: *fctx
};
Ok(handle)
}
Err(()) => Err(()) Err(()) => Err(())
}; };
@ -247,7 +255,11 @@ pub impl<'self> FontHandle {
return Err(()); return Err(());
} }
if FontHandle::set_char_size(face, style.pt_size).is_ok() { if FontHandle::set_char_size(face, style.pt_size).is_ok() {
Ok(FontHandle { source: FontSourceFile(file), face: face }) Ok(FontHandle {
source: FontSourceFile(file),
face: face,
handle: *fctx
})
} else { } else {
Err(()) Err(())
} }
@ -268,7 +280,11 @@ pub impl<'self> FontHandle {
return Err(()); return Err(());
} }
Ok(FontHandle { source: FontSourceFile(file), face: face }) Ok(FontHandle {
source: FontSourceFile(file),
face: face,
handle: *fctx
})
} }
priv fn get_face_rec(&'self self) -> &'self FT_FaceRec { priv fn get_face_rec(&'self self) -> &'self FT_FaceRec {

View file

@ -6,7 +6,7 @@ extern mod freetype;
extern mod fontconfig; extern mod fontconfig;
use fontconfig::fontconfig::{ use fontconfig::fontconfig::{
FcChar8, FcResultMatch, FcSetSystem, FcChar8, FcResultMatch, FcSetSystem, FcPattern,
FcResultNoMatch, FcMatchPattern, FC_SLANT_ITALIC, FC_WEIGHT_BOLD FcResultNoMatch, FcMatchPattern, FC_SLANT_ITALIC, FC_WEIGHT_BOLD
}; };
use fontconfig::fontconfig::bindgen::{ use fontconfig::fontconfig::bindgen::{
@ -116,7 +116,7 @@ pub impl FontListHandle {
let font_handle = font_handle.unwrap(); let font_handle = font_handle.unwrap();
debug!("Creating new FontEntry for face: %s", font_handle.face_name()); debug!("Creating new FontEntry for face: %s", font_handle.face_name());
let entry = @FontEntry::new(family, font_handle); let entry = @FontEntry::new(font_handle);
family.entries.push(entry); family.entries.push(entry);
} }
@ -127,10 +127,21 @@ pub impl FontListHandle {
} }
} }
struct AutoPattern {
pattern: *FcPattern
}
impl Drop for AutoPattern {
fn finalize(&self) {
FcPatternDestroy(self.pattern);
}
}
pub fn path_from_identifier(name: ~str, style: &UsedFontStyle) -> Result<~str, ()> { pub fn path_from_identifier(name: ~str, style: &UsedFontStyle) -> Result<~str, ()> {
unsafe { unsafe {
let config = FcConfigGetCurrent(); let config = FcConfigGetCurrent();
let pattern = FcPatternCreate(); let wrapper = AutoPattern { pattern: FcPatternCreate() };
let pattern = wrapper.pattern;
let res = do str::as_c_str("family") |FC_FAMILY| { let res = do str::as_c_str("family") |FC_FAMILY| {
do str::as_c_str(name) |family| { do str::as_c_str(name) |family| {
FcPatternAddString(pattern, FC_FAMILY, family as *FcChar8) FcPatternAddString(pattern, FC_FAMILY, family as *FcChar8)
@ -166,7 +177,8 @@ pub fn path_from_identifier(name: ~str, style: &UsedFontStyle) -> Result<~str, (
} }
FcDefaultSubstitute(pattern); FcDefaultSubstitute(pattern);
let result = FcResultNoMatch; let result = FcResultNoMatch;
let result_pattern = FcFontMatch(config, pattern, &result); let result_wrapper = AutoPattern { pattern: FcFontMatch(config, pattern, &result) };
let result_pattern = result_wrapper.pattern;
if result != FcResultMatch && result_pattern.is_null() { if result != FcResultMatch && result_pattern.is_null() {
debug!("obtaining match to pattern failed"); debug!("obtaining match to pattern failed");
return Err(()); return Err(());

View file

@ -51,7 +51,7 @@ pub impl FontListHandle {
let handle = result::unwrap(FontHandle::new_from_CTFont(&self.fctx, font)); let handle = result::unwrap(FontHandle::new_from_CTFont(&self.fctx, font));
debug!("Creating new FontEntry for face: %s", handle.face_name()); debug!("Creating new FontEntry for face: %s", handle.face_name());
let entry = @FontEntry::new(family, handle); let entry = @FontEntry::new(handle);
family.entries.push(entry) family.entries.push(entry)
} }
} }

View file

@ -51,6 +51,10 @@ pub impl<'self> TextRun {
return run; return run;
} }
fn teardown(&self) {
self.font.teardown();
}
fn compute_potential_breaks(text: &str, glyphs: &mut GlyphStore) { fn compute_potential_breaks(text: &str, glyphs: &mut GlyphStore) {
// TODO(Issue #230): do a better job. See Gecko's LineBreaker. // TODO(Issue #230): do a better job. See Gecko's LineBreaker.

View file

@ -34,18 +34,6 @@ pub fn Document(root: AbstractNode, window: Option<@mut Window>) -> @mut Documen
doc doc
} }
#[unsafe_destructor]
impl Drop for Document {
fn finalize(&self) {
let compartment = global_script_context().js_compartment;
do self.root.with_base |base| {
assert!(base.wrapper.get_wrapper().is_not_null());
let rootable = base.wrapper.get_rootable();
JS_RemoveObjectRoot(compartment.cx.ptr, rootable);
}
}
}
pub impl Document { pub impl Document {
fn getElementsByTagName(&self, tag: DOMString) -> Option<@mut HTMLCollection> { fn getElementsByTagName(&self, tag: DOMString) -> Option<@mut HTMLCollection> {
let mut elements = ~[]; let mut elements = ~[];
@ -67,5 +55,14 @@ pub impl Document {
window.content_changed() window.content_changed()
} }
} }
fn teardown(&self) {
let compartment = global_script_context().js_compartment;
do self.root.with_base |node| {
assert!(node.wrapper.get_wrapper().is_not_null());
let rootable = node.wrapper.get_rootable();
JS_RemoveObjectRoot(compartment.cx.ptr, rootable);
}
}
} }

View file

@ -18,6 +18,14 @@ impl LayoutAuxMethods for AbstractNode {
/// box in the COW model) and populates it with an empty style object. /// box in the COW model) and populates it with an empty style object.
fn initialize_layout_data(self) -> Option<@mut LayoutData> { fn initialize_layout_data(self) -> Option<@mut LayoutData> {
if self.has_layout_data() { if self.has_layout_data() {
{
let layout_data = &mut self.layout_data().flow;
match *layout_data {
Some(ref flow) => flow.teardown(),
None => ()
}
}
self.layout_data().flow = None;
None None
} else { } else {
let data = @mut LayoutData::new(); let data = @mut LayoutData::new();

View file

@ -45,6 +45,14 @@ impl BlockFlowData {
is_root: true is_root: true
} }
} }
pub fn teardown(&mut self) {
self.common.teardown();
for self.box.each |box| {
box.teardown();
}
self.box = None;
}
} }
pub trait BlockLayout { pub trait BlockLayout {

View file

@ -59,6 +59,15 @@ pub enum RenderBox {
UnscannedTextRenderBoxClass(@mut UnscannedTextRenderBox), UnscannedTextRenderBoxClass(@mut UnscannedTextRenderBox),
} }
impl RenderBox {
pub fn teardown(&self) {
match *self {
TextRenderBoxClass(box) => box.teardown(),
_ => ()
}
}
}
/// A box that represents a (replaced content) image and its accompanying borders, shadows, etc. /// A box that represents a (replaced content) image and its accompanying borders, shadows, etc.
pub struct ImageRenderBox { pub struct ImageRenderBox {
base: RenderBoxBase, base: RenderBoxBase,
@ -88,6 +97,12 @@ pub struct TextRenderBox {
text_data: TextBoxData, text_data: TextBoxData,
} }
impl TextRenderBox {
fn teardown(&self) {
self.text_data.teardown();
}
}
/// The data for an unscanned text box. /// The data for an unscanned text box.
pub struct UnscannedTextRenderBox { pub struct UnscannedTextRenderBox {
base: RenderBoxBase, base: RenderBoxBase,

View file

@ -67,6 +67,43 @@ impl Clone for FlowContext {
} }
} }
impl FlowContext {
pub fn teardown(&self) {
match *self {
AbsoluteFlow(data) |
FloatFlow(data) |
InlineBlockFlow(data) |
TableFlow(data) => data.teardown(),
BlockFlow(data) => data.teardown(),
InlineFlow(data) => data.teardown()
}
}
}
impl FlowData {
pub fn teardown(&mut self) {
// Under the assumption that all flows exist in a tree,
// we must restrict ourselves to finalizing flows that
// are descendents and subsequent siblings to ourselves,
// or we risk dynamic borrow failures.
self.parent = None;
for self.first_child.each |flow| {
flow.teardown();
}
self.first_child = None;
self.last_child = None;
for self.next_sibling.each |flow| {
flow.teardown();
}
self.next_sibling = None;
self.prev_sibling = None;
}
}
impl TreeNodeRef<FlowData> for FlowContext { impl TreeNodeRef<FlowData> for FlowContext {
fn with_base<R>(&self, callback: &fn(&FlowData) -> R) -> R { fn with_base<R>(&self, callback: &fn(&FlowData) -> R) -> R {
match *self { match *self {

View file

@ -316,11 +316,18 @@ impl TextRunScanner {
// and then letting `FontGroup` decide which `Font` to stick into the text run. // and then letting `FontGroup` decide which `Font` to stick into the text run.
let font_style = in_boxes[self.clump.begin()].font_style(); let font_style = in_boxes[self.clump.begin()].font_style();
let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style); let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
let run = @TextRun::new(fontgroup.fonts[0], run_str);
// TextRuns contain a cycle which is usually resolved by the teardown
// sequence. If no clump takes ownership, however, it will leak.
let clump = self.clump;
let run = if clump.length() != 0 {
Some(@TextRun::new(fontgroup.fonts[0], run_str))
} else {
None
};
// Make new boxes with the run and adjusted text indices. // Make new boxes with the run and adjusted text indices.
debug!("TextRunScanner: pushing box(es) in range: %?", self.clump); debug!("TextRunScanner: pushing box(es) in range: %?", self.clump);
let clump = self.clump;
for clump.eachi |i| { for clump.eachi |i| {
let range = new_ranges[i - self.clump.begin()]; let range = new_ranges[i - self.clump.begin()];
if range.length() == 0 { if range.length() == 0 {
@ -331,7 +338,7 @@ impl TextRunScanner {
} }
do in_boxes[i].with_imm_base |base| { do in_boxes[i].with_imm_base |base| {
let new_box = @mut adapt_textbox_with_range(*base, run, range); let new_box = @mut adapt_textbox_with_range(*base, run.get(), range);
out_boxes.push(TextRenderBoxClass(new_box)); out_boxes.push(TextRenderBoxClass(new_box));
} }
} }
@ -638,6 +645,14 @@ impl InlineFlowData {
elems: ElementMapping::new(), elems: ElementMapping::new(),
} }
} }
pub fn teardown(&mut self) {
self.common.teardown();
for self.boxes.each |box| {
box.teardown();
}
self.boxes = ~[];
}
} }
pub trait InlineLayout { pub trait InlineLayout {

View file

@ -21,6 +21,10 @@ impl TextBoxData {
range: range, range: range,
} }
} }
pub fn teardown(&self) {
self.run.teardown();
}
} }
pub fn adapt_textbox_with_range(mut base: RenderBoxBase, run: @TextRun, range: Range) pub fn adapt_textbox_with_range(mut base: RenderBoxBase, run: @TextRun, range: Range)

View file

@ -285,6 +285,10 @@ impl ScriptContext {
/// Handles a request to exit the script task and shut down layout. /// Handles a request to exit the script task and shut down layout.
fn handle_exit_msg(&mut self) { fn handle_exit_msg(&mut self) {
self.join_layout();
for self.root_frame.each |frame| {
frame.document.teardown();
}
self.layout_task.send(layout_task::ExitMsg) self.layout_task.send(layout_task::ExitMsg)
} }

View file

@ -5,7 +5,8 @@ var count = 1000000;
var start = new Date(); var start = new Date();
for (var i = 0; i < count; i++) { for (var i = 0; i < count; i++) {
div.setAttribute('id', 'styled'); div.setAttribute('id', 'styled');
div.getBoundingClientRect(); //div.getBoundingClientRect();
} }
var stop = new Date(); var stop = new Date();
window.alert((stop - start) / count * 1e6); window.alert((stop - start) / count * 1e6);
window.close();