Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Print a message on OOM #30801

Merged
merged 2 commits into from
Jan 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions src/liballoc/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
#![feature(unsize)]
#![feature(drop_in_place)]
#![feature(fn_traits)]
#![feature(const_fn)]

#![feature(needs_allocator)]

Expand Down Expand Up @@ -127,15 +128,6 @@ mod boxed_test;
pub mod arc;
pub mod rc;
pub mod raw_vec;
pub mod oom;

/// Common out-of-memory routine
#[cold]
#[inline(never)]
#[unstable(feature = "oom", reason = "not a scrutinized interface",
issue = "27700")]
pub fn oom() -> ! {
// FIXME(#14674): This really needs to do something other than just abort
// here, but any printing done must be *guaranteed* to not
// allocate.
unsafe { core::intrinsics::abort() }
}
pub use oom::oom;
42 changes: 42 additions & 0 deletions src/liballoc/oom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use core::sync::atomic::{AtomicPtr, Ordering};
use core::mem;
use core::intrinsics;

static OOM_HANDLER: AtomicPtr<()> = AtomicPtr::new(default_oom_handler as *mut ());

fn default_oom_handler() -> ! {
// The default handler can't do much more since we can't assume the presence
// of libc or any way of printing an error message.
unsafe { intrinsics::abort() }
}

/// Common out-of-memory routine
#[cold]
#[inline(never)]
#[unstable(feature = "oom", reason = "not a scrutinized interface",
issue = "27700")]
pub fn oom() -> ! {
let value = OOM_HANDLER.load(Ordering::SeqCst);
let handler: fn() -> ! = unsafe { mem::transmute(value) };
handler();
}

/// Set a custom handler for out-of-memory conditions
///
/// To avoid recursive OOM failures, it is critical that the OOM handler does
/// not allocate any memory itself.
#[unstable(feature = "oom", reason = "not a scrutinized interface",
issue = "27700")]
pub fn set_oom_handler(handler: fn() -> !) {
OOM_HANDLER.store(handler as *mut (), Ordering::SeqCst);
}
4 changes: 2 additions & 2 deletions src/libstd/panicking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) {
// debugger provides a useable stacktrace.
if panics >= 3 {
util::dumb_print(format_args!("thread panicked while processing \
panic. aborting."));
panic. aborting.\n"));
unsafe { intrinsics::abort() }
}

Expand All @@ -232,7 +232,7 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) {
// just abort. In the future we may consider resuming
// unwinding or otherwise exiting the thread cleanly.
util::dumb_print(format_args!("thread panicked while panicking. \
aborting."));
aborting.\n"));
unsafe { intrinsics::abort() }
}
}
4 changes: 2 additions & 2 deletions src/libstd/sys/common/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ pub fn dumb_print(args: fmt::Arguments) {
}

pub fn abort(args: fmt::Arguments) -> ! {
dumb_print(format_args!("fatal runtime error: {}", args));
dumb_print(format_args!("fatal runtime error: {}\n", args));
unsafe { intrinsics::abort(); }
}

#[allow(dead_code)] // stack overflow detection not enabled on all platforms
pub unsafe fn report_overflow() {
dumb_print(format_args!("\nthread '{}' has overflowed its stack",
dumb_print(format_args!("\nthread '{}' has overflowed its stack\n",
thread::current().name().unwrap_or("<unknown>")));
}
23 changes: 22 additions & 1 deletion src/libstd/sys/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use io::{self, ErrorKind};
use libc;
use num::One;
use ops::Neg;
use alloc::oom;

#[cfg(target_os = "android")] pub use os::android as platform;
#[cfg(target_os = "bitrig")] pub use os::bitrig as platform;
Expand Down Expand Up @@ -45,6 +46,22 @@ pub mod thread_local;
pub mod time;
pub mod stdio;

// A nicer handler for out-of-memory situations than the default one. This one
// prints a message to stderr before aborting. It is critical that this code
// does not allocate any memory since we are in an OOM situation. Any errors are
// ignored while printing since there's nothing we can do about them and we are
// about to exit anyways.
fn oom_handler() -> ! {
use intrinsics;
let msg = "fatal runtime error: out of memory\n";
unsafe {
libc::write(libc::STDERR_FILENO,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rtabort!?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See @alexcrichton's comment:

I'm a little hesitant about this as it's very tough to ensure there are 0 allocations along the way in the oom handler unless it's basically written from scratch. I would almost prefer that the handler in libstd is written in such a way that all the code is self-contained to be easily inspected for "this doesn't allocate". It'll be a little uglier, but much easier to audit and future-proof from accidental modifications

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool.

msg.as_ptr() as *const libc::c_void,
msg.len() as libc::size_t);
intrinsics::abort();
}
}

#[cfg(not(any(target_os = "nacl", test)))]
pub fn init() {
use libc::signal;
Expand All @@ -58,10 +75,14 @@ pub fn init() {
unsafe {
assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != !0);
}

oom::set_oom_handler(oom_handler);
}

#[cfg(all(target_os = "nacl", not(test)))]
pub fn init() { }
pub fn init() {
oom::set_oom_handler(oom_handler);
}

pub fn decode_error_kind(errno: i32) -> ErrorKind {
match errno as libc::c_int {
Expand Down
22 changes: 21 additions & 1 deletion src/libstd/sys/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use num::Zero;
use os::windows::ffi::{OsStrExt, OsStringExt};
use path::PathBuf;
use time::Duration;
use alloc::oom;

#[macro_use] pub mod compat;

Expand All @@ -42,7 +43,26 @@ pub mod thread_local;
pub mod time;
pub mod stdio;

pub fn init() {}
// See comment in sys/unix/mod.rs
fn oom_handler() -> ! {
use intrinsics;
use ptr;
let msg = "fatal runtime error: out of memory\n";
unsafe {
// WriteFile silently fails if it is passed an invalid handle, so there
// is no need to check the result of GetStdHandle.
c::WriteFile(c::GetStdHandle(c::STD_ERROR_HANDLE),
msg.as_ptr() as c::LPVOID,
msg.len() as c::DWORD,
ptr::null_mut(),
ptr::null_mut());
intrinsics::abort();
}
}

pub fn init() {
oom::set_oom_handler(oom_handler);
}

pub fn decode_error_kind(errno: i32) -> ErrorKind {
match errno as c::DWORD {
Expand Down