use prelude::*;
use core::sync::atomic::{self, AtomicPtr};
use core::mem;
use shim::config;
#[cfg(feature = "tls")]
use tls;
static OOM_HANDLER: AtomicPtr<()> = AtomicPtr::new(config::default_oom_handler as *mut ());
#[cfg(feature = "tls")]
tls! {
static THREAD_OOM_HANDLER: MoveCell<Option<fn() -> !>> = MoveCell::new(None);
}
pub fn 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 {
(mem::transmute::<_, fn() -> !>(OOM_HANDLER.load(atomic::Ordering::SeqCst)))()
}
}
#[inline]
pub fn set_oom_handler(handler: fn() -> !) {
log!(NOTE, "Setting the global OOM handler.");
OOM_HANDLER.store(handler as *mut (), atomic::Ordering::SeqCst);
}
#[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| {
let res = thread_oom.replace(Some(handler));
if res.is_some() {
log!(WARNING, "An old thread OOM handler was overriden.");
}
});
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[should_panic]
fn test_panic_oom() {
fn panic() -> ! {
panic!("cats are not cute.");
}
set_oom_handler(panic);
oom();
}
#[test]
#[should_panic]
#[cfg(feature = "tls")]
fn test_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();
}
}