Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
gc: Add stack walker for new garbage collector.
Safe points are exported in a per-module list via the crate map. A C runtime call walks the crate map at startup and aggregates the list of safe points for the program. Currently the GC doesn't actually deallocate memory on malloc and free. Adding the GC at this stage is primarily of testing value. The GC does attempt to clean up exchange heap and stack-allocated resource on failure. A result of this patch is that the user now needs to be careful about what code they write in destructors, because the GC and/or failure cleanup may need to call destructors. Specifically, calls to malloc are considered unsafe and may result in infinite loops or segfaults.
- Loading branch information
Elliott Slaughter
committed
Sep 7, 2012
1 parent
fb8786f
commit 3f0d207
Showing
12 changed files
with
375 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
import stackwalk::Word; | ||
import libc::size_t; | ||
|
||
extern mod rustrt { | ||
fn rust_annihilate_box(ptr: *Word); | ||
|
||
#[rust_stack] | ||
fn rust_gc_metadata() -> *Word; | ||
|
||
#[rust_stack] | ||
fn rust_call_tydesc_glue(root: *Word, tydesc: *Word, field: size_t); | ||
} | ||
|
||
type SafePoint = { sp_meta: *Word, fn_meta: *Word }; | ||
|
||
unsafe fn is_safe_point(pc: *Word) -> Option<SafePoint> { | ||
let module_meta = rustrt::rust_gc_metadata(); | ||
let num_safe_points_ptr: *u32 = unsafe::reinterpret_cast(&module_meta); | ||
let num_safe_points = *num_safe_points_ptr as Word; | ||
let safe_points: *Word = | ||
ptr::offset(unsafe::reinterpret_cast(&module_meta), 1); | ||
|
||
if ptr::is_null(pc) { | ||
return None; | ||
} | ||
|
||
let mut sp = 0 as Word; | ||
while sp < num_safe_points { | ||
let sp_loc = *ptr::offset(safe_points, sp*3) as *Word; | ||
if sp_loc == pc { | ||
return Some( | ||
{sp_meta: *ptr::offset(safe_points, sp*3 + 1) as *Word, | ||
fn_meta: *ptr::offset(safe_points, sp*3 + 2) as *Word}); | ||
} | ||
sp += 1; | ||
} | ||
return None; | ||
} | ||
|
||
type Visitor = fn(root: **Word, tydesc: *Word); | ||
|
||
unsafe fn walk_safe_point(fp: *Word, sp: SafePoint, visitor: Visitor) { | ||
let fp_bytes: *u8 = unsafe::reinterpret_cast(&fp); | ||
let sp_meta_u32s: *u32 = unsafe::reinterpret_cast(&sp.sp_meta); | ||
|
||
let num_stack_roots = *sp_meta_u32s as uint; | ||
let num_reg_roots = *ptr::offset(sp_meta_u32s, 1) as uint; | ||
|
||
let stack_roots: *u32 = | ||
unsafe::reinterpret_cast(&ptr::offset(sp_meta_u32s, 2)); | ||
let reg_roots: *u8 = | ||
unsafe::reinterpret_cast(&ptr::offset(stack_roots, num_stack_roots)); | ||
let addrspaces: *Word = | ||
unsafe::reinterpret_cast(&ptr::offset(reg_roots, num_reg_roots)); | ||
let tydescs: ***Word = | ||
unsafe::reinterpret_cast(&ptr::offset(addrspaces, num_stack_roots)); | ||
|
||
// Stack roots | ||
let mut sri = 0; | ||
while sri < num_stack_roots { | ||
if *ptr::offset(addrspaces, sri) >= 1 { | ||
let root = | ||
ptr::offset(fp_bytes, *ptr::offset(stack_roots, sri) as Word) | ||
as **Word; | ||
let tydescpp = ptr::offset(tydescs, sri); | ||
let tydesc = if ptr::is_not_null(tydescpp) && | ||
ptr::is_not_null(*tydescpp) { | ||
**tydescpp | ||
} else { | ||
ptr::null() | ||
}; | ||
visitor(root, tydesc); | ||
} | ||
sri += 1; | ||
} | ||
|
||
// Register roots | ||
let mut rri = 0; | ||
while rri < num_reg_roots { | ||
if *ptr::offset(addrspaces, num_stack_roots + rri) == 1 { | ||
// FIXME(#2997): Need to find callee saved registers on the stack. | ||
} | ||
rri += 1; | ||
} | ||
} | ||
|
||
type Memory = uint; | ||
|
||
const task_local_heap: Memory = 1; | ||
const exchange_heap: Memory = 2; | ||
const stack: Memory = 4; | ||
|
||
const need_cleanup: Memory = exchange_heap | stack; | ||
|
||
unsafe fn walk_gc_roots(mem: Memory, visitor: Visitor) { | ||
let mut last_ret: *Word = ptr::null(); | ||
do stackwalk::walk_stack |frame| { | ||
unsafe { | ||
if ptr::is_not_null(last_ret) { | ||
let sp = is_safe_point(last_ret); | ||
match sp { | ||
Some(sp_info) => { | ||
do walk_safe_point(frame.fp, sp_info) |root, tydesc| { | ||
if ptr::is_null(tydesc) { | ||
// Root is a generic box. | ||
let refcount = **root; | ||
if mem | task_local_heap != 0 && refcount != -1 { | ||
visitor(root, tydesc); | ||
} else if mem | exchange_heap != 0 { | ||
visitor(root, tydesc); | ||
} | ||
} else { | ||
// Root is a non-immediate. | ||
if mem | stack != 0 { | ||
visitor(root, tydesc); | ||
} | ||
} | ||
} | ||
} | ||
None => () | ||
} | ||
} | ||
last_ret = *ptr::offset(frame.fp, 1) as *Word; | ||
} | ||
true | ||
} | ||
} | ||
|
||
fn gc() { | ||
unsafe { | ||
let mut i = 0; | ||
do walk_gc_roots(task_local_heap) |_root, _tydesc| { | ||
// FIXME(#2997): Walk roots and mark them. | ||
io::stdout().write([46]); // . | ||
i += 1; | ||
} | ||
} | ||
} | ||
|
||
// This should only be called from fail, as it will drop the roots | ||
// which are *live* on the stack, rather than dropping those that are | ||
// dead. | ||
fn cleanup_stack_for_failure() { | ||
unsafe { | ||
let mut i = 0; | ||
do walk_gc_roots(need_cleanup) |root, tydesc| { | ||
if ptr::is_null(tydesc) { | ||
rustrt::rust_annihilate_box(*root); | ||
} else { | ||
rustrt::rust_call_tydesc_glue(*root, tydesc, 3 as size_t); | ||
} | ||
i += 1; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
#include "rust_crate_map.h" | ||
|
||
void iter_module_map(const mod_entry* map, | ||
void (*fn)(const mod_entry* entry, void *cookie), | ||
void *cookie) { | ||
for (const mod_entry* cur = map; cur->name; cur++) { | ||
fn(cur, cookie); | ||
} | ||
} | ||
|
||
void iter_crate_map(const cratemap* map, | ||
void (*fn)(const mod_entry* map, void *cookie), | ||
void *cookie) { | ||
// First iterate this crate | ||
iter_module_map(map->entries, fn, cookie); | ||
// Then recurse on linked crates | ||
// FIXME (#2673) this does double work in diamond-shaped deps. could | ||
// keep a set of visited addresses, if it turns out to be actually | ||
// slow | ||
for (size_t i = 0; map->children[i]; i++) { | ||
iter_crate_map(map->children[i], fn, cookie); | ||
} | ||
} | ||
|
||
// | ||
// Local Variables: | ||
// mode: C++ | ||
// fill-column: 78; | ||
// indent-tabs-mode: nil | ||
// c-basic-offset: 4 | ||
// buffer-file-coding-system: utf-8-unix | ||
// End: | ||
// |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
#ifndef RUST_CRATE_MAP_H | ||
#define RUST_CRATE_MAP_H | ||
|
||
#include "rust_log.h" | ||
|
||
struct mod_entry { | ||
const char* name; | ||
uint32_t* state; | ||
}; | ||
|
||
struct cratemap { | ||
const mod_entry* entries; | ||
const cratemap* children[1]; | ||
}; | ||
|
||
void iter_module_map(const mod_entry* map, | ||
void (*fn)(const mod_entry* entry, void *cookie), | ||
void *cookie); | ||
|
||
void iter_crate_map(const cratemap* map, | ||
void (*fn)(const mod_entry* entry, void *cookie), | ||
void *cookie); | ||
|
||
// | ||
// Local Variables: | ||
// mode: C++ | ||
// fill-column: 78; | ||
// indent-tabs-mode: nil | ||
// c-basic-offset: 4 | ||
// buffer-file-coding-system: utf-8-unix | ||
// End: | ||
// | ||
|
||
#endif /* RUST_CRATE_MAP_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
#include "rust_gc_metadata.h" | ||
#include "rust_crate_map.h" | ||
#include "rust_globals.h" | ||
|
||
#include <algorithm> | ||
#include <vector> | ||
|
||
struct safe_point { | ||
size_t safe_point_loc; | ||
size_t safe_point_meta; | ||
size_t function_meta; | ||
}; | ||
|
||
struct update_gc_entry_args { | ||
std::vector<safe_point> *safe_points; | ||
}; | ||
|
||
static void | ||
update_gc_entry(const mod_entry* entry, void *cookie) { | ||
update_gc_entry_args *args = (update_gc_entry_args *)cookie; | ||
if (!strcmp(entry->name, "_gc_module_metadata")) { | ||
size_t *next = entry->state; | ||
uint32_t num_safe_points = *(uint32_t *)next; | ||
next++; | ||
|
||
for (uint32_t i = 0; i < num_safe_points; i++) { | ||
safe_point sp = { next[0], next[1], next[2] }; | ||
next += 3; | ||
|
||
args->safe_points->push_back(sp); | ||
} | ||
} | ||
} | ||
|
||
static bool | ||
cmp_safe_point(safe_point a, safe_point b) { | ||
return a.safe_point_loc < b.safe_point_loc; | ||
} | ||
|
||
size_t *global_safe_points = 0; | ||
|
||
void | ||
update_gc_metadata(const void* map) { | ||
std::vector<safe_point> safe_points; | ||
update_gc_entry_args args = { &safe_points }; | ||
|
||
// Extract list of safe points from each module. | ||
iter_crate_map((const cratemap *)map, update_gc_entry, (void *)&args); | ||
std::sort(safe_points.begin(), safe_points.end(), cmp_safe_point); | ||
|
||
// Serialize safe point list into format expected by runtime. | ||
global_safe_points = | ||
(size_t *)malloc((safe_points.size()*3 + 1)*sizeof(size_t)); | ||
if (!global_safe_points) return; | ||
|
||
size_t *next = global_safe_points; | ||
*(uint32_t *)next = safe_points.size(); | ||
next++; | ||
for (uint32_t i = 0; i < safe_points.size(); i++) { | ||
next[0] = safe_points[i].safe_point_loc; | ||
next[1] = safe_points[i].safe_point_meta; | ||
next[2] = safe_points[i].function_meta; | ||
next += 3; | ||
} | ||
} | ||
|
||
extern "C" CDECL void * | ||
rust_gc_metadata() { | ||
return (void *)global_safe_points; | ||
} | ||
|
||
// | ||
// Local Variables: | ||
// mode: C++ | ||
// fill-column: 78; | ||
// indent-tabs-mode: nil | ||
// c-basic-offset: 4 | ||
// buffer-file-coding-system: utf-8-unix | ||
// End: | ||
// |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
#ifndef RUST_GC_METADATA_H | ||
#define RUST_GC_METADATA_H | ||
|
||
void update_gc_metadata(const void* map); | ||
|
||
// | ||
// Local Variables: | ||
// mode: C++ | ||
// fill-column: 78; | ||
// indent-tabs-mode: nil | ||
// c-basic-offset: 4 | ||
// buffer-file-coding-system: utf-8-unix | ||
// End: | ||
// | ||
|
||
#endif /* RUST_GC_METADATA_H */ |
Oops, something went wrong.