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