2012-12-03 18:48:01 -06:00
|
|
|
// 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.
|
|
|
|
|
2012-09-19 18:52:32 -05:00
|
|
|
#[doc(hidden)];
|
|
|
|
|
2013-06-20 04:39:49 -05:00
|
|
|
use libc::{c_char, intptr_t, uintptr_t};
|
|
|
|
use ptr::{mut_null, to_unsafe_ptr};
|
2012-09-14 18:12:18 -05:00
|
|
|
use repr::BoxRepr;
|
2012-09-18 19:34:08 -05:00
|
|
|
use cast::transmute;
|
2013-05-08 06:11:23 -05:00
|
|
|
#[cfg(not(test))] use unstable::lang::clear_task_borrow_list;
|
2012-09-14 18:12:18 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Runtime structures
|
|
|
|
*
|
|
|
|
* NB: These must match the representation in the C++ runtime.
|
|
|
|
*/
|
|
|
|
|
|
|
|
type TaskID = uintptr_t;
|
|
|
|
|
|
|
|
struct StackSegment { priv opaque: () }
|
|
|
|
struct Scheduler { priv opaque: () }
|
|
|
|
struct SchedulerLoop { priv opaque: () }
|
|
|
|
struct Kernel { priv opaque: () }
|
|
|
|
struct Env { priv opaque: () }
|
|
|
|
struct AllocHeader { priv opaque: () }
|
|
|
|
struct MemoryRegion { priv opaque: () }
|
|
|
|
|
2012-09-17 14:00:26 -05:00
|
|
|
#[cfg(target_arch="x86")]
|
|
|
|
struct Registers {
|
2013-03-22 20:52:04 -05:00
|
|
|
data: [u32, ..16]
|
2012-09-24 22:24:10 -05:00
|
|
|
}
|
|
|
|
|
2013-05-22 12:49:43 -05:00
|
|
|
#[cfg(target_arch="arm")]
|
2013-01-29 08:28:08 -06:00
|
|
|
#[cfg(target_arch="mips")]
|
|
|
|
struct Registers {
|
2013-03-22 20:52:04 -05:00
|
|
|
data: [u32, ..32]
|
2013-01-29 08:28:08 -06:00
|
|
|
}
|
|
|
|
|
2012-09-24 22:24:10 -05:00
|
|
|
#[cfg(target_arch="x86")]
|
2012-11-29 18:21:49 -06:00
|
|
|
#[cfg(target_arch="arm")]
|
2013-01-29 08:28:08 -06:00
|
|
|
#[cfg(target_arch="mips")]
|
2012-09-24 22:24:10 -05:00
|
|
|
struct Context {
|
|
|
|
regs: Registers,
|
|
|
|
next: *Context,
|
2013-03-22 20:52:04 -05:00
|
|
|
pad: [u32, ..3]
|
2012-09-17 14:00:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(target_arch="x86_64")]
|
2012-09-14 18:12:18 -05:00
|
|
|
struct Registers {
|
2013-03-22 20:52:04 -05:00
|
|
|
data: [u64, ..22]
|
2012-09-14 18:12:18 -05:00
|
|
|
}
|
|
|
|
|
2012-09-24 22:24:10 -05:00
|
|
|
#[cfg(target_arch="x86_64")]
|
2012-09-14 18:12:18 -05:00
|
|
|
struct Context {
|
|
|
|
regs: Registers,
|
|
|
|
next: *Context,
|
2012-09-24 22:24:10 -05:00
|
|
|
pad: uintptr_t
|
2012-09-14 18:12:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
struct BoxedRegion {
|
|
|
|
env: *Env,
|
|
|
|
backing_region: *MemoryRegion,
|
|
|
|
live_allocs: *BoxRepr
|
|
|
|
}
|
|
|
|
|
2012-09-24 22:24:10 -05:00
|
|
|
#[cfg(target_arch="x86")]
|
2012-11-29 18:21:49 -06:00
|
|
|
#[cfg(target_arch="arm")]
|
2013-01-29 08:28:08 -06:00
|
|
|
#[cfg(target_arch="mips")]
|
2012-09-24 22:24:10 -05:00
|
|
|
struct Task {
|
|
|
|
// Public fields
|
|
|
|
refcount: intptr_t, // 0
|
|
|
|
id: TaskID, // 4
|
2013-03-22 20:52:04 -05:00
|
|
|
pad: [u32, ..2], // 8
|
2012-09-24 22:24:10 -05:00
|
|
|
ctx: Context, // 16
|
|
|
|
stack_segment: *StackSegment, // 96
|
|
|
|
runtime_sp: uintptr_t, // 100
|
|
|
|
scheduler: *Scheduler, // 104
|
|
|
|
scheduler_loop: *SchedulerLoop, // 108
|
|
|
|
|
|
|
|
// Fields known only to the runtime
|
|
|
|
kernel: *Kernel, // 112
|
|
|
|
name: *c_char, // 116
|
|
|
|
list_index: i32, // 120
|
|
|
|
boxed_region: BoxedRegion // 128
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(target_arch="x86_64")]
|
2012-09-14 18:12:18 -05:00
|
|
|
struct Task {
|
|
|
|
// Public fields
|
|
|
|
refcount: intptr_t,
|
|
|
|
id: TaskID,
|
|
|
|
ctx: Context,
|
|
|
|
stack_segment: *StackSegment,
|
|
|
|
runtime_sp: uintptr_t,
|
|
|
|
scheduler: *Scheduler,
|
|
|
|
scheduler_loop: *SchedulerLoop,
|
|
|
|
|
|
|
|
// Fields known only to the runtime
|
|
|
|
kernel: *Kernel,
|
|
|
|
name: *c_char,
|
2012-09-24 22:24:10 -05:00
|
|
|
list_index: i32,
|
2012-09-14 18:12:18 -05:00
|
|
|
boxed_region: BoxedRegion
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Box annihilation
|
|
|
|
*
|
|
|
|
* This runs at task death to free all boxes.
|
|
|
|
*/
|
|
|
|
|
2013-02-20 17:02:21 -06:00
|
|
|
struct AnnihilateStats {
|
|
|
|
n_total_boxes: uint,
|
|
|
|
n_unique_boxes: uint,
|
|
|
|
n_bytes_freed: uint
|
|
|
|
}
|
|
|
|
|
2013-05-02 17:33:18 -05:00
|
|
|
unsafe fn each_live_alloc(read_next_before: bool,
|
|
|
|
f: &fn(box: *mut BoxRepr, uniq: bool) -> 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_before = transmute(copy (*box).header.next);
|
|
|
|
let uniq =
|
|
|
|
(*box).header.ref_count == managed::raw::RC_MANAGED_UNIQUE;
|
|
|
|
|
|
|
|
if !f(box, uniq) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if read_next_before {
|
|
|
|
box = next_before;
|
|
|
|
} else {
|
|
|
|
box = transmute(copy (*box).header.next);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2013-02-20 17:02:21 -06:00
|
|
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
fn debug_mem() -> bool {
|
2013-03-12 14:23:24 -05:00
|
|
|
::rt::env::get().debug_mem
|
2013-02-20 17:02:21 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(windows)]
|
|
|
|
fn debug_mem() -> bool {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
|
2013-06-20 04:39:49 -05:00
|
|
|
#[cfg(stage0)]
|
|
|
|
unsafe fn call_drop_glue(tydesc: *::std::unstable::intrinsics::TyDesc, data: *i8) {
|
|
|
|
use sys::TypeDesc;
|
|
|
|
|
|
|
|
let tydesc: *TypeDesc = transmute(tydesc);
|
|
|
|
let drop_glue: extern "Rust" fn(**TypeDesc, *i8) = transmute((*tydesc).drop_glue);
|
|
|
|
drop_glue(to_unsafe_ptr(&tydesc), data);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(stage0))]
|
|
|
|
unsafe fn call_drop_glue(tydesc: *::std::unstable::intrinsics::TyDesc, data: *i8) {
|
|
|
|
((*tydesc).drop_glue)(to_unsafe_ptr(&tydesc), data);
|
|
|
|
}
|
|
|
|
|
2012-09-14 18:58:23 -05:00
|
|
|
/// Destroys all managed memory (i.e. @ boxes) held by the current task.
|
2013-05-08 06:11:23 -05:00
|
|
|
#[cfg(not(test))]
|
2012-09-14 18:58:23 -05:00
|
|
|
#[lang="annihilate"]
|
|
|
|
pub unsafe fn annihilate() {
|
2013-05-06 19:14:54 -05:00
|
|
|
use unstable::lang::local_free;
|
2012-09-14 20:05:15 -05:00
|
|
|
use io::WriterUtil;
|
2013-02-20 17:02:21 -06:00
|
|
|
use io;
|
|
|
|
use libc;
|
|
|
|
use sys;
|
|
|
|
use managed;
|
2012-09-14 18:58:23 -05:00
|
|
|
|
2013-02-20 17:02:21 -06:00
|
|
|
let mut stats = AnnihilateStats {
|
|
|
|
n_total_boxes: 0,
|
|
|
|
n_unique_boxes: 0,
|
|
|
|
n_bytes_freed: 0
|
|
|
|
};
|
2012-09-14 18:12:18 -05:00
|
|
|
|
2013-05-02 16:08:04 -05:00
|
|
|
// Quick hack: we need to free this list upon task exit, and this
|
|
|
|
// is a convenient place to do it.
|
|
|
|
clear_task_borrow_list();
|
|
|
|
|
2012-09-14 18:12:18 -05:00
|
|
|
// Pass 1: Make all boxes immortal.
|
2013-03-15 14:24:24 -05:00
|
|
|
//
|
|
|
|
// 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| {
|
2013-02-20 17:02:21 -06:00
|
|
|
stats.n_total_boxes += 1;
|
|
|
|
if uniq {
|
|
|
|
stats.n_unique_boxes += 1;
|
|
|
|
} else {
|
|
|
|
(*box).header.ref_count = managed::raw::RC_IMMORTAL;
|
|
|
|
}
|
2012-09-14 18:12:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Pass 2: Drop all boxes.
|
2013-03-15 14:24:24 -05:00
|
|
|
//
|
|
|
|
// 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| {
|
2013-02-20 17:02:21 -06:00
|
|
|
if !uniq {
|
2013-06-20 04:39:49 -05:00
|
|
|
call_drop_glue((*box).header.type_desc, transmute(&(*box).data));
|
2013-02-20 17:02:21 -06:00
|
|
|
}
|
2012-09-14 18:12:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Pass 3: Free all boxes.
|
2013-03-15 14:24:24 -05:00
|
|
|
//
|
|
|
|
// 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| {
|
2013-02-20 17:02:21 -06:00
|
|
|
if !uniq {
|
|
|
|
stats.n_bytes_freed +=
|
|
|
|
(*((*box).header.type_desc)).size
|
|
|
|
+ sys::size_of::<BoxRepr>();
|
2013-02-27 20:34:04 -06:00
|
|
|
local_free(transmute(box));
|
2013-02-20 17:02:21 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if debug_mem() {
|
|
|
|
// We do logging here w/o allocation.
|
|
|
|
let dbg = libc::STDERR_FILENO as io::fd_t;
|
|
|
|
dbg.write_str("annihilator stats:");
|
|
|
|
dbg.write_str("\n total_boxes: ");
|
|
|
|
dbg.write_uint(stats.n_total_boxes);
|
|
|
|
dbg.write_str("\n unique_boxes: ");
|
|
|
|
dbg.write_uint(stats.n_unique_boxes);
|
|
|
|
dbg.write_str("\n bytes_freed: ");
|
|
|
|
dbg.write_uint(stats.n_bytes_freed);
|
|
|
|
dbg.write_str("\n");
|
2012-09-14 18:12:18 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Bindings to the runtime
|
2013-03-05 13:57:50 -06:00
|
|
|
pub mod rustrt {
|
|
|
|
use libc::c_void;
|
|
|
|
|
|
|
|
#[link_name = "rustrt"]
|
|
|
|
pub extern {
|
|
|
|
#[rust_stack]
|
|
|
|
// FIXME (#4386): Unable to make following method private.
|
|
|
|
pub unsafe fn rust_get_task() -> *c_void;
|
|
|
|
}
|
2012-09-14 18:12:18 -05:00
|
|
|
}
|