mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
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:
parent
68cc30c1df
commit
36b8f63984
7 changed files with 88 additions and 40 deletions
|
@ -15,12 +15,14 @@ use layers;
|
|||
use servo_msg::compositor_msg::{Epoch, IdleRenderState, LayerBuffer, LayerBufferSet};
|
||||
use servo_msg::compositor_msg::{RenderListener, RenderingRenderState};
|
||||
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, RendererReadyMsg};
|
||||
use servo_msg::constellation_msg::{Failure, FailureMsg};
|
||||
use servo_msg::platform::surface::NativeSurfaceAzureMethods;
|
||||
use servo_util::time::{ProfilerChan, profile};
|
||||
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::task;
|
||||
use extra::arc::Arc;
|
||||
|
||||
use buffer_map::BufferMap;
|
||||
|
@ -41,7 +43,7 @@ pub enum Msg<T> {
|
|||
UnusedBufferMsg(~[~LayerBuffer]),
|
||||
PaintPermissionGranted,
|
||||
PaintPermissionRevoked,
|
||||
ExitMsg(Chan<()>),
|
||||
ExitMsg(Option<Chan<()>>),
|
||||
}
|
||||
|
||||
/// 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>>,
|
||||
compositor: C,
|
||||
constellation_chan: ConstellationChan,
|
||||
failure_msg: Failure,
|
||||
opts: Opts,
|
||||
profiler_chan: ProfilerChan,
|
||||
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
|
||||
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) => {
|
||||
debug!("render_task: exitmsg response send");
|
||||
response_ch.send(());
|
||||
response_ch.map(|ch| ch.send(()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -382,7 +382,7 @@ impl IOCompositor {
|
|||
let world_zoom = self.world_zoom;
|
||||
let page_window = Size2D(window_size.width 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));
|
||||
|
||||
(true, move)
|
||||
|
|
|
@ -248,12 +248,12 @@ impl CompositorLayer {
|
|||
MouseWindowMouseDownEvent(button, _) => MouseDownEvent(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>) {
|
||||
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
|
||||
|
@ -277,7 +277,7 @@ impl CompositorLayer {
|
|||
let (request, unused) = quadtree.get_tile_rects_page(rect, scale);
|
||||
redisplay = !unused.is_empty(); // workaround to make redisplay visible outside block
|
||||
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
|
||||
self.pipeline.render_chan.try_send(ReRenderMsg(request, scale, self.epoch));
|
||||
|
@ -365,8 +365,8 @@ impl CompositorLayer {
|
|||
self.page_size = Some(new_size);
|
||||
match self.quadtree {
|
||||
Tree(ref mut quadtree) => {
|
||||
self.pipeline.render_chan.send(UnusedBufferMsg(quadtree.resize(new_size.width as uint,
|
||||
new_size.height as uint)));
|
||||
self.pipeline.render_chan.try_send(UnusedBufferMsg(quadtree.resize(new_size.width as uint,
|
||||
new_size.height as uint)));
|
||||
}
|
||||
NoTree(tile_size, max_mem) => {
|
||||
self.quadtree = Tree(Quadtree::new(Size2D(new_size.width as uint,
|
||||
|
@ -455,8 +455,8 @@ impl CompositorLayer {
|
|||
child.page_size = Some(new_size);
|
||||
match child.quadtree {
|
||||
Tree(ref mut quadtree) => {
|
||||
child.pipeline.render_chan.send(UnusedBufferMsg(quadtree.resize(new_size.width as uint,
|
||||
new_size.height as uint)));
|
||||
child.pipeline.render_chan.try_send(UnusedBufferMsg(quadtree.resize(new_size.width as uint,
|
||||
new_size.height as uint)));
|
||||
}
|
||||
NoTree(tile_size, max_mem) => {
|
||||
child.quadtree = Tree(Quadtree::new(Size2D(new_size.width as uint,
|
||||
|
@ -596,7 +596,7 @@ impl CompositorLayer {
|
|||
self.epoch,
|
||||
epoch,
|
||||
self.pipeline.id);
|
||||
self.pipeline.render_chan.send(UnusedBufferMsg(new_buffers.buffers));
|
||||
self.pipeline.render_chan.try_send(UnusedBufferMsg(new_buffers.buffers));
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -615,7 +615,7 @@ impl CompositorLayer {
|
|||
buffer.resolution, buffer));
|
||||
}
|
||||
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);
|
||||
|
@ -719,7 +719,7 @@ impl CompositorLayer {
|
|||
tile.mark_wont_leak()
|
||||
}
|
||||
|
||||
self.pipeline.render_chan.send(UnusedBufferMsg(tiles))
|
||||
self.pipeline.render_chan.try_send(UnusedBufferMsg(tiles));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,8 +8,10 @@ use extra::url::Url;
|
|||
use geom::rect::Rect;
|
||||
use geom::size::Size2D;
|
||||
use gfx::opts::Opts;
|
||||
use gfx::render_task;
|
||||
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::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg};
|
||||
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>) {
|
||||
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 pipeline = @mut Pipeline::create(new_id,
|
||||
subpage_id,
|
||||
|
@ -390,16 +404,20 @@ impl Constellation {
|
|||
self.profiler_chan.clone(),
|
||||
self.window_size,
|
||||
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);
|
||||
|
||||
let frames = self.find_all(pipeline_id);
|
||||
for frame_tree in frames.iter() {
|
||||
frame_tree.pipeline = pipeline;
|
||||
};
|
||||
|
||||
self.pipelines.insert(pipeline_id, pipeline);
|
||||
self.pending_frames.push(FrameChange{
|
||||
before: Some(pipeline_id),
|
||||
after: @mut FrameTree {
|
||||
pipeline: pipeline,
|
||||
parent: None,
|
||||
children: ~[],
|
||||
},
|
||||
navigation_type: constellation_msg::Navigate,
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_init_load(&mut self, url: Url) {
|
||||
|
|
|
@ -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::{ReflowForDisplay, ReflowMsg};
|
||||
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::local_image_cache::{ImageResponder, LocalImageCache};
|
||||
use servo_util::geometry::Au;
|
||||
use servo_util::time::{ProfilerChan, profile};
|
||||
use servo_util::time;
|
||||
use servo_util::task::spawn_named;
|
||||
use servo_util::task::send_on_failure;
|
||||
use servo_util::workqueue::WorkQueue;
|
||||
use std::cast::transmute;
|
||||
use std::cast;
|
||||
use std::cell::RefCell;
|
||||
use std::comm::Port;
|
||||
use std::ptr;
|
||||
use std::task;
|
||||
use std::util;
|
||||
use style::{AuthorOrigin, Stylesheet, Stylist};
|
||||
|
||||
|
@ -240,13 +241,17 @@ impl LayoutTask {
|
|||
port: Port<Msg>,
|
||||
chan: LayoutChan,
|
||||
constellation_chan: ConstellationChan,
|
||||
failure_msg: Failure,
|
||||
script_chan: ScriptChan,
|
||||
render_chan: RenderChan<OpaqueNode>,
|
||||
img_cache_task: ImageCacheTask,
|
||||
opts: Opts,
|
||||
profiler_chan: ProfilerChan,
|
||||
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
|
||||
let mut layout = LayoutTask::new(id,
|
||||
port,
|
||||
|
@ -400,7 +405,7 @@ impl LayoutTask {
|
|||
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()
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ use script::layout_interface::LayoutChan;
|
|||
use script::script_task::LoadMsg;
|
||||
use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan};
|
||||
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::resource_task::ResourceTask;
|
||||
use servo_util::time::ProfilerChan;
|
||||
|
@ -58,10 +58,16 @@ impl Pipeline {
|
|||
let (render_shutdown_port, render_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,
|
||||
render_port,
|
||||
compositor_chan.clone(),
|
||||
constellation_chan.clone(),
|
||||
failure.clone(),
|
||||
opts.clone(),
|
||||
profiler_chan.clone(),
|
||||
render_shutdown_chan);
|
||||
|
@ -70,6 +76,7 @@ impl Pipeline {
|
|||
layout_port,
|
||||
layout_chan.clone(),
|
||||
constellation_chan,
|
||||
failure,
|
||||
script_pipeline.script_chan.clone(),
|
||||
render_chan.clone(),
|
||||
image_cache_task.clone(),
|
||||
|
@ -117,7 +124,10 @@ impl Pipeline {
|
|||
layout_shutdown_port,
|
||||
render_shutdown_port);
|
||||
|
||||
// FIXME(#1434): add back failure supervision
|
||||
let failure = Failure {
|
||||
pipeline_id: id,
|
||||
subpage_id: subpage_id,
|
||||
};
|
||||
|
||||
ScriptTask::create(id,
|
||||
compositor_chan.clone(),
|
||||
|
@ -125,6 +135,7 @@ impl Pipeline {
|
|||
script_port,
|
||||
script_chan.clone(),
|
||||
constellation_chan.clone(),
|
||||
failure.clone(),
|
||||
resource_task,
|
||||
image_cache_task.clone(),
|
||||
window_size);
|
||||
|
@ -133,6 +144,7 @@ impl Pipeline {
|
|||
render_port,
|
||||
compositor_chan.clone(),
|
||||
constellation_chan.clone(),
|
||||
failure.clone(),
|
||||
opts.clone(),
|
||||
profiler_chan.clone(),
|
||||
render_shutdown_chan);
|
||||
|
@ -141,6 +153,7 @@ impl Pipeline {
|
|||
layout_port,
|
||||
layout_chan.clone(),
|
||||
constellation_chan,
|
||||
failure,
|
||||
script_chan.clone(),
|
||||
render_chan.clone(),
|
||||
image_cache_task,
|
||||
|
@ -182,7 +195,7 @@ impl Pipeline {
|
|||
|
||||
pub fn revoke_paint_permission(&self) {
|
||||
debug!("pipeline revoking render channel paint permission");
|
||||
self.render_chan.send(PaintPermissionRevoked);
|
||||
self.render_chan.try_send(PaintPermissionRevoked);
|
||||
}
|
||||
|
||||
pub fn reload(&mut self) {
|
||||
|
@ -193,12 +206,13 @@ impl Pipeline {
|
|||
|
||||
pub fn exit(&self) {
|
||||
// Script task handles shutting down layout, and layout handles shutting down the renderer.
|
||||
self.script_chan.try_send(script_task::ExitPipelineMsg(self.id));
|
||||
|
||||
// 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
|
||||
self.render_shutdown_port.recv_opt();
|
||||
self.layout_shutdown_port.recv_opt();
|
||||
// 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
|
||||
// NOTE: We don't wait for script task as we don't always own it
|
||||
self.render_shutdown_port.recv_opt();
|
||||
self.layout_shutdown_port.recv_opt();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_sendable(&self) -> CompositionPipeline {
|
||||
|
|
|
@ -39,17 +39,18 @@ use js;
|
|||
use servo_msg::compositor_msg::{FinishedLoading, Loading, PerformingLayout, ScriptListener};
|
||||
use servo_msg::constellation_msg::{ConstellationChan, IFrameSandboxed, IFrameUnsandboxed};
|
||||
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_net::image_cache_task::ImageCacheTask;
|
||||
use servo_net::resource_task::ResourceTask;
|
||||
use servo_util::geometry::to_frac_px;
|
||||
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 std::comm::{Port, SharedChan};
|
||||
use std::ptr;
|
||||
use std::str::eq_slice;
|
||||
use std::task;
|
||||
use std::util::replace;
|
||||
|
||||
/// Messages used to control the script task.
|
||||
|
@ -463,10 +464,14 @@ impl ScriptTask {
|
|||
port: Port<ScriptMsg>,
|
||||
chan: ScriptChan,
|
||||
constellation_chan: ConstellationChan,
|
||||
failure_msg: Failure,
|
||||
resource_task: ResourceTask,
|
||||
image_cache_task: ImageCacheTask,
|
||||
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,
|
||||
@compositor as @ScriptListener,
|
||||
layout_chan,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue