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
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) }
}
}
-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>",
}
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
--- /dev/null
+// 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");
+}
--- /dev/null
+// 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");
+}
--- /dev/null
+// 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");
+}
--- /dev/null
+// 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));
+}
--- /dev/null
+// 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();
+ }
+ }
+}
--- /dev/null
+// 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));
+}