Remove watcher threads; replace with more panic handler goodness

This commit is contained in:
Manish Goregaokar 2016-04-14 20:19:01 +05:30
parent d9ffefe562
commit f02fd6330f
No known key found for this signature in database
GPG key ID: 3BBF4D3E2EF79F98
5 changed files with 116 additions and 54 deletions

View file

@ -6,6 +6,7 @@
#![feature(core_intrinsics)]
#![feature(custom_derive)]
#![cfg_attr(feature = "non-geckolib", feature(decode_utf16))]
#![feature(fnbox)]
#![feature(optin_builtin_traits)]
#![feature(plugin)]
#![feature(panic_handler)]
@ -53,6 +54,7 @@ pub mod linked_list;
pub mod non_geckolib;
#[allow(unsafe_code)]
pub mod opts;
pub mod panicking;
#[allow(unsafe_code)]
pub mod prefs;
pub mod print_tree;

View file

@ -0,0 +1,72 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use opts;
use std::any::Any;
use std::boxed::FnBox;
use std::cell::RefCell;
use std::io::{Write, stderr};
use std::panic::{PanicInfo, take_hook, set_hook};
use std::sync::{Once, ONCE_INIT};
use std::thread;
// only set the panic hook once
static HOOK_SET: Once = ONCE_INIT;
/// TLS data pertaining to how failures should be reported
pub struct PanicHandlerLocal {
/// failure handler passed through spawn_named_with_send_on_failure
pub fail: Box<(FnBox(&(Any + Send))) + Send + 'static>
}
thread_local!(pub static LOCAL_INFO: RefCell<Option<PanicHandlerLocal>> = RefCell::new(None));
/// Initiates the custom panic hook
/// Should be called in main() after arguments have been parsed
pub fn initiate_panic_hook() {
// store it locally, we can't trust that opts::get() will work whilst panicking
let full_backtraces = opts::get().full_backtraces;
// Set the panic handler only once. It is global.
HOOK_SET.call_once(|| {
// The original backtrace-printing hook. We still want to call this
let hook = take_hook();
let new_hook = move |info: &PanicInfo| {
let payload = info.payload();
let name = thread::current().name().unwrap_or("<unknown thread>").to_string();
// Notify error handlers stored in LOCAL_INFO if any
LOCAL_INFO.with(|i| {
if let Some(info) = i.borrow_mut().take() {
debug!("Thread `{}` failed, notifying error handlers", name);
(info.fail).call_box((payload, ));
}
});
// Skip cascading SendError/RecvError backtraces if allowed
if !full_backtraces {
if let Some(s) = payload.downcast_ref::<String>() {
if s.contains("SendError") {
let err = stderr();
let _ = write!(err.lock(), "Thread \"{}\" panicked with an unwrap of \
`SendError` (backtrace skipped)\n", name);
return;
} else if s.contains("RecvError") {
let err = stderr();
let _ = write!(err.lock(), "Thread \"{}\" panicked with an unwrap of \
`RecvError` (backtrace skipped)\n", name);
return;
}
}
}
// Call the old hook to get the backtrace
hook(&info);
};
set_hook(Box::new(new_hook));
});
}

View file

@ -3,15 +3,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use ipc_channel::ipc::IpcSender;
use opts;
use panicking;
use serde::Serialize;
use std::any::Any;
use std::borrow::ToOwned;
use std::io::{Write, stderr};
use std::panic::{PanicInfo, take_hook, set_hook};
use std::sync::mpsc::Sender;
use std::thread;
use std::thread::Builder;
use thread_state;
pub type PanicReason = Option<String>;
@ -19,40 +16,7 @@ pub type PanicReason = Option<String>;
pub fn spawn_named<F>(name: String, f: F)
where F: FnOnce() + Send + 'static
{
let builder = thread::Builder::new().name(name);
if opts::get().full_backtraces {
builder.spawn(f).unwrap();
return;
}
let f_with_hook = move || {
let hook = take_hook();
let new_hook = move |info: &PanicInfo| {
let payload = info.payload();
if let Some(s) = payload.downcast_ref::<String>() {
if s.contains("SendError") {
let err = stderr();
let _ = write!(err.lock(), "Thread \"{}\" panicked with an unwrap of \
`SendError` (backtrace skipped)\n",
thread::current().name().unwrap_or("<unknown thread>"));
return;
} else if s.contains("RecvError") {
let err = stderr();
let _ = write!(err.lock(), "Thread \"{}\" panicked with an unwrap of \
`RecvError` (backtrace skipped)\n",
thread::current().name().unwrap_or("<unknown thread>"));
return;
}
}
hook(&info);
};
set_hook(Box::new(new_hook));
f();
};
builder.spawn(f_with_hook).unwrap();
spawn_named_with_send_on_failure_maybe(name, None, f, |_| {});
}
pub trait AddFailureDetails {
@ -100,20 +64,39 @@ pub fn spawn_named_with_send_on_failure<F, T, S>(name: String,
S: Send + SendOnFailure + 'static,
S::Value: From<T>,
{
let future_handle = thread::Builder::new().name(name.to_owned()).spawn(move || {
thread_state::initialize(state);
f()
}).unwrap();
let watcher_name = format!("{}Watcher", name);
Builder::new().name(watcher_name).spawn(move || {
match future_handle.join() {
Ok(()) => (),
Err(err) => {
debug!("{} failed, notifying constellation", name);
msg.add_panic_object(&*err);
dest.send_on_failure(S::Value::from(msg));
}
}
}).unwrap();
spawn_named_with_send_on_failure_maybe(name, Some(state), f,
move |err| {
msg.add_panic_object(err);
dest.send_on_failure(S::Value::from(msg));
});
}
fn spawn_named_with_send_on_failure_maybe<F, G>(name: String,
state: Option<thread_state::ThreadState>,
f: F,
fail: G)
where F: FnOnce() + Send + 'static,
G: FnOnce(&(Any + Send)) + Send + 'static {
let builder = thread::Builder::new().name(name.clone());
let local = panicking::PanicHandlerLocal {
fail: Box::new(fail),
};
let f_with_state = move || {
if let Some(state) = state {
thread_state::initialize(state);
}
// set the handler
panicking::LOCAL_INFO.with(|i| {
*i.borrow_mut() = Some(local);
});
f();
};
builder.spawn(f_with_state).unwrap();
}