Skip to content

Commit

Permalink
Auto merge of #42727 - alexcrichton:allocators-new, r=eddyb
Browse files Browse the repository at this point in the history
rustc: Implement the #[global_allocator] attribute

This PR is an implementation of [RFC 1974] which specifies a new method of
defining a global allocator for a program. This obsoletes the old
`#![allocator]` attribute and also removes support for it.

[RFC 1974]: rust-lang/rfcs#1974

The new `#[global_allocator]` attribute solves many issues encountered with the
`#![allocator]` attribute such as composition and restrictions on the crate
graph itself. The compiler now has much more control over the ABI of the
allocator and how it's implemented, allowing much more freedom in terms of how
this feature is implemented.

cc #27389
  • Loading branch information
bors committed Jul 6, 2017
2 parents 4d526e0 + 695dee0 commit 1685c92
Show file tree
Hide file tree
Showing 115 changed files with 2,828 additions and 1,169 deletions.
18 changes: 18 additions & 0 deletions src/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# `allocator_internals`

This feature does not have a tracking issue, it is an unstable implementation
detail of the `global_allocator` feature not intended for use outside the
compiler.

------------------------
119 changes: 0 additions & 119 deletions src/doc/unstable-book/src/language-features/allocator.md

This file was deleted.

71 changes: 71 additions & 0 deletions src/doc/unstable-book/src/language-features/global-allocator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# `global_allocator`

The tracking issue for this feature is: [#27389]

[#27389]: https://github.com/rust-lang/rust/issues/27389

------------------------

Rust programs may need to change the allocator that they're running with from
time to time. This use case is distinct from an allocator-per-collection (e.g. a
`Vec` with a custom allocator) and instead is more related to changing the
global default allocator, e.g. what `Vec<T>` uses by default.

Currently Rust programs don't have a specified global allocator. The compiler
may link to a version of [jemalloc] on some platforms, but this is not
guaranteed. Libraries, however, like cdylibs and staticlibs are guaranteed
to use the "system allocator" which means something like `malloc` on Unixes and
`HeapAlloc` on Windows.

[jemalloc]: https://github.com/jemalloc/jemalloc

The `#[global_allocator]` attribute, however, allows configuring this choice.
You can use this to implement a completely custom global allocator to route all
default allocation requests to a custom object. Defined in [RFC 1974] usage
looks like:

[RFC 1974]: https://github.com/rust-lang/rfcs/pull/1974

```rust
#![feature(global_allocator, heap_api)]

use std::heap::{Alloc, System, Layout, AllocErr};

struct MyAllocator;

unsafe impl<'a> Alloc for &'a MyAllocator {
unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> {
System.alloc(layout)
}

unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) {
System.dealloc(ptr, layout)
}
}

#[global_allocator]
static GLOBAL: MyAllocator = MyAllocator;

fn main() {
// This `Vec` will allocate memory through `GLOBAL` above
let mut v = Vec::new();
v.push(1);
}
```

And that's it! The `#[global_allocator]` attribute is applied to a `static`
which implements the `Alloc` trait in the `std::heap` module. Note, though,
that the implementation is defined for `&MyAllocator`, not just `MyAllocator`.
You may wish, however, to also provide `Alloc for MyAllocator` for other use
cases.

A crate can only have one instance of `#[global_allocator]` and this instance
may be loaded through a dependency. For example `#[global_allocator]` above
could have been placed in one of the dependencies loaded through `extern crate`.

Note that `Alloc` itself is an `unsafe` trait, with much documentation on the
trait itself about usage and for implementors. Extra care should be taken when
implementing a global allocator as well as the allocator may be called from many
portions of the standard library, such as the panicking routine. As a result it
is highly recommended to not panic during allocation and work in as many
situations with as few dependencies as possible as well.
23 changes: 21 additions & 2 deletions src/liballoc/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
slightly, especially to possibly take into account the \
types being stored to make room for a future \
tracing garbage collector",
issue = "27700")]
issue = "32838")]

use core::cmp;
use core::fmt;
Expand Down Expand Up @@ -73,6 +73,7 @@ impl Layout {
/// * `size`, when rounded up to the nearest multiple of `align`,
/// must not overflow (i.e. the rounded value must be less than
/// `usize::MAX`).
#[inline]
pub fn from_size_align(size: usize, align: usize) -> Option<Layout> {
if !align.is_power_of_two() {
return None;
Expand All @@ -96,13 +97,28 @@ impl Layout {
return None;
}

Some(Layout { size: size, align: align })
unsafe {
Some(Layout::from_size_align_unchecked(size, align))
}
}

/// Creates a layout, bypassing all checks.
///
/// # Unsafety
///
/// This function is unsafe as it does not verify that `align` is a power of
/// two nor that `size` aligned to `align` fits within the address space.
#[inline]
pub unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Layout {
Layout { size: size, align: align }
}

/// The minimum size in bytes for a memory block of this layout.
#[inline]
pub fn size(&self) -> usize { self.size }

/// The minimum byte alignment for a memory block of this layout.
#[inline]
pub fn align(&self) -> usize { self.align }

/// Constructs a `Layout` suitable for holding a value of type `T`.
Expand Down Expand Up @@ -135,6 +151,7 @@ impl Layout {
///
/// Panics if the combination of `self.size` and the given `align`
/// violates the conditions listed in `from_size_align`.
#[inline]
pub fn align_to(&self, align: usize) -> Self {
Layout::from_size_align(self.size, cmp::max(self.align, align)).unwrap()
}
Expand All @@ -155,6 +172,7 @@ impl Layout {
/// to be less than or equal to the alignment of the starting
/// address for the whole allocated block of memory. One way to
/// satisfy this constraint is to ensure `align <= self.align`.
#[inline]
pub fn padding_needed_for(&self, align: usize) -> usize {
let len = self.size();

Expand Down Expand Up @@ -556,6 +574,7 @@ pub unsafe trait Alloc {
/// However, for clients that do not wish to track the capacity
/// returned by `alloc_excess` locally, this method is likely to
/// produce useful results.
#[inline]
fn usable_size(&self, layout: &Layout) -> (usize, usize) {
(layout.size(), layout.size())
}
Expand Down
10 changes: 6 additions & 4 deletions src/liballoc/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst};
use core::borrow;
use core::fmt;
use core::cmp::Ordering;
use core::mem::{align_of_val, size_of_val};
use core::intrinsics::abort;
use core::mem;
use core::mem::uninitialized;
Expand All @@ -34,7 +33,8 @@ use core::marker::Unsize;
use core::hash::{Hash, Hasher};
use core::{isize, usize};
use core::convert::From;
use heap::deallocate;

use heap::{Heap, Alloc, Layout};

/// A soft limit on the amount of references that may be made to an `Arc`.
///
Expand Down Expand Up @@ -503,7 +503,7 @@ impl<T: ?Sized> Arc<T> {

if self.inner().weak.fetch_sub(1, Release) == 1 {
atomic::fence(Acquire);
deallocate(ptr as *mut u8, size_of_val(&*ptr), align_of_val(&*ptr))
Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr))
}
}

Expand Down Expand Up @@ -1007,7 +1007,9 @@ impl<T: ?Sized> Drop for Weak<T> {
// ref, which can only happen after the lock is released.
if self.inner().weak.fetch_sub(1, Release) == 1 {
atomic::fence(Acquire);
unsafe { deallocate(ptr as *mut u8, size_of_val(&*ptr), align_of_val(&*ptr)) }
unsafe {
Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr))
}
}
}
}
Expand Down
Loading

0 comments on commit 1685c92

Please sign in to comment.