Skip to content

Commit

Permalink
Move OOM handling to liballoc and remove the oom lang item
Browse files Browse the repository at this point in the history
This removes the need for `no_std` programs to define that lang item themselves:

Fixes rust-lang#51540
  • Loading branch information
SimonSapin committed Jun 27, 2018
1 parent 68dfaed commit 7c13f64
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 66 deletions.
89 changes: 84 additions & 5 deletions src/liballoc/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
use core::intrinsics::{min_align_of_val, size_of_val};
use core::ptr::{NonNull, Unique};
use core::usize;
#[cfg(target_has_atomic = "ptr")] use core::{mem, ptr};
#[cfg(target_has_atomic = "ptr")] use core::sync::atomic::{AtomicPtr, Ordering};

#[stable(feature = "alloc_module", since = "1.28.0")]
#[doc(inline)]
Expand Down Expand Up @@ -191,12 +193,89 @@ pub(crate) unsafe fn box_free<T: ?Sized>(ptr: Unique<T>) {
#[stable(feature = "global_alloc", since = "1.28.0")]
#[rustc_allocator_nounwind]
pub fn handle_alloc_error(layout: Layout) -> ! {
#[allow(improper_ctypes)]
extern "Rust" {
#[lang = "oom"]
fn oom_impl(layout: Layout) -> !;
#[cfg(target_has_atomic = "ptr")]
{
let hook = HOOK.load(Ordering::SeqCst);
let hook: fn(Layout) = if hook.is_null() {
default_alloc_error_hook
} else {
unsafe { mem::transmute(hook) }
};
hook(layout);
}
#[cfg(not(target_has_atomic = "ptr"))]
{
default_alloc_error_hook(layout)
}

unsafe {
#[cfg(not(stage0))]
{
#[allow(improper_ctypes)]
extern "Rust" {
// This function is generated by the compiler
// and calls __rust_abort_internal in src/libstd/alloc.rs
// if the `std` crate is linked.
fn __rust_maybe_abort_internal();
}
__rust_maybe_abort_internal();
}
// The above does nothing and returns when `std` is not linked.
// In that case we abort "naively" here:
::core::intrinsics::abort()
}
}

fn default_alloc_error_hook(layout: Layout) {
#[cfg(stage0)]
let _ = layout;
#[cfg(not(stage0))]
unsafe {
#[allow(improper_ctypes)]
extern "Rust" {
// This function is generated by the compiler
// and calls __rust_default_alloc_error_hook in src/libstd/alloc.rs
// if the `std` crate is linked.
fn __rust_maybe_default_alloc_error_hook(size: usize, align: usize);
}
__rust_maybe_default_alloc_error_hook(layout.size(), layout.align())
}
}

#[cfg(target_has_atomic = "ptr")]
static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());

/// Registers a custom allocation error hook, replacing any that was previously registered.
///
/// The allocation error hook is invoked when an infallible memory allocation fails, before
/// the runtime aborts. The default hook prints a message to standard error,
/// but this behavior can be customized with the [`set_alloc_error_hook`] and
/// [`take_alloc_error_hook`] functions.
///
/// The hook is provided with a `Layout` struct which contains information
/// about the allocation that failed.
///
/// The allocation error hook is a global resource.
#[unstable(feature = "alloc_error_hook", issue = "51245")]
#[cfg(target_has_atomic = "ptr")]
pub fn set_alloc_error_hook(hook: fn(Layout)) {
HOOK.store(hook as *mut (), Ordering::SeqCst);
}

/// Unregisters the current allocation error hook, returning it.
///
/// *See also the function [`set_alloc_error_hook`].*
///
/// If no custom hook is registered, the default hook will be returned.
#[unstable(feature = "alloc_error_hook", issue = "51245")]
#[cfg(target_has_atomic = "ptr")]
pub fn take_alloc_error_hook() -> fn(Layout) {
let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst);
if hook.is_null() {
default_alloc_error_hook
} else {
unsafe { mem::transmute(hook) }
}
unsafe { oom_impl(layout) }
}

#[cfg(test)]
Expand Down
1 change: 0 additions & 1 deletion src/librustc/middle/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,6 @@ language_item_table! {
ExchangeMallocFnLangItem, "exchange_malloc", exchange_malloc_fn;
BoxFreeFnLangItem, "box_free", box_free_fn;
DropInPlaceFnLangItem, "drop_in_place", drop_in_place_fn;
OomLangItem, "oom", oom;

StartFnLangItem, "start", start_fn;

Expand Down
1 change: 0 additions & 1 deletion src/librustc/middle/weak_lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,5 +151,4 @@ weak_lang_items! {
panic_impl, PanicImplLangItem, rust_begin_unwind;
eh_personality, EhPersonalityLangItem, rust_eh_personality;
eh_unwind_resume, EhUnwindResumeLangItem, rust_eh_unwind_resume;
oom, OomLangItem, rust_oom;
}
70 changes: 14 additions & 56 deletions src/libstd/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,6 @@
#![stable(feature = "alloc_module", since = "1.28.0")]

use core::sync::atomic::{AtomicPtr, Ordering};
use core::{mem, ptr};
use sys_common::util::dumb_print;

#[stable(feature = "alloc_module", since = "1.28.0")]
#[doc(inline)]
pub use alloc_crate::alloc::*;
Expand All @@ -86,64 +82,14 @@ pub use alloc_crate::alloc::*;
#[doc(inline)]
pub use alloc_system::System;

static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut());

/// Registers a custom allocation error hook, replacing any that was previously registered.
///
/// The allocation error hook is invoked when an infallible memory allocation fails, before
/// the runtime aborts. The default hook prints a message to standard error,
/// but this behavior can be customized with the [`set_alloc_error_hook`] and
/// [`take_alloc_error_hook`] functions.
///
/// The hook is provided with a `Layout` struct which contains information
/// about the allocation that failed.
///
/// The allocation error hook is a global resource.
#[unstable(feature = "alloc_error_hook", issue = "51245")]
pub fn set_alloc_error_hook(hook: fn(Layout)) {
HOOK.store(hook as *mut (), Ordering::SeqCst);
}

/// Unregisters the current allocation error hook, returning it.
///
/// *See also the function [`set_alloc_error_hook`].*
///
/// If no custom hook is registered, the default hook will be returned.
#[unstable(feature = "alloc_error_hook", issue = "51245")]
pub fn take_alloc_error_hook() -> fn(Layout) {
let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst);
if hook.is_null() {
default_alloc_error_hook
} else {
unsafe { mem::transmute(hook) }
}
}

fn default_alloc_error_hook(layout: Layout) {
dumb_print(format_args!("memory allocation of {} bytes failed", layout.size()));
}

#[cfg(not(test))]
#[doc(hidden)]
#[lang = "oom"]
#[unstable(feature = "alloc_internals", issue = "0")]
pub extern fn rust_oom(layout: Layout) -> ! {
let hook = HOOK.load(Ordering::SeqCst);
let hook: fn(Layout) = if hook.is_null() {
default_alloc_error_hook
} else {
unsafe { mem::transmute(hook) }
};
hook(layout);
unsafe { ::sys::abort_internal(); }
}

#[cfg(not(test))]
#[doc(hidden)]
#[allow(unused_attributes)]
#[unstable(feature = "alloc_internals", issue = "0")]
pub mod __default_lib_allocator {
use super::{System, Layout, GlobalAlloc};
use sys_common::util::dumb_print;

// for symbol names src/librustc/middle/allocator.rs
// for signatures src/librustc_allocator/lib.rs

Expand Down Expand Up @@ -181,4 +127,16 @@ pub mod __default_lib_allocator {
let layout = Layout::from_size_align_unchecked(size, align);
System.alloc_zeroed(layout)
}

#[no_mangle]
#[rustc_std_internal_symbol]
pub extern fn __rust_default_alloc_error_hook(size: usize, _align: usize) {
dumb_print(format_args!("memory allocation of {} bytes failed", size));
}

#[no_mangle]
#[rustc_std_internal_symbol]
pub unsafe extern fn __rust_abort_internal() {
::sys::abort_internal()
}
}
3 changes: 0 additions & 3 deletions src/test/ui/missing-allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,4 @@ fn panic(_: &core::panic::PanicInfo) -> ! {
loop {}
}

#[lang = "oom"]
fn oom() {}

extern crate alloc;

0 comments on commit 7c13f64

Please sign in to comment.