Fix several hangs / panics during pipeline cleanup of in progress loads.

This fixes a hang found while testing the jQuery test suite.
This commit is contained in:
Glenn Watson 2015-05-19 16:14:25 +10:00
parent 90aacf00f8
commit 35a570ab66
7 changed files with 46 additions and 10 deletions

View file

@ -94,7 +94,13 @@ impl PaintListener for Box<CompositorProxy+'static+Send> {
fn get_graphics_metadata(&mut self) -> Option<NativeGraphicsMetadata> {
let (chan, port) = channel();
self.send(Msg::GetGraphicsMetadata(chan));
port.recv().unwrap()
// If the compositor is shutting down when a paint task
// is being created, the compositor won't respond to
// this message, resulting in an eventual panic. Instead,
// just return None in this case, since the paint task
// will exit shortly and never actually be requested
// to paint buffers by the compositor.
port.recv().unwrap_or(None)
}
fn assign_painted_buffers(&mut self,

View file

@ -145,7 +145,7 @@ impl<C> PaintTask<C> where C: PaintListener + Send + 'static {
time_profiler_chan: time::ProfilerChan,
shutdown_chan: Sender<()>) {
let ConstellationChan(c) = constellation_chan.clone();
spawn_named_with_send_on_failure("PaintTask", task_state::PAINT, move || {
spawn_named_with_send_on_failure(format!("PaintTask {:?}", id), task_state::PAINT, move || {
{
// Ensures that the paint task and graphics context are destroyed before the
// shutdown message.

View file

@ -207,7 +207,7 @@ impl LayoutTaskFactory for LayoutTask {
memory_profiler_chan: mem::ProfilerChan,
shutdown_chan: Sender<()>) {
let ConstellationChan(con_chan) = constellation_chan.clone();
spawn_named_with_send_on_failure("LayoutTask", task_state::LAYOUT, move || {
spawn_named_with_send_on_failure(format!("LayoutTask {:?}", id), task_state::LAYOUT, move || {
{ // Ensures layout task is destroyed before we send shutdown message
let sender = chan.sender();
let layout = LayoutTask::new(id,

View file

@ -366,7 +366,7 @@ pub struct SubpageId(pub u32);
// The type of pipeline exit. During complete shutdowns, pipelines do not have to
// release resources automatically released on process termination.
#[derive(Copy, Clone)]
#[derive(Copy, Clone, Debug)]
pub enum PipelineExitType {
PipelineOnly,
Complete,

View file

@ -584,6 +584,17 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
let document = self.Document().root();
NodeCast::from_ref(document.r()).teardown();
// The above code may not catch all DOM objects
// (e.g. DOM objects removed from the tree that haven't
// been collected yet). Forcing a GC here means that
// those DOM objects will be able to call dispose()
// to free their layout data before the layout task
// exits. Without this, those remaining objects try to
// send a message to free their layout data to the
// layout task when the script task is dropped,
// which causes a panic!
self.Gc();
*self.js_runtime.borrow_mut() = None;
*self.browser_context.borrow_mut() = None;
}

View file

@ -386,7 +386,7 @@ impl ScriptTaskFactory for ScriptTask {
let ConstellationChan(const_chan) = constellation_chan.clone();
let (script_chan, script_port) = channel();
let layout_chan = LayoutChan(layout_chan.sender());
spawn_named_with_send_on_failure("ScriptTask", task_state::SCRIPT, move || {
spawn_named_with_send_on_failure(format!("ScriptTask {:?}", id), task_state::SCRIPT, move || {
let script_task = ScriptTask::new(box compositor as Box<ScriptListener>,
script_port,
NonWorkerScriptChan(script_chan),
@ -1080,11 +1080,30 @@ impl ScriptTask {
/// Handles a request to exit the script task and shut down layout.
/// Returns true if the script task should shut down and false otherwise.
fn handle_exit_pipeline_msg(&self, id: PipelineId, exit_type: PipelineExitType) -> bool {
if self.page.borrow().is_none() {
// The root page doesn't even exist yet, so it
// is safe to exit this script task.
// Check if the exit message is for an in progress load.
let idx = self.incomplete_loads.borrow().iter().position(|load| {
load.pipeline_id == id
});
if let Some(idx) = idx {
// TODO(gw): This probably leaks resources!
return true;
let load = self.incomplete_loads.borrow_mut().remove(idx);
// Tell the layout task to begin shutting down, and wait until it
// processed this message.
let (response_chan, response_port) = channel();
let LayoutChan(chan) = load.layout_chan;
if chan.send(layout_interface::Msg::PrepareToExit(response_chan)).is_ok() {
debug!("shutting down layout for root page {:?}", id);
response_port.recv().unwrap();
chan.send(layout_interface::Msg::ExitNow(exit_type)).ok();
}
let has_pending_loads = self.incomplete_loads.borrow().len() > 0;
let has_root_page = self.page.borrow().is_some();
// Exit if no pending loads and no root page
return !has_pending_loads && !has_root_page;
}
// If root is being exited, shut down all pages

View file

@ -18,7 +18,7 @@ pub fn spawn_named<F>(name: String, f: F)
}
/// Arrange to send a particular message to a channel if the task fails.
pub fn spawn_named_with_send_on_failure<F, T>(name: &'static str,
pub fn spawn_named_with_send_on_failure<F, T>(name: String,
state: task_state::TaskState,
f: F,
msg: T,