heap.rs 10.3 KB
Newer Older
D
Daniel Micay 已提交
1 2 3 4 5 6 7 8 9 10
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

D
Daniel Micay 已提交
11
// FIXME: #13994: port to the sized deallocation API when available
12 13
// FIXME: #13996: mark the `allocate` and `reallocate` return value as `noalias`
//                and `nonnull`
D
Daniel Micay 已提交
14

15 16
#[cfg(not(test))] use core::raw;
#[cfg(not(test))] use util;
D
Daniel Micay 已提交
17 18 19

/// Return a pointer to `size` bytes of memory.
///
20 21 22
/// Behavior is undefined if the requested size is 0 or the alignment is not a
/// power of 2. The alignment must be no larger than the largest supported page
/// size on the platform.
D
Daniel Micay 已提交
23 24
#[inline]
pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
A
Alex Crichton 已提交
25
    imp::allocate(size, align)
D
Daniel Micay 已提交
26 27
}

28 29
/// Extend or shrink the allocation referenced by `ptr` to `size` bytes of
/// memory.
D
Daniel Micay 已提交
30
///
31 32 33
/// Behavior is undefined if the requested size is 0 or the alignment is not a
/// power of 2. The alignment must be no larger than the largest supported page
/// size on the platform.
D
Daniel Micay 已提交
34
///
35 36 37
/// The `old_size` and `align` parameters are the parameters that were used to
/// create the allocation referenced by `ptr`. The `old_size` parameter may also
/// be the value returned by `usable_size` for the requested size.
D
Daniel Micay 已提交
38
#[inline]
39 40
pub unsafe fn reallocate(ptr: *mut u8, size: uint, align: uint,
                         old_size: uint) -> *mut u8 {
A
Alex Crichton 已提交
41
    imp::reallocate(ptr, size, align, old_size)
D
Daniel Micay 已提交
42 43
}

44 45
/// Extend or shrink the allocation referenced by `ptr` to `size` bytes of
/// memory in-place.
D
Daniel Micay 已提交
46
///
47 48
/// Return true if successful, otherwise false if the allocation was not
/// altered.
D
Daniel Micay 已提交
49
///
50 51 52
/// Behavior is undefined if the requested size is 0 or the alignment is not a
/// power of 2. The alignment must be no larger than the largest supported page
/// size on the platform.
D
Daniel Micay 已提交
53 54 55 56 57
///
/// The `old_size` and `align` parameters are the parameters that were used to
/// create the allocation referenced by `ptr`. The `old_size` parameter may be
/// any value in range_inclusive(requested_size, usable_size).
#[inline]
58 59
pub unsafe fn reallocate_inplace(ptr: *mut u8, size: uint, align: uint,
                                 old_size: uint) -> bool {
A
Alex Crichton 已提交
60
    imp::reallocate_inplace(ptr, size, align, old_size)
D
Daniel Micay 已提交
61 62 63 64 65 66
}

/// Deallocate the memory referenced by `ptr`.
///
/// The `ptr` parameter must not be null.
///
67 68 69
/// The `size` and `align` parameters are the parameters that were used to
/// create the allocation referenced by `ptr`. The `size` parameter may also be
/// the value returned by `usable_size` for the requested size.
D
Daniel Micay 已提交
70 71
#[inline]
pub unsafe fn deallocate(ptr: *mut u8, size: uint, align: uint) {
A
Alex Crichton 已提交
72
    imp::deallocate(ptr, size, align)
D
Daniel Micay 已提交
73 74
}

75 76
/// Return the usable size of an allocation created with the specified the
/// `size` and `align`.
D
Daniel Micay 已提交
77 78
#[inline]
pub fn usable_size(size: uint, align: uint) -> uint {
A
Alex Crichton 已提交
79
    imp::usable_size(size, align)
D
Daniel Micay 已提交
80
}
81

82 83
/// Print implementation-defined allocator statistics.
///
84 85
/// These statistics may be inconsistent if other threads use the allocator
/// during the call.
86 87
#[unstable]
pub fn stats_print() {
A
Alex Crichton 已提交
88
    imp::stats_print();
89 90
}

91 92 93 94 95
// The compiler never calls `exchange_free` on ~ZeroSizeType, so zero-size
// allocations can point to this `static`. It would be incorrect to use a null
// pointer, due to enums assuming types like unique pointers are never null.
pub static mut EMPTY: uint = 12345;

96
/// The allocator for unique pointers.
D
Daniel Micay 已提交
97
#[cfg(not(test))]
98 99
#[lang="exchange_malloc"]
#[inline]
100
unsafe fn exchange_malloc(size: uint, align: uint) -> *mut u8 {
101
    if size == 0 {
102
        &EMPTY as *uint as *mut u8
103 104 105 106 107
    } else {
        allocate(size, align)
    }
}

A
Alex Crichton 已提交
108
#[cfg(not(test))]
109 110 111 112 113 114
#[lang="exchange_free"]
#[inline]
unsafe fn exchange_free(ptr: *mut u8, size: uint, align: uint) {
    deallocate(ptr, size, align);
}

115 116 117 118
// FIXME: #7496
#[cfg(not(test))]
#[lang="closure_exchange_malloc"]
#[inline]
119
#[allow(deprecated)]
120 121
unsafe fn closure_exchange_malloc(drop_glue: fn(*mut u8), size: uint,
                                  align: uint) -> *mut u8 {
122
    let total_size = util::get_box_size(size, align);
123 124
    let p = allocate(total_size, 8);

125
    let alloc = p as *mut raw::Box<()>;
126 127 128 129 130
    (*alloc).drop_glue = drop_glue;

    alloc as *mut u8
}

A
Alex Crichton 已提交
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
#[cfg(jemalloc)]
mod imp {
    use core::option::{None, Option};
    use core::ptr::{RawPtr, mut_null, null};
    use core::num::Bitwise;
    use libc::{c_char, c_int, c_void, size_t};

    #[link(name = "jemalloc", kind = "static")]
    extern {
        fn je_mallocx(size: size_t, flags: c_int) -> *mut c_void;
        fn je_rallocx(ptr: *mut c_void, size: size_t,
                      flags: c_int) -> *mut c_void;
        fn je_xallocx(ptr: *mut c_void, size: size_t, extra: size_t,
                      flags: c_int) -> size_t;
        fn je_dallocx(ptr: *mut c_void, flags: c_int);
        fn je_nallocx(size: size_t, flags: c_int) -> size_t;
        fn je_malloc_stats_print(write_cb: Option<extern "C" fn(cbopaque: *mut c_void, *c_char)>,
                                 cbopaque: *mut c_void,
                                 opts: *c_char);
    }

    // -lpthread needs to occur after -ljemalloc, the earlier argument isn't enough
    #[cfg(not(windows), not(target_os = "android"))]
    #[link(name = "pthread")]
    extern {}

    // MALLOCX_ALIGN(a) macro
    #[inline(always)]
    fn mallocx_align(a: uint) -> c_int { a.trailing_zeros() as c_int }

    #[inline]
    pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
        let ptr = je_mallocx(size as size_t, mallocx_align(align)) as *mut u8;
        if ptr.is_null() {
165
            ::oom()
A
Alex Crichton 已提交
166 167 168 169 170 171 172 173 174 175
        }
        ptr
    }

    #[inline]
    pub unsafe fn reallocate(ptr: *mut u8, size: uint, align: uint,
                             _old_size: uint) -> *mut u8 {
        let ptr = je_rallocx(ptr as *mut c_void, size as size_t,
                             mallocx_align(align)) as *mut u8;
        if ptr.is_null() {
176
            ::oom()
A
Alex Crichton 已提交
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
        }
        ptr
    }

    #[inline]
    pub unsafe fn reallocate_inplace(ptr: *mut u8, size: uint, align: uint,
                                     _old_size: uint) -> bool {
        je_xallocx(ptr as *mut c_void, size as size_t, 0,
                   mallocx_align(align)) == size as size_t
    }

    #[inline]
    pub unsafe fn deallocate(ptr: *mut u8, _size: uint, align: uint) {
        je_dallocx(ptr as *mut c_void, mallocx_align(align))
    }

    #[inline]
    pub fn usable_size(size: uint, align: uint) -> uint {
        unsafe { je_nallocx(size as size_t, mallocx_align(align)) as uint }
    }

    pub fn stats_print() {
        unsafe {
            je_malloc_stats_print(None, mut_null(), null())
        }
    }
}

#[cfg(not(jemalloc), unix)]
mod imp {
    use core::mem;
    use core::ptr;
    use libc;
    use libc_heap;

    extern {
        fn posix_memalign(memptr: *mut *mut libc::c_void,
                          align: libc::size_t,
                          size: libc::size_t) -> libc::c_int;
    }

    #[inline]
    pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
        // The posix_memalign manpage states
        //
        //      alignment [...] must be a power of and a multiple of
        //      sizeof(void *)
        //
        // The `align` parameter to this function is the *minimum* alignment for
        // a block of memory, so we special case everything under `*uint` to
        // just pass it to malloc, which is guaranteed to align to at least the
        // size of `*uint`.
        if align < mem::size_of::<*uint>() {
            libc_heap::malloc_raw(size)
        } else {
            let mut out = 0 as *mut libc::c_void;
            let ret = posix_memalign(&mut out,
                                     align as libc::size_t,
                                     size as libc::size_t);
            if ret != 0 {
                ::oom();
            }
            out as *mut u8
        }
    }

    #[inline]
    pub unsafe fn reallocate(ptr: *mut u8, size: uint, align: uint,
                             old_size: uint) -> *mut u8 {
        let new_ptr = allocate(size, align);
        ptr::copy_memory(new_ptr, ptr as *u8, old_size);
        deallocate(ptr, old_size, align);
        return new_ptr;
    }

    #[inline]
    pub unsafe fn reallocate_inplace(_ptr: *mut u8, _size: uint, _align: uint,
                                     _old_size: uint) -> bool {
        false
    }

    #[inline]
    pub unsafe fn deallocate(ptr: *mut u8, _size: uint, _align: uint) {
        libc::free(ptr as *mut libc::c_void)
    }

    #[inline]
    pub fn usable_size(size: uint, _align: uint) -> uint {
        size
    }

    pub fn stats_print() {
    }
}

#[cfg(not(jemalloc), windows)]
mod imp {
    use libc::{c_void, size_t};
    use core::ptr::RawPtr;

    extern {
        fn _aligned_malloc(size: size_t, align: size_t) -> *mut c_void;
        fn _aligned_realloc(block: *mut c_void, size: size_t,
                            align: size_t) -> *mut c_void;
        fn _aligned_free(ptr: *mut c_void);
    }

    #[inline]
    pub unsafe fn allocate(size: uint, align: uint) -> *mut u8 {
        let ptr = _aligned_malloc(size as size_t, align as size_t);
        if ptr.is_null() {
            ::oom();
        }
        ptr as *mut u8
    }

    #[inline]
    pub unsafe fn reallocate(ptr: *mut u8, size: uint, align: uint,
                             _old_size: uint) -> *mut u8 {
        let ptr = _aligned_realloc(ptr as *mut c_void, size as size_t,
                                   align as size_t);
        if ptr.is_null() {
            ::oom();
        }
        ptr as *mut u8
    }

    #[inline]
    pub unsafe fn reallocate_inplace(_ptr: *mut u8, _size: uint, _align: uint,
                                     _old_size: uint) -> bool {
        false
    }

    #[inline]
    pub unsafe fn deallocate(ptr: *mut u8, _size: uint, _align: uint) {
        _aligned_free(ptr as *mut c_void)
    }

    #[inline]
    pub fn usable_size(size: uint, _align: uint) -> uint {
        size
    }

    pub fn stats_print() {}
}

323 324 325 326 327 328 329 330 331 332 333 334
#[cfg(test)]
mod bench {
    extern crate test;
    use self::test::Bencher;

    #[bench]
    fn alloc_owned_small(b: &mut Bencher) {
        b.iter(|| {
            box 10
        })
    }
}