diff --git a/text/0000-global-allocators.md b/text/0000-global-allocators.md index beae4339b56..e50ea59b037 100644 --- a/text/0000-global-allocators.md +++ b/text/0000-global-allocators.md @@ -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. /// @@ -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())); @@ -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 @@ -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() { @@ -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::()`. + +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