@@ -15,10 +15,12 @@ use any::Any;
15
15
use cell:: Cell ;
16
16
use cell:: RefCell ;
17
17
use intrinsics;
18
+ use sync:: StaticRwLock ;
18
19
use sys:: stdio:: Stderr ;
19
20
use sys_common:: backtrace;
20
21
use sys_common:: thread_info;
21
22
use sys_common:: util;
23
+ use thread;
22
24
23
25
thread_local ! { pub static PANIC_COUNT : Cell <usize > = Cell :: new( 0 ) }
24
26
@@ -28,11 +30,138 @@ thread_local! {
28
30
}
29
31
}
30
32
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 > ( ) {
34
163
Some ( s) => * s,
35
- None => match obj . downcast_ref :: < String > ( ) {
164
+ None => match info . payload . downcast_ref :: < String > ( ) {
36
165
Some ( s) => & s[ ..] ,
37
166
None => "Box<Any>" ,
38
167
}
@@ -81,10 +210,21 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) {
81
210
unsafe { intrinsics:: abort ( ) }
82
211
}
83
212
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
+ }
88
228
89
229
if panics >= 2 {
90
230
// If a thread panics while it's already unwinding then we
0 commit comments