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