Skip to content

Commit

Permalink
Rollup merge of rust-lang#30485 - sfackler:panic-handler, r=alexcrichton
Browse files Browse the repository at this point in the history
  • Loading branch information
Manishearth committed Dec 25, 2015
2 parents 711f11e + f1148a5 commit 12f171b
Show file tree
Hide file tree
Showing 8 changed files with 331 additions and 8 deletions.
2 changes: 2 additions & 0 deletions src/libstd/panic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ use sync::{Arc, Mutex, RwLock};
use sys_common::unwind;
use thread::Result;

pub use panicking::{take_handler, set_handler, PanicInfo, Location};

/// A marker trait which represents "panic safe" types in Rust.
///
/// This trait is implemented by default for many types and behaves similarly in
Expand Down
156 changes: 148 additions & 8 deletions src/libstd/panicking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ use any::Any;
use cell::Cell;
use cell::RefCell;
use intrinsics;
use sync::StaticRwLock;
use sys::stdio::Stderr;
use sys_common::backtrace;
use sys_common::thread_info;
use sys_common::util;
use thread;

thread_local! { pub static PANIC_COUNT: Cell<usize> = Cell::new(0) }

Expand All @@ -28,11 +30,138 @@ thread_local! {
}
}

fn log_panic(obj: &(Any+Send), file: &'static str, line: u32,
log_backtrace: bool) {
let msg = match obj.downcast_ref::<&'static str>() {
#[derive(Copy, Clone)]
enum Handler {
Default,
Custom(*mut (Fn(&PanicInfo) + 'static + Sync + Send)),
}

static HANDLER_LOCK: StaticRwLock = StaticRwLock::new();
static mut HANDLER: Handler = Handler::Default;

/// Registers a custom panic handler, replacing any that was previously
/// registered.
///
/// The panic handler is invoked when a thread panics, but before it begins
/// unwinding the stack. The default handler prints a message to standard error
/// and generates a backtrace if requested, but this behavior can be customized
/// with the `set_handler` and `take_handler` functions.
///
/// The handler is provided with a `PanicInfo` struct which contains information
/// about the origin of the panic, including the payload passed to `panic!` and
/// the source code location from which the panic originated.
///
/// The panic handler is a global resource.
///
/// # Panics
///
/// Panics if called from a panicking thread.
#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
pub fn set_handler<F>(handler: F) where F: Fn(&PanicInfo) + 'static + Sync + Send {
if thread::panicking() {
panic!("cannot modify the panic handler from a panicking thread");
}

let handler = Box::new(handler);
unsafe {
let lock = HANDLER_LOCK.write();
let old_handler = HANDLER;
HANDLER = Handler::Custom(Box::into_raw(handler));
drop(lock);

if let Handler::Custom(ptr) = old_handler {
Box::from_raw(ptr);
}
}
}

/// Unregisters the current panic handler, returning it.
///
/// If no custom handler is registered, the default handler will be returned.
///
/// # Panics
///
/// Panics if called from a panicking thread.
#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
pub fn take_handler() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
if thread::panicking() {
panic!("cannot modify the panic handler from a panicking thread");
}

unsafe {
let lock = HANDLER_LOCK.write();
let handler = HANDLER;
HANDLER = Handler::Default;
drop(lock);

match handler {
Handler::Default => Box::new(default_handler),
Handler::Custom(ptr) => {Box::from_raw(ptr)} // FIXME #30530
}
}
}

/// A struct providing information about a panic.
#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
pub struct PanicInfo<'a> {
payload: &'a (Any + Send),
location: Location<'a>,
}

impl<'a> PanicInfo<'a> {
/// Returns the payload associated with the panic.
///
/// This will commonly, but not always, be a `&'static str` or `String`.
#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
pub fn payload(&self) -> &(Any + Send) {
self.payload
}

/// Returns information about the location from which the panic originated,
/// if available.
///
/// This method will currently always return `Some`, but this may change
/// in future versions.
#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
pub fn location(&self) -> Option<&Location> {
Some(&self.location)
}
}

/// A struct containing information about the location of a panic.
#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
pub struct Location<'a> {
file: &'a str,
line: u32,
}

impl<'a> Location<'a> {
/// Returns the name of the source file from which the panic originated.
#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
pub fn file(&self) -> &str {
self.file
}

/// Returns the line number from which the panic originated.
#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
pub fn line(&self) -> u32 {
self.line
}
}

fn default_handler(info: &PanicInfo) {
let panics = PANIC_COUNT.with(|s| s.get());

// If this is a double panic, make sure that we print a backtrace
// for this panic. Otherwise only print it if logging is enabled.
let log_backtrace = panics >= 2 || backtrace::log_enabled();

let file = info.location.file;
let line = info.location.line;

let msg = match info.payload.downcast_ref::<&'static str>() {
Some(s) => *s,
None => match obj.downcast_ref::<String>() {
None => match info.payload.downcast_ref::<String>() {
Some(s) => &s[..],
None => "Box<Any>",
}
Expand Down Expand Up @@ -81,10 +210,21 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) {
unsafe { intrinsics::abort() }
}

// If this is a double panic, make sure that we print a backtrace
// for this panic. Otherwise only print it if logging is enabled.
let log_backtrace = panics >= 2 || backtrace::log_enabled();
log_panic(obj, file, line, log_backtrace);
let info = PanicInfo {
payload: obj,
location: Location {
file: file,
line: line,
},
};

unsafe {
let _lock = HANDLER_LOCK.read();
match HANDLER {
Handler::Default => default_handler(&info),
Handler::Custom(ptr) => (*ptr)(&info),
}
}

if panics >= 2 {
// If a thread panics while it's already unwinding then we
Expand Down
22 changes: 22 additions & 0 deletions src/test/run-fail/panic-set-handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// error-pattern:greetings from the panic handler

#![feature(std_panic, panic_handler)]
use std::panic;
use std::io::{self, Write};

fn main() {
panic::set_handler(|i| {
write!(io::stderr(), "greetings from the panic handler");
});
panic!("foobar");
}
23 changes: 23 additions & 0 deletions src/test/run-fail/panic-set-unset-handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// error-pattern:thread '<main>' panicked at 'foobar'

#![feature(std_panic, panic_handler)]
use std::panic;
use std::io::{self, Write};

fn main() {
panic::set_handler(|i| {
write!(io::stderr(), "greetings from the panic handler");
});
panic::take_handler();
panic!("foobar");
}
19 changes: 19 additions & 0 deletions src/test/run-fail/panic-take-handler-nop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// error-pattern:thread '<main>' panicked at 'foobar'

#![feature(std_panic, panic_handler)]
use std::panic;

fn main() {
panic::take_handler();
panic!("foobar");
}
33 changes: 33 additions & 0 deletions src/test/run-pass/panic-handler-chain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(panic_handler, const_fn, std_panic)]

use std::sync::atomic::{AtomicUsize, Ordering};
use std::panic;
use std::thread;

static A: AtomicUsize = AtomicUsize::new(0);
static B: AtomicUsize = AtomicUsize::new(0);

fn main() {
panic::set_handler(|_| { A.fetch_add(1, Ordering::SeqCst); });
let handler = panic::take_handler();
panic::set_handler(move |info| {
B.fetch_add(1, Ordering::SeqCst);
handler(info);
});

let _ = thread::spawn(|| {
panic!();
}).join();

assert_eq!(1, A.load(Ordering::SeqCst));
assert_eq!(1, B.load(Ordering::SeqCst));
}
57 changes: 57 additions & 0 deletions src/test/run-pass/panic-handler-flail-wildly.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(panic_handler, std_panic)]

use std::panic;
use std::thread;

fn a() {
panic::set_handler(|_| println!("hello yes this is a"));
panic::take_handler();
panic::set_handler(|_| println!("hello yes this is a part 2"));
panic::take_handler();
}

fn b() {
panic::take_handler();
panic::take_handler();
panic::take_handler();
panic::take_handler();
panic::take_handler();
panic!();
}

fn c() {
panic::set_handler(|_| ());
panic::set_handler(|_| ());
panic::set_handler(|_| ());
panic::set_handler(|_| ());
panic::set_handler(|_| ());
panic::set_handler(|_| ());
panic!();
}

fn main() {
for _ in 0..10 {
let mut handles = vec![];
for _ in 0..10 {
handles.push(thread::spawn(a));
}
for _ in 0..10 {
handles.push(thread::spawn(b));
}
for _ in 0..10 {
handles.push(thread::spawn(c));
}
for handle in handles {
let _ = handle.join();
}
}
}
27 changes: 27 additions & 0 deletions src/test/run-pass/panic-handler-set-twice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(panic_handler, const_fn, std_panic)]

use std::sync::atomic::{AtomicUsize, Ordering};
use std::panic;
use std::thread;

static A: AtomicUsize = AtomicUsize::new(0);

fn main() {
panic::set_handler(|_| ());
panic::set_handler(|info| { A.fetch_add(1, Ordering::SeqCst); });

let _ = thread::spawn(|| {
panic!();
}).join();

assert_eq!(1, A.load(Ordering::SeqCst));
}

0 comments on commit 12f171b

Please sign in to comment.