123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- //! General error handling.
- use prelude::*;
- use core::sync::atomic::{self, AtomicPtr};
- use core::mem;
- use shim::config;
- #[cfg(feature = "tls")]
- use tls;
- /// The global OOM handler.
- static OOM_HANDLER: AtomicPtr<()> = AtomicPtr::new(config::default_oom_handler as *mut ());
- #[cfg(feature = "tls")]
- tls! {
- /// The thread-local OOM handler.
- static THREAD_OOM_HANDLER: MoveCell<Option<fn() -> !>> = MoveCell::new(None);
- }
- /// Call the OOM handler.
- ///
- /// This is used one out-of-memory errors, and will never return. Usually, it simply consists
- /// of aborting the process.
- ///
- /// # An important note
- ///
- /// This is for OOM-conditions, not malformed or too big allocations, but when the system is unable
- /// to gather memory for the allocation (SBRK fails).
- ///
- /// The rule of thumb is that this should be called, if and only if unwinding (which allocates)
- /// will hit the same error.
- pub fn oom() -> ! {
- // If TLS is enabled, we will use the thread-local OOM.
- #[cfg(feature = "tls")]
- {
- if let Some(handler) = THREAD_OOM_HANDLER.with(|x| x.replace(None)) {
- log!(DEBUG, "Calling the local OOM handler.");
- handler();
- }
- }
- log!(DEBUG, "Calling the global OOM handler.");
- unsafe {
- // LAST AUDIT: 2016-08-21 (Ticki).
- // Transmute the atomic pointer to a function pointer and call it.
- (mem::transmute::<_, fn() -> !>(OOM_HANDLER.load(atomic::Ordering::SeqCst)))()
- }
- }
- /// Set the OOM handler.
- ///
- /// This is called when the process is out-of-memory.
- #[inline]
- pub fn set_oom_handler(handler: fn() -> !) {
- log!(NOTE, "Setting the global OOM handler.");
- OOM_HANDLER.store(handler as *mut (), atomic::Ordering::SeqCst);
- }
- /// Override the OOM handler for the current thread.
- ///
- /// # Panics
- ///
- /// This might panic if a thread OOM handler already exists.
- #[inline]
- #[cfg(feature = "tls")]
- pub fn set_thread_oom_handler(handler: fn() -> !) {
- log!(NOTE, "Setting the thread OOM handler.");
- THREAD_OOM_HANDLER.with(|thread_oom| {
- // Replace it with the new handler.
- let old = thread_oom.replace(Some(handler));
- // Throw a warning if it overrides another handler.
- if old.is_some() {
- log!(WARNING, "An old thread OOM handler was overriden.");
- }
- });
- }
- #[cfg(test)]
- mod test {
- use super::*;
- #[test]
- #[should_panic]
- fn panic_oom() {
- fn panic() -> ! {
- panic!("cats are not cute.");
- }
- set_oom_handler(panic);
- oom();
- }
- #[test]
- #[should_panic]
- #[cfg(feature = "tls")]
- fn panic_thread_oom() {
- fn infinite() -> ! {
- #[allow(empty_loop)]
- loop {}
- }
- fn panic() -> ! {
- panic!("cats are not cute.");
- }
- set_oom_handler(infinite);
- set_thread_oom_handler(panic);
- oom();
- }
- }
|