Skip to content

Commit f1148a5

Browse files
committed
Implement custom panic handlers
1 parent 27d5511 commit f1148a5

8 files changed

+331
-8
lines changed

src/libstd/panic.rs

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use sync::{Arc, Mutex, RwLock};
2121
use sys_common::unwind;
2222
use thread::Result;
2323

24+
pub use panicking::{take_handler, set_handler, PanicInfo, Location};
25+
2426
/// A marker trait which represents "panic safe" types in Rust.
2527
///
2628
/// This trait is implemented by default for many types and behaves similarly in

src/libstd/panicking.rs

+148-8
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@ use any::Any;
1515
use cell::Cell;
1616
use cell::RefCell;
1717
use intrinsics;
18+
use sync::StaticRwLock;
1819
use sys::stdio::Stderr;
1920
use sys_common::backtrace;
2021
use sys_common::thread_info;
2122
use sys_common::util;
23+
use thread;
2224

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

@@ -28,11 +30,138 @@ thread_local! {
2830
}
2931
}
3032

31-
fn log_panic(obj: &(Any+Send), file: &'static str, line: u32,
32-
log_backtrace: bool) {
33-
let msg = match obj.downcast_ref::<&'static str>() {
33+
#[derive(Copy, Clone)]
34+
enum Handler {
35+
Default,
36+
Custom(*mut (Fn(&PanicInfo) + 'static + Sync + Send)),
37+
}
38+
39+
static HANDLER_LOCK: StaticRwLock = StaticRwLock::new();
40+
static mut HANDLER: Handler = Handler::Default;
41+
42+
/// Registers a custom panic handler, replacing any that was previously
43+
/// registered.
44+
///
45+
/// The panic handler is invoked when a thread panics, but before it begins
46+
/// unwinding the stack. The default handler prints a message to standard error
47+
/// and generates a backtrace if requested, but this behavior can be customized
48+
/// with the `set_handler` and `take_handler` functions.
49+
///
50+
/// The handler is provided with a `PanicInfo` struct which contains information
51+
/// about the origin of the panic, including the payload passed to `panic!` and
52+
/// the source code location from which the panic originated.
53+
///
54+
/// The panic handler is a global resource.
55+
///
56+
/// # Panics
57+
///
58+
/// Panics if called from a panicking thread.
59+
#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
60+
pub fn set_handler<F>(handler: F) where F: Fn(&PanicInfo) + 'static + Sync + Send {
61+
if thread::panicking() {
62+
panic!("cannot modify the panic handler from a panicking thread");
63+
}
64+
65+
let handler = Box::new(handler);
66+
unsafe {
67+
let lock = HANDLER_LOCK.write();
68+
let old_handler = HANDLER;
69+
HANDLER = Handler::Custom(Box::into_raw(handler));
70+
drop(lock);
71+
72+
if let Handler::Custom(ptr) = old_handler {
73+
Box::from_raw(ptr);
74+
}
75+
}
76+
}
77+
78+
/// Unregisters the current panic handler, returning it.
79+
///
80+
/// If no custom handler is registered, the default handler will be returned.
81+
///
82+
/// # Panics
83+
///
84+
/// Panics if called from a panicking thread.
85+
#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
86+
pub fn take_handler() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> {
87+
if thread::panicking() {
88+
panic!("cannot modify the panic handler from a panicking thread");
89+
}
90+
91+
unsafe {
92+
let lock = HANDLER_LOCK.write();
93+
let handler = HANDLER;
94+
HANDLER = Handler::Default;
95+
drop(lock);
96+
97+
match handler {
98+
Handler::Default => Box::new(default_handler),
99+
Handler::Custom(ptr) => {Box::from_raw(ptr)} // FIXME #30530
100+
}
101+
}
102+
}
103+
104+
/// A struct providing information about a panic.
105+
#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
106+
pub struct PanicInfo<'a> {
107+
payload: &'a (Any + Send),
108+
location: Location<'a>,
109+
}
110+
111+
impl<'a> PanicInfo<'a> {
112+
/// Returns the payload associated with the panic.
113+
///
114+
/// This will commonly, but not always, be a `&'static str` or `String`.
115+
#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
116+
pub fn payload(&self) -> &(Any + Send) {
117+
self.payload
118+
}
119+
120+
/// Returns information about the location from which the panic originated,
121+
/// if available.
122+
///
123+
/// This method will currently always return `Some`, but this may change
124+
/// in future versions.
125+
#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
126+
pub fn location(&self) -> Option<&Location> {
127+
Some(&self.location)
128+
}
129+
}
130+
131+
/// A struct containing information about the location of a panic.
132+
#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
133+
pub struct Location<'a> {
134+
file: &'a str,
135+
line: u32,
136+
}
137+
138+
impl<'a> Location<'a> {
139+
/// Returns the name of the source file from which the panic originated.
140+
#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
141+
pub fn file(&self) -> &str {
142+
self.file
143+
}
144+
145+
/// Returns the line number from which the panic originated.
146+
#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")]
147+
pub fn line(&self) -> u32 {
148+
self.line
149+
}
150+
}
151+
152+
fn default_handler(info: &PanicInfo) {
153+
let panics = PANIC_COUNT.with(|s| s.get());
154+
155+
// If this is a double panic, make sure that we print a backtrace
156+
// for this panic. Otherwise only print it if logging is enabled.
157+
let log_backtrace = panics >= 2 || backtrace::log_enabled();
158+
159+
let file = info.location.file;
160+
let line = info.location.line;
161+
162+
let msg = match info.payload.downcast_ref::<&'static str>() {
34163
Some(s) => *s,
35-
None => match obj.downcast_ref::<String>() {
164+
None => match info.payload.downcast_ref::<String>() {
36165
Some(s) => &s[..],
37166
None => "Box<Any>",
38167
}
@@ -81,10 +210,21 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) {
81210
unsafe { intrinsics::abort() }
82211
}
83212

84-
// If this is a double panic, make sure that we print a backtrace
85-
// for this panic. Otherwise only print it if logging is enabled.
86-
let log_backtrace = panics >= 2 || backtrace::log_enabled();
87-
log_panic(obj, file, line, log_backtrace);
213+
let info = PanicInfo {
214+
payload: obj,
215+
location: Location {
216+
file: file,
217+
line: line,
218+
},
219+
};
220+
221+
unsafe {
222+
let _lock = HANDLER_LOCK.read();
223+
match HANDLER {
224+
Handler::Default => default_handler(&info),
225+
Handler::Custom(ptr) => (*ptr)(&info),
226+
}
227+
}
88228

89229
if panics >= 2 {
90230
// If a thread panics while it's already unwinding then we
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// error-pattern:greetings from the panic handler
12+
13+
#![feature(std_panic, panic_handler)]
14+
use std::panic;
15+
use std::io::{self, Write};
16+
17+
fn main() {
18+
panic::set_handler(|i| {
19+
write!(io::stderr(), "greetings from the panic handler");
20+
});
21+
panic!("foobar");
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// error-pattern:thread '<main>' panicked at 'foobar'
12+
13+
#![feature(std_panic, panic_handler)]
14+
use std::panic;
15+
use std::io::{self, Write};
16+
17+
fn main() {
18+
panic::set_handler(|i| {
19+
write!(io::stderr(), "greetings from the panic handler");
20+
});
21+
panic::take_handler();
22+
panic!("foobar");
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// error-pattern:thread '<main>' panicked at 'foobar'
12+
13+
#![feature(std_panic, panic_handler)]
14+
use std::panic;
15+
16+
fn main() {
17+
panic::take_handler();
18+
panic!("foobar");
19+
}
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
#![feature(panic_handler, const_fn, std_panic)]
11+
12+
use std::sync::atomic::{AtomicUsize, Ordering};
13+
use std::panic;
14+
use std::thread;
15+
16+
static A: AtomicUsize = AtomicUsize::new(0);
17+
static B: AtomicUsize = AtomicUsize::new(0);
18+
19+
fn main() {
20+
panic::set_handler(|_| { A.fetch_add(1, Ordering::SeqCst); });
21+
let handler = panic::take_handler();
22+
panic::set_handler(move |info| {
23+
B.fetch_add(1, Ordering::SeqCst);
24+
handler(info);
25+
});
26+
27+
let _ = thread::spawn(|| {
28+
panic!();
29+
}).join();
30+
31+
assert_eq!(1, A.load(Ordering::SeqCst));
32+
assert_eq!(1, B.load(Ordering::SeqCst));
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
#![feature(panic_handler, std_panic)]
11+
12+
use std::panic;
13+
use std::thread;
14+
15+
fn a() {
16+
panic::set_handler(|_| println!("hello yes this is a"));
17+
panic::take_handler();
18+
panic::set_handler(|_| println!("hello yes this is a part 2"));
19+
panic::take_handler();
20+
}
21+
22+
fn b() {
23+
panic::take_handler();
24+
panic::take_handler();
25+
panic::take_handler();
26+
panic::take_handler();
27+
panic::take_handler();
28+
panic!();
29+
}
30+
31+
fn c() {
32+
panic::set_handler(|_| ());
33+
panic::set_handler(|_| ());
34+
panic::set_handler(|_| ());
35+
panic::set_handler(|_| ());
36+
panic::set_handler(|_| ());
37+
panic::set_handler(|_| ());
38+
panic!();
39+
}
40+
41+
fn main() {
42+
for _ in 0..10 {
43+
let mut handles = vec![];
44+
for _ in 0..10 {
45+
handles.push(thread::spawn(a));
46+
}
47+
for _ in 0..10 {
48+
handles.push(thread::spawn(b));
49+
}
50+
for _ in 0..10 {
51+
handles.push(thread::spawn(c));
52+
}
53+
for handle in handles {
54+
let _ = handle.join();
55+
}
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
#![feature(panic_handler, const_fn, std_panic)]
11+
12+
use std::sync::atomic::{AtomicUsize, Ordering};
13+
use std::panic;
14+
use std::thread;
15+
16+
static A: AtomicUsize = AtomicUsize::new(0);
17+
18+
fn main() {
19+
panic::set_handler(|_| ());
20+
panic::set_handler(|info| { A.fetch_add(1, Ordering::SeqCst); });
21+
22+
let _ = thread::spawn(|| {
23+
panic!();
24+
}).join();
25+
26+
assert_eq!(1, A.load(Ordering::SeqCst));
27+
}

0 commit comments

Comments
 (0)