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