Merge remote-tracking branch 'remotes/origin/master' into remove-str-trailing-nulls

This commit is contained in:
Erick Tryzelaar 2013-08-08 19:27:03 -07:00
commit 56730c094c
159 changed files with 3803 additions and 910 deletions

View file

@ -12,7 +12,7 @@
use clone::Clone;
use container::Container;
use iterator::{Iterator, range};
use iterator::Iterator;
use option::{Option, Some, None};
use sys;
use unstable::raw::Repr;
@ -92,8 +92,8 @@ pub fn append<T:Clone>(lhs: @[T], rhs: &[T]) -> @[T] {
for x in lhs.iter() {
push((*x).clone());
}
for i in range(0u, rhs.len()) {
push(rhs[i].clone());
for elt in rhs.iter() {
push(elt.clone());
}
}
}

View file

@ -314,7 +314,7 @@ mod pipesy {
#[allow(non_camel_case_types)]
pub mod oneshot {
priv use std::kinds::Send;
use std::kinds::Send;
use ptr::to_mut_unsafe_ptr;
pub fn init<T: Send>() -> (server::Oneshot<T>, client::Oneshot<T>) {
@ -341,7 +341,7 @@ mod pipesy {
#[allow(non_camel_case_types)]
pub mod client {
priv use std::kinds::Send;
use std::kinds::Send;
#[allow(non_camel_case_types)]
pub fn try_send<T: Send>(pipe: Oneshot<T>, x_0: T) ->
@ -489,7 +489,7 @@ mod pipesy {
#[allow(non_camel_case_types)]
pub mod streamp {
priv use std::kinds::Send;
use std::kinds::Send;
pub fn init<T: Send>() -> (server::Open<T>, client::Open<T>) {
pub use std::pipes::HasBuffer;
@ -501,7 +501,7 @@ mod pipesy {
#[allow(non_camel_case_types)]
pub mod client {
priv use std::kinds::Send;
use std::kinds::Send;
#[allow(non_camel_case_types)]
pub fn try_data<T: Send>(pipe: Open<T>, x_0: T) ->

View file

@ -24,7 +24,7 @@ use vec;
use vec::{OwnedVector, ImmutableVector};
/// `Either` is a type that represents one of two alternatives
#[deriving(Clone, Eq)]
#[deriving(Clone, Eq, IterBytes)]
pub enum Either<L, R> {
Left(L),
Right(R)

368
src/libstd/fmt/mod.rs Normal file
View file

@ -0,0 +1,368 @@
// Copyright 2013 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.
use prelude::*;
use cast;
use int;
use rt::io::Decorator;
use rt::io::mem::MemWriter;
use rt::io;
use str;
use sys;
use uint;
use util;
use vec;
pub mod parse;
pub mod rt;
/// A struct to represent both where to emit formatting strings to and how they
/// should be formatted. A mutable version of this is passed to all formatting
/// traits.
pub struct Formatter<'self> {
/// Flags for formatting (packed version of rt::Flag)
flags: uint,
/// Character used as 'fill' whenever there is alignment
fill: char,
/// Boolean indication of whether the output should be left-aligned
alignleft: bool,
/// Optionally specified integer width that the output should be
width: Option<uint>,
/// Optionally specified precision for numeric types
precision: Option<uint>,
/// Output buffer.
buf: &'self mut io::Writer,
priv curarg: vec::VecIterator<'self, Argument<'self>>,
priv args: &'self [Argument<'self>],
}
/// This struct represents the generic "argument" which is taken by the Xprintf
/// family of functions. It contains a function to format the given value. At
/// compile time it is ensured that the function and the value have the correct
/// types, and then this struct is used to canonicalize arguments to one type.
pub struct Argument<'self> {
priv formatter: extern "Rust" fn(&util::Void, &mut Formatter),
priv value: &'self util::Void,
}
#[allow(missing_doc)]
pub trait Bool { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait Char { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait Signed { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait Unsigned { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait Octal { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait Binary { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait LowerHex { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait UpperHex { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait String { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait Poly { fn fmt(&Self, &mut Formatter); }
#[allow(missing_doc)]
pub trait Pointer { fn fmt(&Self, &mut Formatter); }
/// The sprintf function takes a precompiled format string and a list of
/// arguments, to return the resulting formatted string.
///
/// This is currently an unsafe function because the types of all arguments
/// aren't verified by immediate callers of this function. This currently does
/// not validate that the correct types of arguments are specified for each
/// format specifier, nor that each argument itself contains the right function
/// for formatting the right type value. Because of this, the function is marked
/// as `unsafe` if this is being called manually.
///
/// Thankfully the rust compiler provides the macro `ifmt!` which will perform
/// all of this validation at compile-time and provides a safe interface for
/// invoking this function.
///
/// # Arguments
///
/// * fmts - the precompiled format string to emit.
/// * args - the list of arguments to the format string. These are only the
/// positional arguments (not named)
///
/// Note that this function assumes that there are enough arguments for the
/// format string.
pub unsafe fn sprintf(fmt: &[rt::Piece], args: &[Argument]) -> ~str {
let output = MemWriter::new();
{
let mut formatter = Formatter {
flags: 0,
width: None,
precision: None,
// FIXME(#8248): shouldn't need a transmute
buf: cast::transmute(&output as &io::Writer),
alignleft: false,
fill: ' ',
args: args,
curarg: args.iter(),
};
for piece in fmt.iter() {
formatter.run(piece, None);
}
}
return str::from_bytes_owned(output.inner());
}
impl<'self> Formatter<'self> {
fn run(&mut self, piece: &rt::Piece, cur: Option<&str>) {
let setcount = |slot: &mut Option<uint>, cnt: &parse::Count| {
match *cnt {
parse::CountIs(n) => { *slot = Some(n); }
parse::CountImplied => { *slot = None; }
parse::CountIsParam(i) => {
let v = self.args[i].value;
unsafe { *slot = Some(*(v as *util::Void as *uint)); }
}
parse::CountIsNextParam => {
let v = self.curarg.next().unwrap().value;
unsafe { *slot = Some(*(v as *util::Void as *uint)); }
}
}
};
match *piece {
rt::String(s) => { self.buf.write(s.as_bytes()); }
rt::CurrentArgument(()) => { self.buf.write(cur.unwrap().as_bytes()); }
rt::Argument(ref arg) => {
// Fill in the format parameters into the formatter
self.fill = arg.format.fill;
self.alignleft = arg.format.alignleft;
self.flags = arg.format.flags;
setcount(&mut self.width, &arg.format.width);
setcount(&mut self.precision, &arg.format.precision);
// Extract the correct argument
let value = match arg.position {
rt::ArgumentNext => { *self.curarg.next().unwrap() }
rt::ArgumentIs(i) => self.args[i],
};
// Then actually do some printing
match arg.method {
None => { (value.formatter)(value.value, self); }
Some(ref method) => { self.execute(*method, value); }
}
}
}
}
fn execute(&mut self, method: &rt::Method, arg: Argument) {
match *method {
// Pluralization is selection upon a numeric value specified as the
// parameter.
rt::Plural(offset, ref selectors, ref default) => {
// This is validated at compile-time to be a pointer to a
// '&uint' value.
let value: &uint = unsafe { cast::transmute(arg.value) };
let value = *value;
// First, attempt to match against explicit values without the
// offsetted value
for s in selectors.iter() {
match s.selector {
Right(val) if value == val => {
return self.runplural(value, s.result);
}
_ => {}
}
}
// Next, offset the value and attempt to match against the
// keyword selectors.
let value = value - match offset { Some(i) => i, None => 0 };
for s in selectors.iter() {
let run = match s.selector {
Left(parse::Zero) => value == 0,
Left(parse::One) => value == 1,
Left(parse::Two) => value == 2,
// XXX: Few/Many should have a user-specified boundary
// One possible option would be in the function
// pointer of the 'arg: Argument' struct.
Left(parse::Few) => value < 8,
Left(parse::Many) => value >= 8,
Right(*) => false
};
if run {
return self.runplural(value, s.result);
}
}
self.runplural(value, *default);
}
// Select is just a matching against the string specified.
rt::Select(ref selectors, ref default) => {
// This is validated at compile-time to be a pointer to a
// string slice,
let value: & &str = unsafe { cast::transmute(arg.value) };
let value = *value;
for s in selectors.iter() {
if s.selector == value {
for piece in s.result.iter() {
self.run(piece, Some(value));
}
return;
}
}
for piece in default.iter() {
self.run(piece, Some(value));
}
}
}
}
fn runplural(&mut self, value: uint, pieces: &[rt::Piece]) {
do uint::to_str_bytes(value, 10) |buf| {
let valuestr = str::from_bytes_slice(buf);
for piece in pieces.iter() {
self.run(piece, Some(valuestr));
}
}
}
}
/// This is a function which calls are emitted to by the compiler itself to
/// create the Argument structures that are passed into the `sprintf` function.
#[doc(hidden)]
pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter),
t: &'a T) -> Argument<'a> {
unsafe {
Argument {
formatter: cast::transmute(f),
value: cast::transmute(t)
}
}
}
/// When the compiler determines that the type of an argument *must* be a string
/// (such as for select), then it invokes this method.
#[doc(hidden)]
pub fn argumentstr<'a>(s: &'a &str) -> Argument<'a> {
argument(String::fmt, s)
}
/// When the compiler determines that the type of an argument *must* be a uint
/// (such as for plural), then it invokes this method.
#[doc(hidden)]
pub fn argumentuint<'a>(s: &'a uint) -> Argument<'a> {
argument(Unsigned::fmt, s)
}
// Implementations of the core formatting traits
impl Bool for bool {
fn fmt(b: &bool, f: &mut Formatter) {
String::fmt(&(if *b {"true"} else {"false"}), f);
}
}
impl<'self> String for &'self str {
fn fmt(s: & &'self str, f: &mut Formatter) {
// XXX: formatting args
f.buf.write(s.as_bytes())
}
}
impl Char for char {
fn fmt(c: &char, f: &mut Formatter) {
// XXX: formatting args
// XXX: shouldn't require an allocation
let mut s = ~"";
s.push_char(*c);
f.buf.write(s.as_bytes());
}
}
impl Signed for int {
fn fmt(c: &int, f: &mut Formatter) {
// XXX: formatting args
do int::to_str_bytes(*c, 10) |buf| {
f.buf.write(buf);
}
}
}
impl Unsigned for uint {
fn fmt(c: &uint, f: &mut Formatter) {
// XXX: formatting args
do uint::to_str_bytes(*c, 10) |buf| {
f.buf.write(buf);
}
}
}
impl Octal for uint {
fn fmt(c: &uint, f: &mut Formatter) {
// XXX: formatting args
do uint::to_str_bytes(*c, 8) |buf| {
f.buf.write(buf);
}
}
}
impl LowerHex for uint {
fn fmt(c: &uint, f: &mut Formatter) {
// XXX: formatting args
do uint::to_str_bytes(*c, 16) |buf| {
f.buf.write(buf);
}
}
}
impl UpperHex for uint {
fn fmt(c: &uint, f: &mut Formatter) {
// XXX: formatting args
do uint::to_str_bytes(*c, 16) |buf| {
let mut local = [0u8, ..16];
for (l, &b) in local.mut_iter().zip(buf.iter()) {
*l = match b as char {
'a' .. 'f' => (b - 'a' as u8) + 'A' as u8,
_ => b,
};
}
f.buf.write(local.slice_to(buf.len()));
}
}
}
impl<T> Poly for T {
fn fmt(t: &T, f: &mut Formatter) {
// XXX: formatting args
let s = sys::log_str(t);
f.buf.write(s.as_bytes());
}
}
// n.b. use 'const' to get an implementation for both '*mut' and '*' at the same
// time.
impl<T> Pointer for *const T {
fn fmt(t: &*const T, f: &mut Formatter) {
// XXX: formatting args
f.buf.write("0x".as_bytes());
LowerHex::fmt(&(*t as uint), f);
}
}
// If you expected tests to be here, look instead at the run-pass/ifmt.rs test,
// it's a lot easier than creating all of the rt::Piece structures here.

896
src/libstd/fmt/parse.rs Normal file
View file

@ -0,0 +1,896 @@
// Copyright 2013 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.
use prelude::*;
use char;
use str;
use iterator;
condition! { pub parse_error: ~str -> (); }
/// A piece is a portion of the format string which represents the next part to
/// emit. These are emitted as a stream by the `Parser` class.
#[deriving(Eq)]
pub enum Piece<'self> {
/// A literal string which should directly be emitted
String(&'self str),
/// A back-reference to whatever the current argument is. This is used
/// inside of a method call to refer back to the original argument.
CurrentArgument,
/// This describes that formatting should process the next argument (as
/// specified inside) for emission.
Argument(Argument<'self>),
}
/// Representation of an argument specification.
#[deriving(Eq)]
pub struct Argument<'self> {
/// Where to find this argument
position: Position<'self>,
/// How to format the argument
format: FormatSpec<'self>,
/// If not `None`, what method to invoke on the argument
method: Option<~Method<'self>>
}
/// Specification for the formatting of an argument in the format string.
#[deriving(Eq)]
pub struct FormatSpec<'self> {
/// Optionally specified character to fill alignment with
fill: Option<char>,
/// Optionally specified alignment
align: Option<Alignment>,
/// Packed version of various flags provided
flags: uint,
/// The integer precision to use
precision: Count,
/// The string width requested for the resulting format
width: Count,
/// The descriptor string representing the name of the format desired for
/// this argument, this can be empty or any number of characters, although
/// it is required to be one word.
ty: &'self str
}
/// Enum describing where an argument for a format can be located.
#[deriving(Eq)]
pub enum Position<'self> {
ArgumentNext, ArgumentIs(uint), ArgumentNamed(&'self str)
}
/// Enum of alignments which are supoprted.
#[deriving(Eq)]
pub enum Alignment { AlignLeft, AlignRight }
/// Various flags which can be applied to format strings, the meaning of these
/// flags is defined by the formatters themselves.
#[deriving(Eq)]
pub enum Flag {
FlagSignPlus,
FlagSignMinus,
FlagAlternate,
}
/// A count is used for the precision and width parameters of an integer, and
/// can reference either an argument or a literal integer.
#[deriving(Eq)]
pub enum Count {
CountIs(uint),
CountIsParam(uint),
CountIsNextParam,
CountImplied,
}
/// Enum describing all of the possible methods which the formatting language
/// currently supports.
#[deriving(Eq)]
pub enum Method<'self> {
/// A plural method selects on an integer over a list of either integer or
/// keyword-defined clauses. The meaning of the keywords is defined by the
/// current locale.
///
/// An offset is optionally present at the beginning which is used to match
/// against keywords, but it is not matched against the literal integers.
///
/// The final element of this enum is the default "other" case which is
/// always required to be specified.
Plural(Option<uint>, ~[PluralArm<'self>], ~[Piece<'self>]),
/// A select method selects over a string. Each arm is a different string
/// which can be selected for.
///
/// As with `Plural`, a default "other" case is required as well.
Select(~[SelectArm<'self>], ~[Piece<'self>]),
}
/// Structure representing one "arm" of the `plural` function.
#[deriving(Eq)]
pub struct PluralArm<'self> {
/// A selector can either be specified by a keyword or with an integer
/// literal.
selector: Either<PluralKeyword, uint>,
/// Array of pieces which are the format of this arm
result: ~[Piece<'self>],
}
/// Enum of the 5 CLDR plural keywords. There is one more, "other", but that is
/// specially placed in the `Plural` variant of `Method`
///
/// http://www.icu-project.org/apiref/icu4c/classicu_1_1PluralRules.html
#[deriving(Eq, IterBytes)]
pub enum PluralKeyword {
Zero, One, Two, Few, Many
}
/// Structure representing one "arm" of the `select` function.
#[deriving(Eq)]
pub struct SelectArm<'self> {
/// String selector which guards this arm
selector: &'self str,
/// Array of pieces which are the format of this arm
result: ~[Piece<'self>],
}
/// The parser structure for interpreting the input format string. This is
/// modelled as an iterator over `Piece` structures to form a stream of tokens
/// being output.
///
/// This is a recursive-descent parser for the sake of simplicity, and if
/// necessary there's probably lots of room for improvement performance-wise.
pub struct Parser<'self> {
priv input: &'self str,
priv cur: str::CharOffsetIterator<'self>,
}
impl<'self> iterator::Iterator<Piece<'self>> for Parser<'self> {
fn next(&mut self) -> Option<Piece<'self>> {
match self.cur.clone().next() {
Some((_, '#')) => { self.cur.next(); Some(CurrentArgument) }
Some((_, '{')) => {
self.cur.next();
let ret = Some(Argument(self.argument()));
if !self.consume('}') {
self.err(~"unterminated format string");
}
ret
}
Some((pos, '\\')) => {
self.cur.next();
self.escape(); // ensure it's a valid escape sequence
Some(String(self.string(pos + 1))) // skip the '\' character
}
Some((_, '}')) | None => { None }
Some((pos, _)) => {
Some(String(self.string(pos)))
}
}
}
}
impl<'self> Parser<'self> {
/// Creates a new parser for the given format string
pub fn new<'a>(s: &'a str) -> Parser<'a> {
Parser {
input: s,
cur: s.char_offset_iter(),
}
}
/// Notifies of an error. The message doesn't actually need to be of type
/// ~str, but I think it does when this eventually uses conditions so it
/// might as well start using it now.
fn err(&self, msg: ~str) {
parse_error::cond.raise(msg);
}
/// Optionally consumes the specified character. If the character is not at
/// the current position, then the current iterator isn't moved and false is
/// returned, otherwise the character is consumed and true is returned.
fn consume(&mut self, c: char) -> bool {
match self.cur.clone().next() {
Some((_, maybe)) if c == maybe => {
self.cur.next();
true
}
Some(*) | None => false,
}
}
/// Attempts to consume any amount of whitespace followed by a character
fn wsconsume(&mut self, c: char) -> bool {
self.ws(); self.consume(c)
}
/// Consumes all whitespace characters until the first non-whitespace
/// character
fn ws(&mut self) {
loop {
match self.cur.clone().next() {
Some((_, c)) if char::is_whitespace(c) => { self.cur.next(); }
Some(*) | None => { return }
}
}
}
/// Consumes an escape sequence, failing if there is not a valid character
/// to be escaped.
fn escape(&mut self) -> char {
match self.cur.next() {
Some((_, c @ '#')) | Some((_, c @ '{')) |
Some((_, c @ '\\')) | Some((_, c @ '}')) => { c }
Some((_, c)) => {
self.err(fmt!("invalid escape character `%c`", c));
c
}
None => {
self.err(~"expected an escape sequence, but format string was \
terminated");
' '
}
}
}
/// Parses all of a string which is to be considered a "raw literal" in a
/// format string. This is everything outside of the braces.
fn string(&mut self, start: uint) -> &'self str {
loop {
// we may not consume the character, so clone the iterator
match self.cur.clone().next() {
Some((pos, '\\')) | Some((pos, '#')) |
Some((pos, '}')) | Some((pos, '{')) => {
return self.input.slice(start, pos);
}
Some(*) => { self.cur.next(); }
None => {
self.cur.next();
return self.input.slice(start, self.input.len());
}
}
}
}
/// Parses an Argument structure, or what's contained within braces inside
/// the format string
fn argument(&mut self) -> Argument<'self> {
Argument {
position: self.position(),
format: self.format(),
method: self.method(),
}
}
/// Parses a positional argument for a format. This could either be an
/// integer index of an argument, a named argument, or a blank string.
fn position(&mut self) -> Position<'self> {
match self.integer() {
Some(i) => { ArgumentIs(i) }
None => {
match self.cur.clone().next() {
Some((_, c)) if char::is_alphabetic(c) => {
ArgumentNamed(self.word())
}
_ => ArgumentNext
}
}
}
}
/// Parses a format specifier at the current position, returning all of the
/// relevant information in the FormatSpec struct.
fn format(&mut self) -> FormatSpec<'self> {
let mut spec = FormatSpec {
fill: None,
align: None,
flags: 0,
precision: CountImplied,
width: CountImplied,
ty: self.input.slice(0, 0),
};
if !self.consume(':') { return spec }
// fill character
match self.cur.clone().next() {
Some((_, c)) => {
match self.cur.clone().skip(1).next() {
Some((_, '>')) | Some((_, '<')) => {
spec.fill = Some(c);
self.cur.next();
}
Some(*) | None => {}
}
}
None => {}
}
// Alignment
if self.consume('<') {
spec.align = Some(AlignLeft);
} else if self.consume('>') {
spec.align = Some(AlignRight);
}
// Sign flags
if self.consume('+') {
spec.flags |= 1 << (FlagSignPlus as uint);
} else if self.consume('-') {
spec.flags |= 1 << (FlagSignMinus as uint);
}
// Alternate marker
if self.consume('#') {
spec.flags |= 1 << (FlagAlternate as uint);
}
// Width and precision
spec.width = self.count();
if self.consume('.') {
if self.consume('*') {
spec.precision = CountIsNextParam;
} else {
spec.precision = self.count();
}
}
// Finally the actual format specifier
spec.ty = self.word();
return spec;
}
/// Parses a method to be applied to the previously specified argument and
/// its format. The two current supported methods are 'plural' and 'select'
fn method(&mut self) -> Option<~Method<'self>> {
if !self.wsconsume(',') {
return None;
}
self.ws();
match self.word() {
"select" => {
if !self.wsconsume(',') {
self.err(~"`select` must be followed by `,`");
}
Some(self.select())
}
"plural" => {
if !self.wsconsume(',') {
self.err(~"`plural` must be followed by `,`");
}
Some(self.plural())
}
"" => {
self.err(~"expected method after comma");
return None;
}
method => {
self.err(fmt!("unknown method: `%s`", method));
return None;
}
}
}
/// Parses a 'select' statement (after the initial 'select' word)
fn select(&mut self) -> ~Method<'self> {
let mut other = None;
let mut arms = ~[];
// Consume arms one at a time
loop {
self.ws();
let selector = self.word();
if selector == "" {
self.err(~"cannot have an empty selector");
break
}
if !self.wsconsume('{') {
self.err(~"selector must be followed by `{`");
}
let pieces = self.collect();
if !self.wsconsume('}') {
self.err(~"selector case must be terminated by `}`");
}
if selector == "other" {
if !other.is_none() {
self.err(~"multiple `other` statements in `select");
}
other = Some(pieces);
} else {
arms.push(SelectArm { selector: selector, result: pieces });
}
self.ws();
match self.cur.clone().next() {
Some((_, '}')) => { break }
Some(*) | None => {}
}
}
// The "other" selector must be present
let other = match other {
Some(arm) => { arm }
None => {
self.err(~"`select` statement must provide an `other` case");
~[]
}
};
~Select(arms, other)
}
/// Parses a 'plural' statement (after the initial 'plural' word)
fn plural(&mut self) -> ~Method<'self> {
let mut offset = None;
let mut other = None;
let mut arms = ~[];
// First, attempt to parse the 'offset:' field. We know the set of
// selector words which can appear in plural arms, and the only ones
// which start with 'o' are "other" and "offset", hence look two
// characters deep to see if we can consume the word "offset"
self.ws();
let mut it = self.cur.clone();
match it.next() {
Some((_, 'o')) => {
match it.next() {
Some((_, 'f')) => {
let word = self.word();
if word != "offset" {
self.err(fmt!("expected `offset`, found `%s`",
word));
} else {
if !self.consume(':') {
self.err(~"`offset` must be followed by `:`");
}
match self.integer() {
Some(i) => { offset = Some(i); }
None => {
self.err(~"offset must be an integer");
}
}
}
}
Some(*) | None => {}
}
}
Some(*) | None => {}
}
// Next, generate all the arms
loop {
let mut isother = false;
let selector = if self.wsconsume('=') {
match self.integer() {
Some(i) => Right(i),
None => {
self.err(~"plural `=` selectors must be followed by an \
integer");
Right(0)
}
}
} else {
let word = self.word();
match word {
"other" => { isother = true; Left(Zero) }
"zero" => Left(Zero),
"one" => Left(One),
"two" => Left(Two),
"few" => Left(Few),
"many" => Left(Many),
word => {
self.err(fmt!("unexpected plural selector `%s`", word));
if word == "" {
break
} else {
Left(Zero)
}
}
}
};
if !self.wsconsume('{') {
self.err(~"selector must be followed by `{`");
}
let pieces = self.collect();
if !self.wsconsume('}') {
self.err(~"selector case must be terminated by `}`");
}
if isother {
if !other.is_none() {
self.err(~"multiple `other` statements in `select");
}
other = Some(pieces);
} else {
arms.push(PluralArm { selector: selector, result: pieces });
}
self.ws();
match self.cur.clone().next() {
Some((_, '}')) => { break }
Some(*) | None => {}
}
}
let other = match other {
Some(arm) => { arm }
None => {
self.err(~"`plural` statement must provide an `other` case");
~[]
}
};
~Plural(offset, arms, other)
}
/// Parses a Count parameter at the current position. This does not check
/// for 'CountIsNextParam' because that is only used in precision, not
/// width.
fn count(&mut self) -> Count {
match self.integer() {
Some(i) => {
if self.consume('$') {
CountIsParam(i)
} else {
CountIs(i)
}
}
None => { CountImplied }
}
}
/// Parses a word starting at the current position. A word is considered to
/// be an alphabetic character followed by any number of alphanumeric
/// characters.
fn word(&mut self) -> &'self str {
let start = match self.cur.clone().next() {
Some((pos, c)) if char::is_alphabetic(c) => {
self.cur.next();
pos
}
Some(*) | None => { return self.input.slice(0, 0); }
};
let mut end;
loop {
match self.cur.clone().next() {
Some((_, c)) if char::is_alphanumeric(c) => {
self.cur.next();
}
Some((pos, _)) => { end = pos; break }
None => { end = self.input.len(); break }
}
}
self.input.slice(start, end)
}
/// Optionally parses an integer at the current position. This doesn't deal
/// with overflow at all, it's just accumulating digits.
fn integer(&mut self) -> Option<uint> {
let mut cur = 0;
let mut found = false;
loop {
match self.cur.clone().next() {
Some((_, c)) => {
match char::to_digit(c, 10) {
Some(i) => {
cur = cur * 10 + i;
found = true;
self.cur.next();
}
None => { break }
}
}
None => { break }
}
}
if found {
return Some(cur);
} else {
return None;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use prelude::*;
use realstd::fmt::{String};
fn same(fmt: &'static str, p: ~[Piece<'static>]) {
let mut parser = Parser::new(fmt);
assert_eq!(p, parser.collect());
}
fn fmtdflt() -> FormatSpec<'static> {
return FormatSpec {
fill: None,
align: None,
flags: 0,
precision: CountImplied,
width: CountImplied,
ty: "",
}
}
fn musterr(s: &str) {
Parser::new(s).next();
}
#[test]
fn simple() {
same("asdf", ~[String("asdf")]);
same("a\\{b", ~[String("a"), String("{b")]);
same("a\\#b", ~[String("a"), String("#b")]);
same("a\\}b", ~[String("a"), String("}b")]);
same("a\\}", ~[String("a"), String("}")]);
same("\\}", ~[String("}")]);
}
#[test] #[should_fail] fn invalid01() { musterr("{") }
#[test] #[should_fail] fn invalid02() { musterr("\\") }
#[test] #[should_fail] fn invalid03() { musterr("\\a") }
#[test] #[should_fail] fn invalid04() { musterr("{3a}") }
#[test] #[should_fail] fn invalid05() { musterr("{:|}") }
#[test] #[should_fail] fn invalid06() { musterr("{:>>>}") }
#[test]
fn format_nothing() {
same("{}", ~[Argument(Argument {
position: ArgumentNext,
format: fmtdflt(),
method: None,
})]);
}
#[test]
fn format_position() {
same("{3}", ~[Argument(Argument {
position: ArgumentIs(3),
format: fmtdflt(),
method: None,
})]);
}
#[test]
fn format_position_nothing_else() {
same("{3:}", ~[Argument(Argument {
position: ArgumentIs(3),
format: fmtdflt(),
method: None,
})]);
}
#[test]
fn format_type() {
same("{3:a}", ~[Argument(Argument {
position: ArgumentIs(3),
format: FormatSpec {
fill: None,
align: None,
flags: 0,
precision: CountImplied,
width: CountImplied,
ty: "a",
},
method: None,
})]);
}
#[test]
fn format_align_fill() {
same("{3:>}", ~[Argument(Argument {
position: ArgumentIs(3),
format: FormatSpec {
fill: None,
align: Some(AlignRight),
flags: 0,
precision: CountImplied,
width: CountImplied,
ty: "",
},
method: None,
})]);
same("{3:0<}", ~[Argument(Argument {
position: ArgumentIs(3),
format: FormatSpec {
fill: Some('0'),
align: Some(AlignLeft),
flags: 0,
precision: CountImplied,
width: CountImplied,
ty: "",
},
method: None,
})]);
same("{3:*<abcd}", ~[Argument(Argument {
position: ArgumentIs(3),
format: FormatSpec {
fill: Some('*'),
align: Some(AlignLeft),
flags: 0,
precision: CountImplied,
width: CountImplied,
ty: "abcd",
},
method: None,
})]);
}
#[test]
fn format_counts() {
same("{:10s}", ~[Argument(Argument {
position: ArgumentNext,
format: FormatSpec {
fill: None,
align: None,
flags: 0,
precision: CountImplied,
width: CountIs(10),
ty: "s",
},
method: None,
})]);
same("{:10$.10s}", ~[Argument(Argument {
position: ArgumentNext,
format: FormatSpec {
fill: None,
align: None,
flags: 0,
precision: CountIs(10),
width: CountIsParam(10),
ty: "s",
},
method: None,
})]);
same("{:.*s}", ~[Argument(Argument {
position: ArgumentNext,
format: FormatSpec {
fill: None,
align: None,
flags: 0,
precision: CountIsNextParam,
width: CountImplied,
ty: "s",
},
method: None,
})]);
same("{:.10$s}", ~[Argument(Argument {
position: ArgumentNext,
format: FormatSpec {
fill: None,
align: None,
flags: 0,
precision: CountIsParam(10),
width: CountImplied,
ty: "s",
},
method: None,
})]);
}
#[test]
fn format_flags() {
same("{:-}", ~[Argument(Argument {
position: ArgumentNext,
format: FormatSpec {
fill: None,
align: None,
flags: (1 << FlagSignMinus as uint),
precision: CountImplied,
width: CountImplied,
ty: "",
},
method: None,
})]);
same("{:+#}", ~[Argument(Argument {
position: ArgumentNext,
format: FormatSpec {
fill: None,
align: None,
flags: (1 << FlagSignPlus as uint) | (1 << FlagAlternate as uint),
precision: CountImplied,
width: CountImplied,
ty: "",
},
method: None,
})]);
}
#[test]
fn format_mixture() {
same("abcd {3:a} efg", ~[String("abcd "), Argument(Argument {
position: ArgumentIs(3),
format: FormatSpec {
fill: None,
align: None,
flags: 0,
precision: CountImplied,
width: CountImplied,
ty: "a",
},
method: None,
}), String(" efg")]);
}
#[test]
fn select_simple() {
same("{, select, other { haha } }", ~[Argument(Argument{
position: ArgumentNext,
format: fmtdflt(),
method: Some(~Select(~[], ~[String(" haha ")]))
})]);
same("{1, select, other { haha } }", ~[Argument(Argument{
position: ArgumentIs(1),
format: fmtdflt(),
method: Some(~Select(~[], ~[String(" haha ")]))
})]);
same("{1, select, other {#} }", ~[Argument(Argument{
position: ArgumentIs(1),
format: fmtdflt(),
method: Some(~Select(~[], ~[CurrentArgument]))
})]);
same("{1, select, other {{2, select, other {lol}}} }", ~[Argument(Argument{
position: ArgumentIs(1),
format: fmtdflt(),
method: Some(~Select(~[], ~[Argument(Argument{
position: ArgumentIs(2),
format: fmtdflt(),
method: Some(~Select(~[], ~[String("lol")]))
})])) // wat
})]);
}
#[test]
fn select_cases() {
same("{1, select, a{1} b{2} c{3} other{4} }", ~[Argument(Argument{
position: ArgumentIs(1),
format: fmtdflt(),
method: Some(~Select(~[
SelectArm{ selector: "a", result: ~[String("1")] },
SelectArm{ selector: "b", result: ~[String("2")] },
SelectArm{ selector: "c", result: ~[String("3")] },
], ~[String("4")]))
})]);
}
#[test] #[should_fail] fn badselect01() {
musterr("{select, }")
}
#[test] #[should_fail] fn badselect02() {
musterr("{1, select}")
}
#[test] #[should_fail] fn badselect03() {
musterr("{1, select, }")
}
#[test] #[should_fail] fn badselect04() {
musterr("{1, select, a {}}")
}
#[test] #[should_fail] fn badselect05() {
musterr("{1, select, other }}")
}
#[test] #[should_fail] fn badselect06() {
musterr("{1, select, other {}")
}
#[test] #[should_fail] fn badselect07() {
musterr("{select, other {}")
}
#[test] #[should_fail] fn badselect08() {
musterr("{1 select, other {}")
}
#[test] #[should_fail] fn badselect09() {
musterr("{:d select, other {}")
}
#[test] #[should_fail] fn badselect10() {
musterr("{1:d select, other {}")
}
#[test]
fn plural_simple() {
same("{, plural, other { haha } }", ~[Argument(Argument{
position: ArgumentNext,
format: fmtdflt(),
method: Some(~Plural(None, ~[], ~[String(" haha ")]))
})]);
same("{:, plural, other { haha } }", ~[Argument(Argument{
position: ArgumentNext,
format: fmtdflt(),
method: Some(~Plural(None, ~[], ~[String(" haha ")]))
})]);
same("{, plural, offset:1 =2{2} =3{3} many{yes} other{haha} }",
~[Argument(Argument{
position: ArgumentNext,
format: fmtdflt(),
method: Some(~Plural(Some(1), ~[
PluralArm{ selector: Right(2), result: ~[String("2")] },
PluralArm{ selector: Right(3), result: ~[String("3")] },
PluralArm{ selector: Left(Many), result: ~[String("yes")] }
], ~[String("haha")]))
})]);
}
}

62
src/libstd/fmt/rt.rs Normal file
View file

@ -0,0 +1,62 @@
// Copyright 2013 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.
//! This is an internal module used by the ifmt! runtime. These structures are
//! emitted to static arrays to precompile format strings ahead of time.
//!
//! These definitions are similar to their `ct` equivalents, but differ in that
//! these can be statically allocated and are slightly optimized for the runtime
#[allow(missing_doc)];
#[doc(hidden)];
use either::Either;
use fmt::parse;
use option::Option;
pub enum Piece<'self> {
String(&'self str),
// FIXME(#8259): this shouldn't require the unit-value here
CurrentArgument(()),
Argument(Argument<'self>),
}
pub struct Argument<'self> {
position: Position,
format: FormatSpec,
method: Option<&'self Method<'self>>
}
pub struct FormatSpec {
fill: char,
alignleft: bool,
flags: uint,
precision: parse::Count,
width: parse::Count,
}
pub enum Position {
ArgumentNext, ArgumentIs(uint)
}
pub enum Method<'self> {
Plural(Option<uint>, &'self [PluralArm<'self>], &'self [Piece<'self>]),
Select(&'self [SelectArm<'self>], &'self [Piece<'self>]),
}
pub struct PluralArm<'self> {
selector: Either<parse::PluralKeyword, uint>,
result: &'self [Piece<'self>],
}
pub struct SelectArm<'self> {
selector: &'self str,
result: &'self [Piece<'self>],
}

View file

@ -19,7 +19,7 @@ use container::{Container, Mutable, Map, MutableMap, Set, MutableSet};
use clone::Clone;
use cmp::{Eq, Equiv};
use hash::Hash;
use iterator::{Iterator, IteratorUtil, FromIterator, Extendable, range};
use iterator::{Iterator, IteratorUtil, FromIterator, Extendable};
use iterator::{FilterMap, Chain, Repeat, Zip};
use num;
use option::{None, Option, Some};
@ -238,7 +238,7 @@ impl<K:Hash + Eq,V> HashMap<K, V> {
let len_buckets = self.buckets.len();
let bucket = self.buckets[idx].take();
let value = do bucket.map_consume |bucket| {
let value = do bucket.map_move |bucket| {
bucket.value
};
@ -265,8 +265,8 @@ impl<K:Hash + Eq,V> Container for HashMap<K, V> {
impl<K:Hash + Eq,V> Mutable for HashMap<K, V> {
/// Clear the map, removing all key-value pairs.
fn clear(&mut self) {
for idx in range(0u, self.buckets.len()) {
self.buckets[idx] = None;
for bkt in self.buckets.mut_iter() {
*bkt = None;
}
self.size = 0;
}
@ -479,7 +479,7 @@ impl<K: Hash + Eq, V> HashMap<K, V> {
impl<K: Hash + Eq, V: Clone> HashMap<K, V> {
/// Like `find`, but returns a copy of the value.
pub fn find_copy(&self, k: &K) -> Option<V> {
self.find(k).map_consume(|v| (*v).clone())
self.find(k).map_move(|v| (*v).clone())
}
/// Like `get`, but returns a copy of the value.

View file

@ -18,9 +18,9 @@ implementing the `Iterator` trait.
*/
use cmp;
use num::{Zero, One, Saturating};
use num::{Zero, One, Integer, Saturating};
use option::{Option, Some, None};
use ops::{Add, Mul};
use ops::{Add, Mul, Sub};
use cmp::Ord;
use clone::Clone;
use uint;
@ -674,7 +674,7 @@ impl<A, T: Iterator<A>> IteratorUtil<A> for T {
Some((y, y_val))
}
}
}).map_consume(|(x, _)| x)
}).map_move(|(x, _)| x)
}
#[inline]
@ -689,7 +689,7 @@ impl<A, T: Iterator<A>> IteratorUtil<A> for T {
Some((y, y_val))
}
}
}).map_consume(|(x, _)| x)
}).map_move(|(x, _)| x)
}
}
@ -1382,7 +1382,7 @@ impl<'self, A, T: Iterator<A>, B, U: Iterator<B>> Iterator<B> for
return Some(x)
}
}
match self.iter.next().map_consume(|x| (self.f)(x)) {
match self.iter.next().map_move(|x| (self.f)(x)) {
None => return self.backiter.chain_mut_ref(|it| it.next()),
next => self.frontiter = next,
}
@ -1414,7 +1414,7 @@ impl<'self,
y => return y
}
}
match self.iter.next_back().map_consume(|x| (self.f)(x)) {
match self.iter.next_back().map_move(|x| (self.f)(x)) {
None => return self.frontiter.chain_mut_ref(|it| it.next_back()),
next => self.backiter = next,
}
@ -1531,7 +1531,7 @@ pub fn range<A: Add<A, A> + Ord + Clone + One>(start: A, stop: A) -> Range<A> {
Range{state: start, stop: stop, one: One::one()}
}
impl<A: Add<A, A> + Ord + Clone + One> Iterator<A> for Range<A> {
impl<A: Add<A, A> + Ord + Clone> Iterator<A> for Range<A> {
#[inline]
fn next(&mut self) -> Option<A> {
if self.state < self.stop {
@ -1544,6 +1544,22 @@ impl<A: Add<A, A> + Ord + Clone + One> Iterator<A> for Range<A> {
}
}
impl<A: Sub<A, A> + Integer + Ord + Clone> DoubleEndedIterator<A> for Range<A> {
#[inline]
fn next_back(&mut self) -> Option<A> {
if self.stop > self.state {
// Integer doesn't technically define this rule, but we're going to assume that every
// Integer is reachable from every other one by adding or subtracting enough Ones. This
// seems like a reasonable-enough rule that every Integer should conform to, even if it
// can't be statically checked.
self.stop = self.stop - self.one;
Some(self.stop.clone())
} else {
None
}
}
}
impl<A: Add<A, A> + Clone> Iterator<A> for Counter<A> {
#[inline]
fn next(&mut self) -> Option<A> {
@ -2121,4 +2137,17 @@ mod tests {
check_randacc_iter(xs.iter().cycle().take_(27), 27);
check_randacc_iter(empty.iter().cycle(), 0);
}
#[test]
fn test_double_ended_range() {
assert_eq!(range(11i, 14).invert().collect::<~[int]>(), ~[13i, 12, 11]);
for _ in range(10i, 0).invert() {
fail!("unreachable");
}
assert_eq!(range(11u, 14).invert().collect::<~[uint]>(), ~[13u, 12, 11]);
for _ in range(10u, 0).invert() {
fail!("unreachable");
}
}
}

View file

@ -110,16 +110,16 @@ fn test_tls_multitask() {
set(my_key, @~"parent data");
do task::spawn {
// TLS shouldn't carry over.
assert!(get(my_key, |k| k.map(|&k| *k)).is_none());
assert!(get(my_key, |k| k.map_move(|k| *k)).is_none());
set(my_key, @~"child data");
assert!(*(get(my_key, |k| k.map(|&k| *k)).unwrap()) ==
assert!(*(get(my_key, |k| k.map_move(|k| *k)).unwrap()) ==
~"child data");
// should be cleaned up for us
}
// Must work multiple times
assert!(*(get(my_key, |k| k.map(|&k| *k)).unwrap()) == ~"parent data");
assert!(*(get(my_key, |k| k.map(|&k| *k)).unwrap()) == ~"parent data");
assert!(*(get(my_key, |k| k.map(|&k| *k)).unwrap()) == ~"parent data");
assert!(*(get(my_key, |k| k.map_move(|k| *k)).unwrap()) == ~"parent data");
assert!(*(get(my_key, |k| k.map_move(|k| *k)).unwrap()) == ~"parent data");
assert!(*(get(my_key, |k| k.map_move(|k| *k)).unwrap()) == ~"parent data");
}
#[test]
@ -127,7 +127,7 @@ fn test_tls_overwrite() {
static my_key: Key<@~str> = &Key;
set(my_key, @~"first data");
set(my_key, @~"next data"); // Shouldn't leak.
assert!(*(get(my_key, |k| k.map(|&k| *k)).unwrap()) == ~"next data");
assert!(*(get(my_key, |k| k.map_move(|k| *k)).unwrap()) == ~"next data");
}
#[test]

View file

@ -278,18 +278,22 @@ impl One for f64 {
#[cfg(not(test))]
impl Add<f64,f64> for f64 {
#[inline]
fn add(&self, other: &f64) -> f64 { *self + *other }
}
#[cfg(not(test))]
impl Sub<f64,f64> for f64 {
#[inline]
fn sub(&self, other: &f64) -> f64 { *self - *other }
}
#[cfg(not(test))]
impl Mul<f64,f64> for f64 {
#[inline]
fn mul(&self, other: &f64) -> f64 { *self * *other }
}
#[cfg(not(test))]
impl Div<f64,f64> for f64 {
#[inline]
fn div(&self, other: &f64) -> f64 { *self / *other }
}
#[cfg(not(test))]

View file

@ -124,14 +124,6 @@ pub fn range_step_inclusive(start: $T, last: $T, step: $T, it: &fn($T) -> bool)
range_step_core(start, last, step, Closed, it)
}
#[inline]
/// Iterate over the range (`hi`..`lo`]
pub fn range_rev(hi: $T, lo: $T, it: &fn($T) -> bool) -> bool {
if hi == min_value { return true; }
range_step_inclusive(hi-1, lo, -1 as $T, it)
}
impl Num for $T {}
#[cfg(not(test))]
@ -889,10 +881,6 @@ mod tests {
fn test_ranges() {
let mut l = ~[];
do range_rev(14,11) |i| {
l.push(i);
true
};
do range_step(20,26,2) |i| {
l.push(i);
true
@ -917,8 +905,7 @@ mod tests {
l.push(i);
true
};
assert_eq!(l, ~[13,12,11,
20,22,24,
assert_eq!(l, ~[20,22,24,
36,34,32,
max_value-2,
max_value-3,max_value-1,
@ -926,9 +913,6 @@ mod tests {
min_value+3,min_value+1]);
// None of the `fail`s should execute.
do range_rev(0,10) |_i| {
fail!(~"unreachable");
};
do range_step(10,0,1) |_i| {
fail!(~"unreachable");
};

View file

@ -422,9 +422,9 @@ pub fn float_to_str_common<T:NumCast+Zero+One+Eq+Ord+NumStrConv+Float+Round+
// Some constants for from_str_bytes_common's input validation,
// they define minimum radix values for which the character is a valid digit.
priv static DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u;
priv static DIGIT_I_RADIX: uint = ('i' as uint) - ('a' as uint) + 11u;
priv static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u;
static DIGIT_P_RADIX: uint = ('p' as uint) - ('a' as uint) + 11u;
static DIGIT_I_RADIX: uint = ('i' as uint) - ('a' as uint) + 11u;
static DIGIT_E_RADIX: uint = ('e' as uint) - ('a' as uint) + 11u;
/**
* Parses a byte slice as a number. This is meant to

View file

@ -125,13 +125,6 @@ pub fn range_step_inclusive(start: $T, last: $T, step: $T_SIGNED, it: &fn($T) ->
range_step_core(start, last, step, Closed, it)
}
#[inline]
/// Iterate over the range (`hi`..`lo`]
pub fn range_rev(hi: $T, lo: $T, it: &fn($T) -> bool) -> bool {
if hi == min_value { return true; }
range_step_inclusive(hi-1, lo, -1 as $T_SIGNED, it)
}
impl Num for $T {}
#[cfg(not(test))]
@ -654,10 +647,6 @@ mod tests {
pub fn test_ranges() {
let mut l = ~[];
do range_rev(14,11) |i| {
l.push(i);
true
};
do range_step(20,26,2) |i| {
l.push(i);
true
@ -683,8 +672,7 @@ mod tests {
true
};
assert_eq!(l, ~[13,12,11,
20,22,24,
assert_eq!(l, ~[20,22,24,
36,34,32,
max_value-2,
max_value-3,max_value-1,
@ -692,9 +680,6 @@ mod tests {
min_value+3,min_value+1]);
// None of the `fail`s should execute.
do range_rev(0,0) |_i| {
fail!("unreachable");
};
do range_step(10,0,1) |_i| {
fail!("unreachable");
};

View file

@ -208,6 +208,12 @@ impl<T> Option<T> {
match *self { Some(ref mut x) => Some(f(x)), None => None }
}
/// Applies a function to the contained value or returns a default
#[inline]
pub fn map_default<'a, U>(&'a self, def: U, f: &fn(&'a T) -> U) -> U {
match *self { None => def, Some(ref t) => f(t) }
}
/// Maps a `Some` value from one type to another by a mutable reference,
/// or returns a default value.
#[inline]
@ -218,21 +224,15 @@ impl<T> Option<T> {
/// As `map`, but consumes the option and gives `f` ownership to avoid
/// copying.
#[inline]
pub fn map_consume<U>(self, f: &fn(v: T) -> U) -> Option<U> {
match self { None => None, Some(v) => Some(f(v)) }
}
/// Applies a function to the contained value or returns a default
#[inline]
pub fn map_default<'a, U>(&'a self, def: U, f: &fn(&'a T) -> U) -> U {
match *self { None => def, Some(ref t) => f(t) }
pub fn map_move<U>(self, f: &fn(T) -> U) -> Option<U> {
match self { Some(x) => Some(f(x)), None => None }
}
/// As `map_default`, but consumes the option and gives `f`
/// ownership to avoid copying.
#[inline]
pub fn map_consume_default<U>(self, def: U, f: &fn(v: T) -> U) -> U {
match self { None => def, Some(v) => f(v) }
pub fn map_move_default<U>(self, def: U, f: &fn(T) -> U) -> U {
match self { None => def, Some(t) => f(t) }
}
/// Take the value out of the option, leaving a `None` in its place.
@ -241,20 +241,6 @@ impl<T> Option<T> {
util::replace(self, None)
}
/// As `map_consume`, but swaps a None into the original option rather
/// than consuming it by-value.
#[inline]
pub fn take_map<U>(&mut self, blk: &fn(T) -> U) -> Option<U> {
self.take().map_consume(blk)
}
/// As `map_consume_default`, but swaps a None into the original option
/// rather than consuming it by-value.
#[inline]
pub fn take_map_default<U> (&mut self, def: U, blk: &fn(T) -> U) -> U {
self.take().map_consume_default(def, blk)
}
/// Apply a function to the contained value or do nothing.
/// Returns true if the contained value was mutated.
pub fn mutate(&mut self, f: &fn(T) -> T) -> bool {

View file

@ -501,9 +501,7 @@ pub fn self_exe_path() -> Option<Path> {
}
}
do load_self().map |pth| {
Path(*pth).dir_path()
}
load_self().map_move(|path| Path(path).dir_path())
}

View file

@ -610,15 +610,32 @@ impl<R: Rng> RngUtil for R {
}
/// Create a random number generator with a default algorithm and seed.
///
/// It returns the cryptographically-safest `Rng` algorithm currently
/// available in Rust. If you require a specifically seeded `Rng` for
/// consistency over time you should pick one algorithm and create the
/// `Rng` yourself.
pub fn rng() -> IsaacRng {
IsaacRng::new()
}
/// Create a weak random number generator with a default algorithm and seed.
///
/// It returns the fatest `Rng` algorithm currently available in Rust without
/// consideration for cryptography or security. If you require a specifically
/// seeded `Rng` for consistency over time you should pick one algorithm and
/// create the `Rng` yourself.
pub fn weak_rng() -> XorShiftRng {
XorShiftRng::new()
}
static RAND_SIZE_LEN: u32 = 8;
static RAND_SIZE: u32 = 1 << RAND_SIZE_LEN;
/// A random number generator that uses the [ISAAC
/// algorithm](http://en.wikipedia.org/wiki/ISAAC_%28cipher%29).
///
/// The ISAAC algorithm is suitable for cryptographic purposes.
pub struct IsaacRng {
priv cnt: u32,
priv rsl: [u32, .. RAND_SIZE],
@ -794,8 +811,11 @@ impl Rng for IsaacRng {
}
/// An [Xorshift random number
/// generator](http://en.wikipedia.org/wiki/Xorshift). Not suitable for
/// cryptographic purposes.
/// generator](http://en.wikipedia.org/wiki/Xorshift).
///
/// The Xorshift algorithm is not suitable for cryptographic purposes
/// but is very fast. If you do not know for sure that it fits your
/// requirements, use a more secure one such as `IsaacRng`.
pub struct XorShiftRng {
priv x: u32,
priv y: u32,

View file

@ -149,6 +149,40 @@ impl<T, E: ToStr> Result<T, E> {
}
}
/// Call a method based on a previous result
///
/// If `self` is `Ok` then the value is extracted and passed to `op`
/// whereupon `op`s result is wrapped in `Ok` and returned. if `self` is
/// `Err` then it is immediately returned. This function can be used to
/// compose the results of two functions.
///
/// Example:
///
/// let res = do read_file(file).map_move |buf| {
/// parse_bytes(buf)
/// }
#[inline]
pub fn map_move<U>(self, op: &fn(T) -> U) -> Result<U,E> {
match self {
Ok(t) => Ok(op(t)),
Err(e) => Err(e)
}
}
/// Call a method based on a previous result
///
/// If `self` is `Err` then the value is extracted and passed to `op`
/// whereupon `op`s result is wrapped in an `Err` and returned. if `self` is
/// `Ok` then it is immediately returned. This function can be used to pass
/// through a successful result while handling an error.
#[inline]
pub fn map_err_move<F>(self, op: &fn(E) -> F) -> Result<T,F> {
match self {
Ok(t) => Ok(t),
Err(e) => Err(op(e))
}
}
/// Call a method based on a previous result
///
/// If `self` is `Ok` then the value is extracted and passed to `op`
@ -312,7 +346,9 @@ pub fn iter_vec2<S, T, U: ToStr>(ss: &[S], ts: &[T],
#[cfg(test)]
mod tests {
use super::*;
use either;
use str::OwnedStr;
pub fn op1() -> Result<int, ~str> { Ok(666) }
@ -359,14 +395,26 @@ mod tests {
#[test]
pub fn test_impl_map() {
assert_eq!(Ok::<~str, ~str>(~"a").map(|_x| ~"b"), Ok(~"b"));
assert_eq!(Err::<~str, ~str>(~"a").map(|_x| ~"b"), Err(~"a"));
assert_eq!(Ok::<~str, ~str>(~"a").map(|x| (~"b").append(*x)), Ok(~"ba"));
assert_eq!(Err::<~str, ~str>(~"a").map(|x| (~"b").append(*x)), Err(~"a"));
}
#[test]
pub fn test_impl_map_err() {
assert_eq!(Ok::<~str, ~str>(~"a").map_err(|_x| ~"b"), Ok(~"a"));
assert_eq!(Err::<~str, ~str>(~"a").map_err(|_x| ~"b"), Err(~"b"));
assert_eq!(Ok::<~str, ~str>(~"a").map_err(|x| (~"b").append(*x)), Ok(~"a"));
assert_eq!(Err::<~str, ~str>(~"a").map_err(|x| (~"b").append(*x)), Err(~"ba"));
}
#[test]
pub fn test_impl_map_move() {
assert_eq!(Ok::<~str, ~str>(~"a").map_move(|x| x + "b"), Ok(~"ab"));
assert_eq!(Err::<~str, ~str>(~"a").map_move(|x| x + "b"), Err(~"a"));
}
#[test]
pub fn test_impl_map_err_move() {
assert_eq!(Ok::<~str, ~str>(~"a").map_err_move(|x| x + "b"), Ok(~"a"));
assert_eq!(Err::<~str, ~str>(~"a").map_err_move(|x| x + "b"), Err(~"ab"));
}
#[test]

View file

@ -159,7 +159,7 @@ impl<T> ChanOne<T> {
// Port is blocked. Wake it up.
let recvr = BlockedTask::cast_from_uint(task_as_state);
if do_resched {
do recvr.wake().map_consume |woken_task| {
do recvr.wake().map_move |woken_task| {
Scheduler::run_task(woken_task);
};
} else {
@ -225,9 +225,10 @@ impl<T> Select for PortOne<T> {
fn optimistic_check(&mut self) -> bool {
// The optimistic check is never necessary for correctness. For testing
// purposes, making it randomly return false simulates a racing sender.
use rand::{Rand, rng};
let mut rng = rng();
let actually_check = Rand::rand(&mut rng);
use rand::{Rand};
let actually_check = do Local::borrow::<Scheduler, bool> |sched| {
Rand::rand(&mut sched.rng)
};
if actually_check {
unsafe { (*self.packet()).state.load(Acquire) == STATE_ONE }
} else {
@ -381,7 +382,7 @@ impl<T> Drop for ChanOne<T> {
// The port is blocked waiting for a message we will never send. Wake it.
assert!((*this.packet()).payload.is_none());
let recvr = BlockedTask::cast_from_uint(task_as_state);
do recvr.wake().map_consume |woken_task| {
do recvr.wake().map_move |woken_task| {
Scheduler::run_task(woken_task);
};
}
@ -508,7 +509,11 @@ impl<T> Peekable<T> for Port<T> {
}
}
impl<T> Select for Port<T> {
// XXX: Kind of gross. A Port<T> should be selectable so you can make an array
// of them, but a &Port<T> should also be selectable so you can select2 on it
// alongside a PortOne<U> without passing the port by value in recv_ready.
impl<'self, T> Select for &'self Port<T> {
#[inline]
fn optimistic_check(&mut self) -> bool {
do self.next.with_mut_ref |pone| { pone.optimistic_check() }
@ -526,12 +531,29 @@ impl<T> Select for Port<T> {
}
}
impl<T> SelectPort<(T, Port<T>)> for Port<T> {
fn recv_ready(self) -> Option<(T, Port<T>)> {
impl<T> Select for Port<T> {
#[inline]
fn optimistic_check(&mut self) -> bool {
(&*self).optimistic_check()
}
#[inline]
fn block_on(&mut self, sched: &mut Scheduler, task: BlockedTask) -> bool {
(&*self).block_on(sched, task)
}
#[inline]
fn unblock_from(&mut self) -> bool {
(&*self).unblock_from()
}
}
impl<'self, T> SelectPort<T> for &'self Port<T> {
fn recv_ready(self) -> Option<T> {
match self.next.take().recv_ready() {
Some(StreamPayload { val, next }) => {
self.next.put_back(next);
Some((val, self))
Some(val)
}
None => None
}

View file

@ -10,7 +10,12 @@
//! Runtime environment settings
use from_str::FromStr;
use libc::{size_t, c_char, c_int};
use option::{Some, None};
use os;
// OLD RT stuff
pub struct Environment {
/// The number of threads to use by default
@ -47,3 +52,26 @@ pub fn get() -> &Environment {
extern {
fn rust_get_rt_env() -> &Environment;
}
// NEW RT stuff
// Note that these are all accessed without any synchronization.
// They are expected to be initialized once then left alone.
static mut MIN_STACK: uint = 2000000;
pub fn init() {
unsafe {
match os::getenv("RUST_MIN_STACK") {
Some(s) => match FromStr::from_str(s) {
Some(i) => MIN_STACK = i,
None => ()
},
None => ()
}
}
}
pub fn min_stack() -> uint {
unsafe { MIN_STACK }
}

View file

@ -26,7 +26,7 @@ pub struct MemWriter {
}
impl MemWriter {
pub fn new() -> MemWriter { MemWriter { buf: ~[] } }
pub fn new() -> MemWriter { MemWriter { buf: vec::with_capacity(128) } }
}
impl Writer for MemWriter {

View file

@ -402,10 +402,10 @@ impl KillHandle {
|| {
// Prefer to check tombstones that were there first,
// being "more fair" at the expense of tail-recursion.
others.take().map_consume_default(true, |f| f()) && {
others.take().map_move_default(true, |f| f()) && {
let mut inner = this.take().unwrap();
(!inner.any_child_failed) &&
inner.child_tombstones.take_map_default(true, |f| f())
inner.child_tombstones.take().map_move_default(true, |f| f())
}
}
}
@ -424,7 +424,7 @@ impl KillHandle {
let others = Cell::new(other_tombstones); // :(
|| {
// Prefer fairness to tail-recursion, as in above case.
others.take().map_consume_default(true, |f| f()) &&
others.take().map_move_default(true, |f| f()) &&
f.take()()
}
}
@ -493,7 +493,7 @@ impl Death {
{ use util; util::ignore(group); }
// Step 1. Decide if we need to collect child failures synchronously.
do self.on_exit.take_map |on_exit| {
do self.on_exit.take().map_move |on_exit| {
if success {
// We succeeded, but our children might not. Need to wait for them.
let mut inner = self.kill_handle.take_unwrap().unwrap();
@ -501,7 +501,7 @@ impl Death {
success = false;
} else {
// Lockless access to tombstones protected by unwrap barrier.
success = inner.child_tombstones.take_map_default(true, |f| f());
success = inner.child_tombstones.take().map_move_default(true, |f| f());
}
}
on_exit(success);
@ -510,12 +510,12 @@ impl Death {
// Step 2. Possibly alert possibly-watching parent to failure status.
// Note that as soon as parent_handle goes out of scope, the parent
// can successfully unwrap its handle and collect our reported status.
do self.watching_parent.take_map |mut parent_handle| {
do self.watching_parent.take().map_move |mut parent_handle| {
if success {
// Our handle might be None if we had an exit callback, and
// already unwrapped it. But 'success' being true means no
// child failed, so there's nothing to do (see below case).
do self.kill_handle.take_map |own_handle| {
do self.kill_handle.take().map_move |own_handle| {
own_handle.reparent_children_to(&mut parent_handle);
};
} else {
@ -590,7 +590,8 @@ impl Death {
#[inline]
pub fn assert_may_sleep(&self) {
if self.wont_sleep != 0 {
rtabort!("illegal atomic-sleep: can't deschedule inside atomically()");
rtabort!("illegal atomic-sleep: attempt to reschedule while \
using an Exclusive or LittleLock");
}
}
}
@ -614,6 +615,7 @@ mod test {
// Test cases don't care about the spare killed flag.
fn make_kill_handle() -> KillHandle { let (h,_) = KillHandle::new(); h }
#[ignore(reason = "linked failure")]
#[test]
fn no_tombstone_success() {
do run_in_newsched_task {
@ -819,6 +821,7 @@ mod test {
}
}
#[ignore(reason = "linked failure")]
#[test]
fn block_and_get_killed() {
do with_test_task |mut task| {
@ -830,6 +833,7 @@ mod test {
}
}
#[ignore(reason = "linked failure")]
#[test]
fn block_already_killed() {
do with_test_task |mut task| {
@ -839,6 +843,7 @@ mod test {
}
}
#[ignore(reason = "linked failure")]
#[test]
fn block_unkillably_and_get_killed() {
do with_test_task |mut task| {
@ -856,6 +861,7 @@ mod test {
}
}
#[ignore(reason = "linked failure")]
#[test]
fn block_on_pipe() {
// Tests the "killable" path of casting to/from uint.
@ -869,6 +875,7 @@ mod test {
}
}
#[ignore(reason = "linked failure")]
#[test]
fn block_unkillably_on_pipe() {
// Tests the "indestructible" path of casting to/from uint.

View file

@ -126,6 +126,7 @@ impl Local for IoFactoryObject {
#[cfg(test)]
mod test {
use option::None;
use unstable::run_in_bare_thread;
use rt::test::*;
use super::*;
@ -137,7 +138,7 @@ mod test {
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, || {});
let task = ~Task::new_root(&mut sched.stack_pool, None, || {});
Local::put(task);
let task: ~Task = Local::take();
cleanup_task(task);
@ -149,11 +150,11 @@ mod test {
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, || {});
let task = ~Task::new_root(&mut sched.stack_pool, None, || {});
Local::put(task);
let task: ~Task = Local::take();
cleanup_task(task);
let task = ~Task::new_root(&mut sched.stack_pool, || {});
let task = ~Task::new_root(&mut sched.stack_pool, None, || {});
Local::put(task);
let task: ~Task = Local::take();
cleanup_task(task);
@ -166,7 +167,7 @@ mod test {
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, || {});
let task = ~Task::new_root(&mut sched.stack_pool, None, || {});
Local::put(task);
unsafe {
@ -182,7 +183,7 @@ mod test {
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, || {});
let task = ~Task::new_root(&mut sched.stack_pool, None, || {});
Local::put(task);
let res = do Local::borrow::<Task,bool> |_task| {

View file

@ -63,8 +63,7 @@ Several modules in `core` are clients of `rt`:
use cell::Cell;
use clone::Clone;
use container::Container;
use iter::Times;
use iterator::{Iterator, IteratorUtil};
use iterator::{Iterator, IteratorUtil, range};
use option::{Some, None};
use ptr::RawPtr;
use rt::local::Local;
@ -212,6 +211,7 @@ pub fn init(argc: int, argv: **u8, crate_map: *u8) {
// Need to propagate the unsafety to `start`.
unsafe {
args::init(argc, argv);
env::init();
logging::init(crate_map);
rust_update_gc_metadata(crate_map);
}
@ -246,11 +246,16 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
let main = Cell::new(main);
// The shared list of sleeping schedulers. Schedulers wake each other
// occassionally to do new work.
// The shared list of sleeping schedulers.
let sleepers = SleeperList::new();
// The shared work queue. Temporary until work stealing is implemented.
let work_queue = WorkQueue::new();
// Create a work queue for each scheduler, ntimes. Create an extra
// for the main thread if that flag is set. We won't steal from it.
let mut work_queues = ~[];
for _ in range(0u, nscheds) {
let work_queue: WorkQueue<~Task> = WorkQueue::new();
work_queues.push(work_queue);
}
// The schedulers.
let mut scheds = ~[];
@ -258,12 +263,15 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
// sent the Shutdown message to terminate the schedulers.
let mut handles = ~[];
do nscheds.times {
for i in range(0u, nscheds) {
rtdebug!("inserting a regular scheduler");
// Every scheduler is driven by an I/O event loop.
let loop_ = ~UvEventLoop::new();
let mut sched = ~Scheduler::new(loop_, work_queue.clone(), sleepers.clone());
let mut sched = ~Scheduler::new(loop_,
work_queues[i].clone(),
work_queues.clone(),
sleepers.clone());
let handle = sched.make_handle();
scheds.push(sched);
@ -279,9 +287,14 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
let friend_handle = friend_sched.make_handle();
scheds.push(friend_sched);
// This scheduler needs a queue that isn't part of the stealee
// set.
let work_queue = WorkQueue::new();
let main_loop = ~UvEventLoop::new();
let mut main_sched = ~Scheduler::new_special(main_loop,
work_queue.clone(),
work_queue,
work_queues.clone(),
sleepers.clone(),
false,
Some(friend_handle));
@ -330,8 +343,7 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
// In the case where we do not use a main_thread scheduler we
// run the main task in one of our threads.
let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool,
main.take());
let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool, None, main.take());
main_task.death.on_exit = Some(on_exit.take());
let main_task_cell = Cell::new(main_task);
@ -351,7 +363,7 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
let sched_cell = Cell::new(sched);
let thread = do Thread::start {
let mut sched = sched_cell.take();
let bootstrap_task = ~do Task::new_root(&mut sched.stack_pool) || {
let bootstrap_task = ~do Task::new_root(&mut sched.stack_pool, None) || {
rtdebug!("boostraping a non-primary scheduler");
};
sched.bootstrap(bootstrap_task);
@ -368,10 +380,10 @@ fn run_(main: ~fn(), use_main_sched: bool) -> int {
let mut main_sched = main_sched.unwrap();
let home = Sched(main_sched.make_handle());
let mut main_task = ~Task::new_root_homed(&mut main_sched.stack_pool,
let mut main_task = ~Task::new_root_homed(&mut main_sched.stack_pool, None,
home, main.take());
main_task.death.on_exit = Some(on_exit.take());
rtdebug!("boostrapping main_task");
rtdebug!("bootstrapping main_task");
main_sched.bootstrap(main_task);
}

View file

@ -13,7 +13,6 @@ use option::{Option, Some, None};
use cast::{transmute, transmute_mut_region, transmute_mut_unsafe};
use clone::Clone;
use unstable::raw;
use super::sleeper_list::SleeperList;
use super::work_queue::WorkQueue;
use super::stack::{StackPool};
@ -28,6 +27,9 @@ use rt::rtio::RemoteCallback;
use rt::metrics::SchedMetrics;
use borrow::{to_uint};
use cell::Cell;
use rand::{XorShiftRng, RngUtil};
use iterator::{range};
use vec::{OwnedVector};
/// The Scheduler is responsible for coordinating execution of Coroutines
/// on a single thread. When the scheduler is running it is owned by
@ -37,9 +39,11 @@ use cell::Cell;
/// XXX: This creates too many callbacks to run_sched_once, resulting
/// in too much allocation and too many events.
pub struct Scheduler {
/// A queue of available work. Under a work-stealing policy there
/// is one per Scheduler.
work_queue: WorkQueue<~Task>,
/// There are N work queues, one per scheduler.
priv work_queue: WorkQueue<~Task>,
/// Work queues for the other schedulers. These are created by
/// cloning the core work queues.
work_queues: ~[WorkQueue<~Task>],
/// The queue of incoming messages from other schedulers.
/// These are enqueued by SchedHandles after which a remote callback
/// is triggered to handle the message.
@ -70,7 +74,10 @@ pub struct Scheduler {
run_anything: bool,
/// If the scheduler shouldn't run some tasks, a friend to send
/// them to.
friend_handle: Option<SchedHandle>
friend_handle: Option<SchedHandle>,
/// A fast XorShift rng for scheduler use
rng: XorShiftRng
}
pub struct SchedHandle {
@ -97,10 +104,13 @@ impl Scheduler {
pub fn new(event_loop: ~EventLoopObject,
work_queue: WorkQueue<~Task>,
work_queues: ~[WorkQueue<~Task>],
sleeper_list: SleeperList)
-> Scheduler {
Scheduler::new_special(event_loop, work_queue, sleeper_list, true, None)
Scheduler::new_special(event_loop, work_queue,
work_queues,
sleeper_list, true, None)
}
@ -108,6 +118,7 @@ impl Scheduler {
// task field is None.
pub fn new_special(event_loop: ~EventLoopObject,
work_queue: WorkQueue<~Task>,
work_queues: ~[WorkQueue<~Task>],
sleeper_list: SleeperList,
run_anything: bool,
friend: Option<SchedHandle>)
@ -120,12 +131,14 @@ impl Scheduler {
no_sleep: false,
event_loop: event_loop,
work_queue: work_queue,
work_queues: work_queues,
stack_pool: StackPool::new(),
sched_task: None,
cleanup_job: None,
metrics: SchedMetrics::new(),
run_anything: run_anything,
friend_handle: friend
friend_handle: friend,
rng: XorShiftRng::new()
}
}
@ -248,7 +261,7 @@ impl Scheduler {
// Second activity is to try resuming a task from the queue.
let result = sched.resume_task_from_queue();
let result = sched.do_work();
let mut sched = match result {
Some(sched) => {
// Failed to dequeue a task, so we return.
@ -325,7 +338,7 @@ impl Scheduler {
/// As enqueue_task, but with the possibility for the blocked task to
/// already have been killed.
pub fn enqueue_blocked_task(&mut self, blocked_task: BlockedTask) {
do blocked_task.wake().map_consume |task| {
do blocked_task.wake().map_move |task| {
self.enqueue_task(task);
};
}
@ -415,47 +428,98 @@ impl Scheduler {
}
}
// Resume a task from the queue - but also take into account that
// it might not belong here.
// Workstealing: In this iteration of the runtime each scheduler
// thread has a distinct work queue. When no work is available
// locally, make a few attempts to steal work from the queues of
// other scheduler threads. If a few steals fail we end up in the
// old "no work" path which is fine.
// If we perform a scheduler action we give away the scheduler ~
// pointer, if it is still available we return it.
fn resume_task_from_queue(~self) -> Option<~Scheduler> {
let mut this = self;
match this.work_queue.pop() {
// First step in the process is to find a task. This function does
// that by first checking the local queue, and if there is no work
// there, trying to steal from the remote work queues.
fn find_work(&mut self) -> Option<~Task> {
rtdebug!("scheduler looking for work");
match self.work_queue.pop() {
Some(task) => {
let mut task = task;
let home = task.take_unwrap_home();
match home {
Sched(home_handle) => {
if home_handle.sched_id != this.sched_id() {
task.give_home(Sched(home_handle));
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;
}
AnySched => {
task.give_home(AnySched);
this.send_to_friend(task);
return Some(this);
}
}
rtdebug!("found a task locally");
return Some(task)
}
None => {
// Our naive stealing, try kinda hard.
rtdebug!("scheduler trying to steal");
let _len = self.work_queues.len();
return self.try_steals(2);
}
}
}
// With no backoff try stealing n times from the queues the
// scheduler knows about. This naive implementation can steal from
// our own queue or from other special schedulers.
fn try_steals(&mut self, n: uint) -> Option<~Task> {
for _ in range(0, n) {
let index = self.rng.gen_uint_range(0, self.work_queues.len());
let work_queues = &mut self.work_queues;
match work_queues[index].steal() {
Some(task) => {
rtdebug!("found task by stealing"); return Some(task)
}
None => ()
}
};
rtdebug!("giving up on stealing");
return None;
}
// Given a task, execute it correctly.
fn process_task(~self, task: ~Task) -> Option<~Scheduler> {
let mut this = self;
let mut task = task;
rtdebug!("processing a task");
let home = task.take_unwrap_home();
match home {
Sched(home_handle) => {
if home_handle.sched_id != this.sched_id() {
rtdebug!("sending task home");
task.give_home(Sched(home_handle));
Scheduler::send_task_home(task);
return Some(this);
} else {
rtdebug!("running task here");
task.give_home(Sched(home_handle));
this.resume_task_immediately(task);
return None;
}
}
AnySched if this.run_anything => {
rtdebug!("running anysched task here");
task.give_home(AnySched);
this.resume_task_immediately(task);
return None;
}
AnySched => {
rtdebug!("sending task to friend");
task.give_home(AnySched);
this.send_to_friend(task);
return Some(this);
}
}
}
// Bundle the helpers together.
fn do_work(~self) -> Option<~Scheduler> {
let mut this = self;
rtdebug!("scheduler calling do work");
match this.find_work() {
Some(task) => {
rtdebug!("found some work! processing the task");
return this.process_task(task);
}
None => {
rtdebug!("no work was found, returning the scheduler struct");
return Some(this);
}
}
@ -533,7 +597,7 @@ impl Scheduler {
sched.enqueue_blocked_task(last_task);
}
};
opt.map_consume(Local::put);
opt.map_move(Local::put);
}
// The primary function for changing contexts. In the current
@ -711,7 +775,6 @@ impl Scheduler {
GiveTask(task, f) => f.to_fn()(self, task)
}
}
}
// The cases for the below function.
@ -745,6 +808,8 @@ impl ClosureConverter for UnsafeTaskReceiver {
#[cfg(test)]
mod test {
extern mod extra;
use prelude::*;
use rt::test::*;
use unstable::run_in_bare_thread;
@ -833,7 +898,7 @@ mod test {
let mut sched = ~new_test_uv_sched();
let sched_handle = sched.make_handle();
let mut task = ~do Task::new_root_homed(&mut sched.stack_pool,
let mut task = ~do Task::new_root_homed(&mut sched.stack_pool, None,
Sched(sched_handle)) {
unsafe { *task_ran_ptr = true };
assert!(Task::on_appropriate_sched());
@ -862,12 +927,15 @@ mod test {
do run_in_bare_thread {
let sleepers = SleeperList::new();
let work_queue = WorkQueue::new();
let normal_queue = WorkQueue::new();
let special_queue = WorkQueue::new();
let queues = ~[normal_queue.clone(), special_queue.clone()];
// Our normal scheduler
let mut normal_sched = ~Scheduler::new(
~UvEventLoop::new(),
work_queue.clone(),
normal_queue,
queues.clone(),
sleepers.clone());
let normal_handle = Cell::new(normal_sched.make_handle());
@ -877,7 +945,8 @@ mod test {
// Our special scheduler
let mut special_sched = ~Scheduler::new_special(
~UvEventLoop::new(),
work_queue.clone(),
special_queue.clone(),
queues.clone(),
sleepers.clone(),
false,
Some(friend_handle));
@ -893,21 +962,21 @@ mod test {
// 3) task not homed, sched requeues
// 4) task not home, send home
let task1 = ~do Task::new_root_homed(&mut special_sched.stack_pool,
let task1 = ~do Task::new_root_homed(&mut special_sched.stack_pool, None,
Sched(t1_handle)) || {
rtassert!(Task::on_appropriate_sched());
};
rtdebug!("task1 id: **%u**", borrow::to_uint(task1));
let task2 = ~do Task::new_root(&mut normal_sched.stack_pool) {
let task2 = ~do Task::new_root(&mut normal_sched.stack_pool, None) {
rtassert!(Task::on_appropriate_sched());
};
let task3 = ~do Task::new_root(&mut normal_sched.stack_pool) {
let task3 = ~do Task::new_root(&mut normal_sched.stack_pool, None) {
rtassert!(Task::on_appropriate_sched());
};
let task4 = ~do Task::new_root_homed(&mut special_sched.stack_pool,
let task4 = ~do Task::new_root_homed(&mut special_sched.stack_pool, None,
Sched(t4_handle)) {
rtassert!(Task::on_appropriate_sched());
};
@ -923,7 +992,7 @@ mod test {
let port = Cell::new(port);
let chan = Cell::new(chan);
let normal_task = ~do Task::new_root(&mut normal_sched.stack_pool) {
let normal_task = ~do Task::new_root(&mut normal_sched.stack_pool, None) {
rtdebug!("*about to submit task2*");
Scheduler::run_task(task2.take());
rtdebug!("*about to submit task4*");
@ -938,7 +1007,7 @@ mod test {
rtdebug!("normal task: %u", borrow::to_uint(normal_task));
let special_task = ~do Task::new_root(&mut special_sched.stack_pool) {
let special_task = ~do Task::new_root(&mut special_sched.stack_pool, None) {
rtdebug!("*about to submit task1*");
Scheduler::run_task(task1.take());
rtdebug!("*about to submit task3*");

View file

@ -182,6 +182,7 @@ mod test {
fn select_stream() {
use util;
use comm::GenericChan;
use iter::Times;
// Sends 10 buffered packets, and uses select to retrieve them all.
// Puts the port in a different spot in the vector each time.
@ -199,9 +200,7 @@ mod test {
// get it back out
util::swap(port.get_mut_ref(), &mut ports[index]);
// NB. Not recv(), because optimistic_check randomly fails.
let (data, new_port) = port.take_unwrap().recv_ready().unwrap();
assert!(data == 31337);
port = Some(new_port);
assert!(port.get_ref().recv_ready().unwrap() == 31337);
}
}
}
@ -265,6 +264,7 @@ mod test {
fn select_racing_senders_helper(killable: bool, send_on_chans: ~[uint]) {
use rt::test::spawntask_random;
use iter::Times;
do run_in_newsched_task {
// A bit of stress, since ordinarily this is just smoke and mirrors.

View file

@ -20,6 +20,7 @@ use libc::{c_void, uintptr_t};
use ptr;
use prelude::*;
use option::{Option, Some, None};
use rt::env;
use rt::kill::Death;
use rt::local::Local;
use rt::logging::StdErrLogger;
@ -85,12 +86,13 @@ impl Task {
// A helper to build a new task using the dynamically found
// scheduler and task. Only works in GreenTask context.
pub fn build_homed_child(f: ~fn(), home: SchedHome) -> ~Task {
pub fn build_homed_child(stack_size: Option<uint>, f: ~fn(), home: SchedHome) -> ~Task {
let f = Cell::new(f);
let home = Cell::new(home);
do Local::borrow::<Task, ~Task> |running_task| {
let mut sched = running_task.sched.take_unwrap();
let new_task = ~running_task.new_child_homed(&mut sched.stack_pool,
stack_size,
home.take(),
f.take());
running_task.sched = Some(sched);
@ -98,25 +100,26 @@ impl Task {
}
}
pub fn build_child(f: ~fn()) -> ~Task {
Task::build_homed_child(f, AnySched)
pub fn build_child(stack_size: Option<uint>, f: ~fn()) -> ~Task {
Task::build_homed_child(stack_size, f, AnySched)
}
pub fn build_homed_root(f: ~fn(), home: SchedHome) -> ~Task {
pub fn build_homed_root(stack_size: Option<uint>, f: ~fn(), home: SchedHome) -> ~Task {
let f = Cell::new(f);
let home = Cell::new(home);
do Local::borrow::<Task, ~Task> |running_task| {
let mut sched = running_task.sched.take_unwrap();
let new_task = ~Task::new_root_homed(&mut sched.stack_pool,
home.take(),
f.take());
stack_size,
home.take(),
f.take());
running_task.sched = Some(sched);
new_task
}
}
pub fn build_root(f: ~fn()) -> ~Task {
Task::build_homed_root(f, AnySched)
pub fn build_root(stack_size: Option<uint>, f: ~fn()) -> ~Task {
Task::build_homed_root(stack_size, f, AnySched)
}
pub fn new_sched_task() -> Task {
@ -137,17 +140,20 @@ impl Task {
}
pub fn new_root(stack_pool: &mut StackPool,
stack_size: Option<uint>,
start: ~fn()) -> Task {
Task::new_root_homed(stack_pool, AnySched, start)
Task::new_root_homed(stack_pool, stack_size, AnySched, start)
}
pub fn new_child(&mut self,
stack_pool: &mut StackPool,
stack_size: Option<uint>,
start: ~fn()) -> Task {
self.new_child_homed(stack_pool, AnySched, start)
self.new_child_homed(stack_pool, stack_size, AnySched, start)
}
pub fn new_root_homed(stack_pool: &mut StackPool,
stack_size: Option<uint>,
home: SchedHome,
start: ~fn()) -> Task {
Task {
@ -160,7 +166,7 @@ impl Task {
death: Death::new(),
destroyed: false,
name: None,
coroutine: Some(Coroutine::new(stack_pool, start)),
coroutine: Some(Coroutine::new(stack_pool, stack_size, start)),
sched: None,
task_type: GreenTask(Some(~home))
}
@ -168,6 +174,7 @@ impl Task {
pub fn new_child_homed(&mut self,
stack_pool: &mut StackPool,
stack_size: Option<uint>,
home: SchedHome,
start: ~fn()) -> Task {
Task {
@ -181,7 +188,7 @@ impl Task {
death: self.death.new_child(),
destroyed: false,
name: None,
coroutine: Some(Coroutine::new(stack_pool, start)),
coroutine: Some(Coroutine::new(stack_pool, stack_size, start)),
sched: None,
task_type: GreenTask(Some(~home))
}
@ -325,11 +332,13 @@ impl Drop for Task {
impl Coroutine {
pub fn new(stack_pool: &mut StackPool, start: ~fn()) -> Coroutine {
static MIN_STACK_SIZE: uint = 3000000; // XXX: Too much stack
pub fn new(stack_pool: &mut StackPool, stack_size: Option<uint>, start: ~fn()) -> Coroutine {
let stack_size = match stack_size {
Some(size) => size,
None => env::min_stack()
};
let start = Coroutine::build_start_wrapper(start);
let mut stack = stack_pool.take_segment(MIN_STACK_SIZE);
let mut stack = stack_pool.take_segment(stack_size);
let initial_context = Context::new(start, &mut stack);
Coroutine {
current_stack_segment: stack,
@ -465,10 +474,10 @@ mod test {
do run_in_newsched_task() {
static key: local_data::Key<@~str> = &local_data::Key;
local_data::set(key, @~"data");
assert!(*local_data::get(key, |k| k.map(|&k| *k)).unwrap() == ~"data");
assert!(*local_data::get(key, |k| k.map_move(|k| *k)).unwrap() == ~"data");
static key2: local_data::Key<@~str> = &local_data::Key;
local_data::set(key2, @~"data");
assert!(*local_data::get(key2, |k| k.map(|&k| *k)).unwrap() == ~"data");
assert!(*local_data::get(key2, |k| k.map_move(|k| *k)).unwrap() == ~"data");
}
}

View file

@ -15,8 +15,8 @@ use cell::Cell;
use clone::Clone;
use container::Container;
use iterator::{Iterator, range};
use vec::{OwnedVector, MutableVector};
use super::io::net::ip::{SocketAddr, Ipv4Addr, Ipv6Addr};
use vec::{OwnedVector, MutableVector, ImmutableVector};
use rt::sched::Scheduler;
use unstable::run_in_bare_thread;
use rt::thread::Thread;
@ -29,8 +29,12 @@ use result::{Result, Ok, Err};
pub fn new_test_uv_sched() -> Scheduler {
let queue = WorkQueue::new();
let queues = ~[queue.clone()];
let mut sched = Scheduler::new(~UvEventLoop::new(),
WorkQueue::new(),
queue,
queues,
SleeperList::new());
// Don't wait for the Shutdown message
@ -57,7 +61,7 @@ pub fn run_in_newsched_task_core(f: ~fn()) {
exit_handle.take().send(Shutdown);
rtassert!(exit_status);
};
let mut task = ~Task::new_root(&mut sched.stack_pool, f);
let mut task = ~Task::new_root(&mut sched.stack_pool, None, f);
task.death.on_exit = Some(on_exit);
sched.bootstrap(task);
@ -164,15 +168,21 @@ pub fn run_in_mt_newsched_task(f: ~fn()) {
};
let sleepers = SleeperList::new();
let work_queue = WorkQueue::new();
let mut handles = ~[];
let mut scheds = ~[];
let mut work_queues = ~[];
for _ in range(0u, nthreads) {
let work_queue = WorkQueue::new();
work_queues.push(work_queue);
}
for i in range(0u, nthreads) {
let loop_ = ~UvEventLoop::new();
let mut sched = ~Scheduler::new(loop_,
work_queue.clone(),
work_queues[i].clone(),
work_queues.clone(),
sleepers.clone());
let handle = sched.make_handle();
@ -190,8 +200,7 @@ pub fn run_in_mt_newsched_task(f: ~fn()) {
rtassert!(exit_status);
};
let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool,
f.take());
let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool, None, f.take());
main_task.death.on_exit = Some(on_exit);
let mut threads = ~[];
@ -209,7 +218,7 @@ pub fn run_in_mt_newsched_task(f: ~fn()) {
while !scheds.is_empty() {
let mut sched = scheds.pop();
let bootstrap_task = ~do Task::new_root(&mut sched.stack_pool) || {
let bootstrap_task = ~do Task::new_root(&mut sched.stack_pool, None) || {
rtdebug!("bootstrapping non-primary scheduler");
};
let bootstrap_task_cell = Cell::new(bootstrap_task);
@ -232,12 +241,12 @@ pub fn run_in_mt_newsched_task(f: ~fn()) {
/// Test tasks will abort on failure instead of unwinding
pub fn spawntask(f: ~fn()) {
Scheduler::run_task(Task::build_child(f));
Scheduler::run_task(Task::build_child(None, f));
}
/// Create a new task and run it right now. Aborts on failure
pub fn spawntask_later(f: ~fn()) {
Scheduler::run_task_later(Task::build_child(f));
Scheduler::run_task_later(Task::build_child(None, f));
}
pub fn spawntask_random(f: ~fn()) {
@ -259,7 +268,7 @@ pub fn spawntask_try(f: ~fn()) -> Result<(),()> {
let chan = Cell::new(chan);
let on_exit: ~fn(bool) = |exit_status| chan.take().send(exit_status);
let mut new_task = Task::build_root(f);
let mut new_task = Task::build_root(None, f);
new_task.death.on_exit = Some(on_exit);
Scheduler::run_task(new_task);
@ -285,7 +294,7 @@ pub fn spawntask_thread(f: ~fn()) -> Thread {
pub fn with_test_task(blk: ~fn(~Task) -> ~Task) {
do run_in_bare_thread {
let mut sched = ~new_test_uv_sched();
let task = blk(~Task::new_root(&mut sched.stack_pool, ||{}));
let task = blk(~Task::new_root(&mut sched.stack_pool, None, ||{}));
cleanup_task(task);
}
}

View file

@ -634,7 +634,6 @@ fn spawn_process_os(prog: &str, args: &[~str],
use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
use libc::funcs::bsd44::getdtablesize;
use int;
mod rustrt {
use libc::c_void;
@ -667,10 +666,9 @@ fn spawn_process_os(prog: &str, args: &[~str],
fail!("failure in dup3(err_fd, 2): %s", os::last_os_error());
}
// close all other fds
do int::range_rev(getdtablesize() as int, 3) |fd| {
for fd in range(3, getdtablesize()).invert() {
close(fd as c_int);
true
};
}
do with_dirp(dir) |dirp| {
if !dirp.is_null() && chdir(dirp) == -1 {
@ -780,14 +778,14 @@ fn with_dirp<T>(d: Option<&Path>, cb: &fn(*libc::c_char) -> T) -> T {
}
#[cfg(windows)]
priv fn free_handle(handle: *()) {
fn free_handle(handle: *()) {
unsafe {
libc::funcs::extra::kernel32::CloseHandle(cast::transmute(handle));
}
}
#[cfg(unix)]
priv fn free_handle(_handle: *()) {
fn free_handle(_handle: *()) {
// unix has no process handle object, just a pid
}
@ -842,7 +840,7 @@ pub fn process_output(prog: &str, args: &[~str]) -> ProcessOutput {
* operate on a none-existant process or, even worse, on a newer process
* with the same id.
*/
priv fn waitpid(pid: pid_t) -> int {
fn waitpid(pid: pid_t) -> int {
return waitpid_os(pid);
#[cfg(windows)]

View file

@ -178,6 +178,7 @@ pub mod rand;
pub mod run;
pub mod sys;
pub mod cast;
pub mod fmt;
pub mod repr;
pub mod cleanup;
pub mod reflect;
@ -217,4 +218,6 @@ mod std {
pub use unstable;
pub use str;
pub use os;
pub use fmt;
pub use to_bytes;
}

View file

@ -855,7 +855,7 @@ pub fn count_bytes<'b>(s: &'b str, start: uint, n: uint) -> uint {
}
// https://tools.ietf.org/html/rfc3629
priv static UTF8_CHAR_WIDTH: [u8, ..256] = [
static UTF8_CHAR_WIDTH: [u8, ..256] = [
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 0x1F
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
@ -898,15 +898,15 @@ macro_rules! utf8_acc_cont_byte(
)
// UTF-8 tags and ranges
priv static TAG_CONT_U8: u8 = 128u8;
priv static TAG_CONT: uint = 128u;
priv static MAX_ONE_B: uint = 128u;
priv static TAG_TWO_B: uint = 192u;
priv static MAX_TWO_B: uint = 2048u;
priv static TAG_THREE_B: uint = 224u;
priv static MAX_THREE_B: uint = 65536u;
priv static TAG_FOUR_B: uint = 240u;
priv static MAX_UNICODE: uint = 1114112u;
static TAG_CONT_U8: u8 = 128u8;
static TAG_CONT: uint = 128u;
static MAX_ONE_B: uint = 128u;
static TAG_TWO_B: uint = 192u;
static MAX_TWO_B: uint = 2048u;
static TAG_THREE_B: uint = 224u;
static MAX_THREE_B: uint = 65536u;
static TAG_FOUR_B: uint = 240u;
static MAX_UNICODE: uint = 1114112u;
/// Unsafe operations
pub mod raw {
@ -2085,7 +2085,7 @@ impl<'self> StrSlice<'self> for &'self str {
} else {
self.matches_index_iter(needle)
.next()
.map_consume(|(start, _end)| start)
.map_move(|(start, _end)| start)
}
}

View file

@ -305,7 +305,7 @@ pub fn to_ascii_lower(string: &str) -> ~str {
}
#[inline]
priv fn map_bytes(string: &str, map: &'static [u8]) -> ~str {
fn map_bytes(string: &str, map: &'static [u8]) -> ~str {
let len = string.len();
let mut result = str::with_capacity(len);
unsafe {
@ -329,7 +329,7 @@ pub fn eq_ignore_ascii_case(a: &str, b: &str) -> bool {
|(byte_a, byte_b)| ASCII_LOWER_MAP[*byte_a] == ASCII_LOWER_MAP[*byte_b])
}
priv static ASCII_LOWER_MAP: &'static [u8] = &[
static ASCII_LOWER_MAP: &'static [u8] = &[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
@ -364,7 +364,7 @@ priv static ASCII_LOWER_MAP: &'static [u8] = &[
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
];
priv static ASCII_UPPER_MAP: &'static [u8] = &[
static ASCII_UPPER_MAP: &'static [u8] = &[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,

View file

@ -142,7 +142,8 @@ pub struct TaskOpts {
indestructible: bool,
notify_chan: Option<Chan<TaskResult>>,
name: Option<~str>,
sched: SchedOpts
sched: SchedOpts,
stack_size: Option<uint>
}
/**
@ -197,7 +198,8 @@ impl TaskBuilder {
indestructible: self.opts.indestructible,
notify_chan: notify_chan,
name: name,
sched: self.opts.sched
sched: self.opts.sched,
stack_size: self.opts.stack_size
},
gen_body: gen_body,
can_not_copy: None,
@ -351,7 +353,8 @@ impl TaskBuilder {
indestructible: x.opts.indestructible,
notify_chan: notify_chan,
name: name,
sched: x.opts.sched
sched: x.opts.sched,
stack_size: x.opts.stack_size
};
let f = match gen_body {
Some(gen) => {
@ -422,7 +425,8 @@ pub fn default_task_opts() -> TaskOpts {
name: None,
sched: SchedOpts {
mode: DefaultScheduler,
}
},
stack_size: None
}
}
@ -655,6 +659,7 @@ pub unsafe fn rekillable<U>(f: &fn() -> U) -> U {
}
}
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_kill_unkillable_task() {
use rt::test::*;
@ -675,6 +680,7 @@ fn test_kill_unkillable_task() {
}
}
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_kill_rekillable_task() {
use rt::test::*;
@ -716,6 +722,7 @@ fn test_cant_dup_task_builder() {
#[cfg(test)]
fn block_forever() { let (po, _ch) = stream::<()>(); po.recv(); }
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_spawn_unlinked_unsup_no_fail_down() { // grandchild sends on a port
use rt::test::run_in_newsched_task;
@ -734,6 +741,7 @@ fn test_spawn_unlinked_unsup_no_fail_down() { // grandchild sends on a port
po.recv();
}
}
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_spawn_unlinked_unsup_no_fail_up() { // child unlinked fails
use rt::test::run_in_newsched_task;
@ -741,6 +749,7 @@ fn test_spawn_unlinked_unsup_no_fail_up() { // child unlinked fails
do spawn_unlinked { fail!(); }
}
}
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails
use rt::test::run_in_newsched_task;
@ -750,6 +759,7 @@ fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails
do 16.times { task::yield(); }
}
}
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_spawn_unlinked_sup_fail_down() {
use rt::test::run_in_newsched_task;
@ -762,6 +772,7 @@ fn test_spawn_unlinked_sup_fail_down() {
}
}
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_spawn_linked_sup_fail_up() { // child fails; parent fails
use rt::test::run_in_newsched_task;
@ -782,6 +793,7 @@ fn test_spawn_linked_sup_fail_up() { // child fails; parent fails
assert!(result.is_err());
}
}
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_spawn_linked_sup_fail_down() { // parent fails; child fails
use rt::test::run_in_newsched_task;
@ -798,6 +810,7 @@ fn test_spawn_linked_sup_fail_down() { // parent fails; child fails
assert!(result.is_err());
}
}
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails
use rt::test::run_in_newsched_task;
@ -810,6 +823,7 @@ fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails
assert!(result.is_err());
}
}
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails
use rt::test::run_in_newsched_task;
@ -822,6 +836,7 @@ fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails
assert!(result.is_err());
}
}
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails
use rt::test::run_in_newsched_task;
@ -840,6 +855,7 @@ fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails
// A couple bonus linked failure tests - testing for failure propagation even
// when the middle task exits successfully early before kill signals are sent.
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_spawn_failure_propagate_grandchild() {
use rt::test::run_in_newsched_task;
@ -856,6 +872,7 @@ fn test_spawn_failure_propagate_grandchild() {
}
}
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_spawn_failure_propagate_secondborn() {
use rt::test::run_in_newsched_task;
@ -872,6 +889,7 @@ fn test_spawn_failure_propagate_secondborn() {
}
}
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_spawn_failure_propagate_nephew_or_niece() {
use rt::test::run_in_newsched_task;
@ -888,6 +906,7 @@ fn test_spawn_failure_propagate_nephew_or_niece() {
}
}
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_spawn_linked_sup_propagate_sibling() {
use rt::test::run_in_newsched_task;
@ -1191,6 +1210,7 @@ fn test_avoid_copying_the_body_unlinked() {
}
}
#[ignore(reason = "linked failure")]
#[test]
#[ignore(cfg(windows))]
#[should_fail]
@ -1226,6 +1246,7 @@ fn test_unkillable() {
po.recv();
}
#[ignore(reason = "linked failure")]
#[test]
#[ignore(cfg(windows))]
#[should_fail]
@ -1292,6 +1313,7 @@ fn test_simple_newsched_spawn() {
}
}
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_spawn_watched() {
use rt::test::run_in_newsched_task;
@ -1314,6 +1336,7 @@ fn test_spawn_watched() {
}
}
#[ignore(reason = "linked failure")]
#[test] #[ignore(cfg(windows))]
fn test_indestructible() {
use rt::test::run_in_newsched_task;

View file

@ -98,6 +98,7 @@ use rt::kill::KillHandle;
use rt::sched::Scheduler;
use rt::uv::uvio::UvEventLoop;
use rt::thread::Thread;
use rt::work_queue::WorkQueue;
#[cfg(test)] use task::default_task_opts;
#[cfg(test)] use comm;
@ -500,7 +501,7 @@ impl RuntimeGlue {
OldTask(ptr) => rt::rust_task_kill_other(ptr),
NewTask(handle) => {
let mut handle = handle;
do handle.kill().map_consume |killed_task| {
do handle.kill().map_move |killed_task| {
let killed_task = Cell::new(killed_task);
do Local::borrow::<Scheduler, ()> |sched| {
sched.enqueue_task(killed_task.take());
@ -682,7 +683,7 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
// Child task runs this code.
// If child data is 'None', the enlist is vacuously successful.
let enlist_success = do child_data.take().map_consume_default(true) |child_data| {
let enlist_success = do child_data.take().map_move_default(true) |child_data| {
let child_data = Cell::new(child_data); // :(
do Local::borrow::<Task, bool> |me| {
let (child_tg, ancestors, is_main) = child_data.take();
@ -713,19 +714,25 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
let mut task = unsafe {
if opts.sched.mode != SingleThreaded {
if opts.watched {
Task::build_child(child_wrapper)
Task::build_child(opts.stack_size, child_wrapper)
} else {
Task::build_root(child_wrapper)
Task::build_root(opts.stack_size, child_wrapper)
}
} else {
// Creating a 1:1 task:thread ...
let sched = Local::unsafe_borrow::<Scheduler>();
let sched_handle = (*sched).make_handle();
// Since this is a 1:1 scheduler we create a queue not in
// the stealee set. The run_anything flag is set false
// which will disable stealing.
let work_queue = WorkQueue::new();
// Create a new scheduler to hold the new task
let new_loop = ~UvEventLoop::new();
let mut new_sched = ~Scheduler::new_special(new_loop,
(*sched).work_queue.clone(),
work_queue,
(*sched).work_queues.clone(),
(*sched).sleeper_list.clone(),
false,
Some(sched_handle));
@ -736,16 +743,16 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
// Pin the new task to the new scheduler
let new_task = if opts.watched {
Task::build_homed_child(child_wrapper, Sched(new_sched_handle))
Task::build_homed_child(opts.stack_size, child_wrapper, Sched(new_sched_handle))
} else {
Task::build_homed_root(child_wrapper, Sched(new_sched_handle))
Task::build_homed_root(opts.stack_size, child_wrapper, Sched(new_sched_handle))
};
// Create a task that will later be used to join with the new scheduler
// thread when it is ready to terminate
let (thread_port, thread_chan) = oneshot();
let thread_port_cell = Cell::new(thread_port);
let join_task = do Task::build_child() {
let join_task = do Task::build_child(None) {
rtdebug!("running join task");
let thread_port = thread_port_cell.take();
let thread: Thread = thread_port.recv();
@ -762,8 +769,8 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
let mut orig_sched_handle = orig_sched_handle_cell.take();
let join_task = join_task_cell.take();
let bootstrap_task = ~do Task::new_root(&mut new_sched.stack_pool) || {
rtdebug!("bootstrapping a 1:1 scheduler");
let bootstrap_task = ~do Task::new_root(&mut new_sched.stack_pool, None) || {
rtdebug!("boostrapping a 1:1 scheduler");
};
new_sched.bootstrap(bootstrap_task);
@ -854,7 +861,7 @@ fn spawn_raw_oldsched(mut opts: TaskOpts, f: ~fn()) {
// Even if the below code fails to kick the child off, we must
// send Something on the notify channel.
let notifier = notify_chan.map_consume(|c| AutoNotify(c));
let notifier = notify_chan.map_move(|c| AutoNotify(c));
if enlist_many(OldTask(child), &child_arc, &mut ancestors) {
let group = @@mut Taskgroup(child_arc, ancestors, is_main, notifier);

View file

@ -271,8 +271,8 @@ impl<T> TrieNode<T> {
impl<T> TrieNode<T> {
fn each<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) -> bool {
for idx in range(0u, self.children.len()) {
match self.children[idx] {
for elt in self.children.iter() {
match *elt {
Internal(ref x) => if !x.each(|i,t| f(i,t)) { return false },
External(k, ref v) => if !f(&k, v) { return false },
Nothing => ()
@ -282,13 +282,14 @@ impl<T> TrieNode<T> {
}
fn each_reverse<'a>(&'a self, f: &fn(&uint, &'a T) -> bool) -> bool {
do uint::range_rev(self.children.len(), 0) |idx| {
match self.children[idx] {
Internal(ref x) => x.each_reverse(|i,t| f(i,t)),
External(k, ref v) => f(&k, v),
Nothing => true
for elt in self.children.rev_iter() {
match *elt {
Internal(ref x) => if !x.each_reverse(|i,t| f(i,t)) { return false },
External(k, ref v) => if !f(&k, v) { return false },
Nothing => ()
}
}
true
}
fn mutate_values<'a>(&'a mut self, f: &fn(&uint, &mut T) -> bool) -> bool {
@ -539,10 +540,9 @@ mod test_map {
fn test_each_break() {
let mut m = TrieMap::new();
do uint::range_rev(uint::max_value, uint::max_value - 10000) |x| {
for x in range(uint::max_value - 10000, uint::max_value).invert() {
m.insert(x, x / 2);
true
};
}
let mut n = uint::max_value - 10000;
do m.each |k, v| {
@ -580,10 +580,9 @@ mod test_map {
fn test_each_reverse_break() {
let mut m = TrieMap::new();
do uint::range_rev(uint::max_value, uint::max_value - 10000) |x| {
for x in range(uint::max_value - 10000, uint::max_value).invert() {
m.insert(x, x / 2);
true
};
}
let mut n = uint::max_value - 1;
do m.each_reverse |k, v| {
@ -634,10 +633,9 @@ mod test_map {
let last = uint::max_value;
let mut map = TrieMap::new();
do uint::range_rev(last, first) |x| {
for x in range(first, last).invert() {
map.insert(x, x / 2);
true
};
}
let mut i = 0;
for (k, &v) in map.iter() {

View file

@ -136,7 +136,7 @@ pub fn start(main: *u8, argc: int, argv: **c_char,
use os;
unsafe {
let use_old_rt = os::getenv("RUST_NEWRT").is_none();
let use_old_rt = os::getenv("RUST_OLDRT").is_some();
if use_old_rt {
return rust_start(main as *c_void, argc as c_int, argv,
crate_map as *c_void) as int;

View file

@ -1602,8 +1602,8 @@ impl<T:Clone> OwnedCopyableVector<T> for ~[T] {
let new_len = self.len() + rhs.len();
self.reserve(new_len);
for i in range(0u, rhs.len()) {
self.push(unsafe { raw::get(rhs, i) })
for elt in rhs.iter() {
self.push((*elt).clone())
}
}