Restore failure handling

We probably leak some threads and resources, e.g. when the script task crashes
and doesn't get a chance to send layout data back to layout to be deallocated.

Not tested with iframes yet.
This commit is contained in:
Keegan McAllister 2014-02-11 16:15:19 -08:00
parent 68cc30c1df
commit 36b8f63984
7 changed files with 88 additions and 40 deletions

View file

@ -15,12 +15,14 @@ use layers;
use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBuffer, LayerBufferSet}; use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBuffer, LayerBufferSet};
use servo_msg::compositor_msg::{RenderListener, RenderingRenderState}; use servo_msg::compositor_msg::{RenderListener, RenderingRenderState};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, RendererReadyMsg}; use servo_msg::constellation_msg::{ConstellationChan, PipelineId, RendererReadyMsg};
use servo_msg::constellation_msg::{Failure, FailureMsg};
use servo_msg::platform::surface::NativeSurfaceAzureMethods; use servo_msg::platform::surface::NativeSurfaceAzureMethods;
use servo_util::time::{ProfilerChan, profile}; use servo_util::time::{ProfilerChan, profile};
use servo_util::time; use servo_util::time;
use servo_util::task::spawn_named; use servo_util::task::send_on_failure;
use std::comm::{Chan, Port, SharedChan}; use std::comm::{Chan, Port, SharedChan};
use std::task;
use extra::arc::Arc; use extra::arc::Arc;
use buffer_map::BufferMap; use buffer_map::BufferMap;
@ -41,7 +43,7 @@ pub enum Msg<T> {
UnusedBufferMsg(~[~LayerBuffer]), UnusedBufferMsg(~[~LayerBuffer]),
PaintPermissionGranted, PaintPermissionGranted,
PaintPermissionRevoked, PaintPermissionRevoked,
ExitMsg(Chan<()>), ExitMsg(Option<Chan<()>>),
} }
/// A request from the compositor to the renderer for tiles that need to be (re)displayed. /// A request from the compositor to the renderer for tiles that need to be (re)displayed.
@ -143,10 +145,14 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
port: Port<Msg<T>>, port: Port<Msg<T>>,
compositor: C, compositor: C,
constellation_chan: ConstellationChan, constellation_chan: ConstellationChan,
failure_msg: Failure,
opts: Opts, opts: Opts,
profiler_chan: ProfilerChan, profiler_chan: ProfilerChan,
shutdown_chan: Chan<()>) { shutdown_chan: Chan<()>) {
spawn_named("RenderTask", proc() { let mut builder = task::task();
send_on_failure(&mut builder, FailureMsg(failure_msg), (*constellation_chan).clone());
builder.name("RenderTask");
builder.spawn(proc() {
{ // Ensures RenderTask and graphics context are destroyed before shutdown msg { // Ensures RenderTask and graphics context are destroyed before shutdown msg
let native_graphics_context = compositor.get_graphics_metadata().map( let native_graphics_context = compositor.get_graphics_metadata().map(
@ -237,7 +243,7 @@ impl<C: RenderListener + Send,T:Send+Freeze> RenderTask<C,T> {
} }
ExitMsg(response_ch) => { ExitMsg(response_ch) => {
debug!("render_task: exitmsg response send"); debug!("render_task: exitmsg response send");
response_ch.send(()); response_ch.map(|ch| ch.send(()));
break; break;
} }
} }

View file

@ -382,7 +382,7 @@ impl IOCompositor {
let world_zoom = self.world_zoom; let world_zoom = self.world_zoom;
let page_window = Size2D(window_size.width as f32 / world_zoom, let page_window = Size2D(window_size.width as f32 / world_zoom,
window_size.height as f32 / world_zoom); window_size.height as f32 / world_zoom);
assert!(layer.resize(id, new_size, page_window, epoch)); layer.resize(id, new_size, page_window, epoch);
let move = self.fragment_point.take().map_default(false, |point| layer.move(point, page_window)); let move = self.fragment_point.take().map_default(false, |point| layer.move(point, page_window));
(true, move) (true, move)

View file

@ -248,12 +248,12 @@ impl CompositorLayer {
MouseWindowMouseDownEvent(button, _) => MouseDownEvent(button, cursor), MouseWindowMouseDownEvent(button, _) => MouseDownEvent(button, cursor),
MouseWindowMouseUpEvent(button, _) => MouseUpEvent(button, cursor), MouseWindowMouseUpEvent(button, _) => MouseUpEvent(button, cursor),
}; };
self.pipeline.script_chan.send(SendEventMsg(self.pipeline.id.clone(), message)); self.pipeline.script_chan.try_send(SendEventMsg(self.pipeline.id.clone(), message));
} }
pub fn send_mouse_move_event(&self, cursor: Point2D<f32>) { pub fn send_mouse_move_event(&self, cursor: Point2D<f32>) {
let message = MouseMoveEvent(cursor); let message = MouseMoveEvent(cursor);
self.pipeline.script_chan.send(SendEventMsg(self.pipeline.id.clone(), message)); self.pipeline.script_chan.try_send(SendEventMsg(self.pipeline.id.clone(), message));
} }
// Given the current window size, determine which tiles need to be (re)rendered // Given the current window size, determine which tiles need to be (re)rendered
@ -277,7 +277,7 @@ impl CompositorLayer {
let (request, unused) = quadtree.get_tile_rects_page(rect, scale); let (request, unused) = quadtree.get_tile_rects_page(rect, scale);
redisplay = !unused.is_empty(); // workaround to make redisplay visible outside block redisplay = !unused.is_empty(); // workaround to make redisplay visible outside block
if redisplay { // send back unused tiles if redisplay { // send back unused tiles
self.pipeline.render_chan.send(UnusedBufferMsg(unused)); self.pipeline.render_chan.try_send(UnusedBufferMsg(unused));
} }
if !request.is_empty() { // ask for tiles if !request.is_empty() { // ask for tiles
self.pipeline.render_chan.try_send(ReRenderMsg(request, scale, self.epoch)); self.pipeline.render_chan.try_send(ReRenderMsg(request, scale, self.epoch));
@ -365,8 +365,8 @@ impl CompositorLayer {
self.page_size = Some(new_size); self.page_size = Some(new_size);
match self.quadtree { match self.quadtree {
Tree(ref mut quadtree) => { Tree(ref mut quadtree) => {
self.pipeline.render_chan.send(UnusedBufferMsg(quadtree.resize(new_size.width as uint, self.pipeline.render_chan.try_send(UnusedBufferMsg(quadtree.resize(new_size.width as uint,
new_size.height as uint))); new_size.height as uint)));
} }
NoTree(tile_size, max_mem) => { NoTree(tile_size, max_mem) => {
self.quadtree = Tree(Quadtree::new(Size2D(new_size.width as uint, self.quadtree = Tree(Quadtree::new(Size2D(new_size.width as uint,
@ -455,8 +455,8 @@ impl CompositorLayer {
child.page_size = Some(new_size); child.page_size = Some(new_size);
match child.quadtree { match child.quadtree {
Tree(ref mut quadtree) => { Tree(ref mut quadtree) => {
child.pipeline.render_chan.send(UnusedBufferMsg(quadtree.resize(new_size.width as uint, child.pipeline.render_chan.try_send(UnusedBufferMsg(quadtree.resize(new_size.width as uint,
new_size.height as uint))); new_size.height as uint)));
} }
NoTree(tile_size, max_mem) => { NoTree(tile_size, max_mem) => {
child.quadtree = Tree(Quadtree::new(Size2D(new_size.width as uint, child.quadtree = Tree(Quadtree::new(Size2D(new_size.width as uint,
@ -596,7 +596,7 @@ impl CompositorLayer {
self.epoch, self.epoch,
epoch, epoch,
self.pipeline.id); self.pipeline.id);
self.pipeline.render_chan.send(UnusedBufferMsg(new_buffers.buffers)); self.pipeline.render_chan.try_send(UnusedBufferMsg(new_buffers.buffers));
return None; return None;
} }
@ -615,7 +615,7 @@ impl CompositorLayer {
buffer.resolution, buffer)); buffer.resolution, buffer));
} }
if !unused_tiles.is_empty() { // send back unused buffers if !unused_tiles.is_empty() { // send back unused buffers
self.pipeline.render_chan.send(UnusedBufferMsg(unused_tiles)); self.pipeline.render_chan.try_send(UnusedBufferMsg(unused_tiles));
} }
} }
self.build_layer_tree(graphics_context); self.build_layer_tree(graphics_context);
@ -719,7 +719,7 @@ impl CompositorLayer {
tile.mark_wont_leak() tile.mark_wont_leak()
} }
self.pipeline.render_chan.send(UnusedBufferMsg(tiles)) self.pipeline.render_chan.try_send(UnusedBufferMsg(tiles));
} }
} }
} }

View file

@ -8,8 +8,10 @@ use extra::url::Url;
use geom::rect::Rect; use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::opts::Opts; use gfx::opts::Opts;
use gfx::render_task;
use pipeline::{Pipeline, CompositionPipeline}; use pipeline::{Pipeline, CompositionPipeline};
use script::script_task::{ResizeMsg, ResizeInactiveMsg}; use script::script_task::{ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg};
use script::layout_interface;
use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg}; use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg};
use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg}; use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg};
use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg, NavigateMsg}; use servo_msg::constellation_msg::{LoadCompleteMsg, LoadIframeUrlMsg, LoadUrlMsg, Msg, NavigateMsg};
@ -380,6 +382,18 @@ impl Constellation {
} }
fn handle_failure_msg(&mut self, pipeline_id: PipelineId, subpage_id: Option<SubpageId>) { fn handle_failure_msg(&mut self, pipeline_id: PipelineId, subpage_id: Option<SubpageId>) {
debug!("handling failure message from pipeline {:?}, {:?}", pipeline_id, subpage_id);
let old_pipeline = match self.pipelines.find(&pipeline_id) {
None => return, // already failed?
Some(id) => *id
};
old_pipeline.script_chan.try_send(ExitPipelineMsg(pipeline_id));
old_pipeline.render_chan.try_send(render_task::ExitMsg(None));
old_pipeline.layout_chan.try_send(layout_interface::ExitNowMsg);
self.pipelines.remove(&pipeline_id);
let new_id = self.get_next_pipeline_id(); let new_id = self.get_next_pipeline_id();
let pipeline = @mut Pipeline::create(new_id, let pipeline = @mut Pipeline::create(new_id,
subpage_id, subpage_id,
@ -390,16 +404,20 @@ impl Constellation {
self.profiler_chan.clone(), self.profiler_chan.clone(),
self.window_size, self.window_size,
self.opts.clone()); self.opts.clone());
let failure = "about:failure";
let url = parse_url(failure, None); self.pipelines.insert(new_id, pipeline);
let url = parse_url("about:failure", None);
pipeline.load(url); pipeline.load(url);
let frames = self.find_all(pipeline_id); self.pending_frames.push(FrameChange{
for frame_tree in frames.iter() { before: Some(pipeline_id),
frame_tree.pipeline = pipeline; after: @mut FrameTree {
}; pipeline: pipeline,
parent: None,
self.pipelines.insert(pipeline_id, pipeline); children: ~[],
},
navigation_type: constellation_msg::Navigate,
});
} }
fn handle_init_load(&mut self, url: Url) { fn handle_init_load(&mut self, url: Url) {

View file

@ -40,19 +40,20 @@ use script::layout_interface::{ContentChangedDocumentDamage, LayoutChan, Msg, Pr
use script::layout_interface::{QueryMsg, ReapLayoutDataMsg, Reflow, ReflowDocumentDamage, UntrustedNodeAddress}; use script::layout_interface::{QueryMsg, ReapLayoutDataMsg, Reflow, ReflowDocumentDamage, UntrustedNodeAddress};
use script::layout_interface::{ReflowForDisplay, ReflowMsg}; use script::layout_interface::{ReflowForDisplay, ReflowMsg};
use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg}; use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg};
use servo_msg::constellation_msg::{ConstellationChan, PipelineId}; use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg}; use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::{ImageResponder, LocalImageCache}; use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
use servo_util::geometry::Au; use servo_util::geometry::Au;
use servo_util::time::{ProfilerChan, profile}; use servo_util::time::{ProfilerChan, profile};
use servo_util::time; use servo_util::time;
use servo_util::task::spawn_named; use servo_util::task::send_on_failure;
use servo_util::workqueue::WorkQueue; use servo_util::workqueue::WorkQueue;
use std::cast::transmute; use std::cast::transmute;
use std::cast; use std::cast;
use std::cell::RefCell; use std::cell::RefCell;
use std::comm::Port; use std::comm::Port;
use std::ptr; use std::ptr;
use std::task;
use std::util; use std::util;
use style::{AuthorOrigin, Stylesheet, Stylist}; use style::{AuthorOrigin, Stylesheet, Stylist};
@ -240,13 +241,17 @@ impl LayoutTask {
port: Port<Msg>, port: Port<Msg>,
chan: LayoutChan, chan: LayoutChan,
constellation_chan: ConstellationChan, constellation_chan: ConstellationChan,
failure_msg: Failure,
script_chan: ScriptChan, script_chan: ScriptChan,
render_chan: RenderChan<OpaqueNode>, render_chan: RenderChan<OpaqueNode>,
img_cache_task: ImageCacheTask, img_cache_task: ImageCacheTask,
opts: Opts, opts: Opts,
profiler_chan: ProfilerChan, profiler_chan: ProfilerChan,
shutdown_chan: Chan<()>) { shutdown_chan: Chan<()>) {
spawn_named("LayoutTask", proc() { let mut builder = task::task();
send_on_failure(&mut builder, FailureMsg(failure_msg), (*constellation_chan).clone());
builder.name("LayoutTask");
builder.spawn(proc() {
{ // Ensures layout task is destroyed before we send shutdown message { // Ensures layout task is destroyed before we send shutdown message
let mut layout = LayoutTask::new(id, let mut layout = LayoutTask::new(id,
port, port,
@ -400,7 +405,7 @@ impl LayoutTask {
Some(ref mut traversal) => traversal.shutdown(), Some(ref mut traversal) => traversal.shutdown(),
} }
self.render_chan.send(render_task::ExitMsg(response_chan)); self.render_chan.send(render_task::ExitMsg(Some(response_chan)));
response_port.recv() response_port.recv()
} }

View file

@ -15,7 +15,7 @@ use script::layout_interface::LayoutChan;
use script::script_task::LoadMsg; use script::script_task::LoadMsg;
use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan}; use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan};
use script::script_task; use script::script_task;
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, SubpageId}; use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineId, SubpageId};
use servo_net::image_cache_task::ImageCacheTask; use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask; use servo_net::resource_task::ResourceTask;
use servo_util::time::ProfilerChan; use servo_util::time::ProfilerChan;
@ -58,10 +58,16 @@ impl Pipeline {
let (render_shutdown_port, render_shutdown_chan) = Chan::new(); let (render_shutdown_port, render_shutdown_chan) = Chan::new();
let (layout_shutdown_port, layout_shutdown_chan) = Chan::new(); let (layout_shutdown_port, layout_shutdown_chan) = Chan::new();
let failure = Failure {
pipeline_id: id,
subpage_id: subpage_id,
};
RenderTask::create(id, RenderTask::create(id,
render_port, render_port,
compositor_chan.clone(), compositor_chan.clone(),
constellation_chan.clone(), constellation_chan.clone(),
failure.clone(),
opts.clone(), opts.clone(),
profiler_chan.clone(), profiler_chan.clone(),
render_shutdown_chan); render_shutdown_chan);
@ -70,6 +76,7 @@ impl Pipeline {
layout_port, layout_port,
layout_chan.clone(), layout_chan.clone(),
constellation_chan, constellation_chan,
failure,
script_pipeline.script_chan.clone(), script_pipeline.script_chan.clone(),
render_chan.clone(), render_chan.clone(),
image_cache_task.clone(), image_cache_task.clone(),
@ -117,7 +124,10 @@ impl Pipeline {
layout_shutdown_port, layout_shutdown_port,
render_shutdown_port); render_shutdown_port);
// FIXME(#1434): add back failure supervision let failure = Failure {
pipeline_id: id,
subpage_id: subpage_id,
};
ScriptTask::create(id, ScriptTask::create(id,
compositor_chan.clone(), compositor_chan.clone(),
@ -125,6 +135,7 @@ impl Pipeline {
script_port, script_port,
script_chan.clone(), script_chan.clone(),
constellation_chan.clone(), constellation_chan.clone(),
failure.clone(),
resource_task, resource_task,
image_cache_task.clone(), image_cache_task.clone(),
window_size); window_size);
@ -133,6 +144,7 @@ impl Pipeline {
render_port, render_port,
compositor_chan.clone(), compositor_chan.clone(),
constellation_chan.clone(), constellation_chan.clone(),
failure.clone(),
opts.clone(), opts.clone(),
profiler_chan.clone(), profiler_chan.clone(),
render_shutdown_chan); render_shutdown_chan);
@ -141,6 +153,7 @@ impl Pipeline {
layout_port, layout_port,
layout_chan.clone(), layout_chan.clone(),
constellation_chan, constellation_chan,
failure,
script_chan.clone(), script_chan.clone(),
render_chan.clone(), render_chan.clone(),
image_cache_task, image_cache_task,
@ -182,7 +195,7 @@ impl Pipeline {
pub fn revoke_paint_permission(&self) { pub fn revoke_paint_permission(&self) {
debug!("pipeline revoking render channel paint permission"); debug!("pipeline revoking render channel paint permission");
self.render_chan.send(PaintPermissionRevoked); self.render_chan.try_send(PaintPermissionRevoked);
} }
pub fn reload(&mut self) { pub fn reload(&mut self) {
@ -193,12 +206,13 @@ impl Pipeline {
pub fn exit(&self) { pub fn exit(&self) {
// Script task handles shutting down layout, and layout handles shutting down the renderer. // Script task handles shutting down layout, and layout handles shutting down the renderer.
self.script_chan.try_send(script_task::ExitPipelineMsg(self.id)); // For now, if the script task has failed, we give up on clean shutdown.
if self.script_chan.try_send(script_task::ExitPipelineMsg(self.id)) {
// Wait until all slave tasks have terminated and run destructors // Wait until all slave tasks have terminated and run destructors
// NOTE: We don't wait for script task as we don't always own it // NOTE: We don't wait for script task as we don't always own it
self.render_shutdown_port.recv_opt(); self.render_shutdown_port.recv_opt();
self.layout_shutdown_port.recv_opt(); self.layout_shutdown_port.recv_opt();
}
} }
pub fn to_sendable(&self) -> CompositionPipeline { pub fn to_sendable(&self) -> CompositionPipeline {

View file

@ -39,17 +39,18 @@ use js;
use servo_msg::compositor_msg::{FinishedLoading, Loading, PerformingLayout, ScriptListener}; use servo_msg::compositor_msg::{FinishedLoading, Loading, PerformingLayout, ScriptListener};
use servo_msg::constellation_msg::{ConstellationChan, IFrameSandboxed, IFrameUnsandboxed}; use servo_msg::constellation_msg::{ConstellationChan, IFrameSandboxed, IFrameUnsandboxed};
use servo_msg::constellation_msg::{LoadIframeUrlMsg, LoadCompleteMsg, LoadUrlMsg, NavigationDirection}; use servo_msg::constellation_msg::{LoadIframeUrlMsg, LoadCompleteMsg, LoadUrlMsg, NavigationDirection};
use servo_msg::constellation_msg::{PipelineId, SubpageId}; use servo_msg::constellation_msg::{PipelineId, SubpageId, Failure, FailureMsg};
use servo_msg::constellation_msg; use servo_msg::constellation_msg;
use servo_net::image_cache_task::ImageCacheTask; use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask; use servo_net::resource_task::ResourceTask;
use servo_util::geometry::to_frac_px; use servo_util::geometry::to_frac_px;
use servo_util::url::parse_url; use servo_util::url::parse_url;
use servo_util::task::spawn_named; use servo_util::task::send_on_failure;
use servo_util::namespace::Null; use servo_util::namespace::Null;
use std::comm::{Port, SharedChan}; use std::comm::{Port, SharedChan};
use std::ptr; use std::ptr;
use std::str::eq_slice; use std::str::eq_slice;
use std::task;
use std::util::replace; use std::util::replace;
/// Messages used to control the script task. /// Messages used to control the script task.
@ -463,10 +464,14 @@ impl ScriptTask {
port: Port<ScriptMsg>, port: Port<ScriptMsg>,
chan: ScriptChan, chan: ScriptChan,
constellation_chan: ConstellationChan, constellation_chan: ConstellationChan,
failure_msg: Failure,
resource_task: ResourceTask, resource_task: ResourceTask,
image_cache_task: ImageCacheTask, image_cache_task: ImageCacheTask,
window_size: Size2D<uint>) { window_size: Size2D<uint>) {
spawn_named("ScriptTask", proc() { let mut builder = task::task();
send_on_failure(&mut builder, FailureMsg(failure_msg), (*constellation_chan).clone());
builder.name("ScriptTask");
builder.spawn(proc() {
let script_task = ScriptTask::new(id, let script_task = ScriptTask::new(id,
@compositor as @ScriptListener, @compositor as @ScriptListener,
layout_chan, layout_chan,