diff options
| author | bors <bors@rust-lang.org> | 2014-05-10 23:21:44 -0700 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2014-05-10 23:21:44 -0700 |
| commit | adb8b0b230d5e5c79b4f873825b3d3cff8d1bc8f (patch) | |
| tree | 770b50ee78ec3b79feeaf70f73f50f1166bed980 /src/libstd | |
| parent | 11571cd9c1cde63c3b46ca65e608b84647785ac8 (diff) | |
| parent | 81fadbbc4182c5a34e0d2ff698471abfc1ec0e33 (diff) | |
| download | rust-adb8b0b230d5e5c79b4f873825b3d3cff8d1bc8f.tar.gz rust-adb8b0b230d5e5c79b4f873825b3d3cff8d1bc8f.zip | |
auto merge of #14006 : thestinger/rust/jemalloc, r=alexcrichton
Closes #11807
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/c_str.rs | 2 | ||||
| -rw-r--r-- | src/libstd/c_vec.rs | 2 | ||||
| -rw-r--r-- | src/libstd/lib.rs | 1 | ||||
| -rw-r--r-- | src/libstd/rc.rs | 9 | ||||
| -rw-r--r-- | src/libstd/rt/global_heap.rs | 139 | ||||
| -rw-r--r-- | src/libstd/rt/heap.rs | 222 | ||||
| -rw-r--r-- | src/libstd/rt/libc_heap.rs | 51 | ||||
| -rw-r--r-- | src/libstd/rt/local_heap.rs | 12 | ||||
| -rw-r--r-- | src/libstd/rt/mod.rs | 11 | ||||
| -rw-r--r-- | src/libstd/rt/util.rs | 17 | ||||
| -rw-r--r-- | src/libstd/slice.rs | 60 | ||||
| -rw-r--r-- | src/libstd/unstable/mutex.rs | 2 | ||||
| -rw-r--r-- | src/libstd/vec.rs | 89 |
13 files changed, 429 insertions, 188 deletions
diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs index 7de74dbe507..b33d211aa19 100644 --- a/src/libstd/c_str.rs +++ b/src/libstd/c_str.rs @@ -81,7 +81,7 @@ use str::StrSlice; use str; use slice::{ImmutableVector, MutableVector}; use slice; -use rt::global_heap::malloc_raw; +use rt::libc_heap::malloc_raw; use raw::Slice; /// The representation of a C String. diff --git a/src/libstd/c_vec.rs b/src/libstd/c_vec.rs index 4ef5af9275c..8c2c4fd1f0b 100644 --- a/src/libstd/c_vec.rs +++ b/src/libstd/c_vec.rs @@ -160,7 +160,7 @@ mod tests { use super::CVec; use libc; use ptr; - use rt::global_heap::malloc_raw; + use rt::libc_heap::malloc_raw; fn malloc(n: uint) -> CVec<u8> { unsafe { diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 0ac6f1dba4f..3f22a76c1f4 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -110,6 +110,7 @@ // Don't link to std. We are std. #![no_std] +#![allow(deprecated)] #![deny(missing_doc)] // When testing libstd, bring in libuv as the I/O backend so tests can print diff --git a/src/libstd/rc.rs b/src/libstd/rc.rs index 51ab885a85f..e0fe75fd907 100644 --- a/src/libstd/rc.rs +++ b/src/libstd/rc.rs @@ -32,7 +32,8 @@ use ops::{Deref, Drop}; use option::{Option, Some, None}; use ptr; use ptr::RawPtr; -use rt::global_heap::exchange_free; +use mem::{min_align_of, size_of}; +use rt::heap::exchange_free; struct RcBox<T> { value: T, @@ -104,7 +105,8 @@ impl<T> Drop for Rc<T> { self.dec_weak(); if self.weak() == 0 { - exchange_free(self.ptr as *u8) + exchange_free(self.ptr as *mut u8, size_of::<RcBox<T>>(), + min_align_of::<RcBox<T>>()) } } } @@ -177,7 +179,8 @@ impl<T> Drop for Weak<T> { // the weak count starts at 1, and will only go to // zero if all the strong pointers have disappeared. if self.weak() == 0 { - exchange_free(self.ptr as *u8) + exchange_free(self.ptr as *mut u8, size_of::<RcBox<T>>(), + min_align_of::<RcBox<T>>()) } } } diff --git a/src/libstd/rt/global_heap.rs b/src/libstd/rt/global_heap.rs deleted file mode 100644 index 7d54c3faf42..00000000000 --- a/src/libstd/rt/global_heap.rs +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2012 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. - - -//! The global (exchange) heap. - -use libc::{c_void, size_t, free, malloc, realloc}; -use ptr::{RawPtr, mut_null}; -use intrinsics::abort; -use raw; -use mem::size_of; - -#[inline] -pub fn get_box_size(body_size: uint, body_align: uint) -> uint { - let header_size = size_of::<raw::Box<()>>(); - let total_size = align_to(header_size, body_align) + body_size; - total_size -} - -// Rounds |size| to the nearest |alignment|. Invariant: |alignment| is a power -// of two. -#[inline] -fn align_to(size: uint, align: uint) -> uint { - assert!(align != 0); - (size + align - 1) & !(align - 1) -} - -/// A wrapper around libc::malloc, aborting on out-of-memory -#[inline] -pub unsafe fn malloc_raw(size: uint) -> *mut u8 { - // `malloc(0)` may allocate, but it may also return a null pointer - // http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html - if size == 0 { - mut_null() - } else { - let p = malloc(size as size_t); - if p.is_null() { - // we need a non-allocating way to print an error here - abort(); - } - p as *mut u8 - } -} - -/// A wrapper around libc::realloc, aborting on out-of-memory -#[inline] -pub unsafe fn realloc_raw(ptr: *mut u8, size: uint) -> *mut u8 { - // `realloc(ptr, 0)` may allocate, but it may also return a null pointer - // http://pubs.opengroup.org/onlinepubs/9699919799/functions/realloc.html - if size == 0 { - free(ptr as *mut c_void); - mut_null() - } else { - let p = realloc(ptr as *mut c_void, size as size_t); - if p.is_null() { - // we need a non-allocating way to print an error here - abort(); - } - p as *mut u8 - } -} - -/// The allocator for unique pointers without contained managed pointers. -#[cfg(not(test))] -#[lang="exchange_malloc"] -#[inline] -pub unsafe fn exchange_malloc(size: uint) -> *mut u8 { - // The compiler never calls `exchange_free` on Box<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. - static EMPTY: () = (); - - if size == 0 { - &EMPTY as *() as *mut u8 - } else { - malloc_raw(size) - } -} - -// FIXME: #7496 -#[cfg(not(test))] -#[lang="closure_exchange_malloc"] -#[inline] -pub unsafe fn closure_exchange_malloc_(drop_glue: fn(*mut u8), size: uint, align: uint) -> *u8 { - closure_exchange_malloc(drop_glue, size, align) -} - -#[inline] -pub unsafe fn closure_exchange_malloc(drop_glue: fn(*mut u8), size: uint, align: uint) -> *u8 { - let total_size = get_box_size(size, align); - let p = malloc_raw(total_size); - - let alloc = p as *mut raw::Box<()>; - (*alloc).drop_glue = drop_glue; - - alloc as *u8 -} - -// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from -// inside a landing pad may corrupt the state of the exception handler. -#[cfg(not(test))] -#[lang="exchange_free"] -#[inline] -pub unsafe fn exchange_free_(ptr: *u8) { - exchange_free(ptr) -} - -#[inline] -pub unsafe fn exchange_free(ptr: *u8) { - free(ptr as *mut c_void); -} - -#[cfg(test)] -mod bench { - extern crate test; - use self::test::Bencher; - - #[bench] - fn alloc_owned_small(b: &mut Bencher) { - b.iter(|| { - box 10 - }) - } - - #[bench] - fn alloc_owned_big(b: &mut Bencher) { - b.iter(|| { - box [10, ..1000] - }) - } -} diff --git a/src/libstd/rt/heap.rs b/src/libstd/rt/heap.rs new file mode 100644 index 00000000000..ffe6dccfa15 --- /dev/null +++ b/src/libstd/rt/heap.rs @@ -0,0 +1,222 @@ +// 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. + +// FIXME: #13994: port to the sized deallocation API when available +// FIXME: #13996: need a way to mark the `allocate` and `reallocate` return values as `noalias` + +use intrinsics::{abort, cttz32}; +use libc::{c_int, c_void, size_t}; +use ptr::RawPtr; + +#[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; +} + +// -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 { unsafe { cttz32(a as u32) as c_int } } + +/// Return a pointer to `size` bytes of memory. +/// +/// 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. +#[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() { + abort() + } + ptr +} + +/// Extend or shrink the allocation referenced by `ptr` to `size` bytes of memory. +/// +/// 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. +/// +/// 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. +#[inline] +#[allow(unused_variable)] // for the parameter names in the documentation +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() { + abort() + } + ptr +} + +/// Extend or shrink the allocation referenced by `ptr` to `size` bytes of memory in-place. +/// +/// Return true if successful, otherwise false if the allocation was not altered. +/// +/// 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. +/// +/// 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] +#[allow(unused_variable)] // for the parameter names in the documentation +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 +} + +/// Deallocate the memory referenced by `ptr`. +/// +/// The `ptr` parameter must not be null. +/// +/// 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. +#[inline] +#[allow(unused_variable)] // for the parameter names in the documentation +pub unsafe fn deallocate(ptr: *mut u8, size: uint, align: uint) { + je_dallocx(ptr as *mut c_void, mallocx_align(align)) +} + +/// Return the usable size of an allocation created with the specified the `size` and `align`. +#[inline] +pub fn usable_size(size: uint, align: uint) -> uint { + unsafe { je_nallocx(size as size_t, mallocx_align(align)) as uint } +} + +/// The allocator for unique pointers. +#[cfg(stage0)] +#[lang="exchange_malloc"] +#[inline(always)] +pub unsafe fn exchange_malloc_(size: uint) -> *mut u8 { + exchange_malloc(size) +} + +/// The allocator for unique pointers. +#[cfg(not(test), not(stage0))] +#[lang="exchange_malloc"] +#[inline(always)] +pub unsafe fn exchange_malloc_(size: uint, align: uint) -> *mut u8 { + exchange_malloc(size, align) +} + +/// The allocator for unique pointers. +#[cfg(stage0)] +#[inline] +pub unsafe fn exchange_malloc(size: uint) -> *mut u8 { + // 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. + static EMPTY: () = (); + + if size == 0 { + &EMPTY as *() as *mut u8 + } else { + allocate(size, 8) + } +} + +/// The allocator for unique pointers. +#[cfg(not(stage0))] +#[inline] +pub unsafe fn exchange_malloc(size: uint, align: uint) -> *mut u8 { + // 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. + static EMPTY: () = (); + + if size == 0 { + &EMPTY as *() as *mut u8 + } else { + allocate(size, align) + } +} + +#[cfg(not(test))] +#[lang="exchange_free"] +#[inline] +// FIXME: #13994 (rustc should pass align and size here) +pub unsafe fn exchange_free_(ptr: *mut u8) { + exchange_free(ptr, 0, 8) +} + +#[inline] +pub unsafe fn exchange_free(ptr: *mut u8, size: uint, align: uint) { + deallocate(ptr, size, align); +} + +// FIXME: #7496 +#[cfg(not(test))] +#[lang="closure_exchange_malloc"] +#[inline] +unsafe fn closure_exchange_malloc(drop_glue: fn(*mut u8), size: uint, align: uint) -> *mut u8 { + let total_size = ::rt::util::get_box_size(size, align); + let p = allocate(total_size, 8); + + let alloc = p as *mut ::raw::Box<()>; + (*alloc).drop_glue = drop_glue; + + alloc as *mut u8 +} + +// hack for libcore +#[no_mangle] +#[doc(hidden)] +#[deprecated] +#[cfg(stage0, not(test))] +pub extern "C" fn rust_malloc(size: uint) -> *mut u8 { + unsafe { exchange_malloc(size) } +} + +// hack for libcore +#[no_mangle] +#[doc(hidden)] +#[deprecated] +#[cfg(not(stage0), not(test))] +pub extern "C" fn rust_malloc(size: uint, align: uint) -> *mut u8 { + unsafe { exchange_malloc(size, align) } +} + +// hack for libcore +#[no_mangle] +#[doc(hidden)] +#[deprecated] +#[cfg(not(test))] +pub extern "C" fn rust_free(ptr: *mut u8, size: uint, align: uint) { + unsafe { exchange_free(ptr, size, align) } +} + +#[cfg(test)] +mod bench { + extern crate test; + use self::test::Bencher; + + #[bench] + fn alloc_owned_small(b: &mut Bencher) { + b.iter(|| { + box 10 + }) + } + + #[bench] + fn alloc_owned_big(b: &mut Bencher) { + b.iter(|| { + box [10, ..1000] + }) + } +} diff --git a/src/libstd/rt/libc_heap.rs b/src/libstd/rt/libc_heap.rs new file mode 100644 index 00000000000..ece51ab9989 --- /dev/null +++ b/src/libstd/rt/libc_heap.rs @@ -0,0 +1,51 @@ +// Copyright 2012 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. + + +//! The global (exchange) heap. + +use libc::{c_void, size_t, free, malloc, realloc}; +use ptr::{RawPtr, mut_null}; +use intrinsics::abort; + +/// A wrapper around libc::malloc, aborting on out-of-memory +#[inline] +pub unsafe fn malloc_raw(size: uint) -> *mut u8 { + // `malloc(0)` may allocate, but it may also return a null pointer + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/malloc.html + if size == 0 { + mut_null() + } else { + let p = malloc(size as size_t); + if p.is_null() { + // we need a non-allocating way to print an error here + abort(); + } + p as *mut u8 + } +} + +/// A wrapper around libc::realloc, aborting on out-of-memory +#[inline] +pub unsafe fn realloc_raw(ptr: *mut u8, size: uint) -> *mut u8 { + // `realloc(ptr, 0)` may allocate, but it may also return a null pointer + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/realloc.html + if size == 0 { + free(ptr as *mut c_void); + mut_null() + } else { + let p = realloc(ptr as *mut c_void, size as size_t); + if p.is_null() { + // we need a non-allocating way to print an error here + abort(); + } + p as *mut u8 + } +} diff --git a/src/libstd/rt/local_heap.rs b/src/libstd/rt/local_heap.rs index caf0d9028c5..efc8072594b 100644 --- a/src/libstd/rt/local_heap.rs +++ b/src/libstd/rt/local_heap.rs @@ -12,12 +12,13 @@ use cast; use iter::Iterator; +use libc::{c_void, free}; use mem; use ops::Drop; use option::{Option, None, Some}; use ptr; use ptr::RawPtr; -use rt::global_heap; +use rt::libc_heap; use rt::local::Local; use rt::task::Task; use raw; @@ -58,7 +59,7 @@ impl LocalHeap { #[inline] pub fn alloc(&mut self, drop_glue: fn(*mut u8), size: uint, align: uint) -> *mut Box { - let total_size = global_heap::get_box_size(size, align); + let total_size = ::rt::util::get_box_size(size, align); let alloc = self.memory_region.malloc(total_size); { // Make sure that we can't use `mybox` outside of this scope @@ -187,7 +188,7 @@ impl MemoryRegion { fn malloc(&mut self, size: uint) -> *mut Box { let total_size = size + AllocHeader::size(); let alloc: *AllocHeader = unsafe { - global_heap::malloc_raw(total_size) as *AllocHeader + libc_heap::malloc_raw(total_size) as *AllocHeader }; let alloc: &mut AllocHeader = unsafe { cast::transmute(alloc) }; @@ -206,8 +207,7 @@ impl MemoryRegion { let total_size = size + AllocHeader::size(); let alloc: *AllocHeader = unsafe { - global_heap::realloc_raw(orig_alloc as *mut u8, - total_size) as *AllocHeader + libc_heap::realloc_raw(orig_alloc as *mut u8, total_size) as *AllocHeader }; let alloc: &mut AllocHeader = unsafe { cast::transmute(alloc) }; @@ -226,7 +226,7 @@ impl MemoryRegion { self.release(cast::transmute(alloc)); rtassert!(self.live_allocations > 0); self.live_allocations -= 1; - global_heap::exchange_free(alloc as *u8) + free(alloc as *mut c_void) } } diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 5b9c314d42b..a04cbabedd6 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -26,7 +26,7 @@ language and an implementation must be provided regardless of the execution environment. Of foremost importance is the global exchange heap, in the module -`global_heap`. Very little practical Rust code can be written without +`heap`. Very little practical Rust code can be written without access to the global heap. Unlike most of `rt` the global heap is truly a global resource and generally operates independently of the rest of the runtime. @@ -86,10 +86,13 @@ pub mod shouldnt_be_public { // Internal macros used by the runtime. mod macros; -// The global (exchange) heap. -pub mod global_heap; +/// Wrappers around malloc / realloc aborting on out-of-memory. +pub mod libc_heap; -// Implementations of language-critical runtime features like @. +/// The low-level memory allocation API. +pub mod heap; + +/// Implementations of language-critical runtime features like @. pub mod task; // The EventLoop and internal synchronous I/O interface. diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index 84284ca1faf..e8b1acb1024 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -26,6 +26,23 @@ use slice::ImmutableVector; // FIXME: Once the runtime matures remove the `true` below to turn off rtassert, etc. pub static ENFORCE_SANITY: bool = true || !cfg!(rtopt) || cfg!(rtdebug) || cfg!(rtassert); +#[deprecated] +#[doc(hidden)] +#[inline] +pub fn get_box_size(body_size: uint, body_align: uint) -> uint { + let header_size = ::mem::size_of::<::raw::Box<()>>(); + let total_size = align_to(header_size, body_align) + body_size; + total_size +} + +// Rounds |size| to the nearest |alignment|. Invariant: |alignment| is a power +// of two. +#[inline] +fn align_to(size: uint, align: uint) -> uint { + assert!(align != 0); + (size + align - 1) & !(align - 1) +} + /// Get the number of cores available pub fn num_cpus() -> uint { unsafe { diff --git a/src/libstd/slice.rs b/src/libstd/slice.rs index 21084407b8d..b97c55ad701 100644 --- a/src/libstd/slice.rs +++ b/src/libstd/slice.rs @@ -110,7 +110,7 @@ use ops::Drop; use option::{None, Option, Some}; use ptr::RawPtr; use ptr; -use rt::global_heap::{exchange_free}; +use rt::heap::{exchange_malloc, exchange_free}; use unstable::finally::try_finally; use vec::Vec; @@ -292,9 +292,9 @@ pub trait CloneableVector<T> { impl<'a, T: Clone> CloneableVector<T> for &'a [T] { /// Returns a copy of `v`. #[inline] + #[cfg(stage0)] fn to_owned(&self) -> ~[T] { use RawVec = core::raw::Vec; - use rt::global_heap::{malloc_raw, exchange_free}; use num::{CheckedAdd, CheckedMul}; use option::Expect; @@ -305,7 +305,8 @@ impl<'a, T: Clone> CloneableVector<T> for &'a [T] { let size = size.expect("overflow in to_owned()"); unsafe { - let ret = malloc_raw(size) as *mut RawVec<()>; + // this should pass the real required alignment + let ret = exchange_malloc(size) as *mut RawVec<()>; (*ret).fill = len * mem::nonzero_size_of::<T>(); (*ret).alloc = len * mem::nonzero_size_of::<T>(); @@ -329,7 +330,55 @@ impl<'a, T: Clone> CloneableVector<T> for &'a [T] { for j in range(0, *i as int) { ptr::read(&*p.offset(j)); } - exchange_free(ret as *u8); + // FIXME: #13994 (should pass align and size here) + exchange_free(ret as *mut u8, 0, 8); + }); + cast::transmute(ret) + } + } + + /// Returns a copy of `v`. + #[inline] + #[cfg(not(stage0))] + fn to_owned(&self) -> ~[T] { + use RawVec = core::raw::Vec; + use num::{CheckedAdd, CheckedMul}; + use option::Expect; + + let len = self.len(); + let data_size = len.checked_mul(&mem::size_of::<T>()); + let data_size = data_size.expect("overflow in to_owned()"); + let size = mem::size_of::<RawVec<()>>().checked_add(&data_size); + let size = size.expect("overflow in to_owned()"); + + unsafe { + // this should pass the real required alignment + let ret = exchange_malloc(size, 8) as *mut RawVec<()>; + + (*ret).fill = len * mem::nonzero_size_of::<T>(); + (*ret).alloc = len * mem::nonzero_size_of::<T>(); + + // Be careful with the following loop. We want it to be optimized + // to a memcpy (or something similarly fast) when T is Copy. LLVM + // is easily confused, so any extra operations during the loop can + // prevent this optimization. + let mut i = 0; + let p = &mut (*ret).data as *mut _ as *mut T; + try_finally( + &mut i, (), + |i, ()| while *i < len { + mem::move_val_init( + &mut(*p.offset(*i as int)), + self.unsafe_ref(*i).clone()); + *i += 1; + }, + |i| if *i < len { + // we must be failing, clean up after ourselves + for j in range(0, *i as int) { + ptr::read(&*p.offset(j)); + } + // FIXME: #13994 (should pass align and size here) + exchange_free(ret as *mut u8, 0, 8); }); cast::transmute(ret) } @@ -768,7 +817,8 @@ impl<T> Drop for MoveItems<T> { // destroy the remaining elements for _x in *self {} unsafe { - exchange_free(self.allocation as *u8) + // FIXME: #13994 (should pass align and size here) + exchange_free(self.allocation, 0, 8) } } } diff --git a/src/libstd/unstable/mutex.rs b/src/libstd/unstable/mutex.rs index 8faedcbd9ed..c9d70915694 100644 --- a/src/libstd/unstable/mutex.rs +++ b/src/libstd/unstable/mutex.rs @@ -434,7 +434,7 @@ mod imp { #[cfg(windows)] mod imp { - use rt::global_heap::malloc_raw; + use rt::libc_heap::malloc_raw; use libc::{HANDLE, BOOL, LPSECURITY_ATTRIBUTES, c_void, DWORD, LPCSTR}; use libc; use ptr; diff --git a/src/libstd/vec.rs b/src/libstd/vec.rs index da01da26709..aa10be1d1be 100644 --- a/src/libstd/vec.rs +++ b/src/libstd/vec.rs @@ -12,13 +12,12 @@ use cast::{forget, transmute}; use clone::Clone; -use cmp::{Ord, Eq, Ordering, TotalEq, TotalOrd}; +use cmp::{Ord, Eq, Ordering, TotalEq, TotalOrd, max}; use container::{Container, Mutable}; use default::Default; use fmt; use iter::{DoubleEndedIterator, FromIterator, Extendable, Iterator, range}; -use libc::{free, c_void}; -use mem::{size_of, move_val_init}; +use mem::{min_align_of, move_val_init, size_of}; use mem; use num; use num::{CheckedMul, CheckedAdd}; @@ -26,9 +25,9 @@ use ops::{Add, Drop}; use option::{None, Option, Some, Expect}; use ptr::RawPtr; use ptr; -use rt::global_heap::{malloc_raw, realloc_raw}; use raw::Slice; use RawVec = raw::Vec; +use rt::heap::{allocate, reallocate, deallocate}; use slice::{ImmutableEqVector, ImmutableVector, Items, MutItems, MutableVector}; use slice::{MutableTotalOrdVector, OwnedVector, Vector}; use slice::{MutableVectorAllocating}; @@ -92,11 +91,12 @@ impl<T> Vec<T> { /// let vec: Vec<int> = Vec::with_capacity(10); /// ``` pub fn with_capacity(capacity: uint) -> Vec<T> { + if size_of::<T>() == 0 { return Vec { len: 0, cap: ::uint::MAX, ptr: 0 as *mut T } } if capacity == 0 { Vec::new() } else { let size = capacity.checked_mul(&size_of::<T>()).expect("capacity overflow"); - let ptr = unsafe { malloc_raw(size) }; + let ptr = unsafe { allocate(size, min_align_of::<T>()) }; Vec { len: 0, cap: capacity, ptr: ptr as *mut T } } } @@ -401,6 +401,23 @@ impl<T> Container for Vec<T> { } } +// FIXME: #13996: need a way to mark the return value as `noalias` +#[inline(never)] +unsafe fn alloc_or_realloc<T>(ptr: *mut T, size: uint, old_size: uint) -> *mut T { + if old_size == 0 { + allocate(size, min_align_of::<T>()) as *mut T + } else { + reallocate(ptr as *mut u8, size, min_align_of::<T>(), old_size) as *mut T + } +} + +#[inline] +unsafe fn dealloc<T>(ptr: *mut T, len: uint) { + if size_of::<T>() != 0 { + deallocate(ptr as *mut u8, len * size_of::<T>(), min_align_of::<T>()) + } +} + impl<T> Vec<T> { /// Returns the number of elements the vector can hold without /// reallocating. @@ -477,33 +494,38 @@ impl<T> Vec<T> { /// assert_eq!(vec.capacity(), 11); /// ``` pub fn reserve_exact(&mut self, capacity: uint) { + if size_of::<T>() == 0 { return } if capacity > self.cap { let size = capacity.checked_mul(&size_of::<T>()).expect("capacity overflow"); - self.cap = capacity; unsafe { - self.ptr = realloc_raw(self.ptr as *mut u8, size) as *mut T; + self.ptr = alloc_or_realloc(self.ptr, size, self.cap * size_of::<T>()); } + self.cap = capacity; } } - /// Shrink the capacity of the vector to match the length + /// Shrink the capacity of the vector as much as possible /// /// # Example /// /// ```rust /// let mut vec = vec!(1, 2, 3); /// vec.shrink_to_fit(); - /// assert_eq!(vec.capacity(), vec.len()); /// ``` pub fn shrink_to_fit(&mut self) { + if size_of::<T>() == 0 { return } if self.len == 0 { - unsafe { free(self.ptr as *mut c_void) }; - self.cap = 0; - self.ptr = 0 as *mut T; + if self.cap != 0 { + unsafe { + dealloc(self.ptr, self.cap) + } + self.cap = 0; + } } else { unsafe { // Overflow check is unnecessary as the vector is already at least this large. - self.ptr = realloc_raw(self.ptr as *mut u8, self.len * size_of::<T>()) as *mut T; + self.ptr = reallocate(self.ptr as *mut u8, self.len * size_of::<T>(), + min_align_of::<T>(), self.cap * size_of::<T>()) as *mut T; } self.cap = self.len; } @@ -546,15 +568,20 @@ impl<T> Vec<T> { /// ``` #[inline] pub fn push(&mut self, value: T) { + if size_of::<T>() == 0 { + // zero-size types consume no memory, so we can't rely on the address space running out + self.len = self.len.checked_add(&1).expect("length overflow"); + unsafe { forget(value); } + return + } if self.len == self.cap { - if self.cap == 0 { self.cap += 2 } let old_size = self.cap * size_of::<T>(); - self.cap = self.cap * 2; - let size = old_size * 2; + let size = max(old_size, 2 * size_of::<T>()) * 2; if old_size > size { fail!("capacity overflow") } unsafe { - self.ptr = realloc_raw(self.ptr as *mut u8, size) as *mut T; + self.ptr = alloc_or_realloc(self.ptr, size, self.cap * size_of::<T>()); } + self.cap = max(self.cap, 2) * 2; } unsafe { @@ -638,9 +665,10 @@ impl<T> Vec<T> { pub fn move_iter(self) -> MoveItems<T> { unsafe { let iter = transmute(self.as_slice().iter()); - let ptr = self.ptr as *mut c_void; + let ptr = self.ptr; + let cap = self.cap; forget(self); - MoveItems { allocation: ptr, iter: iter } + MoveItems { allocation: ptr, cap: cap, iter: iter } } } @@ -1386,11 +1414,13 @@ impl<T> Drop for Vec<T> { fn drop(&mut self) { // This is (and should always remain) a no-op if the fields are // zeroed (when moving out, because of #[unsafe_no_drop_flag]). - unsafe { - for x in self.as_mut_slice().iter() { - ptr::read(x); + if self.cap != 0 { + unsafe { + for x in self.as_mut_slice().iter() { + ptr::read(x); + } + dealloc(self.ptr, self.cap) } - free(self.ptr as *mut c_void) } } } @@ -1409,7 +1439,8 @@ impl<T:fmt::Show> fmt::Show for Vec<T> { /// An iterator that moves out of a vector. pub struct MoveItems<T> { - allocation: *mut c_void, // the block of memory allocated for the vector + allocation: *mut T, // the block of memory allocated for the vector + cap: uint, // the capacity of the vector iter: Items<'static, T> } @@ -1440,9 +1471,11 @@ impl<T> DoubleEndedIterator<T> for MoveItems<T> { impl<T> Drop for MoveItems<T> { fn drop(&mut self) { // destroy the remaining elements - for _x in *self {} - unsafe { - free(self.allocation) + if self.cap != 0 { + for _x in *self {} + unsafe { + dealloc(self.allocation, self.cap); + } } } } @@ -1493,7 +1526,7 @@ impl<T> FromVec<T> for ~[T] { let vp = v.as_mut_ptr(); unsafe { - let ret = malloc_raw(size) as *mut RawVec<()>; + let ret = allocate(size, 8) as *mut RawVec<()>; (*ret).fill = len * mem::nonzero_size_of::<T>(); (*ret).alloc = len * mem::nonzero_size_of::<T>(); |
