]> git.proxmox.com Git - rustc.git/blame - src/libstd/rt/unwind.rs
Imported Upstream version 1.0.0~beta.3
[rustc.git] / src / libstd / rt / unwind.rs
CommitLineData
1a4d82fc
JJ
1// Copyright 2013 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//! Implementation of Rust stack unwinding
12//!
13//! For background on exception handling and stack unwinding please see
14//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
15//! documents linked from it.
16//! These are also good reads:
17//! http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/
18//! http://monoinfinito.wordpress.com/series/exception-handling-in-c/
19//! http://www.airs.com/blog/index.php?s=exception+frames
20//!
21//! ## A brief summary
22//!
23//! Exception handling happens in two phases: a search phase and a cleanup phase.
24//!
25//! In both phases the unwinder walks stack frames from top to bottom using
26//! information from the stack frame unwind sections of the current process's
27//! modules ("module" here refers to an OS module, i.e. an executable or a
28//! dynamic library).
29//!
30//! For each stack frame, it invokes the associated "personality routine", whose
31//! address is also stored in the unwind info section.
32//!
33//! In the search phase, the job of a personality routine is to examine exception
34//! object being thrown, and to decide whether it should be caught at that stack
35//! frame. Once the handler frame has been identified, cleanup phase begins.
36//!
37//! In the cleanup phase, personality routines invoke cleanup code associated
38//! with their stack frames (i.e. destructors). Once stack has been unwound down
39//! to the handler frame level, unwinding stops and the last personality routine
40//! transfers control to its catch block.
41//!
42//! ## Frame unwind info registration
43//!
44//! Each module has its own frame unwind info section (usually ".eh_frame"), and
45//! unwinder needs to know about all of them in order for unwinding to be able to
46//! cross module boundaries.
47//!
48//! On some platforms, like Linux, this is achieved by dynamically enumerating
49//! currently loaded modules via the dl_iterate_phdr() API and finding all
50//! .eh_frame sections.
51//!
52//! Others, like Windows, require modules to actively register their unwind info
53//! sections by calling __register_frame_info() API at startup. In the latter
54//! case it is essential that there is only one copy of the unwinder runtime in
55//! the process. This is usually achieved by linking to the dynamic version of
56//! the unwind runtime.
57//!
58//! Currently Rust uses unwind runtime provided by libgcc.
59
60use prelude::v1::*;
61
62use any::Any;
c34b1796 63use boxed;
1a4d82fc
JJ
64use cell::Cell;
65use cmp;
85aaf69f 66use panicking;
1a4d82fc
JJ
67use fmt;
68use intrinsics;
69use libc::c_void;
70use mem;
71use sync::atomic::{self, Ordering};
c34b1796 72use sys_common::mutex::{Mutex, MUTEX_INIT};
1a4d82fc
JJ
73
74use rt::libunwind as uw;
75
76struct Exception {
77 uwe: uw::_Unwind_Exception,
85aaf69f 78 cause: Option<Box<Any + Send + 'static>>,
1a4d82fc
JJ
79}
80
9346a6ac 81pub type Callback = fn(msg: &(Any + Send), file: &'static str, line: u32);
1a4d82fc
JJ
82
83// Variables used for invoking callbacks when a thread starts to unwind.
84//
85// For more information, see below.
c34b1796 86const MAX_CALLBACKS: usize = 16;
85aaf69f
SL
87static CALLBACKS: [atomic::AtomicUsize; MAX_CALLBACKS] =
88 [atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT,
89 atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT,
90 atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT,
91 atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT,
92 atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT,
93 atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT,
94 atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT,
95 atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT];
96static CALLBACK_CNT: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT;
1a4d82fc
JJ
97
98thread_local! { static PANICKING: Cell<bool> = Cell::new(false) }
99
100/// Invoke a closure, capturing the cause of panic if one occurs.
101///
85aaf69f
SL
102/// This function will return `Ok(())` if the closure did not panic, and will
103/// return `Err(cause)` if the closure panics. The `cause` returned is the
1a4d82fc
JJ
104/// object with which panic was originally invoked.
105///
106/// This function also is unsafe for a variety of reasons:
107///
108/// * This is not safe to call in a nested fashion. The unwinding
109/// interface for Rust is designed to have at most one try/catch block per
110/// thread, not multiple. No runtime checking is currently performed to uphold
111/// this invariant, so this function is not safe. A nested try/catch block
112/// may result in corruption of the outer try/catch block's state, especially
113/// if this is used within a thread itself.
114///
115/// * It is not sound to trigger unwinding while already unwinding. Rust threads
116/// have runtime checks in place to ensure this invariant, but it is not
117/// guaranteed that a rust thread is in place when invoking this function.
118/// Unwinding twice can lead to resource leaks where some destructors are not
119/// run.
120pub unsafe fn try<F: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> {
121 let mut f = Some(f);
122
123 let prev = PANICKING.with(|s| s.get());
124 PANICKING.with(|s| s.set(false));
125 let ep = rust_try(try_fn::<F>, &mut f as *mut _ as *mut c_void);
126 PANICKING.with(|s| s.set(prev));
127 return if ep.is_null() {
128 Ok(())
129 } else {
130 let my_ep = ep as *mut Exception;
131 rtdebug!("caught {}", (*my_ep).uwe.exception_class);
132 let cause = (*my_ep).cause.take();
133 uw::_Unwind_DeleteException(ep);
134 Err(cause.unwrap())
135 };
136
137 extern fn try_fn<F: FnOnce()>(opt_closure: *mut c_void) {
138 let opt_closure = opt_closure as *mut Option<F>;
139 unsafe { (*opt_closure).take().unwrap()(); }
140 }
141
142 #[link(name = "rustrt_native", kind = "static")]
143 #[cfg(not(test))]
144 extern {}
145
146 extern {
147 // Rust's try-catch
148 // When f(...) returns normally, the return value is null.
149 // When f(...) throws, the return value is a pointer to the caught
150 // exception object.
151 fn rust_try(f: extern fn(*mut c_void),
152 data: *mut c_void) -> *mut uw::_Unwind_Exception;
153 }
154}
155
85aaf69f 156/// Determines whether the current thread is unwinding because of panic.
1a4d82fc
JJ
157pub fn panicking() -> bool {
158 PANICKING.with(|s| s.get())
159}
160
161// An uninlined, unmangled function upon which to slap yer breakpoints
162#[inline(never)]
163#[no_mangle]
85aaf69f
SL
164#[allow(private_no_mangle_fns)]
165fn rust_panic(cause: Box<Any + Send + 'static>) -> ! {
1a4d82fc
JJ
166 rtdebug!("begin_unwind()");
167
168 unsafe {
c34b1796 169 let exception: Box<_> = box Exception {
1a4d82fc
JJ
170 uwe: uw::_Unwind_Exception {
171 exception_class: rust_exception_class(),
172 exception_cleanup: exception_cleanup,
173 private: [0; uw::unwinder_private_data_size],
174 },
175 cause: Some(cause),
176 };
c34b1796
AL
177 let exception_param = boxed::into_raw(exception) as *mut uw::_Unwind_Exception;
178 let error = uw::_Unwind_RaiseException(exception_param);
179 rtabort!("Could not unwind stack, error = {}", error as isize)
1a4d82fc
JJ
180 }
181
182 extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code,
183 exception: *mut uw::_Unwind_Exception) {
184 rtdebug!("exception_cleanup()");
185 unsafe {
c34b1796 186 let _: Box<Exception> = Box::from_raw(exception as *mut Exception);
1a4d82fc
JJ
187 }
188 }
189}
190
191// Rust's exception class identifier. This is used by personality routines to
192// determine whether the exception was thrown by their own runtime.
193fn rust_exception_class() -> uw::_Unwind_Exception_Class {
194 // M O Z \0 R U S T -- vendor, language
195 0x4d4f5a_00_52555354
196}
197
198// We could implement our personality routine in pure Rust, however exception
199// info decoding is tedious. More importantly, personality routines have to
200// handle various platform quirks, which are not fun to maintain. For this
201// reason, we attempt to reuse personality routine of the C language:
202// __gcc_personality_v0.
203//
204// Since C does not support exception catching, __gcc_personality_v0 simply
205// always returns _URC_CONTINUE_UNWIND in search phase, and always returns
206// _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase.
207//
208// This is pretty close to Rust's exception handling approach, except that Rust
209// does have a single "catch-all" handler at the bottom of each thread's stack.
210// So we have two versions of the personality routine:
211// - rust_eh_personality, used by all cleanup landing pads, which never catches,
212// so the behavior of __gcc_personality_v0 is perfectly adequate there, and
213// - rust_eh_personality_catch, used only by rust_try(), which always catches.
214//
215// Note, however, that for implementation simplicity, rust_eh_personality_catch
216// lacks code to install a landing pad, so in order to obtain exception object
217// pointer (which it needs to return upstream), rust_try() employs another trick:
218// it calls into the nested rust_try_inner(), whose landing pad does not resume
219// unwinds. Instead, it extracts the exception pointer and performs a "normal"
220// return.
221//
222// See also: rt/rust_try.ll
223
224#[cfg(all(not(target_arch = "arm"),
225 not(all(windows, target_arch = "x86_64")),
226 not(test)))]
227#[doc(hidden)]
228pub mod eabi {
229 use rt::libunwind as uw;
230 use libc::c_int;
231
232 extern "C" {
233 fn __gcc_personality_v0(version: c_int,
234 actions: uw::_Unwind_Action,
235 exception_class: uw::_Unwind_Exception_Class,
236 ue_header: *mut uw::_Unwind_Exception,
237 context: *mut uw::_Unwind_Context)
238 -> uw::_Unwind_Reason_Code;
239 }
240
241 #[lang="eh_personality"]
242 #[no_mangle] // referenced from rust_try.ll
85aaf69f 243 #[allow(private_no_mangle_fns)]
1a4d82fc
JJ
244 extern fn rust_eh_personality(
245 version: c_int,
246 actions: uw::_Unwind_Action,
247 exception_class: uw::_Unwind_Exception_Class,
248 ue_header: *mut uw::_Unwind_Exception,
249 context: *mut uw::_Unwind_Context
250 ) -> uw::_Unwind_Reason_Code
251 {
252 unsafe {
253 __gcc_personality_v0(version, actions, exception_class, ue_header,
254 context)
255 }
256 }
257
258 #[no_mangle] // referenced from rust_try.ll
259 pub extern "C" fn rust_eh_personality_catch(
260 _version: c_int,
261 actions: uw::_Unwind_Action,
262 _exception_class: uw::_Unwind_Exception_Class,
263 _ue_header: *mut uw::_Unwind_Exception,
264 _context: *mut uw::_Unwind_Context
265 ) -> uw::_Unwind_Reason_Code
266 {
267
268 if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
269 uw::_URC_HANDLER_FOUND // catch!
270 }
271 else { // cleanup phase
272 uw::_URC_INSTALL_CONTEXT
273 }
274 }
275}
276
277// iOS on armv7 is using SjLj exceptions and therefore requires to use
278// a specialized personality routine: __gcc_personality_sj0
279
280#[cfg(all(target_os = "ios", target_arch = "arm", not(test)))]
281#[doc(hidden)]
282pub mod eabi {
283 use rt::libunwind as uw;
284 use libc::c_int;
285
286 extern "C" {
287 fn __gcc_personality_sj0(version: c_int,
288 actions: uw::_Unwind_Action,
289 exception_class: uw::_Unwind_Exception_Class,
290 ue_header: *mut uw::_Unwind_Exception,
291 context: *mut uw::_Unwind_Context)
292 -> uw::_Unwind_Reason_Code;
293 }
294
295 #[lang="eh_personality"]
296 #[no_mangle] // referenced from rust_try.ll
297 pub extern "C" fn rust_eh_personality(
298 version: c_int,
299 actions: uw::_Unwind_Action,
300 exception_class: uw::_Unwind_Exception_Class,
301 ue_header: *mut uw::_Unwind_Exception,
302 context: *mut uw::_Unwind_Context
303 ) -> uw::_Unwind_Reason_Code
304 {
305 unsafe {
306 __gcc_personality_sj0(version, actions, exception_class, ue_header,
307 context)
308 }
309 }
310
311 #[no_mangle] // referenced from rust_try.ll
312 pub extern "C" fn rust_eh_personality_catch(
313 _version: c_int,
314 actions: uw::_Unwind_Action,
315 _exception_class: uw::_Unwind_Exception_Class,
316 _ue_header: *mut uw::_Unwind_Exception,
317 _context: *mut uw::_Unwind_Context
318 ) -> uw::_Unwind_Reason_Code
319 {
320 if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
321 uw::_URC_HANDLER_FOUND // catch!
322 }
323 else { // cleanup phase
324 unsafe {
325 __gcc_personality_sj0(_version, actions, _exception_class, _ue_header,
326 _context)
327 }
328 }
329 }
330}
331
332
333// ARM EHABI uses a slightly different personality routine signature,
334// but otherwise works the same.
335#[cfg(all(target_arch = "arm", not(target_os = "ios"), not(test)))]
336#[doc(hidden)]
337pub mod eabi {
338 use rt::libunwind as uw;
339 use libc::c_int;
340
341 extern "C" {
342 fn __gcc_personality_v0(state: uw::_Unwind_State,
343 ue_header: *mut uw::_Unwind_Exception,
344 context: *mut uw::_Unwind_Context)
345 -> uw::_Unwind_Reason_Code;
346 }
347
348 #[lang="eh_personality"]
349 #[no_mangle] // referenced from rust_try.ll
85aaf69f 350 #[allow(private_no_mangle_fns)]
1a4d82fc
JJ
351 extern "C" fn rust_eh_personality(
352 state: uw::_Unwind_State,
353 ue_header: *mut uw::_Unwind_Exception,
354 context: *mut uw::_Unwind_Context
355 ) -> uw::_Unwind_Reason_Code
356 {
357 unsafe {
358 __gcc_personality_v0(state, ue_header, context)
359 }
360 }
361
362 #[no_mangle] // referenced from rust_try.ll
363 pub extern "C" fn rust_eh_personality_catch(
364 state: uw::_Unwind_State,
365 _ue_header: *mut uw::_Unwind_Exception,
366 _context: *mut uw::_Unwind_Context
367 ) -> uw::_Unwind_Reason_Code
368 {
369 if (state as c_int & uw::_US_ACTION_MASK as c_int)
370 == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase
371 uw::_URC_HANDLER_FOUND // catch!
372 }
373 else { // cleanup phase
374 uw::_URC_INSTALL_CONTEXT
375 }
376 }
377}
378
379// Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx)
380//
381// This looks a bit convoluted because rather than implementing a native SEH handler,
382// GCC reuses the same personality routine as for the other architectures by wrapping it
383// with an "API translator" layer (_GCC_specific_handler).
384
385#[cfg(all(windows, target_arch = "x86_64", not(test)))]
386#[doc(hidden)]
387#[allow(non_camel_case_types, non_snake_case)]
388pub mod eabi {
389 pub use self::EXCEPTION_DISPOSITION::*;
390 use rt::libunwind as uw;
391 use libc::{c_void, c_int};
392
393 #[repr(C)]
1a4d82fc
JJ
394 pub struct EXCEPTION_RECORD;
395 #[repr(C)]
1a4d82fc
JJ
396 pub struct CONTEXT;
397 #[repr(C)]
1a4d82fc
JJ
398 pub struct DISPATCHER_CONTEXT;
399
400 #[repr(C)]
c34b1796 401 #[derive(Copy, Clone)]
1a4d82fc
JJ
402 pub enum EXCEPTION_DISPOSITION {
403 ExceptionContinueExecution,
404 ExceptionContinueSearch,
405 ExceptionNestedException,
406 ExceptionCollidedUnwind
407 }
408
409 type _Unwind_Personality_Fn =
410 extern "C" fn(
411 version: c_int,
412 actions: uw::_Unwind_Action,
413 exception_class: uw::_Unwind_Exception_Class,
414 ue_header: *mut uw::_Unwind_Exception,
415 context: *mut uw::_Unwind_Context
416 ) -> uw::_Unwind_Reason_Code;
417
418 extern "C" {
419 fn __gcc_personality_seh0(
420 exceptionRecord: *mut EXCEPTION_RECORD,
421 establisherFrame: *mut c_void,
422 contextRecord: *mut CONTEXT,
423 dispatcherContext: *mut DISPATCHER_CONTEXT
424 ) -> EXCEPTION_DISPOSITION;
425
426 fn _GCC_specific_handler(
427 exceptionRecord: *mut EXCEPTION_RECORD,
428 establisherFrame: *mut c_void,
429 contextRecord: *mut CONTEXT,
430 dispatcherContext: *mut DISPATCHER_CONTEXT,
431 personality: _Unwind_Personality_Fn
432 ) -> EXCEPTION_DISPOSITION;
433 }
434
435 #[lang="eh_personality"]
436 #[no_mangle] // referenced from rust_try.ll
85aaf69f 437 #[allow(private_no_mangle_fns)]
1a4d82fc
JJ
438 extern "C" fn rust_eh_personality(
439 exceptionRecord: *mut EXCEPTION_RECORD,
440 establisherFrame: *mut c_void,
441 contextRecord: *mut CONTEXT,
442 dispatcherContext: *mut DISPATCHER_CONTEXT
443 ) -> EXCEPTION_DISPOSITION
444 {
445 unsafe {
446 __gcc_personality_seh0(exceptionRecord, establisherFrame,
447 contextRecord, dispatcherContext)
448 }
449 }
450
451 #[no_mangle] // referenced from rust_try.ll
452 pub extern "C" fn rust_eh_personality_catch(
453 exceptionRecord: *mut EXCEPTION_RECORD,
454 establisherFrame: *mut c_void,
455 contextRecord: *mut CONTEXT,
456 dispatcherContext: *mut DISPATCHER_CONTEXT
457 ) -> EXCEPTION_DISPOSITION
458 {
459 extern "C" fn inner(
460 _version: c_int,
461 actions: uw::_Unwind_Action,
462 _exception_class: uw::_Unwind_Exception_Class,
463 _ue_header: *mut uw::_Unwind_Exception,
464 _context: *mut uw::_Unwind_Context
465 ) -> uw::_Unwind_Reason_Code
466 {
467 if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
468 uw::_URC_HANDLER_FOUND // catch!
469 }
470 else { // cleanup phase
471 uw::_URC_INSTALL_CONTEXT
472 }
473 }
474
475 unsafe {
476 _GCC_specific_handler(exceptionRecord, establisherFrame,
477 contextRecord, dispatcherContext,
478 inner)
479 }
480 }
481}
482
483#[cfg(not(test))]
484/// Entry point of panic from the libcore crate.
485#[lang = "panic_fmt"]
486pub extern fn rust_begin_unwind(msg: fmt::Arguments,
9346a6ac 487 file: &'static str, line: u32) -> ! {
1a4d82fc
JJ
488 begin_unwind_fmt(msg, &(file, line))
489}
490
491/// The entry point for unwinding with a formatted message.
492///
493/// This is designed to reduce the amount of code required at the call
494/// site as much as possible (so that `panic!()` has as low an impact
495/// on (e.g.) the inlining of other functions as possible), by moving
496/// the actual formatting into this shared place.
497#[inline(never)] #[cold]
9346a6ac 498pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, u32)) -> ! {
85aaf69f 499 use fmt::Write;
1a4d82fc
JJ
500
501 // We do two allocations here, unfortunately. But (a) they're
502 // required with the current scheme, and (b) we don't handle
503 // panic + OOM properly anyway (see comment in begin_unwind
504 // below).
505
506 let mut s = String::new();
507 let _ = write!(&mut s, "{}", msg);
c34b1796 508 begin_unwind_inner(Box::new(s), file_line)
1a4d82fc
JJ
509}
510
511/// This is the entry point of unwinding for panic!() and assert!().
512#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
9346a6ac 513#[cfg(stage0)]
c34b1796 514pub fn begin_unwind<M: Any + Send>(msg: M, file_line: &(&'static str, usize)) -> ! {
1a4d82fc
JJ
515 // Note that this should be the only allocation performed in this code path.
516 // Currently this means that panic!() on OOM will invoke this code path,
517 // but then again we're not really ready for panic on OOM anyway. If
518 // we do start doing this, then we should propagate this allocation to
519 // be performed in the parent of this thread instead of the thread that's
520 // panicking.
521
9346a6ac
AL
522 // see below for why we do the `Any` coercion here.
523 let (file, line) = *file_line;
524 begin_unwind_inner(Box::new(msg), &(file, line as u32))
525}
526
527/// This is the entry point of unwinding for panic!() and assert!().
528#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
529#[cfg(not(stage0))]
530pub fn begin_unwind<M: Any + Send>(msg: M, file_line: &(&'static str, u32)) -> ! {
531 // Note that this should be the only allocation performed in this code path.
532 // Currently this means that panic!() on OOM will invoke this code path,
533 // but then again we're not really ready for panic on OOM anyway. If
534 // we do start doing this, then we should propagate this allocation to
535 // be performed in the parent of this thread instead of the thread that's
536 // panicking.
537
1a4d82fc 538 // see below for why we do the `Any` coercion here.
c34b1796 539 begin_unwind_inner(Box::new(msg), file_line)
1a4d82fc
JJ
540}
541
542/// The core of the unwinding.
543///
544/// This is non-generic to avoid instantiation bloat in other crates
545/// (which makes compilation of small crates noticeably slower). (Note:
546/// we need the `Any` object anyway, we're not just creating it to
547/// avoid being generic.)
548///
549/// Doing this split took the LLVM IR line counts of `fn main() { panic!()
550/// }` from ~1900/3700 (-O/no opts) to 180/590.
551#[inline(never)] #[cold] // this is the slow path, please never inline this
c34b1796 552fn begin_unwind_inner(msg: Box<Any + Send>,
9346a6ac 553 file_line: &(&'static str, u32)) -> ! {
c34b1796
AL
554 // Make sure the default failure handler is registered before we look at the
555 // callbacks. We also use a raw sys-based mutex here instead of a
556 // `std::sync` one as accessing TLS can cause weird recursive problems (and
557 // we don't need poison checking).
558 unsafe {
559 static LOCK: Mutex = MUTEX_INIT;
560 static mut INIT: bool = false;
561 LOCK.lock();
562 if !INIT {
563 register(panicking::on_panic);
564 INIT = true;
565 }
566 LOCK.unlock();
567 }
1a4d82fc
JJ
568
569 // First, invoke call the user-defined callbacks triggered on thread panic.
570 //
571 // By the time that we see a callback has been registered (by reading
572 // MAX_CALLBACKS), the actual callback itself may have not been stored yet,
573 // so we just chalk it up to a race condition and move on to the next
574 // callback. Additionally, CALLBACK_CNT may briefly be higher than
575 // MAX_CALLBACKS, so we're sure to clamp it as necessary.
576 let callbacks = {
577 let amt = CALLBACK_CNT.load(Ordering::SeqCst);
85aaf69f 578 &CALLBACKS[..cmp::min(amt, MAX_CALLBACKS)]
1a4d82fc 579 };
85aaf69f 580 for cb in callbacks {
1a4d82fc
JJ
581 match cb.load(Ordering::SeqCst) {
582 0 => {}
583 n => {
584 let f: Callback = unsafe { mem::transmute(n) };
585 let (file, line) = *file_line;
586 f(&*msg, file, line);
587 }
588 }
589 };
590
591 // Now that we've run all the necessary unwind callbacks, we actually
592 // perform the unwinding.
593 if panicking() {
594 // If a thread panics while it's already unwinding then we
595 // have limited options. Currently our preference is to
596 // just abort. In the future we may consider resuming
597 // unwinding or otherwise exiting the thread cleanly.
598 rterrln!("thread panicked while panicking. aborting.");
599 unsafe { intrinsics::abort() }
600 }
601 PANICKING.with(|s| s.set(true));
602 rust_panic(msg);
603}
604
605/// Register a callback to be invoked when a thread unwinds.
606///
607/// This is an unsafe and experimental API which allows for an arbitrary
608/// callback to be invoked when a thread panics. This callback is invoked on both
609/// the initial unwinding and a double unwinding if one occurs. Additionally,
610/// the local `Task` will be in place for the duration of the callback, and
611/// the callback must ensure that it remains in place once the callback returns.
612///
613/// Only a limited number of callbacks can be registered, and this function
614/// returns whether the callback was successfully registered or not. It is not
615/// currently possible to unregister a callback once it has been registered.
1a4d82fc
JJ
616pub unsafe fn register(f: Callback) -> bool {
617 match CALLBACK_CNT.fetch_add(1, Ordering::SeqCst) {
618 // The invocation code has knowledge of this window where the count has
619 // been incremented, but the callback has not been stored. We're
620 // guaranteed that the slot we're storing into is 0.
621 n if n < MAX_CALLBACKS => {
622 let prev = CALLBACKS[n].swap(mem::transmute(f), Ordering::SeqCst);
623 rtassert!(prev == 0);
624 true
625 }
626 // If we accidentally bumped the count too high, pull it back.
627 _ => {
628 CALLBACK_CNT.store(MAX_CALLBACKS, Ordering::SeqCst);
629 false
630 }
631 }
632}