From c1ad16db5a3b4774c7698a5cdb1433df2366b5b9 Mon Sep 17 00:00:00 2001 From: OGINO Masanori Date: Sat, 3 Aug 2013 07:06:27 +0900 Subject: [PATCH 01/19] Fix building problems in extra::unicode. Signed-off-by: OGINO Masanori --- src/libextra/unicode.rs | 51 ++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/src/libextra/unicode.rs b/src/libextra/unicode.rs index 4949ee79e5d8..3957551c8463 100644 --- a/src/libextra/unicode.rs +++ b/src/libextra/unicode.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#[forbid(deprecated_mode)]; #[allow(missing_doc)]; pub mod icu { @@ -159,7 +158,10 @@ pub mod icu { pub static UCHAR_INVALID_CODE : UProperty = -1; pub mod libicu { - #[link_name = "icuuc"] + use unicode::icu::*; + + // #[link_name = "icuuc"] + #[link_args = "-licuuc"] #[abi = "cdecl"] extern { pub fn u_hasBinaryProperty(c: UChar32, which: UProperty) -> UBool; @@ -174,13 +176,17 @@ pub mod icu { } pub fn is_XID_start(c: char) -> bool { - return icu::libicu::u_hasBinaryProperty(c, icu::UCHAR_XID_START) - == icu::TRUE; + unsafe { + return icu::libicu::u_hasBinaryProperty(c, icu::UCHAR_XID_START) + == icu::TRUE; + } } pub fn is_XID_continue(c: char) -> bool { - return icu::libicu::u_hasBinaryProperty(c, icu::UCHAR_XID_START) - == icu::TRUE; + unsafe { + return icu::libicu::u_hasBinaryProperty(c, icu::UCHAR_XID_START) + == icu::TRUE; + } } /* @@ -189,7 +195,9 @@ Function: is_digit Returns true if a character is a digit. */ pub fn is_digit(c: char) -> bool { - return icu::libicu::u_isdigit(c) == icu::TRUE; + unsafe { + return icu::libicu::u_isdigit(c) == icu::TRUE; + } } /* @@ -198,7 +206,9 @@ Function: is_lower Returns true if a character is a lowercase letter. */ pub fn is_lower(c: char) -> bool { - return icu::libicu::u_islower(c) == icu::TRUE; + unsafe { + return icu::libicu::u_islower(c) == icu::TRUE; + } } /* @@ -207,7 +217,9 @@ Function: is_space Returns true if a character is space. */ pub fn is_space(c: char) -> bool { - return icu::libicu::u_isspace(c) == icu::TRUE; + unsafe { + return icu::libicu::u_isspace(c) == icu::TRUE; + } } /* @@ -216,33 +228,36 @@ Function: is_upper Returns true if a character is an uppercase letter. */ pub fn is_upper(c: char) -> bool { - return icu::libicu::u_isupper(c) == icu::TRUE; + unsafe { + return icu::libicu::u_isupper(c) == icu::TRUE; + } } #[cfg(test)] mod tests { + use unicode::*; #[test] fn test_is_digit() { - assert!((unicode::icu::is_digit('0'))); - assert!((!unicode::icu::is_digit('m'))); + assert!((is_digit('0'))); + assert!((!is_digit('m'))); } #[test] fn test_is_lower() { - assert!((unicode::icu::is_lower('m'))); - assert!((!unicode::icu::is_lower('M'))); + assert!((is_lower('m'))); + assert!((!is_lower('M'))); } #[test] fn test_is_space() { - assert!((unicode::icu::is_space(' '))); - assert!((!unicode::icu::is_space('m'))); + assert!((is_space(' '))); + assert!((!is_space('m'))); } #[test] fn test_is_upper() { - assert!((unicode::icu::is_upper('M'))); - assert!((!unicode::icu::is_upper('m'))); + assert!((is_upper('M'))); + assert!((!is_upper('m'))); } } From 9c08db58ab8457878ce67c99e1e7a32ce7ee053a Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sat, 3 Aug 2013 19:59:46 -0700 Subject: [PATCH 02/19] librustc: Implement `#[no_main]`, which omits the entry point entirely. Useful for SDL and possibly Android too. --- src/librustc/driver/session.rs | 3 ++- src/librustc/middle/entry.rs | 6 ++++++ src/librustc/middle/trans/base.rs | 15 +++++++++------ src/librustc/middle/typeck/mod.rs | 3 ++- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index e43f85008d5c..d725e2db1eba 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -179,7 +179,8 @@ pub struct crate_metadata { #[deriving(Eq)] pub enum EntryFnType { EntryMain, - EntryStart + EntryStart, + EntryNone, } pub struct Session_ { diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs index 469c1c2f93ef..39e569a29c90 100644 --- a/src/librustc/middle/entry.rs +++ b/src/librustc/middle/entry.rs @@ -50,6 +50,12 @@ pub fn find_entry_point(session: Session, crate: &Crate, ast_map: ast_map::map) return; } + // If the user wants no main function at all, then stop here. + if attr::contains_name(crate.attrs, "no_main") { + *session.entry_type = Some(session::EntryNone); + return + } + let ctxt = @mut EntryContext { session: session, ast_map: ast_map, diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index e0a7cd8cc0b5..ff6fa714322d 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2295,13 +2295,16 @@ pub fn is_entry_fn(sess: &Session, node_id: ast::NodeId) -> bool { // Create a _rust_main(args: ~[str]) function which will be called from the // runtime rust_start function pub fn create_entry_wrapper(ccx: @mut CrateContext, - _sp: span, main_llfn: ValueRef) { + _sp: span, + main_llfn: ValueRef) { let et = ccx.sess.entry_type.unwrap(); - if et == session::EntryMain { - let llfn = create_main(ccx, main_llfn); - create_entry_fn(ccx, llfn, true); - } else { - create_entry_fn(ccx, main_llfn, false); + match et { + session::EntryMain => { + let llfn = create_main(ccx, main_llfn); + create_entry_fn(ccx, llfn, true); + } + session::EntryStart => create_entry_fn(ccx, main_llfn, false), + session::EntryNone => {} // Do nothing. } fn create_main(ccx: @mut CrateContext, main_llfn: ValueRef) -> ValueRef { diff --git a/src/librustc/middle/typeck/mod.rs b/src/librustc/middle/typeck/mod.rs index 6128c1699674..e6c27fc8f836 100644 --- a/src/librustc/middle/typeck/mod.rs +++ b/src/librustc/middle/typeck/mod.rs @@ -408,9 +408,10 @@ fn check_for_entry_fn(ccx: &CrateCtxt) { Some((id, sp)) => match *tcx.sess.entry_type { Some(session::EntryMain) => check_main_fn_ty(ccx, id, sp), Some(session::EntryStart) => check_start_fn_ty(ccx, id, sp), + Some(session::EntryNone) => {} None => tcx.sess.bug("entry function without a type") }, - None => tcx.sess.bug("type checking without entry function") + None => {} } } } From ea84c1fd693104a9f8d06ee0654d0a3bf470a32a Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Thu, 1 Aug 2013 23:31:53 -0700 Subject: [PATCH 03/19] std: Remove gc and stackwalk These are both obsoleted by the forthcoming new GC. --- src/libstd/gc.rs | 358 ---------------------------------------- src/libstd/stackwalk.rs | 80 --------- src/libstd/std.rs | 2 - src/libstd/sys.rs | 4 - 4 files changed, 444 deletions(-) delete mode 100644 src/libstd/gc.rs delete mode 100644 src/libstd/stackwalk.rs diff --git a/src/libstd/gc.rs b/src/libstd/gc.rs deleted file mode 100644 index 2e27f82f6f6e..000000000000 --- a/src/libstd/gc.rs +++ /dev/null @@ -1,358 +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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[doc(hidden)]; -#[allow(non_uppercase_statics)]; - -/*! Precise garbage collector - -The precise GC exposes two functions, gc and -cleanup_stack_for_failure. The gc function is the entry point to the -garbage collector itself. The cleanup_stack_for_failure is the entry -point for GC-based cleanup. - -Precise GC depends on changes to LLVM's GC which add support for -automatic rooting and addrspace-based metadata marking. Rather than -explicitly rooting pointers with LLVM's gcroot intrinsic, the GC -merely creates allocas for pointers, and allows an LLVM pass to -automatically infer roots based on the allocas present in a function -(and live at a given location). The compiler communicates the type of -the pointer to LLVM by setting the addrspace of the pointer type. The -compiler then emits a map from addrspace to tydesc, which LLVM then -uses to match pointers with their tydesc. The GC reads the metadata -table produced by LLVM, and uses it to determine which glue functions -to call to free objects on their respective heaps. - -GC-based cleanup is a replacement for landing pads which relies on the -GC infrastructure to find pointers on the stack to cleanup. Whereas -the normal GC needs to walk task-local heap allocations, the cleanup -code needs to walk exchange heap allocations and stack-allocations -with destructors. - -*/ - -use cast; -use container::{Set, MutableSet}; -use io; -use libc::{uintptr_t}; -use option::{None, Option, Some}; -use ptr; -use hashmap::HashSet; -use stackwalk::walk_stack; -use sys; -use unstable::intrinsics::{TyDesc}; - -pub use stackwalk::Word; - -// Mirrors rust_stack.h stk_seg -pub struct StackSegment { - prev: *StackSegment, - next: *StackSegment, - end: uintptr_t, - // And other fields which we don't care about... -} - -pub mod rustrt { - use stackwalk::Word; - use super::StackSegment; - - #[link_name = "rustrt"] - extern { - #[rust_stack] - pub fn rust_gc_metadata() -> *Word; - - pub fn rust_get_stack_segment() -> *StackSegment; - pub fn rust_get_c_stack() -> *StackSegment; - } -} - -unsafe fn bump(ptr: *T, count: uint) -> *U { - return ptr::offset(ptr, count as int) as *U; -} - -unsafe fn align_to_pointer(ptr: *T) -> *T { - let align = sys::min_align_of::<*T>(); - let ptr = ptr as uint; - let ptr = (ptr + (align - 1)) & -align; - return ptr as *T; -} - -unsafe fn get_safe_point_count() -> uint { - let module_meta = rustrt::rust_gc_metadata(); - return *module_meta; -} - -struct SafePoint { - sp_meta: *Word, - fn_meta: *Word, -} - -// Returns the safe point metadata for the given program counter, if -// any. -unsafe fn is_safe_point(pc: *Word) -> Option { - let module_meta = rustrt::rust_gc_metadata(); - let num_safe_points = *module_meta; - let safe_points: *Word = bump(module_meta, 1); - - if ptr::is_null(pc) { - return None; - } - - // FIXME (#2997): Use binary rather than linear search. - let mut spi = 0; - while spi < num_safe_points { - let sp: **Word = bump(safe_points, spi*3); - let sp_loc = *sp; - if sp_loc == pc { - return Some(SafePoint { - sp_meta: *bump(sp, 1), - fn_meta: *bump(sp, 2), - }); - } - spi += 1; - } - return None; -} - -type Visitor<'self> = &'self fn(root: **Word, tydesc: *TyDesc); - -// Walks the list of roots for the given safe point, and calls visitor -// on each root. -unsafe fn _walk_safe_point(fp: *Word, sp: SafePoint, visitor: Visitor) { - let fp_bytes = fp as *u8; - let sp_meta = sp.sp_meta as *u32; - - let num_stack_roots = *sp_meta as uint; - let num_reg_roots = *ptr::offset(sp_meta, 1) as uint; - - let stack_roots: *u32 = bump(sp_meta, 2); - let reg_roots: *u8 = bump(stack_roots, num_stack_roots); - let addrspaces: *Word = align_to_pointer(bump(reg_roots, num_reg_roots)); - let tydescs: ***TyDesc = bump(addrspaces, num_stack_roots); - - // Stack roots - let mut sri = 0; - while sri < num_stack_roots { - if *ptr::offset(addrspaces, sri as int) >= 1 { - let root = - ptr::offset(fp_bytes, *ptr::offset(stack_roots, sri as int) as int) - as **Word; - let tydescpp = ptr::offset(tydescs, sri as int); - 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) as int) == 1 { - // FIXME(#2997): Need to find callee saved registers on the stack. - } - rri += 1; - } -} - -unsafe fn walk_safe_point(fp: *Word, sp: SafePoint, visitor: Visitor) { - _walk_safe_point(fp, sp, visitor) -} - -// Is fp contained in segment? -unsafe fn is_frame_in_segment(fp: *Word, segment: *StackSegment) -> bool { - let begin = segment as Word; - let end = (*segment).end as Word; - let frame = fp as Word; - - return begin <= frame && frame <= end; -} - -struct Segment { segment: *StackSegment, boundary: bool } - -// Find and return the segment containing the given frame pointer. At -// stack segment boundaries, returns true for boundary, so that the -// caller can do any special handling to identify where the correct -// return address is in the stack frame. -unsafe fn find_segment_for_frame(fp: *Word, segment: *StackSegment) - -> Segment { - // Check if frame is in either current frame or previous frame. - let in_segment = is_frame_in_segment(fp, segment); - let in_prev_segment = ptr::is_not_null((*segment).prev) && - is_frame_in_segment(fp, (*segment).prev); - - // If frame is not in either segment, walk down segment list until - // we find the segment containing this frame. - if !in_segment && !in_prev_segment { - let mut segment = segment; - while ptr::is_not_null((*segment).next) && - is_frame_in_segment(fp, (*segment).next) { - segment = (*segment).next; - } - return Segment {segment: segment, boundary: false}; - } - - // If frame is in previous frame, then we're at a boundary. - if !in_segment && in_prev_segment { - return Segment {segment: (*segment).prev, boundary: true}; - } - - // Otherwise, we're somewhere on the inside of the frame. - return Segment {segment: segment, boundary: false}; -} - -type Memory = uint; - -static task_local_heap: Memory = 1; -static exchange_heap: Memory = 2; -static stack: Memory = 4; - -static need_cleanup: Memory = exchange_heap | stack; - -// Walks stack, searching for roots of the requested type, and passes -// each root to the visitor. -unsafe fn _walk_gc_roots(mem: Memory, sentinel: **Word, visitor: Visitor) { - let mut segment = rustrt::rust_get_stack_segment(); - let mut last_ret: *Word = ptr::null(); - // To avoid collecting memory used by the GC itself, skip stack - // frames until past the root GC stack frame. The root GC stack - // frame is marked by a sentinel, which is a box pointer stored on - // the stack. - let mut reached_sentinel = ptr::is_null(sentinel); - do walk_stack |frame| { - let pc = last_ret; - let Segment {segment: next_segment, boundary: boundary} = - find_segment_for_frame(frame.fp, segment); - segment = next_segment; - // Each stack segment is bounded by a morestack frame. The - // morestack frame includes two return addresses, one for - // morestack itself, at the normal offset from the frame - // pointer, and then a second return address for the - // function prologue (which called morestack after - // determining that it had hit the end of the stack). - // Since morestack itself takes two parameters, the offset - // for this second return address is 3 greater than the - // return address for morestack. - let ret_offset = if boundary { 4 } else { 1 }; - last_ret = *ptr::offset(frame.fp, ret_offset as int) as *Word; - - if !ptr::is_null(pc) { - - let mut delay_reached_sentinel = reached_sentinel; - let sp = is_safe_point(pc); - match sp { - Some(sp_info) => { - do walk_safe_point(frame.fp, sp_info) |root, tydesc| { - // Skip roots until we see the sentinel. - if !reached_sentinel && root == sentinel { - delay_reached_sentinel = true; - } - - // Skip null pointers, which can occur when a - // unique pointer has already been freed. - if reached_sentinel && !ptr::is_null(*root) { - 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 && refcount == -1 { - visitor(root, tydesc); - } - } else { - // Root is a non-immediate. - if mem | stack != 0 { - visitor(root, tydesc); - } - } - } - } - } - None => () - } - reached_sentinel = delay_reached_sentinel; - } - } -} - -unsafe fn walk_gc_roots(mem: Memory, sentinel: **Word, visitor: Visitor) { - _walk_gc_roots(mem, sentinel, visitor) -} -pub fn gc() { - unsafe { - // Abort when GC is disabled. - if get_safe_point_count() == 0 { - return; - } - - do walk_gc_roots(task_local_heap, ptr::null()) |_root, _tydesc| { - // FIXME(#2997): Walk roots and mark them. - io::stdout().write([46]); // . - } - } -} - -#[cfg(gc)] -fn expect_sentinel() -> bool { true } - -#[cfg(nogc)] -fn expect_sentinel() -> bool { false } - -// Entry point for GC-based cleanup. Walks stack looking for exchange -// heap and stack allocations requiring drop, and runs all -// destructors. -// -// 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. -pub fn cleanup_stack_for_failure() { - unsafe { - // Abort when GC is disabled. - if get_safe_point_count() == 0 { - return; - } - - // Leave a sentinel on the stack to mark the current frame. The - // stack walker will ignore any frames above the sentinel, thus - // avoiding collecting any memory being used by the stack walker - // itself. - // - // However, when core itself is not compiled with GC, then none of - // the functions in core will have GC metadata, which means we - // won't be able to find the sentinel root on the stack. In this - // case, we can safely skip the sentinel since we won't find our - // own stack roots on the stack anyway. - let sentinel_box = ~0; - let sentinel: **Word = if expect_sentinel() { - cast::transmute(&sentinel_box) - } else { - ptr::null() - }; - - let mut roots = HashSet::new(); - do walk_gc_roots(need_cleanup, sentinel) |root, tydesc| { - // Track roots to avoid double frees. - if !roots.contains(&*root) { - roots.insert(*root); - - if ptr::is_null(tydesc) { - // FIXME #4420: Destroy this box - // FIXME #4330: Destroy this box - } else { - ((*tydesc).drop_glue)(*root as *i8); - } - } - } - } -} diff --git a/src/libstd/stackwalk.rs b/src/libstd/stackwalk.rs deleted file mode 100644 index cc516fb559ea..000000000000 --- a/src/libstd/stackwalk.rs +++ /dev/null @@ -1,80 +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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[allow(missing_doc)]; - -use cast::transmute; -use unstable::intrinsics; - -pub type Word = uint; - -pub struct Frame { - fp: *Word -} - -pub fn Frame(fp: *Word) -> Frame { - Frame { - fp: fp - } -} - -pub fn walk_stack(visit: &fn(Frame)) { - - debug!("beginning stack walk"); - - do frame_address |frame_pointer| { - let mut frame_address: *Word = unsafe { - transmute(frame_pointer) - }; - loop { - let fr = Frame(frame_address); - - debug!("frame: %x", unsafe { transmute(fr.fp) }); - visit(fr); - - unsafe { - let next_fp: **Word = transmute(frame_address); - frame_address = *next_fp; - if *frame_address == 0u { - debug!("encountered task_start_wrapper. ending walk"); - // This is the task_start_wrapper_frame. There is - // no stack beneath it and it is a foreign frame. - break; - } - } - } - } -} - -#[test] -fn test_simple() { - do walk_stack |_frame| { - } -} - -#[test] -fn test_simple_deep() { - fn run(i: int) { - if i == 0 { return } - - do walk_stack |_frame| { - // Would be nice to test something here... - } - run(i - 1); - } - - run(10); -} - -fn frame_address(f: &fn(x: *u8)) { - unsafe { - intrinsics::frame_address(f) - } -} diff --git a/src/libstd/std.rs b/src/libstd/std.rs index 76d65192e01a..568709c89da7 100644 --- a/src/libstd/std.rs +++ b/src/libstd/std.rs @@ -170,7 +170,6 @@ pub mod local_data; /* Runtime and platform support */ -pub mod gc; pub mod libc; pub mod os; pub mod path; @@ -196,7 +195,6 @@ pub mod unstable; mod unicode; #[path = "num/cmath.rs"] mod cmath; -mod stackwalk; // XXX: This shouldn't be pub, and it should be reexported under 'unstable' // but name resolution doesn't work without it being pub. diff --git a/src/libstd/sys.rs b/src/libstd/sys.rs index 51609709cdbc..9d853087123e 100644 --- a/src/libstd/sys.rs +++ b/src/libstd/sys.rs @@ -13,7 +13,6 @@ #[allow(missing_doc)]; use cast; -use gc; use io; use libc; use libc::{c_char, size_t}; @@ -147,7 +146,6 @@ pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! { match context { OldTaskContext => { unsafe { - gc::cleanup_stack_for_failure(); rustrt::rust_upcall_fail(msg, file, line); cast::transmute(()) } @@ -180,8 +178,6 @@ pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! { msg, file, line as int); } - gc::cleanup_stack_for_failure(); - let task = Local::unsafe_borrow::(); if (*task).unwinder.unwinding { rtabort!("unwinding again"); From d8e74b3dcb6575d6dac77c4dbcd0308a581008da Mon Sep 17 00:00:00 2001 From: Dmitry Ermolov Date: Wed, 31 Jul 2013 20:24:35 +0400 Subject: [PATCH 04/19] Additional check in treemap iterator test. --- src/libextra/treemap.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libextra/treemap.rs b/src/libextra/treemap.rs index 1736e62aee96..4e19310f1809 100644 --- a/src/libextra/treemap.rs +++ b/src/libextra/treemap.rs @@ -983,6 +983,7 @@ mod test_treemap { assert_eq!(*v, n * 2); n += 1; } + assert_eq!(n, 5); } #[test] From 98a66568cec371250ed3070a160406417c8564ce Mon Sep 17 00:00:00 2001 From: Dmitry Ermolov Date: Fri, 2 Aug 2013 00:57:34 +0400 Subject: [PATCH 05/19] Remove redundant print. --- src/libextra/treemap.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libextra/treemap.rs b/src/libextra/treemap.rs index 4e19310f1809..12a3553720c4 100644 --- a/src/libextra/treemap.rs +++ b/src/libextra/treemap.rs @@ -1257,7 +1257,6 @@ mod test_set { let mut n = 0; for x in m.iter() { - printfln!(x); assert_eq!(*x, n); n += 1 } From 73ec9f36e4a0995fc08abf75ac9aaa0298d816d9 Mon Sep 17 00:00:00 2001 From: Dmitry Ermolov Date: Fri, 2 Aug 2013 00:59:07 +0400 Subject: [PATCH 06/19] Implemented TreeMap::{lower_bound_iter,upper_bound_iter} (issue #4604) --- src/libextra/treemap.rs | 150 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 146 insertions(+), 4 deletions(-) diff --git a/src/libextra/treemap.rs b/src/libextra/treemap.rs index 12a3553720c4..487ad050e786 100644 --- a/src/libextra/treemap.rs +++ b/src/libextra/treemap.rs @@ -184,7 +184,68 @@ impl TreeMap { /// Get a lazy iterator over the key-value pairs in the map. /// Requires that it be frozen (immutable). pub fn iter<'a>(&'a self) -> TreeMapIterator<'a, K, V> { - TreeMapIterator{stack: ~[], node: &self.root, remaining: self.length} + TreeMapIterator { + stack: ~[], + node: &self.root, + remaining_min: self.length, + remaining_max: self.length + } + } + + /// Get a lazy iterator that should be initialized using + /// `iter_traverse_left`/`iter_traverse_right`/`iter_traverse_complete`. + fn iter_for_traversal<'a>(&'a self) -> TreeMapIterator<'a, K, V> { + TreeMapIterator { + stack: ~[], + node: &self.root, + remaining_min: 0, + remaining_max: self.length + } + } + + /// Return a lazy iterator to the first key-value pair whose key is not less than `k` + /// If all keys in map are less than `k` an empty iterator is returned. + pub fn lower_bound_iter<'a>(&'a self, k: &K) -> TreeMapIterator<'a, K, V> { + let mut iter: TreeMapIterator<'a, K, V> = self.iter_for_traversal(); + loop { + match *iter.node { + Some(ref r) => { + match k.cmp(&r.key) { + Less => iter_traverse_left(&mut iter), + Greater => iter_traverse_right(&mut iter), + Equal => { + iter_traverse_complete(&mut iter); + return iter; + } + } + } + None => { + iter_traverse_complete(&mut iter); + return iter; + } + } + } + } + + /// Return a lazy iterator to the first key-value pair whose key is greater than `k` + /// If all keys in map are not greater than `k` an empty iterator is returned. + pub fn upper_bound_iter<'a>(&'a self, k: &K) -> TreeMapIterator<'a, K, V> { + let mut iter: TreeMapIterator<'a, K, V> = self.iter_for_traversal(); + loop { + match *iter.node { + Some(ref r) => { + match k.cmp(&r.key) { + Less => iter_traverse_left(&mut iter), + Greater => iter_traverse_right(&mut iter), + Equal => iter_traverse_right(&mut iter) + } + } + None => { + iter_traverse_complete(&mut iter); + return iter; + } + } + } } /// Get a lazy iterator that consumes the treemap. @@ -205,7 +266,8 @@ impl TreeMap { pub struct TreeMapIterator<'self, K, V> { priv stack: ~[&'self ~TreeNode], priv node: &'self Option<~TreeNode>, - priv remaining: uint + priv remaining_min: uint, + priv remaining_max: uint } impl<'self, K, V> Iterator<(&'self K, &'self V)> for TreeMapIterator<'self, K, V> { @@ -222,7 +284,10 @@ impl<'self, K, V> Iterator<(&'self K, &'self V)> for TreeMapIterator<'self, K, V None => { let res = self.stack.pop(); self.node = &res.right; - self.remaining -= 1; + self.remaining_max -= 1; + if self.remaining_min > 0 { + self.remaining_min -= 1; + } return Some((&res.key, &res.value)); } } @@ -232,7 +297,46 @@ impl<'self, K, V> Iterator<(&'self K, &'self V)> for TreeMapIterator<'self, K, V #[inline] fn size_hint(&self) -> (uint, Option) { - (self.remaining, Some(self.remaining)) + (self.remaining_min, Some(self.remaining_max)) + } +} + +/// iter_traverse_left, iter_traverse_right and iter_traverse_complete are used to +/// initialize TreeMapIterator pointing to element inside tree structure. +/// +/// They should be used in following manner: +/// - create iterator using TreeMap::iter_for_traversal +/// - find required node using `iter_traverse_left`/`iter_traverse_right` +/// (current node is `TreeMapIterator::node` field) +/// - complete initialization with `iter_traverse_complete` +#[inline] +fn iter_traverse_left<'a, K, V>(it: &mut TreeMapIterator<'a, K, V>) { + let node = it.node.get_ref(); + it.stack.push(node); + it.node = &node.left; +} + +#[inline] +fn iter_traverse_right<'a, K, V>(it: &mut TreeMapIterator<'a, K, V>) { + it.node = &(it.node.get_ref().right); +} + +/// iter_traverse_left, iter_traverse_right and iter_traverse_complete are used to +/// initialize TreeMapIterator pointing to element inside tree structure. +/// +/// Completes traversal. Should be called before using iterator. +/// Iteration will start from `self.node`. +/// If `self.node` is None iteration will start from last node from which we +/// traversed left. +#[inline] +fn iter_traverse_complete<'a, K, V>(it: &mut TreeMapIterator<'a, K, V>) { + static none: Option<~TreeNode> = None; + match *it.node { + Some(ref n) => { + it.stack.push(n); + it.node = &none; + } + None => () } } @@ -417,6 +521,20 @@ impl TreeSet { TreeSetIterator{iter: self.map.iter()} } + /// Get a lazy iterator pointing to the first value not less than `v` (greater or equal). + /// If all elements in the set are less than `v` empty iterator is returned. + #[inline] + pub fn lower_bound_iter<'a>(&'a self, v: &T) -> TreeSetIterator<'a, T> { + TreeSetIterator{iter: self.map.lower_bound_iter(v)} + } + + /// Get a lazy iterator pointing to the first value greater than `v`. + /// If all elements in the set are not greater than `v` empty iterator is returned. + #[inline] + pub fn upper_bound_iter<'a>(&'a self, v: &T) -> TreeSetIterator<'a, T> { + TreeSetIterator{iter: self.map.upper_bound_iter(v)} + } + /// Visit all values in reverse order #[inline] pub fn each_reverse(&self, f: &fn(&T) -> bool) -> bool { @@ -986,6 +1104,30 @@ mod test_treemap { assert_eq!(n, 5); } + #[test] + fn test_interval_iteration() { + let mut m = TreeMap::new(); + for i in range(1, 100) { + assert!(m.insert(i * 2, i * 4)); + } + + for i in range(1, 198) { + let mut lb_it = m.lower_bound_iter(&i); + let (&k, &v) = lb_it.next().unwrap(); + let lb = i + i % 2; + assert_eq!(lb, k); + assert_eq!(lb * 2, v); + + let mut ub_it = m.upper_bound_iter(&i); + let (&k, &v) = ub_it.next().unwrap(); + let ub = i + 2 - i % 2; + assert_eq!(ub, k); + assert_eq!(ub * 2, v); + } + let mut end_it = m.lower_bound_iter(&199); + assert_eq!(end_it.next(), None); + } + #[test] fn test_each_reverse() { let mut m = TreeMap::new(); From 147c4fd81b25afe8a0b3ef50987d916370d7133b Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sun, 4 Aug 2013 16:10:19 -0400 Subject: [PATCH 07/19] Fixed str::raw::push_byte It was previously pushing the byte on top of the string's null terminator. I added a test to make sure it doesn't break in the future. --- src/libstd/str.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libstd/str.rs b/src/libstd/str.rs index c30888529be1..4398e414cdac 100644 --- a/src/libstd/str.rs +++ b/src/libstd/str.rs @@ -878,7 +878,7 @@ pub mod raw { let new_len = s.len() + 1; s.reserve_at_least(new_len); do s.as_mut_buf |buf, len| { - *ptr::mut_offset(buf, len as int) = b; + *ptr::mut_offset(buf, (len-1) as int) = b; } set_len(&mut *s, new_len); } @@ -2801,6 +2801,13 @@ mod tests { assert!(!" _ ".is_whitespace()); } + #[test] + fn test_push_byte() { + let mut s = ~"ABC"; + unsafe{raw::push_byte(&mut s, 'D' as u8)}; + assert_eq!(s, ~"ABCD"); + } + #[test] fn test_shift_byte() { let mut s = ~"ABC"; From 835e963dbd6514473bfa234eb4423c8ceb478d52 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 3 Aug 2013 17:39:59 -0700 Subject: [PATCH 08/19] std::rt: Improve the error message when the thread-local ptr is null Also fix some incorrect comments and variable names. --- src/libstd/rt/local_ptr.rs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/libstd/rt/local_ptr.rs b/src/libstd/rt/local_ptr.rs index cd7c5daa444d..652e39b05c7e 100644 --- a/src/libstd/rt/local_ptr.rs +++ b/src/libstd/rt/local_ptr.rs @@ -52,7 +52,9 @@ pub unsafe fn put(sched: ~T) { pub unsafe fn take() -> ~T { let key = tls_key(); let void_ptr: *mut c_void = tls::get(key); - rtassert!(void_ptr.is_not_null()); + if void_ptr.is_null() { + rtabort!("thread-local pointer is null. bogus!"); + } let ptr: ~T = cast::transmute(void_ptr); tls::set(key, ptr::mut_null()); return ptr; @@ -68,8 +70,8 @@ pub fn exists() -> bool { } } -/// Borrow the thread-local scheduler from thread-local storage. -/// While the scheduler is borrowed it is not available in TLS. +/// Borrow the thread-local value from thread-local storage. +/// While the value is borrowed it is not available in TLS. /// /// # Safety note /// @@ -88,21 +90,23 @@ pub unsafe fn borrow(f: &fn(&mut T)) { } } -/// Borrow a mutable reference to the thread-local Scheduler +/// Borrow a mutable reference to the thread-local value /// /// # Safety Note /// -/// Because this leaves the Scheduler in thread-local storage it is possible +/// Because this leaves the value in thread-local storage it is possible /// For the Scheduler pointer to be aliased pub unsafe fn unsafe_borrow() -> *mut T { let key = tls_key(); - let mut void_sched: *mut c_void = tls::get(key); - rtassert!(void_sched.is_not_null()); + let mut void_ptr: *mut c_void = tls::get(key); + if void_ptr.is_null() { + rtabort!("thread-local pointer is null. bogus!"); + } { - let sched: *mut *mut c_void = &mut void_sched; - let sched: *mut ~T = sched as *mut ~T; - let sched: *mut T = &mut **sched; - return sched; + let ptr: *mut *mut c_void = &mut void_ptr; + let ptr: *mut ~T = ptr as *mut ~T; + let ptr: *mut T = &mut **ptr; + return ptr; } } From 75734a9cd355a7d122bc60dd059a825cf9616c7d Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 3 Aug 2013 17:40:29 -0700 Subject: [PATCH 09/19] std::rt: Run the tests for Local in a bare thread Otherwise interferes with the existing runtime --- src/libstd/rt/local.rs | 77 +++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/src/libstd/rt/local.rs b/src/libstd/rt/local.rs index 7ab63233cff2..131507196b1f 100644 --- a/src/libstd/rt/local.rs +++ b/src/libstd/rt/local.rs @@ -126,6 +126,7 @@ impl Local for IoFactoryObject { #[cfg(test)] mod test { + use unstable::run_in_bare_thread; use rt::test::*; use super::*; use rt::task::Task; @@ -133,56 +134,64 @@ mod test { #[test] fn thread_local_task_smoke_test() { - local_ptr::init_tls_key(); - let mut sched = ~new_test_uv_sched(); - let task = ~Task::new_root(&mut sched.stack_pool, || {}); - Local::put(task); - let task: ~Task = Local::take(); - cleanup_task(task); + do run_in_bare_thread { + local_ptr::init_tls_key(); + let mut sched = ~new_test_uv_sched(); + let task = ~Task::new_root(&mut sched.stack_pool, || {}); + Local::put(task); + let task: ~Task = Local::take(); + cleanup_task(task); + } } #[test] fn thread_local_task_two_instances() { - local_ptr::init_tls_key(); - let mut sched = ~new_test_uv_sched(); - let task = ~Task::new_root(&mut sched.stack_pool, || {}); - Local::put(task); - let task: ~Task = Local::take(); - cleanup_task(task); - let task = ~Task::new_root(&mut sched.stack_pool, || {}); - Local::put(task); - let task: ~Task = Local::take(); - cleanup_task(task); + do run_in_bare_thread { + local_ptr::init_tls_key(); + let mut sched = ~new_test_uv_sched(); + let task = ~Task::new_root(&mut sched.stack_pool, || {}); + Local::put(task); + let task: ~Task = Local::take(); + cleanup_task(task); + let task = ~Task::new_root(&mut sched.stack_pool, || {}); + Local::put(task); + let task: ~Task = Local::take(); + cleanup_task(task); + } } #[test] fn borrow_smoke_test() { - local_ptr::init_tls_key(); - let mut sched = ~new_test_uv_sched(); - let task = ~Task::new_root(&mut sched.stack_pool, || {}); - Local::put(task); + do run_in_bare_thread { + local_ptr::init_tls_key(); + let mut sched = ~new_test_uv_sched(); + let task = ~Task::new_root(&mut sched.stack_pool, || {}); + Local::put(task); - unsafe { - let _task: *mut Task = Local::unsafe_borrow(); + unsafe { + let _task: *mut Task = Local::unsafe_borrow(); + } + let task: ~Task = Local::take(); + cleanup_task(task); } - let task: ~Task = Local::take(); - cleanup_task(task); } #[test] fn borrow_with_return() { - local_ptr::init_tls_key(); - let mut sched = ~new_test_uv_sched(); - let task = ~Task::new_root(&mut sched.stack_pool, || {}); - Local::put(task); + do run_in_bare_thread { + local_ptr::init_tls_key(); + let mut sched = ~new_test_uv_sched(); + let task = ~Task::new_root(&mut sched.stack_pool, || {}); + Local::put(task); - let res = do Local::borrow:: |_task| { - true - }; - assert!(res) - let task: ~Task = Local::take(); - cleanup_task(task); + let res = do Local::borrow:: |_task| { + true + }; + assert!(res) + let task: ~Task = Local::take(); + cleanup_task(task); + } } } From f0f7e1b3fcfed8b77516a871ae82a4aa8df07764 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 3 Aug 2013 23:28:47 -0700 Subject: [PATCH 10/19] std::rt: 3MB stacks! rustc needs *even more* megabytes when run without optimizations --- src/libstd/rt/task.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index abafe1cf209c..ffe9f118b477 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -303,7 +303,7 @@ impl Task { impl Drop for Task { fn drop(&self) { rtdebug!("called drop for a task: %u", borrow::to_uint(self)); - assert!(self.destroyed) + rtassert!(self.destroyed) } } @@ -313,7 +313,7 @@ impl Drop for Task { impl Coroutine { pub fn new(stack_pool: &mut StackPool, start: ~fn()) -> Coroutine { - static MIN_STACK_SIZE: uint = 2000000; // XXX: Too much stack + static MIN_STACK_SIZE: uint = 3000000; // XXX: Too much stack let start = Coroutine::build_start_wrapper(start); let mut stack = stack_pool.take_segment(MIN_STACK_SIZE); From a27f339cb4480e723aa7e06070683966d026d1ae Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 3 Aug 2013 23:29:21 -0700 Subject: [PATCH 11/19] std::rt: Don't allow schedulers to exit before handling all messages Every time run_sched_once performs a 'scheduling action' it needs to guarantee that it runs at least one more time, so enqueue another run_sched_once callback. The primary reason it needs to do this is because not all async callbacks are guaranteed to run, it's only guaranteed that *a* callback will run after enqueing one - some may get dropped. At the moment this means we wastefully create lots of callbacks to ensure that there will *definitely* be a callback queued up to continue running the scheduler. The logic really needs to be tightened up here. --- src/libstd/rt/sched.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs index 18cfeade157d..a5c8abc2a6c0 100644 --- a/src/libstd/rt/sched.rs +++ b/src/libstd/rt/sched.rs @@ -172,6 +172,10 @@ impl Scheduler { rtdebug!("stopping scheduler %u", stask.sched.get_ref().sched_id()); + // Should not have any messages + let message = stask.sched.get_mut_ref().message_queue.pop(); + assert!(message.is_none()); + stask.destroyed = true; } @@ -336,11 +340,14 @@ impl Scheduler { match this.message_queue.pop() { Some(PinnedTask(task)) => { let mut task = task; + this.event_loop.callback(Scheduler::run_sched_once); task.give_home(Sched(this.make_handle())); this.resume_task_immediately(task); return None; } Some(TaskFromFriend(task)) => { + this.event_loop.callback(Scheduler::run_sched_once); + rtdebug!("got a task from a friend. lovely!"); return this.sched_schedule_task(task); } Some(Wake) => { @@ -395,6 +402,7 @@ impl Scheduler { /// Take a non-homed task we aren't allowed to run here and send /// it to the designated friend scheduler to execute. fn send_to_friend(&mut self, task: ~Task) { + rtdebug!("sending a task to friend"); match self.friend_handle { Some(ref mut handle) => { handle.send(TaskFromFriend(task)); @@ -426,12 +434,14 @@ impl Scheduler { Scheduler::send_task_home(task); return Some(this); } else { + this.event_loop.callback(Scheduler::run_sched_once); task.give_home(Sched(home_handle)); this.resume_task_immediately(task); return None; } } AnySched if this.run_anything => { + this.event_loop.callback(Scheduler::run_sched_once); task.give_home(AnySched); this.resume_task_immediately(task); return None; From 2f8346b949169bd0f4b8fe0630eb4bc49ae35f11 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sat, 3 Aug 2013 23:50:28 -0700 Subject: [PATCH 12/19] std::rt: Remove the test for context() This is no longer testable once newsched is turned on --- src/libstd/rt/mod.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 4cfe0efacfef..33e83fd90404 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -432,13 +432,3 @@ pub fn context() -> RuntimeContext { pub fn rust_try_get_task() -> *rust_task; } } - -#[test] -fn test_context() { - use unstable::run_in_bare_thread; - - assert_eq!(context(), OldTaskContext); - do run_in_bare_thread { - assert_eq!(context(), GlobalContext); - } -} From 3f4c6cead67cedea5042d5ba84fb490dfee5462a Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sun, 4 Aug 2013 00:40:10 -0700 Subject: [PATCH 13/19] Remove old tests and code for `select` Not compatible with newsched --- src/libextra/comm.rs | 9 +-- src/libstd/comm.rs | 110 +------------------------------- src/libstd/pipes.rs | 44 ------------- src/test/run-pass/issue-3176.rs | 39 ----------- 4 files changed, 3 insertions(+), 199 deletions(-) delete mode 100644 src/test/run-pass/issue-3176.rs diff --git a/src/libextra/comm.rs b/src/libextra/comm.rs index 44581efc6f07..776e25cac890 100644 --- a/src/libextra/comm.rs +++ b/src/libextra/comm.rs @@ -18,9 +18,8 @@ Higher level communication abstractions. use std::comm::{GenericChan, GenericSmartChan, GenericPort}; -use std::comm::{Chan, Port, Selectable, Peekable}; +use std::comm::{Chan, Port, Peekable}; use std::comm; -use std::pipes; /// An extension of `pipes::stream` that allows both sending and receiving. pub struct DuplexStream { @@ -75,12 +74,6 @@ impl Peekable for DuplexStream { } } -impl Selectable for DuplexStream { - fn header(&mut self) -> *mut pipes::PacketHeader { - self.port.header() - } -} - /// Creates a bidirectional stream. pub fn DuplexStream() -> (DuplexStream, DuplexStream) diff --git a/src/libstd/comm.rs b/src/libstd/comm.rs index a0731dc3494c..4356f1143da4 100644 --- a/src/libstd/comm.rs +++ b/src/libstd/comm.rs @@ -14,7 +14,6 @@ Message passing #[allow(missing_doc)]; -use cast::transmute; use either::{Either, Left, Right}; use kinds::Send; use option::{Option, Some}; @@ -23,12 +22,6 @@ pub use rt::comm::SendDeferred; use rtcomm = rt::comm; use rt; -use pipes::{wait_many, PacketHeader}; - -// FIXME #5160: Making this public exposes some plumbing from -// pipes. Needs some refactoring -pub use pipes::Selectable; - /// A trait for things that can send multiple messages. pub trait GenericChan { /// Sends a message. @@ -146,15 +139,6 @@ impl Peekable for Port { } } -impl Selectable for Port { - fn header(&mut self) -> *mut PacketHeader { - match self.inner { - Left(ref mut port) => port.header(), - Right(_) => fail!("can't select on newsched ports") - } - } -} - /// A channel that can be shared between many senders. pub struct SharedChan { inner: Either>, rtcomm::SharedChan> @@ -318,8 +302,8 @@ mod pipesy { use kinds::Send; use option::{Option, Some, None}; - use pipes::{recv, try_recv, peek, PacketHeader}; - use super::{GenericChan, GenericSmartChan, GenericPort, Peekable, Selectable}; + use pipes::{recv, try_recv, peek}; + use super::{GenericChan, GenericSmartChan, GenericPort, Peekable}; use cast::transmute_mut; /*proto! oneshot ( @@ -651,80 +635,6 @@ mod pipesy { } } - impl Selectable for Port { - fn header(&mut self) -> *mut PacketHeader { - match self.endp { - Some(ref mut endp) => endp.header(), - None => fail!("peeking empty stream") - } - } -} - -} - -/// Returns the index of an endpoint that is ready to receive. -pub fn selecti(endpoints: &mut [T]) -> uint { - wait_many(endpoints) -} - -/// Returns 0 or 1 depending on which endpoint is ready to receive -pub fn select2i(a: &mut A, b: &mut B) - -> Either<(), ()> { - let mut endpoints = [ a.header(), b.header() ]; - match wait_many(endpoints) { - 0 => Left(()), - 1 => Right(()), - _ => fail!("wait returned unexpected index"), - } -} - -/// Receive a message from one of two endpoints. -pub trait Select2 { - /// Receive a message or return `None` if a connection closes. - fn try_select(&mut self) -> Either, Option>; - /// Receive a message or fail if a connection closes. - fn select(&mut self) -> Either; -} - -impl, - Right:Selectable + GenericPort> - Select2 - for (Left, Right) { - fn select(&mut self) -> Either { - // XXX: Bad borrow check workaround. - unsafe { - let this: &(Left, Right) = transmute(self); - match *this { - (ref lp, ref rp) => { - let lp: &mut Left = transmute(lp); - let rp: &mut Right = transmute(rp); - match select2i(lp, rp) { - Left(()) => Left(lp.recv()), - Right(()) => Right(rp.recv()), - } - } - } - } - } - - fn try_select(&mut self) -> Either, Option> { - // XXX: Bad borrow check workaround. - unsafe { - let this: &(Left, Right) = transmute(self); - match *this { - (ref lp, ref rp) => { - let lp: &mut Left = transmute(lp); - let rp: &mut Right = transmute(rp); - match select2i(lp, rp) { - Left(()) => Left (lp.try_recv()), - Right(()) => Right(rp.try_recv()), - } - } - } - } - } } #[cfg(test)] @@ -732,22 +642,6 @@ mod test { use either::Right; use super::{Chan, Port, oneshot, stream}; - #[test] - fn test_select2() { - let (p1, c1) = stream(); - let (p2, c2) = stream(); - - c1.send(~"abc"); - - let mut tuple = (p1, p2); - match tuple.select() { - Right(_) => fail!(), - _ => (), - } - - c2.send(123); - } - #[test] fn test_oneshot() { let (p, c) = oneshot(); diff --git a/src/libstd/pipes.rs b/src/libstd/pipes.rs index 1fd534825a54..78f937e058a4 100644 --- a/src/libstd/pipes.rs +++ b/src/libstd/pipes.rs @@ -868,47 +868,3 @@ pub mod rt { pub fn make_some(val: T) -> Option { Some(val) } pub fn make_none() -> Option { None } } - -#[cfg(test)] -mod test { - use either::Right; - use comm::{Chan, Port, oneshot, recv_one, stream, Select2, - GenericChan, Peekable}; - - #[test] - fn test_select2() { - let (p1, c1) = stream(); - let (p2, c2) = stream(); - - c1.send(~"abc"); - - let mut tuple = (p1, p2); - match tuple.select() { - Right(_) => fail!(), - _ => (), - } - - c2.send(123); - } - - #[test] - fn test_oneshot() { - let (p, c) = oneshot(); - - c.send(()); - - recv_one(p) - } - - #[test] - fn test_peek_terminated() { - let (port, chan): (Port, Chan) = stream(); - - { - // Destroy the channel - let _chan = chan; - } - - assert!(!port.peek()); - } -} diff --git a/src/test/run-pass/issue-3176.rs b/src/test/run-pass/issue-3176.rs deleted file mode 100644 index df242ee3d305..000000000000 --- a/src/test/run-pass/issue-3176.rs +++ /dev/null @@ -1,39 +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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// xfail-fast -// xfail-win32 #7999 - -use std::comm::{Select2, Selectable}; -use std::comm; -use std::task; - -pub fn main() { - let (p,c) = comm::stream(); - do task::try || { - let (p2,c2) = comm::stream(); - do task::spawn || { - p2.recv(); - error!("sibling fails"); - fail!(); - } - let (p3,c3) = comm::stream(); - c.send(c3); - c2.send(()); - error!("child blocks"); - let (p, c) = comm::stream(); - let mut tuple = (p, p3); - tuple.select(); - c.send(()); - }; - error!("parent tries"); - assert!(!p.recv().try_send(())); - error!("all done!"); -} From 9c39992021477740673a5b4982014678743c7fdf Mon Sep 17 00:00:00 2001 From: Luqman Aden Date: Thu, 1 Aug 2013 06:39:37 -0400 Subject: [PATCH 14/19] Add support for vanilla linux on arm. --- mk/platform.mk | 36 ++++++++++++++++++++++++++++++----- src/librustc/driver/driver.rs | 2 +- src/rt/arch/arm/record_sp.S | 14 ++++++++++++++ 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/mk/platform.mk b/mk/platform.mk index 61a170c21d51..6ee5420664d8 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -241,7 +241,7 @@ CFG_RUN_TARG_i686-apple-darwin=$(call CFG_RUN_i686-apple-darwin,,$(2)) # arm-linux-androideabi configuration CC_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-gcc CXX_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-g++ -CPP_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-gcc +CPP_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-gcc -E AR_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-ar CFG_LIB_NAME_arm-linux-androideabi=lib$(1).so CFG_LIB_GLOB_arm-linux-androideabi=lib$(1)-*.so @@ -272,7 +272,7 @@ AR_arm-unknown-linux-gnueabihf=arm-linux-gnueabihf-ar CFG_LIB_NAME_arm-unknown-linux-gnueabihf=lib$(1).so CFG_LIB_GLOB_arm-unknown-linux-gnueabihf=lib$(1)-*.so CFG_LIB_DSYM_GLOB_arm-unknown-linux-gnueabihf=lib$(1)-*.dylib.dSYM -CFG_GCCISH_CFLAGS_arm-unknown-linux-gnueabihf := -Wall -g -fPIC +CFG_GCCISH_CFLAGS_arm-unknown-linux-gnueabihf := -Wall -g -fPIC -D__arm__ CFG_GCCISH_CXXFLAGS_arm-unknown-linux-gnueabihf := -fno-rtti CFG_GCCISH_LINK_FLAGS_arm-unknown-linux-gnueabihf := -shared -fPIC -g CFG_GCCISH_DEF_FLAG_arm-unknown-linux-gnueabihf := -Wl,--export-dynamic,--dynamic-list= @@ -290,6 +290,32 @@ CFG_RUN_arm-unknown-linux-gnueabihf= CFG_RUN_TARG_arm-unknown-linux-gnueabihf= RUSTC_FLAGS_arm-unknown-linux-gnueabihf := --linker=$(CC_arm-unknown-linux-gnueabihf) +# arm-unknown-linux-gnueabi configuration +CC_arm-unknown-linux-gnueabi=arm-linux-gnueabi-gcc +CXX_arm-unknown-linux-gnueabi=arm-linux-gnueabi-g++ +CPP_arm-unknown-linux-gnueabi=arm-linux-gnueabi-gcc -E +AR_arm-unknown-linux-gnueabi=arm-linux-gnueabi-ar +CFG_LIB_NAME_arm-unknown-linux-gnueabi=lib$(1).so +CFG_LIB_GLOB_arm-unknown-linux-gnueabi=lib$(1)-*.so +CFG_LIB_DSYM_GLOB_arm-unknown-linux-gnueabi=lib$(1)-*.dylib.dSYM +CFG_GCCISH_CFLAGS_arm-unknown-linux-gnueabi := -Wall -g -fPIC -D__arm__ +CFG_GCCISH_CXXFLAGS_arm-unknown-linux-gnueabi := -fno-rtti +CFG_GCCISH_LINK_FLAGS_arm-unknown-linux-gnueabi := -shared -fPIC -g +CFG_GCCISH_DEF_FLAG_arm-unknown-linux-gnueabi := -Wl,--export-dynamic,--dynamic-list= +CFG_GCCISH_PRE_LIB_FLAGS_arm-unknown-linux-gnueabi := -Wl,-whole-archive +CFG_GCCISH_POST_LIB_FLAGS_arm-unknown-linux-gnueabi := -Wl,-no-whole-archive +CFG_DEF_SUFFIX_arm-unknown-linux-gnueabi := .linux.def +CFG_INSTALL_NAME_ar,-unknown-linux-gnueabi = +CFG_LIBUV_LINK_FLAGS_arm-unknown-linux-gnueabi = +CFG_EXE_SUFFIX_arm-unknown-linux-gnueabi := +CFG_WINDOWSY_arm-unknown-linux-gnueabi := +CFG_UNIXY_arm-unknown-linux-gnueabi := 1 +CFG_PATH_MUNGE_arm-unknown-linux-gnueabi := true +CFG_LDPATH_arm-unknown-linux-gnueabi := +CFG_RUN_arm-unknown-linux-gnueabi= +CFG_RUN_TARG_arm-unknown-linux-gnueabi= +RUSTC_FLAGS_arm-unknown-linux-gnueabi := --linker=$(CC_arm-unknown-linux-gnueabi) + # mips-unknown-linux-gnu configuration CC_mips-unknown-linux-gnu=mips-linux-gnu-gcc CXX_mips-unknown-linux-gnu=mips-linux-gnu-g++ @@ -450,7 +476,7 @@ define CFG_MAKE_TOOLCHAIN $$(CFG_GCCISH_DEF_FLAG_$(1))$$(3) $$(2) \ $$(call CFG_INSTALL_NAME_$(1),$$(4)) - ifneq ($(1),arm-linux-androideabi) + ifneq ($(HOST_$(1)),arm) # We're using llvm-mc as our assembler because it supports # .cfi pseudo-ops on mac @@ -462,9 +488,9 @@ define CFG_MAKE_TOOLCHAIN -o=$$(1) else - # For the Android cross, use the Android assembler + # For the ARM crosses, use the toolchain assembler # XXX: We should be able to use the LLVM assembler - CFG_ASSEMBLE_$(1)=$$(CPP_$(1)) $$(CFG_DEPEND_FLAGS) $$(2) -c -o $$(1) + CFG_ASSEMBLE_$(1)=$$(CC_$(1)) $$(CFG_DEPEND_FLAGS) $$(2) -c -o $$(1) endif diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 1c983057dba0..ea679d19f73b 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -335,7 +335,7 @@ pub fn phase_5_run_llvm_passes(sess: Session, outputs: &OutputFilenames) { // NB: Android hack - if sess.targ_cfg.arch == abi::Arm && + if sess.targ_cfg.os == session::os_android && (sess.opts.output_type == link::output_type_object || sess.opts.output_type == link::output_type_exe) { let output_type = link::output_type_assembly; diff --git a/src/rt/arch/arm/record_sp.S b/src/rt/arch/arm/record_sp.S index 8d5f24bc5a8c..3c5c7644bebf 100644 --- a/src/rt/arch/arm/record_sp.S +++ b/src/rt/arch/arm/record_sp.S @@ -14,22 +14,36 @@ .globl get_sp record_sp_limit: + // First, try to read TLS address from coprocessor mrc p15, #0, r3, c13, c0, #3 + cmp r3, #0 + // Otherwise, try to read from magic address 0xFFFF0FF0 + mvneq r3, #0xF000 + ldreq r3, [r3, #-15] + #if __ANDROID__ add r3, r3, #252 #elif __linux__ add r3, r3, #4 #endif + str r0, [r3] mov pc, lr get_sp_limit: + // First, try to read TLS address from coprocessor mrc p15, #0, r3, c13, c0, #3 + cmp r3, #0 + // Otherwise, try to read from magic address 0xFFFF0FF0 + mvneq r3, #0xF000 + ldreq r3, [r3, #-15] + #if __ANDROID__ add r3, r3, #252 #elif __linux__ add r3, r3, #4 #endif + ldr r0, [r3] mov pc, lr From 4898a0de04600afefcb095b55ea0d924f125a892 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sun, 4 Aug 2013 18:38:06 -0700 Subject: [PATCH 15/19] extra: Don't recurse in DList drop glue. #8295 The compiler-generated dtor for DList recurses deeply to drop Nodes. For big lists this can overflow the stack. --- src/libextra/dlist.rs | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/src/libextra/dlist.rs b/src/libextra/dlist.rs index 4142bdadaf63..75487a44f260 100644 --- a/src/libextra/dlist.rs +++ b/src/libextra/dlist.rs @@ -92,6 +92,11 @@ impl Rawlink { Some(unsafe { cast::transmute(self.p) }) } } + + /// Return the `Rawlink` and replace with `Rawlink::none()` + fn take(&mut self) -> Rawlink { + util::replace(self, Rawlink::none()) + } } impl Clone for Rawlink { @@ -280,13 +285,16 @@ impl DList { /// Add all elements from `other` to the end of the list /// /// O(1) - pub fn append(&mut self, other: DList) { + pub fn append(&mut self, mut other: DList) { match self.list_tail.resolve() { None => *self = other, Some(tail) => { - match other { - DList{list_head: None, _} => return, - DList{list_head: Some(node), list_tail: o_tail, length: o_length} => { + // Carefully empty `other`. + let o_tail = other.list_tail.take(); + let o_length = other.length; + match other.list_head.take() { + None => return, + Some(node) => { tail.next = link_with_prev(node, self.list_tail); self.list_tail = o_tail; self.length += o_length; @@ -404,6 +412,32 @@ impl DList { } } +#[unsafe_destructor] +impl Drop for DList { + fn drop(&self) { + let mut_self = unsafe { + cast::transmute_mut(self) + }; + // Dissolve the dlist in backwards direction + // Just dropping the list_head can lead to stack exhaustion + // when length is >> 1_000_000 + let mut tail = mut_self.list_tail; + loop { + match tail.resolve() { + None => break, + Some(prev) => { + prev.next.take(); // release ~Node + tail = prev.prev; + } + } + } + mut_self.length = 0; + mut_self.list_head = None; + mut_self.list_tail = Rawlink::none(); + } +} + + impl<'self, A> Iterator<&'self A> for DListIterator<'self, A> { #[inline] fn next(&mut self) -> Option<&'self A> { From d123df26ff1796f4712b10290a3b6647d754a6e3 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sun, 4 Aug 2013 21:54:59 -0700 Subject: [PATCH 16/19] std: Fix newsched logging truncation The truncation needs to be done in the console logger in order to catch all the logging output, and because truncation only matters when outputting to the console. --- src/libstd/logging.rs | 10 ---------- src/libstd/rt/logging.rs | 21 +++++++++++++++++---- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/libstd/logging.rs b/src/libstd/logging.rs index c662e5997afa..6e11d14aea9a 100644 --- a/src/libstd/logging.rs +++ b/src/libstd/logging.rs @@ -85,16 +85,6 @@ pub fn log_type(level: u32, object: &T) { fn newsched_log_str(msg: ~str) { use rt::task::Task; use rt::local::Local; - use str::StrSlice; - use container::Container; - - // Truncate the string - let buf_bytes = 256; - let msg = if msg.len() > buf_bytes { - msg.slice(0, buf_bytes) + "[...]" - } else { - msg - }; unsafe { match Local::try_unsafe_borrow::() { diff --git a/src/libstd/rt/logging.rs b/src/libstd/rt/logging.rs index 11d11daebc25..9056f0d52e09 100644 --- a/src/libstd/rt/logging.rs +++ b/src/libstd/rt/logging.rs @@ -10,6 +10,7 @@ use either::*; use libc; +use str::StrSlice; pub trait Logger { fn log(&mut self, msg: Either<~str, &'static str>); @@ -35,10 +36,22 @@ impl Logger for StdErrLogger { s } }; - let dbg = ::libc::STDERR_FILENO as ::io::fd_t; - dbg.write_str(s); - dbg.write_str("\n"); - dbg.flush(); + + // Truncate the string + let buf_bytes = 256; + if s.len() > buf_bytes { + let s = s.slice(0, buf_bytes) + "[...]"; + print(s); + } else { + print(s) + }; + + fn print(s: &str) { + let dbg = ::libc::STDERR_FILENO as ::io::fd_t; + dbg.write_str(s); + dbg.write_str("\n"); + dbg.flush(); + } } } From 167bdff04164c92c203c319b730bdc2344dbe089 Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Sun, 4 Aug 2013 21:55:52 -0700 Subject: [PATCH 17/19] std::rt: Schedule more scheduler callbacks to avoid dropping messages --- src/libstd/rt/sched.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs index a5c8abc2a6c0..1a75f2569b59 100644 --- a/src/libstd/rt/sched.rs +++ b/src/libstd/rt/sched.rs @@ -339,8 +339,8 @@ impl Scheduler { let mut this = self; match this.message_queue.pop() { Some(PinnedTask(task)) => { - let mut task = task; this.event_loop.callback(Scheduler::run_sched_once); + let mut task = task; task.give_home(Sched(this.make_handle())); this.resume_task_immediately(task); return None; @@ -351,10 +351,12 @@ impl Scheduler { return this.sched_schedule_task(task); } Some(Wake) => { + this.event_loop.callback(Scheduler::run_sched_once); this.sleepy = false; return Some(this); } Some(Shutdown) => { + this.event_loop.callback(Scheduler::run_sched_once); if this.sleepy { // There may be an outstanding handle on the // sleeper list. Pop them all to make sure that's From 1c1add23f60d87bdb215f627896792748004552c Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Mon, 5 Aug 2013 00:32:46 -0700 Subject: [PATCH 18/19] std::rt: Use magic to make TLS work from annihilated boxes. #8302 --- src/libstd/rt/task.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index ffe9f118b477..cb949edd7bb4 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -228,6 +228,19 @@ impl Task { _ => () } + // FIXME #8302: Dear diary. I'm so tired and confused. + // There's some interaction in rustc between the box + // annihilator and the TLS dtor by which TLS is + // accessed from annihilated box dtors *after* TLS is + // destroyed. Somehow setting TLS back to null, as the + // old runtime did, makes this work, but I don't currently + // understand how. I would expect that, if the annihilator + // reinvokes TLS while TLS is uninitialized, that + // TLS would be reinitialized but never destroyed, + // but somehow this works. I have no idea what's going + // on but this seems to make things magically work. FML. + self.storage = LocalStorage(ptr::null(), None); + // Destroy remaining boxes. Also may run user dtors. unsafe { cleanup::annihilate(); } } From eab97b5558d001136179aeb1d4e478670a43d54a Mon Sep 17 00:00:00 2001 From: OGINO Masanori Date: Mon, 5 Aug 2013 17:43:40 +0900 Subject: [PATCH 19/19] Add extra::arena::Arena::new{, _with_size}. Signed-off-by: OGINO Masanori --- src/libextra/arena.rs | 46 ++++++++++++------------ src/test/bench/shootout-binarytrees.rs | 8 ++--- src/test/run-pass/placement-new-arena.rs | 4 +-- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/libextra/arena.rs b/src/libextra/arena.rs index 47b64d769516..ae4356eb4bac 100644 --- a/src/libextra/arena.rs +++ b/src/libextra/arena.rs @@ -67,17 +67,16 @@ pub struct Arena { priv chunks: @mut MutList, } -#[unsafe_destructor] -impl Drop for Arena { - fn drop(&self) { - unsafe { - destroy_chunk(&self.head); - do self.chunks.each |chunk| { - if !chunk.is_pod { - destroy_chunk(chunk); - } - true - }; +impl Arena { + pub fn new() -> Arena { + Arena::new_with_size(32u) + } + + pub fn new_with_size(initial_size: uint) -> Arena { + Arena { + head: chunk(initial_size, false), + pod_head: chunk(initial_size, true), + chunks: @mut MutNil, } } } @@ -92,18 +91,21 @@ fn chunk(size: uint, is_pod: bool) -> Chunk { } } -pub fn arena_with_size(initial_size: uint) -> Arena { - Arena { - head: chunk(initial_size, false), - pod_head: chunk(initial_size, true), - chunks: @mut MutNil, +#[unsafe_destructor] +impl Drop for Arena { + fn drop(&self) { + unsafe { + destroy_chunk(&self.head); + do self.chunks.each |chunk| { + if !chunk.is_pod { + destroy_chunk(chunk); + } + true + }; + } } } -pub fn Arena() -> Arena { - arena_with_size(32u) -} - #[inline] fn round_up_to(base: uint, align: uint) -> uint { (base + (align - 1)) & !(align - 1) @@ -276,7 +278,7 @@ impl Arena { #[test] fn test_arena_destructors() { - let arena = Arena(); + let arena = Arena::new(); for i in range(0u, 10) { // Arena allocate something with drop glue to make sure it // doesn't leak. @@ -291,7 +293,7 @@ fn test_arena_destructors() { #[should_fail] #[ignore(cfg(windows))] fn test_arena_destructors_fail() { - let arena = Arena(); + let arena = Arena::new(); // Put some stuff in the arena. for i in range(0u, 10) { // Arena allocate something with drop glue to make sure it diff --git a/src/test/bench/shootout-binarytrees.rs b/src/test/bench/shootout-binarytrees.rs index 596a5b5422a5..57bf33fb2fda 100644 --- a/src/test/bench/shootout-binarytrees.rs +++ b/src/test/bench/shootout-binarytrees.rs @@ -9,7 +9,7 @@ // except according to those terms. extern mod extra; -use extra::arena; +use extra::arena::Arena; enum Tree<'self> { Nil, @@ -25,7 +25,7 @@ fn item_check(t: &Tree) -> int { } } -fn bottom_up_tree<'r>(arena: &'r arena::Arena, item: int, depth: int) +fn bottom_up_tree<'r>(arena: &'r Arena, item: int, depth: int) -> &'r Tree<'r> { if depth > 0 { return arena.alloc( @@ -57,7 +57,7 @@ fn main() { max_depth = n; } - let stretch_arena = arena::Arena(); + let stretch_arena = Arena::new(); let stretch_depth = max_depth + 1; let stretch_tree = bottom_up_tree(&stretch_arena, 0, stretch_depth); @@ -65,7 +65,7 @@ fn main() { stretch_depth, item_check(stretch_tree)); - let long_lived_arena = arena::Arena(); + let long_lived_arena = Arena::new(); let long_lived_tree = bottom_up_tree(&long_lived_arena, 0, max_depth); let mut depth = min_depth; while depth <= max_depth { diff --git a/src/test/run-pass/placement-new-arena.rs b/src/test/run-pass/placement-new-arena.rs index 9500f83b76b0..f2063b583e4f 100644 --- a/src/test/run-pass/placement-new-arena.rs +++ b/src/test/run-pass/placement-new-arena.rs @@ -11,10 +11,10 @@ // except according to those terms. extern mod extra; -use extra::arena; +use extra::arena::Arena; pub fn main() { - let mut arena = arena::Arena(); + let mut arena = Arena::new(); let p = &mut arena; let x = p.alloc(|| 4u); printf!("%u", *x);