@@ -18,9 +18,9 @@ use crate::intrinsics;
1818use crate :: mem:: { self , ManuallyDrop } ;
1919use crate :: process;
2020use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
21+ use crate :: sync:: { PoisonError , RwLock } ;
2122use crate :: sys:: stdio:: panic_output;
2223use crate :: sys_common:: backtrace;
23- use crate :: sys_common:: rwlock:: StaticRwLock ;
2424use crate :: sys_common:: thread_info;
2525use crate :: thread;
2626
@@ -71,20 +71,29 @@ extern "C" fn __rust_foreign_exception() -> ! {
7171 rtabort ! ( "Rust cannot catch foreign exceptions" ) ;
7272}
7373
74- #[ derive( Copy , Clone ) ]
7574enum Hook {
7675 Default ,
77- Custom ( * mut ( dyn Fn ( & PanicInfo < ' _ > ) + ' static + Sync + Send ) ) ,
76+ Custom ( Box < dyn Fn ( & PanicInfo < ' _ > ) + ' static + Sync + Send > ) ,
7877}
7978
8079impl Hook {
81- fn custom ( f : impl Fn ( & PanicInfo < ' _ > ) + ' static + Sync + Send ) -> Self {
82- Self :: Custom ( Box :: into_raw ( Box :: new ( f) ) )
80+ #[ inline]
81+ fn into_box ( self ) -> Box < dyn Fn ( & PanicInfo < ' _ > ) + ' static + Sync + Send > {
82+ match self {
83+ Hook :: Default => Box :: new ( default_hook) ,
84+ Hook :: Custom ( hook) => hook,
85+ }
8386 }
8487}
8588
86- static HOOK_LOCK : StaticRwLock = StaticRwLock :: new ( ) ;
87- static mut HOOK : Hook = Hook :: Default ;
89+ impl Default for Hook {
90+ #[ inline]
91+ fn default ( ) -> Hook {
92+ Hook :: Default
93+ }
94+ }
95+
96+ static HOOK : RwLock < Hook > = RwLock :: new ( Hook :: Default ) ;
8897
8998/// Registers a custom panic hook, replacing any that was previously registered.
9099///
@@ -125,24 +134,13 @@ pub fn set_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>) {
125134 panic ! ( "cannot modify the panic hook from a panicking thread" ) ;
126135 }
127136
128- // SAFETY:
129- //
130- // - `HOOK` can only be modified while holding write access to `HOOK_LOCK`.
131- // - The argument of `Box::from_raw` is always a valid pointer that was created using
132- // `Box::into_raw`.
133- unsafe {
134- let guard = HOOK_LOCK . write ( ) ;
135- let old_hook = HOOK ;
136- HOOK = Hook :: Custom ( Box :: into_raw ( hook) ) ;
137- drop ( guard) ;
138-
139- if let Hook :: Custom ( ptr) = old_hook {
140- #[ allow( unused_must_use) ]
141- {
142- Box :: from_raw ( ptr) ;
143- }
144- }
145- }
137+ let new = Hook :: Custom ( hook) ;
138+ let mut hook = HOOK . write ( ) . unwrap_or_else ( PoisonError :: into_inner) ;
139+ let old = mem:: replace ( & mut * hook, new) ;
140+ drop ( hook) ;
141+ // Only drop the old hook after releasing the lock to avoid deadlocking
142+ // if its destructor panics.
143+ drop ( old) ;
146144}
147145
148146/// Unregisters the current panic hook, returning it.
@@ -179,22 +177,11 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
179177 panic ! ( "cannot modify the panic hook from a panicking thread" ) ;
180178 }
181179
182- // SAFETY:
183- //
184- // - `HOOK` can only be modified while holding write access to `HOOK_LOCK`.
185- // - The argument of `Box::from_raw` is always a valid pointer that was created using
186- // `Box::into_raw`.
187- unsafe {
188- let guard = HOOK_LOCK . write ( ) ;
189- let hook = HOOK ;
190- HOOK = Hook :: Default ;
191- drop ( guard) ;
180+ let mut hook = HOOK . write ( ) . unwrap_or_else ( PoisonError :: into_inner) ;
181+ let old_hook = mem:: take ( & mut * hook) ;
182+ drop ( hook) ;
192183
193- match hook {
194- Hook :: Default => Box :: new ( default_hook) ,
195- Hook :: Custom ( ptr) => Box :: from_raw ( ptr) ,
196- }
197- }
184+ old_hook. into_box ( )
198185}
199186
200187/// Atomic combination of [`take_hook`] and [`set_hook`]. Use this to replace the panic handler with
@@ -240,24 +227,9 @@ where
240227 panic ! ( "cannot modify the panic hook from a panicking thread" ) ;
241228 }
242229
243- // SAFETY:
244- //
245- // - `HOOK` can only be modified while holding write access to `HOOK_LOCK`.
246- // - The argument of `Box::from_raw` is always a valid pointer that was created using
247- // `Box::into_raw`.
248- unsafe {
249- let guard = HOOK_LOCK . write ( ) ;
250- let old_hook = HOOK ;
251- HOOK = Hook :: Default ;
252-
253- let prev = match old_hook {
254- Hook :: Default => Box :: new ( default_hook) ,
255- Hook :: Custom ( ptr) => Box :: from_raw ( ptr) ,
256- } ;
257-
258- HOOK = Hook :: custom ( move |info| hook_fn ( & prev, info) ) ;
259- drop ( guard) ;
260- }
230+ let mut hook = HOOK . write ( ) . unwrap_or_else ( PoisonError :: into_inner) ;
231+ let prev = mem:: take ( & mut * hook) . into_box ( ) ;
232+ * hook = Hook :: Custom ( Box :: new ( move |info| hook_fn ( & prev, info) ) ) ;
261233}
262234
263235fn default_hook ( info : & PanicInfo < ' _ > ) {
@@ -682,27 +654,26 @@ fn rust_panic_with_hook(
682654 crate :: sys:: abort_internal ( ) ;
683655 }
684656
685- unsafe {
686- let mut info = PanicInfo :: internal_constructor ( message, location, can_unwind) ;
687- let _guard = HOOK_LOCK . read ( ) ;
688- match HOOK {
689- // Some platforms (like wasm) know that printing to stderr won't ever actually
690- // print anything, and if that's the case we can skip the default
691- // hook. Since string formatting happens lazily when calling `payload`
692- // methods, this means we avoid formatting the string at all!
693- // (The panic runtime might still call `payload.take_box()` though and trigger
694- // formatting.)
695- Hook :: Default if panic_output ( ) . is_none ( ) => { }
696- Hook :: Default => {
697- info. set_payload ( payload. get ( ) ) ;
698- default_hook ( & info) ;
699- }
700- Hook :: Custom ( ptr) => {
701- info. set_payload ( payload. get ( ) ) ;
702- ( * ptr) ( & info) ;
703- }
704- } ;
705- }
657+ let mut info = PanicInfo :: internal_constructor ( message, location, can_unwind) ;
658+ let hook = HOOK . read ( ) . unwrap_or_else ( PoisonError :: into_inner) ;
659+ match * hook {
660+ // Some platforms (like wasm) know that printing to stderr won't ever actually
661+ // print anything, and if that's the case we can skip the default
662+ // hook. Since string formatting happens lazily when calling `payload`
663+ // methods, this means we avoid formatting the string at all!
664+ // (The panic runtime might still call `payload.take_box()` though and trigger
665+ // formatting.)
666+ Hook :: Default if panic_output ( ) . is_none ( ) => { }
667+ Hook :: Default => {
668+ info. set_payload ( payload. get ( ) ) ;
669+ default_hook ( & info) ;
670+ }
671+ Hook :: Custom ( ref hook) => {
672+ info. set_payload ( payload. get ( ) ) ;
673+ hook ( & info) ;
674+ }
675+ } ;
676+ drop ( hook) ;
706677
707678 if panics > 1 || !can_unwind {
708679 // If a thread panics while it's already unwinding then we
0 commit comments