Skip to content

Commit

Permalink
Merge pull request #2 from alexcrichton/more-allocators
Browse files Browse the repository at this point in the history
Updates and refinements to various apis and guarantees
  • Loading branch information
sfackler authored May 23, 2017
2 parents 770abea + d80313c commit c405fd9
Showing 1 changed file with 93 additions and 20 deletions.
113 changes: 93 additions & 20 deletions text/0000-global-allocators.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,39 @@ trait described in [RFC 1398][], but is stripped down and the methods take
[RFC 1398]: https://github.com/rust-lang/rfcs/blob/master/text/1398-kinds-of-allocators.md

```rust

/// A trait implemented by objects that can be global allocators.
///
/// Instances of this trait can be used to back allocations done through the
/// `std::heap` API. This trait represents the fundamental ability to allocate
/// memory in Rust.
///
/// To use a global allocator you'll need to use the `#[global_allocator]`
/// attribute like so:
///
/// ```
/// extern crate my_allocator;
///
/// #[global_allocator]
/// static ALLOCATOR: MyAllocator = my_allocator::INIT;
///
/// fn main() {
/// let _b = Box::new(2); // uses `MyAllocator` above
/// }
/// ```
///
/// # Unsafety
///
/// This trait is an `unsafe` trait as there are a number of guarantees a global
/// allocator must adhere to which aren't expressible through the type system.
/// First and foremost types that implement this trait must behave like, well,
/// allocators! All pointers returned from `allocate` that are active in a
/// program (disregarding those `deallocate`d) must point to disjoint chunks of
/// memory. In other words, allocations need to be distinct and can't overlap.
///
/// Additionally it must be safe to allocate a chunk of memory on any thread of
/// a program and then deallocate it on any other thread of the program.
#[lang = "global_allocator"]
pub unsafe trait GlobalAllocator: Send + Sync {
pub unsafe trait GlobalAllocator: Send + Sync + 'static {
/// Returns a pointer to a newly allocated region of memory suitable for the
/// provided `Layout`. The contents of the memory are undefined.
///
Expand Down Expand Up @@ -110,7 +140,7 @@ pub unsafe trait GlobalAllocator: Send + Sync {
///
/// The pointer must correspond to a region of memory previously allocated
/// by this allocator with the provided layout.
pub fn reallocate(&self, ptr: *mut u8, old_layout: Layout, layout: Layout) -> *mut u8 {
pub unsafe fn reallocate(&self, ptr: *mut u8, old_layout: Layout, layout: Layout) -> *mut u8 {
let new_ptr = self.alloc(layout);
if !new_ptr.is_null() {
ptr::copy_nonoverlapping(ptr, new_ptr, cmp::min(old_layout.size(), layout.size()));
Expand All @@ -128,6 +158,12 @@ trait: `usable_size` which is used nowhere in the standard library, and
A global allocator is a type implementing `GlobalAllocator` which can be
constructed in a constant expression.

Note that the precise type signatures used here are a little up for debate. It's
expected that they will be settled (along with accompanying documentation as to
guarantees) as part of stabilization in [rust-lang/rust#27700][stab-issue].

[stab-issue]: https://github.com/rust-lang/rust/issues/27700

## Using an allocator

While the `GlobalAllocator` trait can be used like any other, the most common
Expand All @@ -148,21 +184,21 @@ pub unsafe fn deallocate(&self, ptr: *mut u8, layout: Layout) {
...
}

pub fn reallocate(ptr: *mut u8, old_layout: Layout, layout: Layout) -> *mut u8 {
pub unsafe fn reallocate(ptr: *mut u8, old_layout: Layout, layout: Layout) -> *mut u8 {
...
}
```

Each of these functions simply delegates to the selected global allocator. The
allocator is selected by tagging a static value of a type implementing
`GlobalAllocator` with the `#[allocator]` annotation:
`GlobalAllocator` with the `#[global_allocator]` annotation:

```rust
extern crate my_allocator;

use my_allocator::{MyAllocator, MY_ALLOCATOR_INIT};

#[allocator]
#[global_allocator]
static ALLOCATOR: MyAllocator = MY_ALLOCATOR_INIT;

fn main() {
Expand All @@ -175,23 +211,60 @@ other static would bed.

## Standard library

A small `alloc_api` crate will be created which will contain the `Layout` type.
The initial API will be more conservative than that described in [RFC 1398][],
possibly nothing more than a `from_size_align` constructor and accessors for
`size` and `align`.
A `core::heap` module will be added with the `Layout` type and the
`GlobalAllocator` trait. The initial API of `Layout` will be more conservative
than that described in [RFC 1398][], possibly nothing more than a
`from_size_align` constructor and accessors for `size` and `align`. It is
intended that the API will grow over time with conveniences such as
`Layout::new::<T>()`.

The `alloc::heap` module will reexport these types from `core::heap`. It will
also provide top-level functions (like `allocate` above) which do not take an
allocator but instead are routed through the global allocator. The `alloc`
crate, however, will not provide a global allocator. Instead the compiler will
understand that crates which transitively depend on `alloc` will require an
allocator, with a per-target fallback default allocator used by the compiler.

The standard library will gain a new stable crate - `alloc_system`. This is the
default allocator crate and corresponds to the "system" allocator (i.e. `malloc`
etc on Unix and `HeapAlloc` etc on Windows).
The standard library will grow a `std::heap` module that reexports the contents
of `alloc::heap`. Additionally it will contain a system allocator definition:

The `alloc::heap` module will be reexported in `std` and stabilized. It will
simply contain functions matching directly to those defined by the allocator
API. The `alloc` crate itself may also be stabilized at a later date, but this
RFC does not propose that. `Layout` will be reexported in the `heap` module.
```rust
pub struct SystemAllocator;

impl GlobalAllocator for SystemAllocator {
// ...
}
```

The `SystemAllocator` is defined as having zero size, no fields, and always
referring to the OS allocator (i.e. `malloc` etc on Unix and `HeapAlloc` etc on
Windows).

The existing `alloc_system` and `alloc_jemalloc` crates will likely be
deprecated and eventually removed. The `alloc_system` crate is replaced with the
`SystemAllocator` structure in the standard library and the `alloc_jemalloc`
crate will become available on crates.io. The `alloc_jemalloc` crate will likely
look like:

```rust
pub struct Jemalloc;

impl GlobalAllocator for Jemalloc {
// ...
}
```

The existing `alloc_jemalloc` may continue to exist as an implementation detail
of the Rust compiler, but it will never be stabilized. Applications wishing to
use jemalloc can use a third-party crate from crates.io.
It is not proposed in this RFC to switch the per-platform default allocator just
yet. Assuming everything goes smoothly, however, it will likely be defined as
`SystemAllocator` as platforms transition away from jemalloc-by-default once the
jemalloc-from-crates.io is stable and usable.

The compiler will also no longer forbid cyclic the cyclic dependency between a
crate defining an implementation of an allocator and the `alloc` crate itself.
As a vestige of the current implementation this is only to get around linkage
errors where the liballoc rlib references symbols defined in the "allocator
crate". With this RFC the compiler has far more control over the ABI and linkage
here, so this restriction is no longer necessary.

# How We Teach This
[how-we-teach-this]: #how-we-teach-this
Expand Down

0 comments on commit c405fd9

Please sign in to comment.