aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--components/servo/main.rs2
-rw-r--r--components/util/lib.rs2
-rw-r--r--components/util/panicking.rs72
-rw-r--r--components/util/thread.rs89
-rw-r--r--ports/gonk/src/main.rs3
5 files changed, 115 insertions, 53 deletions
diff --git a/components/servo/main.rs b/components/servo/main.rs
index 08ed645b246..b5e2812a595 100644
--- a/components/servo/main.rs
+++ b/components/servo/main.rs
@@ -35,6 +35,7 @@ extern crate servo;
use servo::Browser;
use servo::compositing::windowing::WindowEvent;
use servo::util::opts::{self, ArgumentParsingResult};
+use servo::util::panicking::initiate_panic_hook;
use std::rc::Rc;
fn main() {
@@ -51,6 +52,7 @@ fn main() {
None
};
+ initiate_panic_hook();
env_logger::init().unwrap();
setup_logging();
diff --git a/components/util/lib.rs b/components/util/lib.rs
index 4f69d82ed87..5eb530f1b19 100644
--- a/components/util/lib.rs
+++ b/components/util/lib.rs
@@ -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;
diff --git a/components/util/panicking.rs b/components/util/panicking.rs
new file mode 100644
index 00000000000..eecfd74b97f
--- /dev/null
+++ b/components/util/panicking.rs
@@ -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));
+ });
+
+
+}
diff --git a/components/util/thread.rs b/components/util/thread.rs
index aa0baac20aa..85a7c52adf8 100644
--- a/components/util/thread.rs
+++ b/components/util/thread.rs
@@ -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));
- }
+ 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);
}
- }).unwrap();
+
+ // set the handler
+ panicking::LOCAL_INFO.with(|i| {
+ *i.borrow_mut() = Some(local);
+ });
+ f();
+ };
+
+
+ builder.spawn(f_with_state).unwrap();
}
diff --git a/ports/gonk/src/main.rs b/ports/gonk/src/main.rs
index 2d8cb9ca551..6d10a3ac833 100644
--- a/ports/gonk/src/main.rs
+++ b/ports/gonk/src/main.rs
@@ -47,6 +47,7 @@ use compositing::windowing::WindowEvent;
use servo::Browser;
use std::env;
use util::opts;
+use util::panicking;
mod input;
mod window;
@@ -61,6 +62,8 @@ fn main() {
// Parse the command line options and store them globally
opts::from_cmdline_args(env::args().collect::<Vec<_>>().as_slice());
+ panicking::initiate_panic_hook();
+
let window = window::Window::new();
// Our wrapper around `Browser` that also implements some