Skip to content

Issue 6112 box annihilator #6114

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 33 additions & 7 deletions src/libcore/cleanup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,22 +126,29 @@ struct AnnihilateStats {
n_bytes_freed: uint
}

unsafe fn each_live_alloc(f: &fn(box: *mut BoxRepr, uniq: bool) -> bool) {
unsafe fn each_live_alloc(read_next_before: bool,
f: &fn(box: *mut BoxRepr, uniq: bool) -> bool) {
//! Walks the internal list of allocations

use managed;

let task: *Task = transmute(rustrt::rust_get_task());
let box = (*task).boxed_region.live_allocs;
let mut box: *mut BoxRepr = transmute(copy box);
while box != mut_null() {
let next = transmute(copy (*box).header.next);
let next_before = transmute(copy (*box).header.next);
let uniq =
(*box).header.ref_count == managed::raw::RC_MANAGED_UNIQUE;

if ! f(box, uniq) {
break
}

box = next
if read_next_before {
box = next_before;
} else {
box = transmute(copy (*box).header.next);
}
}
}

Expand All @@ -159,7 +166,7 @@ fn debug_mem() -> bool {
#[cfg(notest)]
#[lang="annihilate"]
pub unsafe fn annihilate() {
use unstable::lang::local_free;
use unstable::lang::{local_free, debug_ptr};
use io::WriterUtil;
use io;
use libc;
Expand All @@ -173,27 +180,46 @@ pub unsafe fn annihilate() {
};

// Pass 1: Make all boxes immortal.
for each_live_alloc |box, uniq| {
//
// In this pass, nothing gets freed, so it does not matter whether
// we read the next field before or after the callback.
for each_live_alloc(true) |box, uniq| {
stats.n_total_boxes += 1;
if uniq {
debug_ptr("Managed-uniq: ", &*box);
stats.n_unique_boxes += 1;
} else {
debug_ptr("Immortalizing: ", &*box);
(*box).header.ref_count = managed::raw::RC_IMMORTAL;
}
}

// Pass 2: Drop all boxes.
for each_live_alloc |box, uniq| {
//
// In this pass, unique-managed boxes may get freed, but not
// managed boxes, so we must read the `next` field *after* the
// callback, as the original value may have been freed.
for each_live_alloc(false) |box, uniq| {
if !uniq {
debug_ptr("Invoking tydesc/glue on: ", &*box);
let tydesc: *TypeDesc = transmute(copy (*box).header.type_desc);
let drop_glue: DropGlue = transmute(((*tydesc).drop_glue, 0));
debug_ptr("Box data: ", &(*box).data);
debug_ptr("Type descriptor: ", tydesc);
drop_glue(to_unsafe_ptr(&tydesc), transmute(&(*box).data));
debug_ptr("Dropped ", &*box);
}
}

// Pass 3: Free all boxes.
for each_live_alloc |box, uniq| {
//
// In this pass, managed boxes may get freed (but not
// unique-managed boxes, though I think that none of those are
// left), so we must read the `next` field before, since it will
// not be valid after.
for each_live_alloc(true) |box, uniq| {
if !uniq {
debug_ptr("About to free: ", &*box);
stats.n_bytes_freed +=
(*((*box).header.type_desc)).size
+ sys::size_of::<BoxRepr>();
Expand Down
47 changes: 44 additions & 3 deletions src/libcore/unstable/lang.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
//! Runtime calls emitted by the compiler.

use cast::transmute;
use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int};
use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int, STDERR_FILENO};
use managed::raw::BoxRepr;
use str;
use sys;
Expand Down Expand Up @@ -74,7 +74,44 @@ pub fn fail_borrowed() {
#[lang="exchange_malloc"]
#[inline(always)]
pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
transmute(exchange_alloc::malloc(transmute(td), transmute(size)))
let result = transmute(exchange_alloc::malloc(transmute(td), transmute(size)));
debug_ptr("exchange_malloc: ", result);
return result;
}

/// Because this code is so perf. sensitive, use a static constant so that
/// debug printouts are compiled out most of the time.
static ENABLE_DEBUG_PTR: bool = false;

#[inline]
pub fn debug_ptr<T>(tag: &'static str, p: *T) {
//! A useful debugging function that prints a pointer + tag + newline
//! without allocating memory.

if ENABLE_DEBUG_PTR && ::rt::env::get().debug_mem {
debug_ptr_slow(tag, p);
}

fn debug_ptr_slow<T>(tag: &'static str, p: *T) {
use io;
let dbg = STDERR_FILENO as io::fd_t;
let letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8',
'9', 'a', 'b', 'c', 'd', 'e', 'f'];
dbg.write_str(tag);

static uint_nibbles: uint = ::uint::bytes << 1;
let mut buffer = [0_u8, ..uint_nibbles+1];
let mut i = p as uint;
let mut c = uint_nibbles;
while c > 0 {
c -= 1;
buffer[c] = letters[i & 0xF] as u8;
i >>= 4;
}
dbg.write(buffer.slice(0, uint_nibbles));

dbg.write_str("\n");
}
}

// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
Expand All @@ -83,13 +120,16 @@ pub unsafe fn exchange_malloc(td: *c_char, size: uintptr_t) -> *c_char {
#[lang="exchange_free"]
#[inline(always)]
pub unsafe fn exchange_free(ptr: *c_char) {
debug_ptr("exchange_free: ", ptr);
exchange_alloc::free(transmute(ptr))
}

#[lang="malloc"]
#[inline(always)]
pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
return rustrt::rust_upcall_malloc_noswitch(td, size);
let result = rustrt::rust_upcall_malloc_noswitch(td, size);
debug_ptr("local_malloc: ", result);
return result;
}

// NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
Expand All @@ -98,6 +138,7 @@ pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
#[lang="free"]
#[inline(always)]
pub unsafe fn local_free(ptr: *c_char) {
debug_ptr("local_free: ", ptr);
rustrt::rust_upcall_free_noswitch(ptr);
}

Expand Down