auto merge of #7265 : brson/rust/io-upstream, r=brson
r? @graydon, @nikomatsakis, @pcwalton, or @catamorphism
Sorry this is so huge, but it's been accumulating for about a month. There's lots of stuff here, mostly oriented toward enabling multithreaded scheduling and improving compatibility between the old and new runtimes. Adds task pinning so that we can create the 'platform thread' in servo.
[Here](e1555f9b56/src/libstd/rt/mod.rs (L201)) is the current runtime setup code.
About half of this has already been reviewed.
This commit is contained in:
commit
41dcec2fe1
52 changed files with 5652 additions and 2095 deletions
|
|
@ -10,29 +10,21 @@
|
|||
|
||||
//! Runtime calls emitted by the compiler.
|
||||
|
||||
use iterator::IteratorUtil;
|
||||
use uint;
|
||||
use cast::transmute;
|
||||
use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int, STDERR_FILENO};
|
||||
use managed::raw::BoxRepr;
|
||||
use libc::{c_char, c_uchar, c_void, size_t, uintptr_t, c_int};
|
||||
use str;
|
||||
use sys;
|
||||
use rt::{context, OldTaskContext};
|
||||
use rt::task::Task;
|
||||
use rt::local::Local;
|
||||
use option::{Option, Some, None};
|
||||
use io;
|
||||
use rt::borrowck;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type rust_task = c_void;
|
||||
|
||||
pub static FROZEN_BIT: uint = 1 << (uint::bits - 1);
|
||||
pub static MUT_BIT: uint = 1 << (uint::bits - 2);
|
||||
static ALL_BITS: uint = FROZEN_BIT | MUT_BIT;
|
||||
|
||||
pub mod rustrt {
|
||||
use unstable::lang::rust_task;
|
||||
use libc::{c_void, c_char, uintptr_t};
|
||||
use libc::{c_char, uintptr_t};
|
||||
|
||||
pub extern {
|
||||
#[rust_stack]
|
||||
|
|
@ -46,15 +38,6 @@ pub mod rustrt {
|
|||
size: uintptr_t)
|
||||
-> *c_char;
|
||||
|
||||
#[fast_ffi]
|
||||
unsafe fn rust_upcall_free_noswitch(ptr: *c_char);
|
||||
|
||||
#[rust_stack]
|
||||
fn rust_take_task_borrow_list(task: *rust_task) -> *c_void;
|
||||
|
||||
#[rust_stack]
|
||||
fn rust_set_task_borrow_list(task: *rust_task, map: *c_void);
|
||||
|
||||
#[rust_stack]
|
||||
fn rust_try_get_task() -> *rust_task;
|
||||
|
||||
|
|
@ -77,149 +60,6 @@ pub fn fail_bounds_check(file: *c_char, line: size_t,
|
|||
}
|
||||
}
|
||||
|
||||
#[deriving(Eq)]
|
||||
struct BorrowRecord {
|
||||
box: *mut BoxRepr,
|
||||
file: *c_char,
|
||||
line: size_t
|
||||
}
|
||||
|
||||
fn try_take_task_borrow_list() -> Option<~[BorrowRecord]> {
|
||||
unsafe {
|
||||
let cur_task: *rust_task = rustrt::rust_try_get_task();
|
||||
if cur_task.is_not_null() {
|
||||
let ptr = rustrt::rust_take_task_borrow_list(cur_task);
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
let v: ~[BorrowRecord] = transmute(ptr);
|
||||
Some(v)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn swap_task_borrow_list(f: &fn(~[BorrowRecord]) -> ~[BorrowRecord]) {
|
||||
unsafe {
|
||||
let cur_task: *rust_task = rustrt::rust_try_get_task();
|
||||
if cur_task.is_not_null() {
|
||||
let mut borrow_list: ~[BorrowRecord] = {
|
||||
let ptr = rustrt::rust_take_task_borrow_list(cur_task);
|
||||
if ptr.is_null() { ~[] } else { transmute(ptr) }
|
||||
};
|
||||
borrow_list = f(borrow_list);
|
||||
rustrt::rust_set_task_borrow_list(cur_task, transmute(borrow_list));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn clear_task_borrow_list() {
|
||||
// pub because it is used by the box annihilator.
|
||||
let _ = try_take_task_borrow_list();
|
||||
}
|
||||
|
||||
unsafe fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) {
|
||||
debug_borrow("fail_borrowed: ", box, 0, 0, file, line);
|
||||
|
||||
match try_take_task_borrow_list() {
|
||||
None => { // not recording borrows
|
||||
let msg = "borrowed";
|
||||
do str::as_buf(msg) |msg_p, _| {
|
||||
fail_(msg_p as *c_char, file, line);
|
||||
}
|
||||
}
|
||||
Some(borrow_list) => { // recording borrows
|
||||
let mut msg = ~"borrowed";
|
||||
let mut sep = " at ";
|
||||
for borrow_list.rev_iter().advance |entry| {
|
||||
if entry.box == box {
|
||||
msg.push_str(sep);
|
||||
let filename = str::raw::from_c_str(entry.file);
|
||||
msg.push_str(filename);
|
||||
msg.push_str(fmt!(":%u", entry.line as uint));
|
||||
sep = " and at ";
|
||||
}
|
||||
}
|
||||
do str::as_buf(msg) |msg_p, _| {
|
||||
fail_(msg_p as *c_char, file, line)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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: bool = false;
|
||||
|
||||
#[inline]
|
||||
unsafe fn debug_borrow<T>(tag: &'static str,
|
||||
p: *const T,
|
||||
old_bits: uint,
|
||||
new_bits: uint,
|
||||
filename: *c_char,
|
||||
line: size_t) {
|
||||
//! A useful debugging function that prints a pointer + tag + newline
|
||||
//! without allocating memory.
|
||||
|
||||
if ENABLE_DEBUG && ::rt::env::get().debug_borrow {
|
||||
debug_borrow_slow(tag, p, old_bits, new_bits, filename, line);
|
||||
}
|
||||
|
||||
unsafe fn debug_borrow_slow<T>(tag: &'static str,
|
||||
p: *const T,
|
||||
old_bits: uint,
|
||||
new_bits: uint,
|
||||
filename: *c_char,
|
||||
line: size_t) {
|
||||
let dbg = STDERR_FILENO as io::fd_t;
|
||||
dbg.write_str(tag);
|
||||
dbg.write_hex(p as uint);
|
||||
dbg.write_str(" ");
|
||||
dbg.write_hex(old_bits);
|
||||
dbg.write_str(" ");
|
||||
dbg.write_hex(new_bits);
|
||||
dbg.write_str(" ");
|
||||
dbg.write_cstr(filename);
|
||||
dbg.write_str(":");
|
||||
dbg.write_hex(line as uint);
|
||||
dbg.write_str("\n");
|
||||
}
|
||||
}
|
||||
|
||||
trait DebugPrints {
|
||||
fn write_hex(&self, val: uint);
|
||||
unsafe fn write_cstr(&self, str: *c_char);
|
||||
}
|
||||
|
||||
impl DebugPrints for io::fd_t {
|
||||
fn write_hex(&self, mut i: uint) {
|
||||
let letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8',
|
||||
'9', 'a', 'b', 'c', 'd', 'e', 'f'];
|
||||
static UINT_NIBBLES: uint = ::uint::bytes << 1;
|
||||
let mut buffer = [0_u8, ..UINT_NIBBLES+1];
|
||||
let mut c = UINT_NIBBLES;
|
||||
while c > 0 {
|
||||
c -= 1;
|
||||
buffer[c] = letters[i & 0xF] as u8;
|
||||
i >>= 4;
|
||||
}
|
||||
self.write(buffer.slice(0, UINT_NIBBLES));
|
||||
}
|
||||
|
||||
unsafe fn write_cstr(&self, p: *c_char) {
|
||||
use libc::strlen;
|
||||
use vec;
|
||||
|
||||
let len = strlen(p);
|
||||
let p: *u8 = transmute(p);
|
||||
do vec::raw::buf_as_slice(p, len as uint) |s| {
|
||||
self.write(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[lang="malloc"]
|
||||
pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
|
||||
match context() {
|
||||
|
|
@ -228,7 +68,10 @@ pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
|
|||
}
|
||||
_ => {
|
||||
let mut alloc = ::ptr::null();
|
||||
do Local::borrow::<Task> |task| {
|
||||
do Local::borrow::<Task,()> |task| {
|
||||
rtdebug!("task pointer: %x, heap pointer: %x",
|
||||
to_uint(task),
|
||||
to_uint(&task.heap));
|
||||
alloc = task.heap.alloc(td as *c_void, size as uint) as *c_char;
|
||||
}
|
||||
return alloc;
|
||||
|
|
@ -241,110 +84,38 @@ pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
|
|||
// problem occurs, call exit instead.
|
||||
#[lang="free"]
|
||||
pub unsafe fn local_free(ptr: *c_char) {
|
||||
match context() {
|
||||
OldTaskContext => {
|
||||
rustrt::rust_upcall_free_noswitch(ptr);
|
||||
}
|
||||
_ => {
|
||||
do Local::borrow::<Task> |task| {
|
||||
task.heap.free(ptr as *c_void);
|
||||
}
|
||||
}
|
||||
}
|
||||
::rt::local_heap::local_free(ptr);
|
||||
}
|
||||
|
||||
#[lang="borrow_as_imm"]
|
||||
#[inline]
|
||||
pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) -> uint {
|
||||
let a: *mut BoxRepr = transmute(a);
|
||||
let old_ref_count = (*a).header.ref_count;
|
||||
let new_ref_count = old_ref_count | FROZEN_BIT;
|
||||
|
||||
debug_borrow("borrow_as_imm:", a, old_ref_count, new_ref_count, file, line);
|
||||
|
||||
if (old_ref_count & MUT_BIT) != 0 {
|
||||
fail_borrowed(a, file, line);
|
||||
}
|
||||
|
||||
(*a).header.ref_count = new_ref_count;
|
||||
|
||||
old_ref_count
|
||||
borrowck::borrow_as_imm(a, file, line)
|
||||
}
|
||||
|
||||
#[lang="borrow_as_mut"]
|
||||
#[inline]
|
||||
pub unsafe fn borrow_as_mut(a: *u8, file: *c_char, line: size_t) -> uint {
|
||||
let a: *mut BoxRepr = transmute(a);
|
||||
let old_ref_count = (*a).header.ref_count;
|
||||
let new_ref_count = old_ref_count | MUT_BIT | FROZEN_BIT;
|
||||
|
||||
debug_borrow("borrow_as_mut:", a, old_ref_count, new_ref_count, file, line);
|
||||
|
||||
if (old_ref_count & (MUT_BIT|FROZEN_BIT)) != 0 {
|
||||
fail_borrowed(a, file, line);
|
||||
}
|
||||
|
||||
(*a).header.ref_count = new_ref_count;
|
||||
|
||||
old_ref_count
|
||||
borrowck::borrow_as_mut(a, file, line)
|
||||
}
|
||||
|
||||
|
||||
#[lang="record_borrow"]
|
||||
pub unsafe fn record_borrow(a: *u8, old_ref_count: uint,
|
||||
file: *c_char, line: size_t) {
|
||||
if (old_ref_count & ALL_BITS) == 0 {
|
||||
// was not borrowed before
|
||||
let a: *mut BoxRepr = transmute(a);
|
||||
debug_borrow("record_borrow:", a, old_ref_count, 0, file, line);
|
||||
do swap_task_borrow_list |borrow_list| {
|
||||
let mut borrow_list = borrow_list;
|
||||
borrow_list.push(BorrowRecord {box: a, file: file, line: line});
|
||||
borrow_list
|
||||
}
|
||||
}
|
||||
borrowck::record_borrow(a, old_ref_count, file, line)
|
||||
}
|
||||
|
||||
#[lang="unrecord_borrow"]
|
||||
pub unsafe fn unrecord_borrow(a: *u8, old_ref_count: uint,
|
||||
file: *c_char, line: size_t) {
|
||||
if (old_ref_count & ALL_BITS) == 0 {
|
||||
// was not borrowed before, so we should find the record at
|
||||
// the end of the list
|
||||
let a: *mut BoxRepr = transmute(a);
|
||||
debug_borrow("unrecord_borrow:", a, old_ref_count, 0, file, line);
|
||||
do swap_task_borrow_list |borrow_list| {
|
||||
let mut borrow_list = borrow_list;
|
||||
assert!(!borrow_list.is_empty());
|
||||
let br = borrow_list.pop();
|
||||
if br.box != a || br.file != file || br.line != line {
|
||||
let err = fmt!("wrong borrow found, br=%?", br);
|
||||
do str::as_buf(err) |msg_p, _| {
|
||||
fail_(msg_p as *c_char, file, line)
|
||||
}
|
||||
}
|
||||
borrow_list
|
||||
}
|
||||
}
|
||||
borrowck::unrecord_borrow(a, old_ref_count, file, line)
|
||||
}
|
||||
|
||||
#[lang="return_to_mut"]
|
||||
#[inline]
|
||||
pub unsafe fn return_to_mut(a: *u8, orig_ref_count: uint,
|
||||
file: *c_char, line: size_t) {
|
||||
// Sometimes the box is null, if it is conditionally frozen.
|
||||
// See e.g. #4904.
|
||||
if !a.is_null() {
|
||||
let a: *mut BoxRepr = transmute(a);
|
||||
let old_ref_count = (*a).header.ref_count;
|
||||
let new_ref_count =
|
||||
(old_ref_count & !ALL_BITS) | (orig_ref_count & ALL_BITS);
|
||||
|
||||
debug_borrow("return_to_mut:",
|
||||
a, old_ref_count, new_ref_count, file, line);
|
||||
|
||||
(*a).header.ref_count = new_ref_count;
|
||||
}
|
||||
borrowck::return_to_mut(a, orig_ref_count, file, line)
|
||||
}
|
||||
|
||||
#[lang="check_not_borrowed"]
|
||||
|
|
@ -352,12 +123,7 @@ pub unsafe fn return_to_mut(a: *u8, orig_ref_count: uint,
|
|||
pub unsafe fn check_not_borrowed(a: *u8,
|
||||
file: *c_char,
|
||||
line: size_t) {
|
||||
let a: *mut BoxRepr = transmute(a);
|
||||
let ref_count = (*a).header.ref_count;
|
||||
debug_borrow("check_not_borrowed:", a, ref_count, 0, file, line);
|
||||
if (ref_count & FROZEN_BIT) != 0 {
|
||||
fail_borrowed(a, file, line);
|
||||
}
|
||||
borrowck::check_not_borrowed(a, file, line)
|
||||
}
|
||||
|
||||
#[lang="strdup_uniq"]
|
||||
|
|
@ -366,6 +132,11 @@ pub unsafe fn strdup_uniq(ptr: *c_uchar, len: uint) -> ~str {
|
|||
str::raw::from_buf_len(ptr, len)
|
||||
}
|
||||
|
||||
#[lang="annihilate"]
|
||||
pub unsafe fn annihilate() {
|
||||
::cleanup::annihilate()
|
||||
}
|
||||
|
||||
#[lang="start"]
|
||||
pub fn start(main: *u8, argc: int, argv: **c_char,
|
||||
crate_map: *u8) -> int {
|
||||
|
|
|
|||
|
|
@ -205,8 +205,53 @@ extern {
|
|||
fn rust_unlock_little_lock(lock: rust_little_lock);
|
||||
}
|
||||
|
||||
/* *********************************************************************/
|
||||
|
||||
//FIXME: #5042 This should be replaced by proper atomic type
|
||||
pub struct AtomicUint {
|
||||
priv inner: uint
|
||||
}
|
||||
|
||||
impl AtomicUint {
|
||||
pub fn new(val: uint) -> AtomicUint { AtomicUint { inner: val } }
|
||||
pub fn load(&self) -> uint {
|
||||
unsafe { intrinsics::atomic_load(cast::transmute(self)) as uint }
|
||||
}
|
||||
pub fn store(&mut self, val: uint) {
|
||||
unsafe { intrinsics::atomic_store(cast::transmute(self), val as int); }
|
||||
}
|
||||
pub fn add(&mut self, val: int) -> uint {
|
||||
unsafe { intrinsics::atomic_xadd(cast::transmute(self), val as int) as uint }
|
||||
}
|
||||
pub fn cas(&mut self, old:uint, new: uint) -> uint {
|
||||
unsafe { intrinsics::atomic_cxchg(cast::transmute(self), old as int, new as int) as uint }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AtomicInt {
|
||||
priv inner: int
|
||||
}
|
||||
|
||||
impl AtomicInt {
|
||||
pub fn new(val: int) -> AtomicInt { AtomicInt { inner: val } }
|
||||
pub fn load(&self) -> int {
|
||||
unsafe { intrinsics::atomic_load(&self.inner) }
|
||||
}
|
||||
pub fn store(&mut self, val: int) {
|
||||
unsafe { intrinsics::atomic_store(&mut self.inner, val); }
|
||||
}
|
||||
pub fn add(&mut self, val: int) -> int {
|
||||
unsafe { intrinsics::atomic_xadd(&mut self.inner, val) }
|
||||
}
|
||||
pub fn cas(&mut self, old: int, new: int) -> int {
|
||||
unsafe { intrinsics::atomic_cxchg(&mut self.inner, old, new) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use comm;
|
||||
use super::exclusive;
|
||||
use task;
|
||||
|
|
@ -262,4 +307,28 @@ mod tests {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn atomic_int_smoke_test() {
|
||||
let mut i = AtomicInt::new(0);
|
||||
i.store(10);
|
||||
assert!(i.load() == 10);
|
||||
assert!(i.add(1) == 10);
|
||||
assert!(i.load() == 11);
|
||||
assert!(i.cas(11, 12) == 11);
|
||||
assert!(i.cas(11, 13) == 12);
|
||||
assert!(i.load() == 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn atomic_uint_smoke_test() {
|
||||
let mut i = AtomicUint::new(0);
|
||||
i.store(10);
|
||||
assert!(i.load() == 10);
|
||||
assert!(i.add(1) == 10);
|
||||
assert!(i.load() == 11);
|
||||
assert!(i.cas(11, 12) == 11);
|
||||
assert!(i.cas(11, 13) == 12);
|
||||
assert!(i.load() == 12);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue