@@ -15,10 +15,12 @@ use any::Any;
1515use cell:: Cell ;
1616use cell:: RefCell ;
1717use intrinsics;
18+ use sync:: StaticRwLock ;
1819use sys:: stdio:: Stderr ;
1920use sys_common:: backtrace;
2021use sys_common:: thread_info;
2122use sys_common:: util;
23+ use thread;
2224
2325thread_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
0 commit comments