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

rustc: Implement the #[global_allocator] attribute #42727

Merged
merged 1 commit into from
Jul 6, 2017
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
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