xref: /relibc/ralloc/src/fail.rs (revision 5a42b783d46b59c625e781942fbd5bf048a4ac89)
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