]>
Commit | Line | Data |
---|---|---|
b039eaaf SL |
1 | % Custom Allocators |
2 | ||
3 | Allocating memory isn't always the easiest thing to do, and while Rust generally | |
4 | takes care of this by default it often becomes necessary to customize how | |
5 | allocation occurs. The compiler and standard library currently allow switching | |
6 | out the default global allocator in use at compile time. The design is currently | |
7 | spelled out in [RFC 1183][rfc] but this will walk you through how to get your | |
8 | own allocator up and running. | |
9 | ||
10 | [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1183-swap-out-jemalloc.md | |
11 | ||
12 | # Default Allocator | |
13 | ||
14 | The compiler currently ships two default allocators: `alloc_system` and | |
15 | `alloc_jemalloc` (some targets don't have jemalloc, however). These allocators | |
9cc50fc6 | 16 | are normal Rust crates and contain an implementation of the routines to |
b039eaaf SL |
17 | allocate and deallocate memory. The standard library is not compiled assuming |
18 | either one, and the compiler will decide which allocator is in use at | |
19 | compile-time depending on the type of output artifact being produced. | |
20 | ||
21 | Binaries generated by the compiler will use `alloc_jemalloc` by default (where | |
22 | available). In this situation the compiler "controls the world" in the sense of | |
23 | it has power over the final link. Primarily this means that the allocator | |
24 | decision can be left up the compiler. | |
25 | ||
26 | Dynamic and static libraries, however, will use `alloc_system` by default. Here | |
27 | Rust is typically a 'guest' in another application or another world where it | |
28 | cannot authoritatively decide what allocator is in use. As a result it resorts | |
29 | back to the standard APIs (e.g. `malloc` and `free`) for acquiring and releasing | |
30 | memory. | |
31 | ||
32 | # Switching Allocators | |
33 | ||
34 | Although the compiler's default choices may work most of the time, it's often | |
35 | necessary to tweak certain aspects. Overriding the compiler's decision about | |
36 | which allocator is in use is done simply by linking to the desired allocator: | |
37 | ||
38 | ```rust,no_run | |
39 | #![feature(alloc_system)] | |
40 | ||
41 | extern crate alloc_system; | |
42 | ||
43 | fn main() { | |
44 | let a = Box::new(4); // allocates from the system allocator | |
45 | println!("{}", a); | |
46 | } | |
47 | ``` | |
48 | ||
49 | In this example the binary generated will not link to jemalloc by default but | |
50 | instead use the system allocator. Conversely to generate a dynamic library which | |
51 | uses jemalloc by default one would write: | |
52 | ||
53 | ```rust,ignore | |
54 | #![feature(alloc_jemalloc)] | |
55 | #![crate_type = "dylib"] | |
56 | ||
57 | extern crate alloc_jemalloc; | |
58 | ||
59 | pub fn foo() { | |
60 | let a = Box::new(4); // allocates from jemalloc | |
61 | println!("{}", a); | |
62 | } | |
63 | # fn main() {} | |
64 | ``` | |
65 | ||
66 | # Writing a custom allocator | |
67 | ||
68 | Sometimes even the choices of jemalloc vs the system allocator aren't enough and | |
69 | an entirely new custom allocator is required. In this you'll write your own | |
70 | crate which implements the allocator API (e.g. the same as `alloc_system` or | |
71 | `alloc_jemalloc`). As an example, let's take a look at a simplified and | |
72 | annotated version of `alloc_system` | |
73 | ||
74 | ```rust,no_run | |
75 | # // only needed for rustdoc --test down below | |
76 | # #![feature(lang_items)] | |
77 | // The compiler needs to be instructed that this crate is an allocator in order | |
78 | // to realize that when this is linked in another allocator like jemalloc should | |
79 | // not be linked in | |
80 | #![feature(allocator)] | |
81 | #![allocator] | |
82 | ||
83 | // Allocators are not allowed to depend on the standard library which in turn | |
84 | // requires an allocator in order to avoid circular dependencies. This crate, | |
85 | // however, can use all of libcore. | |
b039eaaf SL |
86 | #![no_std] |
87 | ||
88 | // Let's give a unique name to our custom allocator | |
89 | #![crate_name = "my_allocator"] | |
90 | #![crate_type = "rlib"] | |
91 | ||
92 | // Our system allocator will use the in-tree libc crate for FFI bindings. Note | |
93 | // that currently the external (crates.io) libc cannot be used because it links | |
94 | // to the standard library (e.g. `#![no_std]` isn't stable yet), so that's why | |
95 | // this specifically requires the in-tree version. | |
96 | #![feature(libc)] | |
97 | extern crate libc; | |
98 | ||
99 | // Listed below are the five allocation functions currently required by custom | |
100 | // allocators. Their signatures and symbol names are not currently typechecked | |
101 | // by the compiler, but this is a future extension and are required to match | |
102 | // what is found below. | |
103 | // | |
104 | // Note that the standard `malloc` and `realloc` functions do not provide a way | |
105 | // to communicate alignment so this implementation would need to be improved | |
106 | // with respect to alignment in that aspect. | |
107 | ||
108 | #[no_mangle] | |
109 | pub extern fn __rust_allocate(size: usize, _align: usize) -> *mut u8 { | |
110 | unsafe { libc::malloc(size as libc::size_t) as *mut u8 } | |
111 | } | |
112 | ||
113 | #[no_mangle] | |
114 | pub extern fn __rust_deallocate(ptr: *mut u8, _old_size: usize, _align: usize) { | |
115 | unsafe { libc::free(ptr as *mut libc::c_void) } | |
116 | } | |
117 | ||
118 | #[no_mangle] | |
119 | pub extern fn __rust_reallocate(ptr: *mut u8, _old_size: usize, size: usize, | |
120 | _align: usize) -> *mut u8 { | |
121 | unsafe { | |
122 | libc::realloc(ptr as *mut libc::c_void, size as libc::size_t) as *mut u8 | |
123 | } | |
124 | } | |
125 | ||
126 | #[no_mangle] | |
127 | pub extern fn __rust_reallocate_inplace(_ptr: *mut u8, old_size: usize, | |
128 | _size: usize, _align: usize) -> usize { | |
129 | old_size // this api is not supported by libc | |
130 | } | |
131 | ||
132 | #[no_mangle] | |
133 | pub extern fn __rust_usable_size(size: usize, _align: usize) -> usize { | |
134 | size | |
135 | } | |
136 | ||
9cc50fc6 | 137 | # // only needed to get rustdoc to test this |
b039eaaf SL |
138 | # fn main() {} |
139 | # #[lang = "panic_fmt"] fn panic_fmt() {} | |
140 | # #[lang = "eh_personality"] fn eh_personality() {} | |
141 | # #[lang = "eh_unwind_resume"] extern fn eh_unwind_resume() {} | |
92a42be0 SL |
142 | # #[no_mangle] pub extern fn rust_eh_register_frames () {} |
143 | # #[no_mangle] pub extern fn rust_eh_unregister_frames () {} | |
b039eaaf SL |
144 | ``` |
145 | ||
146 | After we compile this crate, it can be used as follows: | |
147 | ||
148 | ```rust,ignore | |
149 | extern crate my_allocator; | |
150 | ||
151 | fn main() { | |
152 | let a = Box::new(8); // allocates memory via our custom allocator crate | |
153 | println!("{}", a); | |
154 | } | |
155 | ``` | |
156 | ||
157 | # Custom allocator limitations | |
158 | ||
159 | There are a few restrictions when working with custom allocators which may cause | |
160 | compiler errors: | |
161 | ||
162 | * Any one artifact may only be linked to at most one allocator. Binaries, | |
163 | dylibs, and staticlibs must link to exactly one allocator, and if none have | |
164 | been explicitly chosen the compiler will choose one. On the other hand rlibs | |
165 | do not need to link to an allocator (but still can). | |
166 | ||
167 | * A consumer of an allocator is tagged with `#![needs_allocator]` (e.g. the | |
168 | `liballoc` crate currently) and an `#[allocator]` crate cannot transitively | |
169 | depend on a crate which needs an allocator (e.g. circular dependencies are not | |
170 | allowed). This basically means that allocators must restrict themselves to | |
171 | libcore currently. |