1 //! General error handling. 2 3 use prelude::*; 4 5 use core::sync::atomic::{self, AtomicPtr}; 6 use core::mem; 7 8 use shim::config; 9 10 #[cfg(feature = "tls")] 11 use tls; 12 13 /// The global OOM handler. 14 static OOM_HANDLER: AtomicPtr<()> = AtomicPtr::new(config::default_oom_handler as *mut ()); 15 #[cfg(feature = "tls")] 16 tls! { 17 /// The thread-local OOM handler. 18 static THREAD_OOM_HANDLER: MoveCell<Option<fn() -> !>> = MoveCell::new(None); 19 } 20 21 /// Call the OOM handler. 22 /// 23 /// This is used one out-of-memory errors, and will never return. Usually, it simply consists 24 /// of aborting the process. 25 /// 26 /// # An important note 27 /// 28 /// This is for OOM-conditions, not malformed or too big allocations, but when the system is unable 29 /// to gather memory for the allocation (SBRK fails). 30 /// 31 /// The rule of thumb is that this should be called, if and only if unwinding (which allocates) 32 /// will hit the same error. 33 pub fn oom() -> ! { 34 // If TLS is enabled, we will use the thread-local OOM. 35 #[cfg(feature = "tls")] 36 { 37 if let Some(handler) = THREAD_OOM_HANDLER.with(|x| x.replace(None)) { 38 log!(DEBUG, "Calling the local OOM handler."); 39 40 handler(); 41 } 42 } 43 44 log!(DEBUG, "Calling the global OOM handler."); 45 46 unsafe { 47 // LAST AUDIT: 2016-08-21 (Ticki). 48 49 // Transmute the atomic pointer to a function pointer and call it. 50 (mem::transmute::<_, fn() -> !>(OOM_HANDLER.load(atomic::Ordering::SeqCst)))() 51 } 52 } 53 54 /// Set the OOM handler. 55 /// 56 /// This is called when the process is out-of-memory. 57 #[inline] 58 pub fn set_oom_handler(handler: fn() -> !) { 59 // Logging... 60 log!(NOTE, "Setting the global OOM handler."); 61 62 OOM_HANDLER.store(handler as *mut (), atomic::Ordering::SeqCst); 63 } 64 65 /// Override the OOM handler for the current thread. 66 /// 67 /// # Panics 68 /// 69 /// This might panic if a thread OOM handler already exists. 70 #[inline] 71 #[cfg(feature = "tls")] 72 pub fn set_thread_oom_handler(handler: fn() -> !) { 73 // Logging... 74 log!(NOTE, "Setting the thread OOM handler."); 75 76 THREAD_OOM_HANDLER.with(|thread_oom| { 77 // Replace it with the new handler. 78 let res = thread_oom.replace(Some(handler)); 79 80 // Throw a warning if it overrides another handler. 81 if res.is_some() { 82 log!(WARNING, "An old thread OOM handler was overriden."); 83 } 84 }); 85 } 86 87 #[cfg(test)] 88 mod test { 89 use super::*; 90 91 #[test] 92 #[should_panic] 93 fn test_panic_oom() { 94 fn panic() -> ! { 95 panic!("cats are not cute."); 96 } 97 98 set_oom_handler(panic); 99 oom(); 100 } 101 102 #[test] 103 #[should_panic] 104 #[cfg(feature = "tls")] 105 fn test_panic_thread_oom() { 106 fn infinite() -> ! { 107 #[allow(empty_loop)] 108 loop {} 109 } 110 fn panic() -> ! { 111 panic!("cats are not cute."); 112 } 113 114 set_oom_handler(infinite); 115 set_thread_oom_handler(panic); 116 oom(); 117 } 118 } 119