diff options
| author | Johannes Hoff <johshoff@gmail.com> | 2014-12-24 13:22:11 +0100 |
|---|---|---|
| committer | Johannes Hoff <johshoff@gmail.com> | 2014-12-24 13:22:11 +0100 |
| commit | 0128159c95d0544e0c30b8b52ce3e7ce348fc114 (patch) | |
| tree | 8af4db0f2758f86434b895169122a9962fb79b21 /src/libstd | |
| parent | 8f827d33cab1be648120fc8ac34651d9cc079b5e (diff) | |
| parent | e64a8193b02ce72ef183274994a25eae281cb89c (diff) | |
| download | rust-0128159c95d0544e0c30b8b52ce3e7ce348fc114.tar.gz rust-0128159c95d0544e0c30b8b52ce3e7ce348fc114.zip | |
Merge branch 'master' into cfg_tmp_dir
Conflicts: src/etc/rustup.sh
Diffstat (limited to 'src/libstd')
146 files changed, 20157 insertions, 5505 deletions
diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs index 933794cb5a4..6c213555ce4 100644 --- a/src/libstd/ascii.rs +++ b/src/libstd/ascii.rs @@ -12,49 +12,69 @@ //! Operations on ASCII strings and characters -#![experimental] +#![unstable = "unsure about placement and naming"] +#![allow(deprecated)] use core::kinds::Sized; use fmt; -use iter::Iterator; +use iter::IteratorExt; use mem; -use option::{Option, Some, None}; -use slice::{SlicePrelude, AsSlice}; -use str::{Str, StrPrelude}; -use string::{mod, String, IntoString}; +use ops::FnMut; +use option::Option; +use option::Option::{Some, None}; +use slice::{SliceExt, AsSlice}; +use str::{Str, StrExt}; +use string::{String, IntoString}; use vec::Vec; /// Datatype to hold one ascii character. It wraps a `u8`, with the highest bit always zero. -#[deriving(Clone, PartialEq, PartialOrd, Ord, Eq, Hash)] +#[deriving(Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)] pub struct Ascii { chr: u8 } impl Ascii { /// Converts an ascii character into a `u8`. #[inline] - pub fn to_byte(self) -> u8 { + #[unstable = "recently renamed"] + pub fn as_byte(&self) -> u8 { self.chr } + /// Deprecated: use `as_byte` instead. + #[deprecated = "use as_byte"] + pub fn to_byte(self) -> u8 { + self.as_byte() + } + /// Converts an ascii character into a `char`. #[inline] - pub fn to_char(self) -> char { + #[unstable = "recently renamed"] + pub fn as_char(&self) -> char { self.chr as char } + /// Deprecated: use `as_char` instead. + #[deprecated = "use as_char"] + pub fn to_char(self) -> char { + self.as_char() + } + /// Convert to lowercase. #[inline] - pub fn to_lowercase(self) -> Ascii { + #[stable] + pub fn to_lowercase(&self) -> Ascii { Ascii{chr: ASCII_LOWER_MAP[self.chr as uint]} } /// Convert to uppercase. #[inline] - pub fn to_uppercase(self) -> Ascii { + #[stable] + pub fn to_uppercase(&self) -> Ascii { Ascii{chr: ASCII_UPPER_MAP[self.chr as uint]} } /// Compares two ascii characters of equality, ignoring case. #[inline] + #[deprecated = "normalize with to_lowercase"] pub fn eq_ignore_case(self, other: Ascii) -> bool { ASCII_LOWER_MAP[self.chr as uint] == ASCII_LOWER_MAP[other.chr as uint] } @@ -63,66 +83,77 @@ impl Ascii { /// Check if the character is a letter (a-z, A-Z) #[inline] + #[stable] pub fn is_alphabetic(&self) -> bool { (self.chr >= 0x41 && self.chr <= 0x5A) || (self.chr >= 0x61 && self.chr <= 0x7A) } /// Check if the character is a number (0-9) #[inline] + #[unstable = "may be renamed"] pub fn is_digit(&self) -> bool { self.chr >= 0x30 && self.chr <= 0x39 } /// Check if the character is a letter or number #[inline] + #[stable] pub fn is_alphanumeric(&self) -> bool { self.is_alphabetic() || self.is_digit() } /// Check if the character is a space or horizontal tab #[inline] + #[experimental = "likely to be removed"] pub fn is_blank(&self) -> bool { self.chr == b' ' || self.chr == b'\t' } /// Check if the character is a control character #[inline] + #[stable] pub fn is_control(&self) -> bool { self.chr < 0x20 || self.chr == 0x7F } /// Checks if the character is printable (except space) #[inline] + #[experimental = "unsure about naming, or whether this is needed"] pub fn is_graph(&self) -> bool { (self.chr - 0x21) < 0x5E } /// Checks if the character is printable (including space) #[inline] + #[unstable = "unsure about naming"] pub fn is_print(&self) -> bool { (self.chr - 0x20) < 0x5F } - /// Checks if the character is lowercase + /// Checks if the character is alphabetic and lowercase #[inline] + #[stable] pub fn is_lowercase(&self) -> bool { (self.chr - b'a') < 26 } - /// Checks if the character is uppercase + /// Checks if the character is alphabetic and uppercase #[inline] + #[stable] pub fn is_uppercase(&self) -> bool { (self.chr - b'A') < 26 } /// Checks if the character is punctuation #[inline] + #[stable] pub fn is_punctuation(&self) -> bool { self.is_graph() && !self.is_alphanumeric() } /// Checks if the character is a valid hex digit #[inline] + #[stable] pub fn is_hex(&self) -> bool { self.is_digit() || ((self.chr | 32u8) - b'a') < 6 } @@ -135,7 +166,8 @@ impl<'a> fmt::Show for Ascii { } /// Trait for converting into an ascii type. -pub trait AsciiCast<T> { +#[experimental = "may be replaced by generic conversion traits"] +pub trait AsciiCast<T> for Sized? { /// Convert to an ascii type, panic on non-ASCII input. #[inline] fn to_ascii(&self) -> T { @@ -160,10 +192,11 @@ pub trait AsciiCast<T> { fn is_ascii(&self) -> bool; } -impl<'a> AsciiCast<&'a[Ascii]> for &'a [u8] { +#[experimental = "may be replaced by generic conversion traits"] +impl<'a> AsciiCast<&'a[Ascii]> for [u8] { #[inline] unsafe fn to_ascii_nocheck(&self) -> &'a[Ascii] { - mem::transmute(*self) + mem::transmute(self) } #[inline] @@ -175,10 +208,11 @@ impl<'a> AsciiCast<&'a[Ascii]> for &'a [u8] { } } -impl<'a> AsciiCast<&'a [Ascii]> for &'a str { +#[experimental = "may be replaced by generic conversion traits"] +impl<'a> AsciiCast<&'a [Ascii]> for str { #[inline] unsafe fn to_ascii_nocheck(&self) -> &'a [Ascii] { - mem::transmute(*self) + mem::transmute(self) } #[inline] @@ -187,6 +221,7 @@ impl<'a> AsciiCast<&'a [Ascii]> for &'a str { } } +#[experimental = "may be replaced by generic conversion traits"] impl AsciiCast<Ascii> for u8 { #[inline] unsafe fn to_ascii_nocheck(&self) -> Ascii { @@ -199,6 +234,7 @@ impl AsciiCast<Ascii> for u8 { } } +#[experimental = "may be replaced by generic conversion traits"] impl AsciiCast<Ascii> for char { #[inline] unsafe fn to_ascii_nocheck(&self) -> Ascii { @@ -212,6 +248,7 @@ impl AsciiCast<Ascii> for char { } /// Trait for copyless casting to an ascii vector. +#[experimental = "may be replaced by generic conversion traits"] pub trait OwnedAsciiCast { /// Check if convertible to ascii fn is_ascii(&self) -> bool; @@ -241,6 +278,7 @@ pub trait OwnedAsciiCast { unsafe fn into_ascii_nocheck(self) -> Vec<Ascii>; } +#[experimental = "may be replaced by generic conversion traits"] impl OwnedAsciiCast for String { #[inline] fn is_ascii(&self) -> bool { @@ -253,6 +291,7 @@ impl OwnedAsciiCast for String { } } +#[experimental = "may be replaced by generic conversion traits"] impl OwnedAsciiCast for Vec<u8> { #[inline] fn is_ascii(&self) -> bool { @@ -274,6 +313,7 @@ impl OwnedAsciiCast for Vec<u8> { /// Trait for converting an ascii type to a string. Needed to convert /// `&[Ascii]` to `&str`. +#[experimental = "may be replaced by generic conversion traits"] pub trait AsciiStr for Sized? { /// Convert to a string. fn as_str_ascii<'a>(&'a self) -> &'a str; @@ -283,6 +323,7 @@ pub trait AsciiStr for Sized? { fn to_lower(&self) -> Vec<Ascii>; /// Convert to vector representing a lower cased ascii string. + #[deprecated = "use iterators instead"] fn to_lowercase(&self) -> Vec<Ascii>; /// Deprecated: use `to_uppercase` @@ -290,12 +331,15 @@ pub trait AsciiStr for Sized? { fn to_upper(&self) -> Vec<Ascii>; /// Convert to vector representing a upper cased ascii string. + #[deprecated = "use iterators instead"] fn to_uppercase(&self) -> Vec<Ascii>; /// Compares two Ascii strings ignoring case. + #[deprecated = "use iterators instead"] fn eq_ignore_case(&self, other: &[Ascii]) -> bool; } +#[experimental = "may be replaced by generic conversion traits"] impl AsciiStr for [Ascii] { #[inline] fn as_str_ascii<'a>(&'a self) -> &'a str { @@ -331,18 +375,18 @@ impl AsciiStr for [Ascii] { impl IntoString for Vec<Ascii> { #[inline] fn into_string(self) -> String { - unsafe { - string::raw::from_utf8(self.into_bytes()) - } + unsafe { String::from_utf8_unchecked(self.into_bytes()) } } } /// Trait to convert to an owned byte vector by consuming self +#[experimental = "may be replaced by generic conversion traits"] pub trait IntoBytes { /// Converts to an owned byte vector by consuming self fn into_bytes(self) -> Vec<u8>; } +#[experimental = "may be replaced by generic conversion traits"] impl IntoBytes for Vec<Ascii> { fn into_bytes(self) -> Vec<u8> { unsafe { @@ -360,6 +404,7 @@ impl IntoBytes for Vec<Ascii> { /// Extension methods for ASCII-subset only operations on owned strings +#[experimental = "would prefer to do this in a more general way"] pub trait OwnedAsciiExt { /// Convert the string to ASCII upper case: /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', @@ -373,6 +418,7 @@ pub trait OwnedAsciiExt { } /// Extension methods for ASCII-subset only operations on string slices +#[experimental = "would prefer to do this in a more general way"] pub trait AsciiExt<T> for Sized? { /// Makes a copy of the string in ASCII upper case: /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', @@ -390,17 +436,18 @@ pub trait AsciiExt<T> for Sized? { fn eq_ignore_ascii_case(&self, other: &Self) -> bool; } +#[experimental = "would prefer to do this in a more general way"] impl AsciiExt<String> for str { #[inline] fn to_ascii_upper(&self) -> String { // Vec<u8>::to_ascii_upper() preserves the UTF-8 invariant. - unsafe { string::raw::from_utf8(self.as_bytes().to_ascii_upper()) } + unsafe { String::from_utf8_unchecked(self.as_bytes().to_ascii_upper()) } } #[inline] fn to_ascii_lower(&self) -> String { // Vec<u8>::to_ascii_lower() preserves the UTF-8 invariant. - unsafe { string::raw::from_utf8(self.as_bytes().to_ascii_lower()) } + unsafe { String::from_utf8_unchecked(self.as_bytes().to_ascii_lower()) } } #[inline] @@ -409,20 +456,22 @@ impl AsciiExt<String> for str { } } +#[experimental = "would prefer to do this in a more general way"] impl OwnedAsciiExt for String { #[inline] fn into_ascii_upper(self) -> String { // Vec<u8>::into_ascii_upper() preserves the UTF-8 invariant. - unsafe { string::raw::from_utf8(self.into_bytes().into_ascii_upper()) } + unsafe { String::from_utf8_unchecked(self.into_bytes().into_ascii_upper()) } } #[inline] fn into_ascii_lower(self) -> String { // Vec<u8>::into_ascii_lower() preserves the UTF-8 invariant. - unsafe { string::raw::from_utf8(self.into_bytes().into_ascii_lower()) } + unsafe { String::from_utf8_unchecked(self.into_bytes().into_ascii_lower()) } } } +#[experimental = "would prefer to do this in a more general way"] impl AsciiExt<Vec<u8>> for [u8] { #[inline] fn to_ascii_upper(&self) -> Vec<u8> { @@ -445,6 +494,7 @@ impl AsciiExt<Vec<u8>> for [u8] { } } +#[experimental = "would prefer to do this in a more general way"] impl OwnedAsciiExt for Vec<u8> { #[inline] fn into_ascii_upper(mut self) -> Vec<u8> { @@ -474,7 +524,10 @@ impl OwnedAsciiExt for Vec<u8> { /// - Any other chars in the range [0x20,0x7e] are not escaped. /// - Any other chars are given hex escapes. /// - Unicode escapes are never generated by this function. -pub fn escape_default(c: u8, f: |u8|) { +#[unstable = "needs to be updated to use an iterator"] +pub fn escape_default<F>(c: u8, mut f: F) where + F: FnMut(u8), +{ match c { b'\t' => { f(b'\\'); f(b't'); } b'\r' => { f(b'\\'); f(b'r'); } @@ -496,7 +549,7 @@ pub fn escape_default(c: u8, f: |u8|) { } } -pub static ASCII_LOWER_MAP: [u8, ..256] = [ +static ASCII_LOWER_MAP: [u8, ..256] = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, @@ -535,7 +588,7 @@ pub static ASCII_LOWER_MAP: [u8, ..256] = [ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, ]; -pub static ASCII_UPPER_MAP: [u8, ..256] = [ +static ASCII_UPPER_MAP: [u8, ..256] = [ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, @@ -580,16 +633,15 @@ mod tests { use prelude::*; use super::*; use char::from_u32; - use str::StrPrelude; - macro_rules! v2ascii ( + macro_rules! v2ascii { ( [$($e:expr),*]) => (&[$(Ascii{chr:$e}),*]); (&[$($e:expr),*]) => (&[$(Ascii{chr:$e}),*]); - ) + } - macro_rules! vec2ascii ( + macro_rules! vec2ascii { ($($e:expr),*) => ([$(Ascii{chr:$e}),*].to_vec()); - ) + } #[test] fn test_ascii() { @@ -628,33 +680,33 @@ mod tests { assert_eq!(test.to_ascii(), b); assert_eq!("( ;".to_ascii(), b); let v = vec![40u8, 32u8, 59u8]; - assert_eq!(v.as_slice().to_ascii(), b); - assert_eq!("( ;".to_string().as_slice().to_ascii(), b); + assert_eq!(v.to_ascii(), b); + assert_eq!("( ;".to_string().to_ascii(), b); - assert_eq!("abCDef&?#".to_ascii().to_lowercase().into_string(), "abcdef&?#".to_string()); - assert_eq!("abCDef&?#".to_ascii().to_uppercase().into_string(), "ABCDEF&?#".to_string()); + assert_eq!("abCDef&?#".to_ascii().to_lowercase().into_string(), "abcdef&?#"); + assert_eq!("abCDef&?#".to_ascii().to_uppercase().into_string(), "ABCDEF&?#"); - assert_eq!("".to_ascii().to_lowercase().into_string(), "".to_string()); - assert_eq!("YMCA".to_ascii().to_lowercase().into_string(), "ymca".to_string()); + assert_eq!("".to_ascii().to_lowercase().into_string(), ""); + assert_eq!("YMCA".to_ascii().to_lowercase().into_string(), "ymca"); let mixed = "abcDEFxyz:.;".to_ascii(); - assert_eq!(mixed.to_uppercase().into_string(), "ABCDEFXYZ:.;".to_string()); + assert_eq!(mixed.to_uppercase().into_string(), "ABCDEFXYZ:.;"); assert!("aBcDeF&?#".to_ascii().eq_ignore_case("AbCdEf&?#".to_ascii())); assert!("".is_ascii()); assert!("a".is_ascii()); - assert!(!"\u2009".is_ascii()); + assert!(!"\u{2009}".is_ascii()); } #[test] fn test_ascii_vec_ng() { - assert_eq!("abCDef&?#".to_ascii().to_lowercase().into_string(), "abcdef&?#".to_string()); - assert_eq!("abCDef&?#".to_ascii().to_uppercase().into_string(), "ABCDEF&?#".to_string()); - assert_eq!("".to_ascii().to_lowercase().into_string(), "".to_string()); - assert_eq!("YMCA".to_ascii().to_lowercase().into_string(), "ymca".to_string()); + assert_eq!("abCDef&?#".to_ascii().to_lowercase().into_string(), "abcdef&?#"); + assert_eq!("abCDef&?#".to_ascii().to_uppercase().into_string(), "ABCDEF&?#"); + assert_eq!("".to_ascii().to_lowercase().into_string(), ""); + assert_eq!("YMCA".to_ascii().to_lowercase().into_string(), "ymca"); let mixed = "abcDEFxyz:.;".to_ascii(); - assert_eq!(mixed.to_uppercase().into_string(), "ABCDEFXYZ:.;".to_string()); + assert_eq!(mixed.to_uppercase().into_string(), "ABCDEFXYZ:.;"); } #[test] @@ -671,8 +723,8 @@ mod tests { #[test] fn test_ascii_into_string() { - assert_eq!(vec2ascii![40, 32, 59].into_string(), "( ;".to_string()); - assert_eq!(vec2ascii!(40, 32, 59).into_string(), "( ;".to_string()); + assert_eq!(vec2ascii![40, 32, 59].into_string(), "( ;"); + assert_eq!(vec2ascii!(40, 32, 59).into_string(), "( ;"); } #[test] @@ -724,31 +776,31 @@ mod tests { #[test] fn test_to_ascii_upper() { - assert_eq!("url()URL()uRl()ürl".to_ascii_upper(), "URL()URL()URL()üRL".to_string()); - assert_eq!("hıKß".to_ascii_upper(), "HıKß".to_string()); + assert_eq!("url()URL()uRl()ürl".to_ascii_upper(), "URL()URL()URL()üRL"); + assert_eq!("hıKß".to_ascii_upper(), "HıKß"); let mut i = 0; while i <= 500 { let upper = if 'a' as u32 <= i && i <= 'z' as u32 { i + 'A' as u32 - 'a' as u32 } else { i }; - assert_eq!((from_u32(i).unwrap()).to_string().as_slice().to_ascii_upper(), - (from_u32(upper).unwrap()).to_string()) + assert_eq!((from_u32(i).unwrap()).to_string().to_ascii_upper(), + (from_u32(upper).unwrap()).to_string()); i += 1; } } #[test] fn test_to_ascii_lower() { - assert_eq!("url()URL()uRl()Ürl".to_ascii_lower(), "url()url()url()Ürl".to_string()); + assert_eq!("url()URL()uRl()Ürl".to_ascii_lower(), "url()url()url()Ürl"); // Dotted capital I, Kelvin sign, Sharp S. - assert_eq!("HİKß".to_ascii_lower(), "hİKß".to_string()); + assert_eq!("HİKß".to_ascii_lower(), "hİKß"); let mut i = 0; while i <= 500 { let lower = if 'A' as u32 <= i && i <= 'Z' as u32 { i + 'a' as u32 - 'A' as u32 } else { i }; - assert_eq!((from_u32(i).unwrap()).to_string().as_slice().to_ascii_lower(), - (from_u32(lower).unwrap()).to_string()) + assert_eq!((from_u32(i).unwrap()).to_string().to_ascii_lower(), + (from_u32(lower).unwrap()).to_string()); i += 1; } } @@ -757,14 +809,14 @@ mod tests { fn test_into_ascii_upper() { assert_eq!(("url()URL()uRl()ürl".to_string()).into_ascii_upper(), "URL()URL()URL()üRL".to_string()); - assert_eq!(("hıKß".to_string()).into_ascii_upper(), "HıKß".to_string()); + assert_eq!(("hıKß".to_string()).into_ascii_upper(), "HıKß"); let mut i = 0; while i <= 500 { let upper = if 'a' as u32 <= i && i <= 'z' as u32 { i + 'A' as u32 - 'a' as u32 } else { i }; assert_eq!((from_u32(i).unwrap()).to_string().into_ascii_upper(), - (from_u32(upper).unwrap()).to_string()) + (from_u32(upper).unwrap()).to_string()); i += 1; } } @@ -772,16 +824,16 @@ mod tests { #[test] fn test_into_ascii_lower() { assert_eq!(("url()URL()uRl()Ürl".to_string()).into_ascii_lower(), - "url()url()url()Ürl".to_string()); + "url()url()url()Ürl"); // Dotted capital I, Kelvin sign, Sharp S. - assert_eq!(("HİKß".to_string()).into_ascii_lower(), "hİKß".to_string()); + assert_eq!(("HİKß".to_string()).into_ascii_lower(), "hİKß"); let mut i = 0; while i <= 500 { let lower = if 'A' as u32 <= i && i <= 'Z' as u32 { i + 'a' as u32 - 'A' as u32 } else { i }; assert_eq!((from_u32(i).unwrap()).to_string().into_ascii_lower(), - (from_u32(lower).unwrap()).to_string()) + (from_u32(lower).unwrap()).to_string()); i += 1; } } @@ -801,7 +853,7 @@ mod tests { let c = i; let lower = if 'A' as u32 <= c && c <= 'Z' as u32 { c + 'a' as u32 - 'A' as u32 } else { c }; - assert!((from_u32(i).unwrap()).to_string().as_slice().eq_ignore_ascii_case( + assert!((from_u32(i).unwrap()).to_string().eq_ignore_ascii_case( (from_u32(lower).unwrap()).to_string().as_slice())); i += 1; } @@ -810,12 +862,12 @@ mod tests { #[test] fn test_to_string() { let s = Ascii{ chr: b't' }.to_string(); - assert_eq!(s, "t".to_string()); + assert_eq!(s, "t"); } #[test] fn test_show() { let c = Ascii { chr: b't' }; - assert_eq!(format!("{}", c), "t".to_string()); + assert_eq!(format!("{}", c), "t"); } } diff --git a/src/libstd/bitflags.rs b/src/libstd/bitflags.rs index d8023dd3e4e..5dd76047779 100644 --- a/src/libstd/bitflags.rs +++ b/src/libstd/bitflags.rs @@ -24,9 +24,9 @@ /// ```{.rust} /// bitflags! { /// flags Flags: u32 { -/// const FLAG_A = 0x00000001, -/// const FLAG_B = 0x00000010, -/// const FLAG_C = 0x00000100, +/// const FLAG_A = 0b00000001, +/// const FLAG_B = 0b00000010, +/// const FLAG_C = 0b00000100, /// const FLAG_ABC = FLAG_A.bits /// | FLAG_B.bits /// | FLAG_C.bits, @@ -50,8 +50,8 @@ /// /// bitflags! { /// flags Flags: u32 { -/// const FLAG_A = 0x00000001, -/// const FLAG_B = 0x00000010, +/// const FLAG_A = 0b00000001, +/// const FLAG_B = 0b00000010, /// } /// } /// @@ -117,7 +117,7 @@ macro_rules! bitflags { ($(#[$attr:meta])* flags $BitFlags:ident: $T:ty { $($(#[$Flag_attr:meta])* const $Flag:ident = $value:expr),+ }) => { - #[deriving(PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] + #[deriving(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)] $(#[$attr])* pub struct $BitFlags { bits: $T, @@ -149,9 +149,9 @@ macro_rules! bitflags { #[inline] pub fn from_bits(bits: $T) -> ::std::option::Option<$BitFlags> { if (bits & !$BitFlags::all().bits()) != 0 { - ::std::option::None + ::std::option::Option::None } else { - ::std::option::Some($BitFlags { bits: bits }) + ::std::option::Option::Some($BitFlags { bits: bits }) } } @@ -208,7 +208,7 @@ macro_rules! bitflags { impl BitOr<$BitFlags, $BitFlags> for $BitFlags { /// Returns the union of the two sets of flags. #[inline] - fn bitor(&self, other: &$BitFlags) -> $BitFlags { + fn bitor(self, other: $BitFlags) -> $BitFlags { $BitFlags { bits: self.bits | other.bits } } } @@ -216,7 +216,7 @@ macro_rules! bitflags { impl BitXor<$BitFlags, $BitFlags> for $BitFlags { /// Returns the left flags, but with all the right flags toggled. #[inline] - fn bitxor(&self, other: &$BitFlags) -> $BitFlags { + fn bitxor(self, other: $BitFlags) -> $BitFlags { $BitFlags { bits: self.bits ^ other.bits } } } @@ -224,7 +224,7 @@ macro_rules! bitflags { impl BitAnd<$BitFlags, $BitFlags> for $BitFlags { /// Returns the intersection between the two sets of flags. #[inline] - fn bitand(&self, other: &$BitFlags) -> $BitFlags { + fn bitand(self, other: $BitFlags) -> $BitFlags { $BitFlags { bits: self.bits & other.bits } } } @@ -232,11 +232,13 @@ macro_rules! bitflags { impl Sub<$BitFlags, $BitFlags> for $BitFlags { /// Returns the set difference of the two sets of flags. #[inline] - fn sub(&self, other: &$BitFlags) -> $BitFlags { + fn sub(self, other: $BitFlags) -> $BitFlags { $BitFlags { bits: self.bits & !other.bits } } } + // NOTE(stage0): Remove impl after a snapshot + #[cfg(stage0)] impl Not<$BitFlags> for $BitFlags { /// Returns the complement of this set of flags. #[inline] @@ -244,6 +246,15 @@ macro_rules! bitflags { $BitFlags { bits: !self.bits } & $BitFlags::all() } } + + #[cfg(not(stage0))] // NOTE(stage0): Remove cfg after a snapshot + impl Not<$BitFlags> for $BitFlags { + /// Returns the complement of this set of flags. + #[inline] + fn not(self) -> $BitFlags { + $BitFlags { bits: !self.bits } & $BitFlags::all() + } + } }; ($(#[$attr:meta])* flags $BitFlags:ident: $T:ty { $($(#[$Flag_attr:meta])* const $Flag:ident = $value:expr),+, @@ -261,7 +272,7 @@ macro_rules! bitflags { #[allow(non_upper_case_globals)] mod tests { use hash; - use option::{Some, None}; + use option::Option::{Some, None}; use ops::{BitOr, BitAnd, BitXor, Sub, Not}; bitflags! { @@ -270,10 +281,10 @@ mod tests { #[doc = "> "] #[doc = "> - Richard Feynman"] flags Flags: u32 { - const FlagA = 0x00000001, + const FlagA = 0b00000001, #[doc = "<pcwalton> macros are way better at generating code than trans is"] - const FlagB = 0x00000010, - const FlagC = 0x00000100, + const FlagB = 0b00000010, + const FlagC = 0b00000100, #[doc = "* cmr bed"] #[doc = "* strcat table"] #[doc = "<strcat> wait what?"] @@ -291,21 +302,21 @@ mod tests { #[test] fn test_bits(){ - assert_eq!(Flags::empty().bits(), 0x00000000); - assert_eq!(FlagA.bits(), 0x00000001); - assert_eq!(FlagABC.bits(), 0x00000111); + assert_eq!(Flags::empty().bits(), 0b00000000); + assert_eq!(FlagA.bits(), 0b00000001); + assert_eq!(FlagABC.bits(), 0b00000111); - assert_eq!(AnotherSetOfFlags::empty().bits(), 0x00); + assert_eq!(AnotherSetOfFlags::empty().bits(), 0b00); assert_eq!(AnotherFlag.bits(), !0_i8); } #[test] fn test_from_bits() { assert!(Flags::from_bits(0) == Some(Flags::empty())); - assert!(Flags::from_bits(0x1) == Some(FlagA)); - assert!(Flags::from_bits(0x10) == Some(FlagB)); - assert!(Flags::from_bits(0x11) == Some(FlagA | FlagB)); - assert!(Flags::from_bits(0x1000) == None); + assert!(Flags::from_bits(0b1) == Some(FlagA)); + assert!(Flags::from_bits(0b10) == Some(FlagB)); + assert!(Flags::from_bits(0b11) == Some(FlagA | FlagB)); + assert!(Flags::from_bits(0b1000) == None); assert!(AnotherSetOfFlags::from_bits(!0_i8) == Some(AnotherFlag)); } @@ -313,11 +324,11 @@ mod tests { #[test] fn test_from_bits_truncate() { assert!(Flags::from_bits_truncate(0) == Flags::empty()); - assert!(Flags::from_bits_truncate(0x1) == FlagA); - assert!(Flags::from_bits_truncate(0x10) == FlagB); - assert!(Flags::from_bits_truncate(0x11) == (FlagA | FlagB)); - assert!(Flags::from_bits_truncate(0x1000) == Flags::empty()); - assert!(Flags::from_bits_truncate(0x1001) == FlagA); + assert!(Flags::from_bits_truncate(0b1) == FlagA); + assert!(Flags::from_bits_truncate(0b10) == FlagB); + assert!(Flags::from_bits_truncate(0b11) == (FlagA | FlagB)); + assert!(Flags::from_bits_truncate(0b1000) == Flags::empty()); + assert!(Flags::from_bits_truncate(0b1001) == FlagA); assert!(AnotherSetOfFlags::from_bits_truncate(0_i8) == AnotherSetOfFlags::empty()); } diff --git a/src/libstd/bool.rs b/src/libstd/bool.rs new file mode 100644 index 00000000000..bbaab5ee3db --- /dev/null +++ b/src/libstd/bool.rs @@ -0,0 +1,15 @@ +// 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. + +//! The boolean type + +#![doc(primitive = "bool")] +#![stable] + diff --git a/src/libstd/c_str.rs b/src/libstd/c_str.rs new file mode 100644 index 00000000000..fb44961017f --- /dev/null +++ b/src/libstd/c_str.rs @@ -0,0 +1,844 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! C-string manipulation and management +//! +//! This modules provides the basic methods for creating and manipulating +//! null-terminated strings for use with FFI calls (back to C). Most C APIs require +//! that the string being passed to them is null-terminated, and by default rust's +//! string types are *not* null terminated. +//! +//! The other problem with translating Rust strings to C strings is that Rust +//! strings can validly contain a null-byte in the middle of the string (0 is a +//! valid Unicode codepoint). This means that not all Rust strings can actually be +//! translated to C strings. +//! +//! # Creation of a C string +//! +//! A C string is managed through the `CString` type defined in this module. It +//! "owns" the internal buffer of characters and will automatically deallocate the +//! buffer when the string is dropped. The `ToCStr` trait is implemented for `&str` +//! and `&[u8]`, but the conversions can fail due to some of the limitations +//! explained above. +//! +//! This also means that currently whenever a C string is created, an allocation +//! must be performed to place the data elsewhere (the lifetime of the C string is +//! not tied to the lifetime of the original string/data buffer). If C strings are +//! heavily used in applications, then caching may be advisable to prevent +//! unnecessary amounts of allocations. +//! +//! Be carefull to remember that the memory is managed by C allocator API and not +//! by Rust allocator API. +//! That means that the CString pointers should be freed with C allocator API +//! if you intend to do that on your own, as the behaviour if you free them with +//! Rust's allocator API is not well defined +//! +//! An example of creating and using a C string would be: +//! +//! ```rust +//! extern crate libc; +//! +//! extern { +//! fn puts(s: *const libc::c_char); +//! } +//! +//! fn main() { +//! let my_string = "Hello, world!"; +//! +//! // Allocate the C string with an explicit local that owns the string. The +//! // `c_buffer` pointer will be deallocated when `my_c_string` goes out of scope. +//! let my_c_string = my_string.to_c_str(); +//! unsafe { +//! puts(my_c_string.as_ptr()); +//! } +//! +//! // Don't save/return the pointer to the C string, the `c_buffer` will be +//! // deallocated when this block returns! +//! my_string.with_c_str(|c_buffer| { +//! unsafe { puts(c_buffer); } +//! }); +//! } +//! ``` + +use core::prelude::*; +use libc; + +use fmt; +use hash; +use kinds::marker; +use mem; +use ptr; +use slice::{mod, ImmutableIntSlice}; +use str; +use string::String; + + +/// The representation of a C String. +/// +/// This structure wraps a `*libc::c_char`, and will automatically free the +/// memory it is pointing to when it goes out of scope. +#[allow(missing_copy_implementations)] +pub struct CString { + buf: *const libc::c_char, + owns_buffer_: bool, +} + +impl Clone for CString { + /// Clone this CString into a new, uniquely owned CString. For safety + /// reasons, this is always a deep clone with the memory allocated + /// with C's allocator API, rather than the usual shallow clone. + fn clone(&self) -> CString { + let len = self.len() + 1; + let buf = unsafe { libc::malloc(len as libc::size_t) } as *mut libc::c_char; + if buf.is_null() { ::alloc::oom() } + unsafe { ptr::copy_nonoverlapping_memory(buf, self.buf, len); } + CString { buf: buf as *const libc::c_char, owns_buffer_: true } + } +} + +impl PartialEq for CString { + fn eq(&self, other: &CString) -> bool { + // Check if the two strings share the same buffer + if self.buf as uint == other.buf as uint { + true + } else { + unsafe { + libc::strcmp(self.buf, other.buf) == 0 + } + } + } +} + +impl PartialOrd for CString { + #[inline] + fn partial_cmp(&self, other: &CString) -> Option<Ordering> { + self.as_bytes().partial_cmp(other.as_bytes()) + } +} + +impl Eq for CString {} + +impl<S: hash::Writer> hash::Hash<S> for CString { + #[inline] + fn hash(&self, state: &mut S) { + self.as_bytes().hash(state) + } +} + +impl CString { + /// Create a C String from a pointer, with memory managed by C's allocator + /// API, so avoid calling it with a pointer to memory managed by Rust's + /// allocator API, as the behaviour would not be well defined. + /// + ///# Panics + /// + /// Panics if `buf` is null + pub unsafe fn new(buf: *const libc::c_char, owns_buffer: bool) -> CString { + assert!(!buf.is_null()); + CString { buf: buf, owns_buffer_: owns_buffer } + } + + /// Return a pointer to the NUL-terminated string data. + /// + /// `.as_ptr` returns an internal pointer into the `CString`, and + /// may be invalidated when the `CString` falls out of scope (the + /// destructor will run, freeing the allocation if there is + /// one). + /// + /// ```rust + /// let foo = "some string"; + /// + /// // right + /// let x = foo.to_c_str(); + /// let p = x.as_ptr(); + /// + /// // wrong (the CString will be freed, invalidating `p`) + /// let p = foo.to_c_str().as_ptr(); + /// ``` + /// + /// # Example + /// + /// ```rust + /// extern crate libc; + /// + /// fn main() { + /// let c_str = "foo bar".to_c_str(); + /// unsafe { + /// libc::puts(c_str.as_ptr()); + /// } + /// } + /// ``` + pub fn as_ptr(&self) -> *const libc::c_char { + self.buf + } + + /// Return a mutable pointer to the NUL-terminated string data. + /// + /// `.as_mut_ptr` returns an internal pointer into the `CString`, and + /// may be invalidated when the `CString` falls out of scope (the + /// destructor will run, freeing the allocation if there is + /// one). + /// + /// ```rust + /// let foo = "some string"; + /// + /// // right + /// let mut x = foo.to_c_str(); + /// let p = x.as_mut_ptr(); + /// + /// // wrong (the CString will be freed, invalidating `p`) + /// let p = foo.to_c_str().as_mut_ptr(); + /// ``` + pub fn as_mut_ptr(&mut self) -> *mut libc::c_char { + self.buf as *mut _ + } + + /// Returns whether or not the `CString` owns the buffer. + pub fn owns_buffer(&self) -> bool { + self.owns_buffer_ + } + + /// Converts the CString into a `&[u8]` without copying. + /// Includes the terminating NUL byte. + #[inline] + pub fn as_bytes<'a>(&'a self) -> &'a [u8] { + unsafe { + slice::from_raw_buf(&self.buf, self.len() + 1).as_unsigned() + } + } + + /// Converts the CString into a `&[u8]` without copying. + /// Does not include the terminating NUL byte. + #[inline] + pub fn as_bytes_no_nul<'a>(&'a self) -> &'a [u8] { + unsafe { + slice::from_raw_buf(&self.buf, self.len()).as_unsigned() + } + } + + /// Converts the CString into a `&str` without copying. + /// Returns None if the CString is not UTF-8. + #[inline] + pub fn as_str<'a>(&'a self) -> Option<&'a str> { + let buf = self.as_bytes_no_nul(); + str::from_utf8(buf).ok() + } + + /// Return a CString iterator. + pub fn iter<'a>(&'a self) -> CChars<'a> { + CChars { + ptr: self.buf, + marker: marker::ContravariantLifetime, + } + } + + /// Unwraps the wrapped `*libc::c_char` from the `CString` wrapper. + /// + /// Any ownership of the buffer by the `CString` wrapper is + /// forgotten, meaning that the backing allocation of this + /// `CString` is not automatically freed if it owns the + /// allocation. In this case, a user of `.unwrap()` should ensure + /// the allocation is freed, to avoid leaking memory. You should + /// use libc's memory allocator in this case. + /// + /// Prefer `.as_ptr()` when just retrieving a pointer to the + /// string data, as that does not relinquish ownership. + pub unsafe fn into_inner(mut self) -> *const libc::c_char { + self.owns_buffer_ = false; + self.buf + } + + /// Deprecated, use into_inner() instead + #[deprecated = "renamed to into_inner()"] + pub unsafe fn unwrap(self) -> *const libc::c_char { self.into_inner() } + + /// Return the number of bytes in the CString (not including the NUL + /// terminator). + #[inline] + pub fn len(&self) -> uint { + unsafe { libc::strlen(self.buf) as uint } + } + + /// Returns if there are no bytes in this string + #[inline] + pub fn is_empty(&self) -> bool { self.len() == 0 } +} + +impl Drop for CString { + fn drop(&mut self) { + if self.owns_buffer_ { + unsafe { + libc::free(self.buf as *mut libc::c_void) + } + } + } +} + +impl fmt::Show for CString { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + String::from_utf8_lossy(self.as_bytes_no_nul()).fmt(f) + } +} + +/// A generic trait for converting a value to a CString. +pub trait ToCStr for Sized? { + /// Copy the receiver into a CString. + /// + /// # Panics + /// + /// Panics the task if the receiver has an interior null. + fn to_c_str(&self) -> CString; + + /// Unsafe variant of `to_c_str()` that doesn't check for nulls. + unsafe fn to_c_str_unchecked(&self) -> CString; + + /// Work with a temporary CString constructed from the receiver. + /// The provided `*libc::c_char` will be freed immediately upon return. + /// + /// # Example + /// + /// ```rust + /// extern crate libc; + /// + /// fn main() { + /// let s = "PATH".with_c_str(|path| unsafe { + /// libc::getenv(path) + /// }); + /// } + /// ``` + /// + /// # Panics + /// + /// Panics the task if the receiver has an interior null. + #[inline] + fn with_c_str<T, F>(&self, f: F) -> T where + F: FnOnce(*const libc::c_char) -> T, + { + let c_str = self.to_c_str(); + f(c_str.as_ptr()) + } + + /// Unsafe variant of `with_c_str()` that doesn't check for nulls. + #[inline] + unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where + F: FnOnce(*const libc::c_char) -> T, + { + let c_str = self.to_c_str_unchecked(); + f(c_str.as_ptr()) + } +} + +impl ToCStr for str { + #[inline] + fn to_c_str(&self) -> CString { + self.as_bytes().to_c_str() + } + + #[inline] + unsafe fn to_c_str_unchecked(&self) -> CString { + self.as_bytes().to_c_str_unchecked() + } + + #[inline] + fn with_c_str<T, F>(&self, f: F) -> T where + F: FnOnce(*const libc::c_char) -> T, + { + self.as_bytes().with_c_str(f) + } + + #[inline] + unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where + F: FnOnce(*const libc::c_char) -> T, + { + self.as_bytes().with_c_str_unchecked(f) + } +} + +impl ToCStr for String { + #[inline] + fn to_c_str(&self) -> CString { + self.as_bytes().to_c_str() + } + + #[inline] + unsafe fn to_c_str_unchecked(&self) -> CString { + self.as_bytes().to_c_str_unchecked() + } + + #[inline] + fn with_c_str<T, F>(&self, f: F) -> T where + F: FnOnce(*const libc::c_char) -> T, + { + self.as_bytes().with_c_str(f) + } + + #[inline] + unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where + F: FnOnce(*const libc::c_char) -> T, + { + self.as_bytes().with_c_str_unchecked(f) + } +} + +// The length of the stack allocated buffer for `vec.with_c_str()` +const BUF_LEN: uint = 128; + +impl ToCStr for [u8] { + fn to_c_str(&self) -> CString { + let mut cs = unsafe { self.to_c_str_unchecked() }; + check_for_null(self, cs.as_mut_ptr()); + cs + } + + unsafe fn to_c_str_unchecked(&self) -> CString { + let self_len = self.len(); + let buf = libc::malloc(self_len as libc::size_t + 1) as *mut u8; + if buf.is_null() { ::alloc::oom() } + + ptr::copy_memory(buf, self.as_ptr(), self_len); + *buf.offset(self_len as int) = 0; + + CString::new(buf as *const libc::c_char, true) + } + + fn with_c_str<T, F>(&self, f: F) -> T where + F: FnOnce(*const libc::c_char) -> T, + { + unsafe { with_c_str(self, true, f) } + } + + unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where + F: FnOnce(*const libc::c_char) -> T, + { + with_c_str(self, false, f) + } +} + +impl<'a, Sized? T: ToCStr> ToCStr for &'a T { + #[inline] + fn to_c_str(&self) -> CString { + (**self).to_c_str() + } + + #[inline] + unsafe fn to_c_str_unchecked(&self) -> CString { + (**self).to_c_str_unchecked() + } + + #[inline] + fn with_c_str<T, F>(&self, f: F) -> T where + F: FnOnce(*const libc::c_char) -> T, + { + (**self).with_c_str(f) + } + + #[inline] + unsafe fn with_c_str_unchecked<T, F>(&self, f: F) -> T where + F: FnOnce(*const libc::c_char) -> T, + { + (**self).with_c_str_unchecked(f) + } +} + +// Unsafe function that handles possibly copying the &[u8] into a stack array. +unsafe fn with_c_str<T, F>(v: &[u8], checked: bool, f: F) -> T where + F: FnOnce(*const libc::c_char) -> T, +{ + let c_str = if v.len() < BUF_LEN { + let mut buf: [u8, .. BUF_LEN] = mem::uninitialized(); + slice::bytes::copy_memory(&mut buf, v); + buf[v.len()] = 0; + + let buf = buf.as_mut_ptr(); + if checked { + check_for_null(v, buf as *mut libc::c_char); + } + + return f(buf as *const libc::c_char) + } else if checked { + v.to_c_str() + } else { + v.to_c_str_unchecked() + }; + + f(c_str.as_ptr()) +} + +#[inline] +fn check_for_null(v: &[u8], buf: *mut libc::c_char) { + for i in range(0, v.len()) { + unsafe { + let p = buf.offset(i as int); + assert!(*p != 0); + } + } +} + +/// External iterator for a CString's bytes. +/// +/// Use with the `std::iter` module. +pub struct CChars<'a> { + ptr: *const libc::c_char, + marker: marker::ContravariantLifetime<'a>, +} + +impl<'a> Iterator<libc::c_char> for CChars<'a> { + fn next(&mut self) -> Option<libc::c_char> { + let ch = unsafe { *self.ptr }; + if ch == 0 { + None + } else { + self.ptr = unsafe { self.ptr.offset(1) }; + Some(ch) + } + } +} + +/// Parses a C "multistring", eg windows env values or +/// the req->ptr result in a uv_fs_readdir() call. +/// +/// Optionally, a `count` can be passed in, limiting the +/// parsing to only being done `count`-times. +/// +/// The specified closure is invoked with each string that +/// is found, and the number of strings found is returned. +pub unsafe fn from_c_multistring<F>(buf: *const libc::c_char, + count: Option<uint>, + mut f: F) + -> uint where + F: FnMut(&CString), +{ + + let mut curr_ptr: uint = buf as uint; + let mut ctr = 0; + let (limited_count, limit) = match count { + Some(limit) => (true, limit), + None => (false, 0) + }; + while ((limited_count && ctr < limit) || !limited_count) + && *(curr_ptr as *const libc::c_char) != 0 as libc::c_char { + let cstr = CString::new(curr_ptr as *const libc::c_char, false); + f(&cstr); + curr_ptr += cstr.len() + 1; + ctr += 1; + } + return ctr; +} + +#[cfg(test)] +mod tests { + use prelude::*; + use ptr; + use thread::Thread; + use libc; + + use super::*; + + #[test] + fn test_str_multistring_parsing() { + unsafe { + let input = b"zero\0one\0\0"; + let ptr = input.as_ptr(); + let expected = ["zero", "one"]; + let mut it = expected.iter(); + let result = from_c_multistring(ptr as *const libc::c_char, None, |c| { + let cbytes = c.as_bytes_no_nul(); + assert_eq!(cbytes, it.next().unwrap().as_bytes()); + }); + assert_eq!(result, 2); + assert!(it.next().is_none()); + } + } + + #[test] + fn test_str_to_c_str() { + let c_str = "".to_c_str(); + unsafe { + assert_eq!(*c_str.as_ptr().offset(0), 0); + } + + let c_str = "hello".to_c_str(); + let buf = c_str.as_ptr(); + unsafe { + assert_eq!(*buf.offset(0), 'h' as libc::c_char); + assert_eq!(*buf.offset(1), 'e' as libc::c_char); + assert_eq!(*buf.offset(2), 'l' as libc::c_char); + assert_eq!(*buf.offset(3), 'l' as libc::c_char); + assert_eq!(*buf.offset(4), 'o' as libc::c_char); + assert_eq!(*buf.offset(5), 0); + } + } + + #[test] + fn test_vec_to_c_str() { + let b: &[u8] = &[]; + let c_str = b.to_c_str(); + unsafe { + assert_eq!(*c_str.as_ptr().offset(0), 0); + } + + let c_str = b"hello".to_c_str(); + let buf = c_str.as_ptr(); + unsafe { + assert_eq!(*buf.offset(0), 'h' as libc::c_char); + assert_eq!(*buf.offset(1), 'e' as libc::c_char); + assert_eq!(*buf.offset(2), 'l' as libc::c_char); + assert_eq!(*buf.offset(3), 'l' as libc::c_char); + assert_eq!(*buf.offset(4), 'o' as libc::c_char); + assert_eq!(*buf.offset(5), 0); + } + + let c_str = b"foo\xFF".to_c_str(); + let buf = c_str.as_ptr(); + unsafe { + assert_eq!(*buf.offset(0), 'f' as libc::c_char); + assert_eq!(*buf.offset(1), 'o' as libc::c_char); + assert_eq!(*buf.offset(2), 'o' as libc::c_char); + assert_eq!(*buf.offset(3), 0xffu8 as libc::c_char); + assert_eq!(*buf.offset(4), 0); + } + } + + #[test] + fn test_unwrap() { + let c_str = "hello".to_c_str(); + unsafe { libc::free(c_str.unwrap() as *mut libc::c_void) } + } + + #[test] + fn test_as_ptr() { + let c_str = "hello".to_c_str(); + let len = unsafe { libc::strlen(c_str.as_ptr()) }; + assert_eq!(len, 5); + } + + #[test] + fn test_iterator() { + let c_str = "".to_c_str(); + let mut iter = c_str.iter(); + assert_eq!(iter.next(), None); + + let c_str = "hello".to_c_str(); + let mut iter = c_str.iter(); + assert_eq!(iter.next(), Some('h' as libc::c_char)); + assert_eq!(iter.next(), Some('e' as libc::c_char)); + assert_eq!(iter.next(), Some('l' as libc::c_char)); + assert_eq!(iter.next(), Some('l' as libc::c_char)); + assert_eq!(iter.next(), Some('o' as libc::c_char)); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_to_c_str_fail() { + assert!(Thread::spawn(move|| { "he\x00llo".to_c_str() }).join().is_err()); + } + + #[test] + fn test_to_c_str_unchecked() { + unsafe { + let c_string = "he\x00llo".to_c_str_unchecked(); + let buf = c_string.as_ptr(); + assert_eq!(*buf.offset(0), 'h' as libc::c_char); + assert_eq!(*buf.offset(1), 'e' as libc::c_char); + assert_eq!(*buf.offset(2), 0); + assert_eq!(*buf.offset(3), 'l' as libc::c_char); + assert_eq!(*buf.offset(4), 'l' as libc::c_char); + assert_eq!(*buf.offset(5), 'o' as libc::c_char); + assert_eq!(*buf.offset(6), 0); + } + } + + #[test] + fn test_as_bytes() { + let c_str = "hello".to_c_str(); + assert_eq!(c_str.as_bytes(), b"hello\0"); + let c_str = "".to_c_str(); + assert_eq!(c_str.as_bytes(), b"\0"); + let c_str = b"foo\xFF".to_c_str(); + assert_eq!(c_str.as_bytes(), b"foo\xFF\0"); + } + + #[test] + fn test_as_bytes_no_nul() { + let c_str = "hello".to_c_str(); + assert_eq!(c_str.as_bytes_no_nul(), b"hello"); + let c_str = "".to_c_str(); + let exp: &[u8] = &[]; + assert_eq!(c_str.as_bytes_no_nul(), exp); + let c_str = b"foo\xFF".to_c_str(); + assert_eq!(c_str.as_bytes_no_nul(), b"foo\xFF"); + } + + #[test] + fn test_as_str() { + let c_str = "hello".to_c_str(); + assert_eq!(c_str.as_str(), Some("hello")); + let c_str = "".to_c_str(); + assert_eq!(c_str.as_str(), Some("")); + let c_str = b"foo\xFF".to_c_str(); + assert_eq!(c_str.as_str(), None); + } + + #[test] + #[should_fail] + fn test_new_fail() { + let _c_str = unsafe { CString::new(ptr::null(), false) }; + } + + #[test] + fn test_clone() { + let a = "hello".to_c_str(); + let b = a.clone(); + assert!(a == b); + } + + #[test] + fn test_clone_noleak() { + fn foo<F>(f: F) where F: FnOnce(&CString) { + let s = "test".to_string(); + let c = s.to_c_str(); + // give the closure a non-owned CString + let mut c_ = unsafe { CString::new(c.as_ptr(), false) }; + f(&c_); + // muck with the buffer for later printing + unsafe { *c_.as_mut_ptr() = 'X' as libc::c_char } + } + + let mut c_: Option<CString> = None; + foo(|c| { + c_ = Some(c.clone()); + c.clone(); + // force a copy, reading the memory + c.as_bytes().to_vec(); + }); + let c_ = c_.unwrap(); + // force a copy, reading the memory + c_.as_bytes().to_vec(); + } +} + +#[cfg(test)] +mod bench { + extern crate test; + + use self::test::Bencher; + use libc; + use prelude::*; + + #[inline] + fn check(s: &str, c_str: *const libc::c_char) { + let s_buf = s.as_ptr(); + for i in range(0, s.len()) { + unsafe { + assert_eq!( + *s_buf.offset(i as int) as libc::c_char, + *c_str.offset(i as int)); + } + } + } + + static S_SHORT: &'static str = "Mary"; + static S_MEDIUM: &'static str = "Mary had a little lamb"; + static S_LONG: &'static str = "\ + Mary had a little lamb, Little lamb + Mary had a little lamb, Little lamb + Mary had a little lamb, Little lamb + Mary had a little lamb, Little lamb + Mary had a little lamb, Little lamb + Mary had a little lamb, Little lamb"; + + fn bench_to_string(b: &mut Bencher, s: &str) { + b.iter(|| { + let c_str = s.to_c_str(); + check(s, c_str.as_ptr()); + }) + } + + #[bench] + fn bench_to_c_str_short(b: &mut Bencher) { + bench_to_string(b, S_SHORT) + } + + #[bench] + fn bench_to_c_str_medium(b: &mut Bencher) { + bench_to_string(b, S_MEDIUM) + } + + #[bench] + fn bench_to_c_str_long(b: &mut Bencher) { + bench_to_string(b, S_LONG) + } + + fn bench_to_c_str_unchecked(b: &mut Bencher, s: &str) { + b.iter(|| { + let c_str = unsafe { s.to_c_str_unchecked() }; + check(s, c_str.as_ptr()) + }) + } + + #[bench] + fn bench_to_c_str_unchecked_short(b: &mut Bencher) { + bench_to_c_str_unchecked(b, S_SHORT) + } + + #[bench] + fn bench_to_c_str_unchecked_medium(b: &mut Bencher) { + bench_to_c_str_unchecked(b, S_MEDIUM) + } + + #[bench] + fn bench_to_c_str_unchecked_long(b: &mut Bencher) { + bench_to_c_str_unchecked(b, S_LONG) + } + + fn bench_with_c_str(b: &mut Bencher, s: &str) { + b.iter(|| { + s.with_c_str(|c_str_buf| check(s, c_str_buf)) + }) + } + + #[bench] + fn bench_with_c_str_short(b: &mut Bencher) { + bench_with_c_str(b, S_SHORT) + } + + #[bench] + fn bench_with_c_str_medium(b: &mut Bencher) { + bench_with_c_str(b, S_MEDIUM) + } + + #[bench] + fn bench_with_c_str_long(b: &mut Bencher) { + bench_with_c_str(b, S_LONG) + } + + fn bench_with_c_str_unchecked(b: &mut Bencher, s: &str) { + b.iter(|| { + unsafe { + s.with_c_str_unchecked(|c_str_buf| check(s, c_str_buf)) + } + }) + } + + #[bench] + fn bench_with_c_str_unchecked_short(b: &mut Bencher) { + bench_with_c_str_unchecked(b, S_SHORT) + } + + #[bench] + fn bench_with_c_str_unchecked_medium(b: &mut Bencher) { + bench_with_c_str_unchecked(b, S_MEDIUM) + } + + #[bench] + fn bench_with_c_str_unchecked_long(b: &mut Bencher) { + bench_with_c_str_unchecked(b, S_LONG) + } +} diff --git a/src/libstd/c_vec.rs b/src/libstd/c_vec.rs index 1267d7411cc..44e7291150e 100644 --- a/src/libstd/c_vec.rs +++ b/src/libstd/c_vec.rs @@ -37,18 +37,20 @@ use kinds::Send; use mem; -use ops::Drop; -use option::{Option, Some, None}; +use ops::{Drop, FnOnce}; +use option::Option; +use option::Option::{Some, None}; use ptr::RawPtr; use ptr; use raw; use slice::AsSlice; +use thunk::{Thunk}; /// The type representing a foreign chunk of memory pub struct CVec<T> { base: *mut T, len: uint, - dtor: Option<proc():Send>, + dtor: Option<Thunk>, } #[unsafe_destructor] @@ -56,7 +58,7 @@ impl<T> Drop for CVec<T> { fn drop(&mut self) { match self.dtor.take() { None => (), - Some(f) => f() + Some(f) => f.invoke(()) } } } @@ -89,15 +91,20 @@ impl<T> CVec<T> { /// /// * base - A foreign pointer to a buffer /// * len - The number of elements in the buffer - /// * dtor - A proc to run when the value is destructed, useful + /// * dtor - A fn to run when the value is destructed, useful /// for freeing the buffer, etc. - pub unsafe fn new_with_dtor(base: *mut T, len: uint, - dtor: proc():Send) -> CVec<T> { + pub unsafe fn new_with_dtor<F>(base: *mut T, + len: uint, + dtor: F) + -> CVec<T> + where F : FnOnce(), F : Send + { assert!(base != ptr::null_mut()); + let dtor: Thunk = Thunk::new(dtor); CVec { base: base, len: len, - dtor: Some(dtor), + dtor: Some(dtor) } } @@ -138,11 +145,15 @@ impl<T> CVec<T> { /// Note that if you want to access the underlying pointer without /// cancelling the destructor, you can simply call `transmute` on the return /// value of `get(0)`. - pub unsafe fn unwrap(mut self) -> *mut T { + pub unsafe fn into_inner(mut self) -> *mut T { self.dtor = None; self.base } + /// Deprecated, use into_inner() instead + #[deprecated = "renamed to into_inner()"] + pub unsafe fn unwrap(self) -> *mut T { self.into_inner() } + /// Returns the number of items in this vector. pub fn len(&self) -> uint { self.len } @@ -172,8 +183,9 @@ mod tests { let mem = libc::malloc(n as libc::size_t); if mem.is_null() { ::alloc::oom() } - CVec::new_with_dtor(mem as *mut u8, n, - proc() { libc::free(mem as *mut libc::c_void); }) + CVec::new_with_dtor(mem as *mut u8, + n, + move|| { libc::free(mem as *mut libc::c_void); }) } } @@ -213,8 +225,9 @@ mod tests { #[test] fn test_unwrap() { unsafe { - let cv = CVec::new_with_dtor(1 as *mut int, 0, - proc() { panic!("Don't run this destructor!") }); + let cv = CVec::new_with_dtor(1 as *mut int, + 0, + move|:| panic!("Don't run this destructor!")); let p = cv.unwrap(); assert_eq!(p, 1 as *mut int); } diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 69375e8d4f8..d749cd77cef 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -10,7 +10,7 @@ // // ignore-lexer-test FIXME #15883 -pub use self::Entry::*; +use self::Entry::*; use self::SearchResult::*; use self::VacantEntryState::*; @@ -20,66 +20,78 @@ use cmp::{max, Eq, Equiv, PartialEq}; use default::Default; use fmt::{mod, Show}; use hash::{Hash, Hasher, RandomSipHasher}; -use iter::{mod, Iterator, FromIterator, Extend}; +use iter::{mod, Iterator, IteratorExt, FromIterator, Extend, Map}; use kinds::Sized; use mem::{mod, replace}; -use num::UnsignedInt; -use ops::{Deref, Index, IndexMut}; -use option::{Some, None, Option}; -use result::{Result, Ok, Err}; +use num::{Int, UnsignedInt}; +use ops::{Deref, FnMut, Index, IndexMut}; +use option::Option; +use option::Option::{Some, None}; +use result::Result; +use result::Result::{Ok, Err}; -use super::table; use super::table::{ + mod, Bucket, - Empty, EmptyBucket, - Full, FullBucket, FullBucketImm, FullBucketMut, RawTable, SafeHash }; - -// FIXME(conventions): update capacity management to match other collections (no auto-shrink) +use super::table::BucketState::{ + Empty, + Full, +}; const INITIAL_LOG2_CAP: uint = 5; pub const INITIAL_CAPACITY: uint = 1 << INITIAL_LOG2_CAP; // 2^5 /// The default behavior of HashMap implements a load factor of 90.9%. -/// This behavior is characterized by the following conditions: +/// This behavior is characterized by the following condition: /// -/// - if size > 0.909 * capacity: grow -/// - if size < 0.25 * capacity: shrink (if this won't bring capacity lower -/// than the minimum) +/// - if size > 0.909 * capacity: grow the map #[deriving(Clone)] -struct DefaultResizePolicy { - /// Doubled minimal capacity. The capacity must never drop below - /// the minimum capacity. (The check happens before the capacity - /// is potentially halved.) - minimum_capacity2: uint -} +struct DefaultResizePolicy; impl DefaultResizePolicy { - fn new(new_capacity: uint) -> DefaultResizePolicy { - DefaultResizePolicy { - minimum_capacity2: new_capacity << 1 - } + fn new() -> DefaultResizePolicy { + DefaultResizePolicy } #[inline] - fn capacity_range(&self, new_size: uint) -> (uint, uint) { - // Here, we are rephrasing the logic by specifying the ranges: + fn min_capacity(&self, usable_size: uint) -> uint { + // Here, we are rephrasing the logic by specifying the lower limit + // on capacity: // - // - if `size * 1.1 < cap < size * 4`: don't resize - // - if `cap < minimum_capacity * 2`: don't shrink - // - otherwise, resize accordingly - ((new_size * 11) / 10, max(new_size << 2, self.minimum_capacity2)) + // - if `cap < size * 1.1`: grow the map + usable_size * 11 / 10 } + /// An inverse of `min_capacity`, approximately. #[inline] - fn reserve(&mut self, new_capacity: uint) { - self.minimum_capacity2 = new_capacity << 1; + fn usable_capacity(&self, cap: uint) -> uint { + // As the number of entries approaches usable capacity, + // min_capacity(size) must be smaller than the internal capacity, + // so that the map is not resized: + // `min_capacity(usable_capacity(x)) <= x`. + // The lef-hand side can only be smaller due to flooring by integer + // division. + // + // This doesn't have to be checked for overflow since allocation size + // in bytes will overflow earlier than multiplication by 10. + cap * 10 / 11 + } +} + +#[test] +fn test_resize_policy() { + use prelude::*; + let rp = DefaultResizePolicy; + for n in range(0u, 1000) { + assert!(rp.min_capacity(rp.usable_capacity(n)) <= n); + assert!(rp.usable_capacity(rp.min_capacity(n)) <= n); } } @@ -282,15 +294,17 @@ pub struct HashMap<K, V, H = RandomSipHasher> { table: RawTable<K, V>, - // We keep this at the end since it might as well have tail padding. resize_policy: DefaultResizePolicy, } /// Search for a pre-hashed key. -fn search_hashed<K, V, M: Deref<RawTable<K, V>>>(table: M, - hash: &SafeHash, - is_match: |&K| -> bool) - -> SearchResult<K, V, M> { +fn search_hashed<K, V, M, F>(table: M, + hash: SafeHash, + mut is_match: F) + -> SearchResult<K, V, M> where + M: Deref<RawTable<K, V>>, + F: FnMut(&K) -> bool, +{ let size = table.size(); let mut probe = Bucket::new(table, hash); let ib = probe.index(); @@ -308,14 +322,9 @@ fn search_hashed<K, V, M: Deref<RawTable<K, V>>>(table: M, } // If the hash doesn't match, it can't be this one.. - if *hash == full.hash() { - let matched = { - let (k, _) = full.read(); - is_match(k) - }; - + if hash == full.hash() { // If the key doesn't match, it can't be this one.. - if matched { + if is_match(full.read().0) { return FoundExisting(full); } } @@ -341,7 +350,7 @@ fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>) -> (K, V) { } // Now we've done all our shifting. Return the value we grabbed earlier. - return (retkey, retval); + (retkey, retval) } /// Perform robin hood bucket stealing at the given `bucket`. You must @@ -372,17 +381,18 @@ fn robin_hood<'a, K: 'a, V: 'a>(mut bucket: FullBucketMut<'a, K, V>, assert!(probe.index() != idx_end); let full_bucket = match probe.peek() { - table::Empty(bucket) => { + Empty(bucket) => { // Found a hole! let b = bucket.put(old_hash, old_key, old_val); // Now that it's stolen, just read the value's pointer // right out of the table! - let (_, v) = Bucket::at_index(b.into_table(), starting_index).peek() - .expect_full() - .into_mut_refs(); - return v; + return Bucket::at_index(b.into_table(), starting_index) + .peek() + .expect_full() + .into_mut_refs() + .1; }, - table::Full(bucket) => bucket + Full(bucket) => bucket }; let probe_ib = full_bucket.index() - full_bucket.distance(); @@ -425,16 +435,18 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { table::make_hash(&self.hasher, x) } + #[allow(deprecated)] fn search_equiv<'a, Sized? Q: Hash<S> + Equiv<K>>(&'a self, q: &Q) -> Option<FullBucketImm<'a, K, V>> { let hash = self.make_hash(q); - search_hashed(&self.table, &hash, |k| q.equiv(k)).into_option() + search_hashed(&self.table, hash, |k| q.equiv(k)).into_option() } + #[allow(deprecated)] fn search_equiv_mut<'a, Sized? Q: Hash<S> + Equiv<K>>(&'a mut self, q: &Q) -> Option<FullBucketMut<'a, K, V>> { let hash = self.make_hash(q); - search_hashed(&mut self.table, &hash, |k| q.equiv(k)).into_option() + search_hashed(&mut self.table, hash, |k| q.equiv(k)).into_option() } /// Search for a key, yielding the index if it's found in the hashtable. @@ -444,7 +456,7 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { where Q: BorrowFrom<K> + Eq + Hash<S> { let hash = self.make_hash(q); - search_hashed(&self.table, &hash, |k| q.eq(BorrowFrom::borrow_from(k))) + search_hashed(&self.table, hash, |k| q.eq(BorrowFrom::borrow_from(k))) .into_option() } @@ -452,14 +464,14 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { where Q: BorrowFrom<K> + Eq + Hash<S> { let hash = self.make_hash(q); - search_hashed(&mut self.table, &hash, |k| q.eq(BorrowFrom::borrow_from(k))) + search_hashed(&mut self.table, hash, |k| q.eq(BorrowFrom::borrow_from(k))) .into_option() } // The caller should ensure that invariants by Robin Hood Hashing hold. fn insert_hashed_ordered(&mut self, hash: SafeHash, k: K, v: V) { let cap = self.table.capacity(); - let mut buckets = Bucket::new(&mut self.table, &hash); + let mut buckets = Bucket::new(&mut self.table, hash); let ib = buckets.index(); while buckets.index() != ib + cap { @@ -529,7 +541,7 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { pub fn with_hasher(hasher: H) -> HashMap<K, V, H> { HashMap { hasher: hasher, - resize_policy: DefaultResizePolicy::new(INITIAL_CAPACITY), + resize_policy: DefaultResizePolicy::new(), table: RawTable::new(0), } } @@ -554,20 +566,39 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { /// ``` #[inline] pub fn with_capacity_and_hasher(capacity: uint, hasher: H) -> HashMap<K, V, H> { - let cap = max(INITIAL_CAPACITY, capacity).next_power_of_two(); + let resize_policy = DefaultResizePolicy::new(); + let min_cap = max(INITIAL_CAPACITY, resize_policy.min_capacity(capacity)); + let internal_cap = min_cap.checked_next_power_of_two().expect("capacity overflow"); + assert!(internal_cap >= capacity, "capacity overflow"); HashMap { hasher: hasher, - resize_policy: DefaultResizePolicy::new(cap), - table: RawTable::new(cap), + resize_policy: resize_policy, + table: RawTable::new(internal_cap), } } - /// The hashtable will never try to shrink below this size. You can use - /// this function to reduce reallocations if your hashtable frequently - /// grows and shrinks by large amounts. + /// Returns the number of elements the map can hold without reallocating. + /// + /// # Example + /// + /// ``` + /// use std::collections::HashMap; + /// let map: HashMap<int, int> = HashMap::with_capacity(100); + /// assert!(map.capacity() >= 100); + /// ``` + #[inline] + #[unstable = "matches collection reform specification, waiting for dust to settle"] + pub fn capacity(&self) -> uint { + self.resize_policy.usable_capacity(self.table.capacity()) + } + + /// Reserves capacity for at least `additional` more elements to be inserted + /// in the `HashMap`. The collection may reserve more space to avoid + /// frequent reallocations. /// - /// This function has no effect on the operational semantics of the - /// hashtable, only on performance. + /// # Panics + /// + /// Panics if the new allocation size overflows `uint`. /// /// # Example /// @@ -576,23 +607,28 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { /// let mut map: HashMap<&str, int> = HashMap::new(); /// map.reserve(10); /// ``` - pub fn reserve(&mut self, new_minimum_capacity: uint) { - let cap = max(INITIAL_CAPACITY, new_minimum_capacity).next_power_of_two(); + #[unstable = "matches collection reform specification, waiting for dust to settle"] + pub fn reserve(&mut self, additional: uint) { + let new_size = self.len().checked_add(additional).expect("capacity overflow"); + let min_cap = self.resize_policy.min_capacity(new_size); - self.resize_policy.reserve(cap); + // An invalid value shouldn't make us run out of space. This includes + // an overflow check. + assert!(new_size <= min_cap); - if self.table.capacity() < cap { - self.resize(cap); + if self.table.capacity() < min_cap { + let new_capacity = max(min_cap.next_power_of_two(), INITIAL_CAPACITY); + self.resize(new_capacity); } } /// Resizes the internal vectors to a new capacity. It's your responsibility to: /// 1) Make sure the new capacity is enough for all the elements, accounting /// for the load factor. - /// 2) Ensure new_capacity is a power of two. + /// 2) Ensure new_capacity is a power of two or zero. fn resize(&mut self, new_capacity: uint) { assert!(self.table.size() <= new_capacity); - assert!(new_capacity.is_power_of_two()); + assert!(new_capacity.is_power_of_two() || new_capacity == 0); let mut old_table = replace(&mut self.table, RawTable::new(new_capacity)); let old_size = old_table.size(); @@ -601,94 +637,106 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { return; } - if new_capacity < old_table.capacity() { - // Shrink the table. Naive algorithm for resizing: - for (h, k, v) in old_table.into_iter() { - self.insert_hashed_nocheck(h, k, v); - } - } else { - // Grow the table. - // Specialization of the other branch. - let mut bucket = Bucket::first(&mut old_table); - - // "So a few of the first shall be last: for many be called, - // but few chosen." - // - // We'll most likely encounter a few buckets at the beginning that - // have their initial buckets near the end of the table. They were - // placed at the beginning as the probe wrapped around the table - // during insertion. We must skip forward to a bucket that won't - // get reinserted too early and won't unfairly steal others spot. - // This eliminates the need for robin hood. - loop { - bucket = match bucket.peek() { - Full(full) => { - if full.distance() == 0 { - // This bucket occupies its ideal spot. - // It indicates the start of another "cluster". - bucket = full.into_bucket(); - break; - } - // Leaving this bucket in the last cluster for later. - full.into_bucket() - } - Empty(b) => { - // Encountered a hole between clusters. - b.into_bucket() - } - }; - bucket.next(); - } + // Grow the table. + // Specialization of the other branch. + let mut bucket = Bucket::first(&mut old_table); - // This is how the buckets might be laid out in memory: - // ($ marks an initialized bucket) - // ________________ - // |$$$_$$$$$$_$$$$$| - // - // But we've skipped the entire initial cluster of buckets - // and will continue iteration in this order: - // ________________ - // |$$$$$$_$$$$$ - // ^ wrap around once end is reached - // ________________ - // $$$_____________| - // ^ exit once table.size == 0 - loop { - bucket = match bucket.peek() { - Full(bucket) => { - let h = bucket.hash(); - let (b, k, v) = bucket.take(); - self.insert_hashed_ordered(h, k, v); - { - let t = b.table(); // FIXME "lifetime too short". - if t.size() == 0 { break } - }; - b.into_bucket() + // "So a few of the first shall be last: for many be called, + // but few chosen." + // + // We'll most likely encounter a few buckets at the beginning that + // have their initial buckets near the end of the table. They were + // placed at the beginning as the probe wrapped around the table + // during insertion. We must skip forward to a bucket that won't + // get reinserted too early and won't unfairly steal others spot. + // This eliminates the need for robin hood. + loop { + bucket = match bucket.peek() { + Full(full) => { + if full.distance() == 0 { + // This bucket occupies its ideal spot. + // It indicates the start of another "cluster". + bucket = full.into_bucket(); + break; } - Empty(b) => b.into_bucket() - }; - bucket.next(); - } + // Leaving this bucket in the last cluster for later. + full.into_bucket() + } + Empty(b) => { + // Encountered a hole between clusters. + b.into_bucket() + } + }; + bucket.next(); + } + + // This is how the buckets might be laid out in memory: + // ($ marks an initialized bucket) + // ________________ + // |$$$_$$$$$$_$$$$$| + // + // But we've skipped the entire initial cluster of buckets + // and will continue iteration in this order: + // ________________ + // |$$$$$$_$$$$$ + // ^ wrap around once end is reached + // ________________ + // $$$_____________| + // ^ exit once table.size == 0 + loop { + bucket = match bucket.peek() { + Full(bucket) => { + let h = bucket.hash(); + let (b, k, v) = bucket.take(); + self.insert_hashed_ordered(h, k, v); + { + let t = b.table(); // FIXME "lifetime too short". + if t.size() == 0 { break } + }; + b.into_bucket() + } + Empty(b) => b.into_bucket() + }; + bucket.next(); } assert_eq!(self.table.size(), old_size); } - /// Performs any necessary resize operations, such that there's space for - /// new_size elements. - fn make_some_room(&mut self, new_size: uint) { - let (grow_at, shrink_at) = self.resize_policy.capacity_range(new_size); - let cap = self.table.capacity(); + /// Shrinks the capacity of the map as much as possible. It will drop + /// down as much as possible while maintaining the internal rules + /// and possibly leaving some space in accordance with the resize policy. + /// + /// # Example + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<int, int> = HashMap::with_capacity(100); + /// map.insert(1, 2); + /// map.insert(3, 4); + /// assert!(map.capacity() >= 100); + /// map.shrink_to_fit(); + /// assert!(map.capacity() >= 2); + /// ``` + #[unstable = "matches collection reform specification, waiting for dust to settle"] + pub fn shrink_to_fit(&mut self) { + let min_capacity = self.resize_policy.min_capacity(self.len()); + let min_capacity = max(min_capacity.next_power_of_two(), INITIAL_CAPACITY); // An invalid value shouldn't make us run out of space. - debug_assert!(grow_at >= new_size); + debug_assert!(self.len() <= min_capacity); - if cap <= grow_at { - let new_capacity = max(cap << 1, INITIAL_CAPACITY); - self.resize(new_capacity); - } else if shrink_at <= cap { - let new_capacity = cap >> 1; - self.resize(new_capacity); + if self.table.capacity() != min_capacity { + let old_table = replace(&mut self.table, RawTable::new(min_capacity)); + let old_size = old_table.size(); + + // Shrink the table. Naive algorithm for resizing: + for (h, k, v) in old_table.into_iter() { + self.insert_hashed_nocheck(h, k, v); + } + + debug_assert_eq!(self.table.size(), old_size); } } @@ -702,34 +750,32 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { self.insert_or_replace_with(hash, k, v, |_, _, _| ()) } - fn insert_or_replace_with<'a>(&'a mut self, - hash: SafeHash, - k: K, - v: V, - found_existing: |&mut K, &mut V, V|) - -> &'a mut V { + fn insert_or_replace_with<'a, F>(&'a mut self, + hash: SafeHash, + k: K, + v: V, + mut found_existing: F) + -> &'a mut V where + F: FnMut(&mut K, &mut V, V), + { // Worst case, we'll find one empty bucket among `size + 1` buckets. let size = self.table.size(); - let mut probe = Bucket::new(&mut self.table, &hash); + let mut probe = Bucket::new(&mut self.table, hash); let ib = probe.index(); loop { let mut bucket = match probe.peek() { Empty(bucket) => { // Found a hole! - let bucket = bucket.put(hash, k, v); - let (_, val) = bucket.into_mut_refs(); - return val; - }, + return bucket.put(hash, k, v).into_mut_refs().1; + } Full(bucket) => bucket }; + // hash matches? if bucket.hash() == hash { - let found_match = { - let (bucket_k, _) = bucket.read_mut(); - k == *bucket_k - }; - if found_match { + // key matches? + if k == *bucket.read_mut().0 { let (bucket_k, bucket_v) = bucket.into_mut_refs(); debug_assert!(k == *bucket_k); // Key already exists. Get its reference. @@ -759,13 +805,7 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { /// Deprecated: use `get` and `BorrowFrom` instead. #[deprecated = "use get and BorrowFrom instead"] pub fn find_equiv<'a, Sized? Q: Hash<S> + Equiv<K>>(&'a self, k: &Q) -> Option<&'a V> { - match self.search_equiv(k) { - None => None, - Some(bucket) => { - let (_, v_ref) = bucket.into_refs(); - Some(v_ref) - } - } + self.search_equiv(k).map(|bucket| bucket.into_refs().1) } /// Deprecated: use `remove` and `BorrowFrom` instead. @@ -775,16 +815,9 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { return None } - let potential_new_size = self.table.size() - 1; - self.make_some_room(potential_new_size); + self.reserve(1); - match self.search_equiv_mut(k) { - Some(bucket) => { - let (_k, val) = pop_internal(bucket); - Some(val) - } - _ => None - } + self.search_equiv_mut(k).map(|bucket| pop_internal(bucket).1) } /// An iterator visiting all keys in arbitrary order. @@ -805,8 +838,11 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { /// } /// ``` #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn keys(&self) -> Keys<K, V> { - self.iter().map(|(k, _v)| k) + pub fn keys<'a>(&'a self) -> Keys<'a, K, V> { + fn first<A, B>((a, _): (A, B)) -> A { a } + let first: fn((&'a K,&'a V)) -> &'a K = first; // coerce to fn ptr + + Keys { inner: self.iter().map(first) } } /// An iterator visiting all values in arbitrary order. @@ -827,8 +863,11 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { /// } /// ``` #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn values(&self) -> Values<K, V> { - self.iter().map(|(_k, v)| v) + pub fn values<'a>(&'a self) -> Values<'a, K, V> { + fn second<A, B>((_, b): (A, B)) -> B { b } + let second: fn((&'a K,&'a V)) -> &'a V = second; // coerce to fn ptr + + Values { inner: self.iter().map(second) } } /// An iterator visiting all key-value pairs in arbitrary order. @@ -877,8 +916,8 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { /// } /// ``` #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn iter_mut(&mut self) -> MutEntries<K, V> { - MutEntries { inner: self.table.iter_mut() } + pub fn iter_mut(&mut self) -> IterMut<K, V> { + IterMut { inner: self.table.iter_mut() } } /// Creates a consuming iterator, that is, one that moves each key-value @@ -899,20 +938,19 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { /// let vec: Vec<(&str, int)> = map.into_iter().collect(); /// ``` #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn into_iter(self) -> MoveEntries<K, V> { - MoveEntries { - inner: self.table.into_iter().map(|(_, k, v)| (k, v)) + pub fn into_iter(self) -> IntoIter<K, V> { + fn last_two<A, B, C>((_, b, c): (A, B, C)) -> (B, C) { (b, c) } + let last_two: fn((SafeHash, K, V)) -> (K, V) = last_two; + + IntoIter { + inner: self.table.into_iter().map(last_two) } } /// Gets the given key's corresponding entry in the map for in-place manipulation pub fn entry<'a>(&'a mut self, key: K) -> Entry<'a, K, V> { - // Gotta resize now, and we don't know which direction, so try both? - let size = self.table.size(); - self.make_some_room(size + 1); - if size > 0 { - self.make_some_room(size - 1); - } + // Gotta resize now. + self.reserve(1); let hash = self.make_hash(&key); search_entry_hashed(&mut self.table, hash, key) @@ -949,6 +987,36 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { #[unstable = "matches collection reform specification, waiting for dust to settle"] pub fn is_empty(&self) -> bool { self.len() == 0 } + /// Clears the map, returning all key-value pairs as an iterator. Keeps the + /// allocated memory for reuse. + /// + /// # Example + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut a = HashMap::new(); + /// a.insert(1u, "a"); + /// a.insert(2u, "b"); + /// + /// for (k, v) in a.drain().take(1) { + /// assert!(k == 1 || k == 2); + /// assert!(v == "a" || v == "b"); + /// } + /// + /// assert!(a.is_empty()); + /// ``` + #[inline] + #[unstable = "matches collection reform specification, waiting for dust to settle"] + pub fn drain(&mut self) -> Drain<K, V> { + fn last_two<A, B, C>((_, b, c): (A, B, C)) -> (B, C) { (b, c) } + let last_two: fn((SafeHash, K, V)) -> (K, V) = last_two; // coerce to fn pointer + + Drain { + inner: self.table.drain().map(last_two), + } + } + /// Clears the map, removing all key-value pairs. Keeps the allocated memory /// for reuse. /// @@ -963,23 +1031,9 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { /// assert!(a.is_empty()); /// ``` #[unstable = "matches collection reform specification, waiting for dust to settle"] + #[inline] pub fn clear(&mut self) { - // Prevent reallocations from happening from now on. Makes it possible - // for the map to be reused but has a downside: reserves permanently. - self.resize_policy.reserve(self.table.size()); - - let cap = self.table.capacity(); - let mut buckets = Bucket::first(&mut self.table); - - while buckets.index() != cap { - buckets = match buckets.peek() { - Empty(b) => b.next(), - Full(full) => { - let (b, _, _) = full.take(); - b.next() - } - }; - } + self.drain(); } /// Deprecated: Renamed to `get`. @@ -1008,10 +1062,7 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { pub fn get<Sized? Q>(&self, k: &Q) -> Option<&V> where Q: Hash<S> + Eq + BorrowFrom<K> { - self.search(k).map(|bucket| { - let (_, v) = bucket.into_refs(); - v - }) + self.search(k).map(|bucket| bucket.into_refs().1) } /// Returns true if the map contains a value for the specified key. @@ -1066,13 +1117,7 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { pub fn get_mut<Sized? Q>(&mut self, k: &Q) -> Option<&mut V> where Q: Hash<S> + Eq + BorrowFrom<K> { - match self.search_mut(k) { - Some(bucket) => { - let (_, v) = bucket.into_mut_refs(); - Some(v) - } - _ => None - } + self.search_mut(k).map(|bucket| bucket.into_mut_refs().1) } /// Deprecated: Renamed to `insert`. @@ -1100,8 +1145,7 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { #[unstable = "matches collection reform specification, waiting for dust to settle"] pub fn insert(&mut self, k: K, v: V) -> Option<V> { let hash = self.make_hash(&k); - let potential_new_size = self.table.size() + 1; - self.make_some_room(potential_new_size); + self.reserve(1); let mut retval = None; self.insert_or_replace_with(hash, k, v, |_, val_ref, val| { @@ -1141,13 +1185,7 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> { return None } - let potential_new_size = self.table.size() - 1; - self.make_some_room(potential_new_size); - - self.search_mut(k).map(|bucket| { - let (_k, val) = pop_internal(bucket); - val - }) + self.search_mut(k).map(|bucket| pop_internal(bucket).1) } } @@ -1155,7 +1193,7 @@ fn search_entry_hashed<'a, K: Eq, V>(table: &'a mut RawTable<K,V>, hash: SafeHas -> Entry<'a, K, V> { // Worst case, we'll find one empty bucket among `size + 1` buckets. let size = table.size(); - let mut probe = Bucket::new(table, &hash); + let mut probe = Bucket::new(table, hash); let ib = probe.index(); loop { @@ -1171,13 +1209,10 @@ fn search_entry_hashed<'a, K: Eq, V>(table: &'a mut RawTable<K,V>, hash: SafeHas Full(bucket) => bucket }; + // hash matches? if bucket.hash() == hash { - let is_eq = { - let (bucket_k, _) = bucket.read(); - k == *bucket_k - }; - - if is_eq { + // key matches? + if k == *bucket.read().0 { return Occupied(OccupiedEntry{ elem: bucket, }); @@ -1243,7 +1278,9 @@ impl<K: Eq + Hash<S> + Show, V: Show, S, H: Hasher<S>> Show for HashMap<K, V, H> } } +#[stable] impl<K: Eq + Hash<S>, V, S, H: Hasher<S> + Default> Default for HashMap<K, V, H> { + #[stable] fn default() -> HashMap<K, V, H> { HashMap::with_hasher(Default::default()) } @@ -1263,10 +1300,7 @@ impl<K: Hash<S> + Eq, Sized? Q, V, S, H: Hasher<S>> IndexMut<Q, V> for HashMap<K { #[inline] fn index_mut<'a>(&'a mut self, index: &Q) -> &'a mut V { - match self.get_mut(index) { - Some(v) => v, - None => panic!("no entry found for key") - } + self.get_mut(index).expect("no entry found for key") } } @@ -1276,13 +1310,38 @@ pub struct Entries<'a, K: 'a, V: 'a> { } /// HashMap mutable values iterator -pub struct MutEntries<'a, K: 'a, V: 'a> { - inner: table::MutEntries<'a, K, V> +pub struct IterMut<'a, K: 'a, V: 'a> { + inner: table::IterMut<'a, K, V> } /// HashMap move iterator -pub struct MoveEntries<K, V> { - inner: iter::Map<'static, (SafeHash, K, V), (K, V), table::MoveEntries<K, V>> +pub struct IntoIter<K, V> { + inner: iter::Map< + (SafeHash, K, V), + (K, V), + table::IntoIter<K, V>, + fn((SafeHash, K, V)) -> (K, V), + > +} + +/// HashMap keys iterator +pub struct Keys<'a, K: 'a, V: 'a> { + inner: Map<(&'a K, &'a V), &'a K, Entries<'a, K, V>, fn((&'a K, &'a V)) -> &'a K> +} + +/// HashMap values iterator +pub struct Values<'a, K: 'a, V: 'a> { + inner: Map<(&'a K, &'a V), &'a V, Entries<'a, K, V>, fn((&'a K, &'a V)) -> &'a V> +} + +/// HashMap drain iterator +pub struct Drain<'a, K: 'a, V: 'a> { + inner: iter::Map< + (SafeHash, K, V), + (K, V), + table::Drain<'a, K, V>, + fn((SafeHash, K, V)) -> (K, V), + > } /// A view into a single occupied location in a HashMap @@ -1315,28 +1374,31 @@ enum VacantEntryState<K, V, M> { } impl<'a, K, V> Iterator<(&'a K, &'a V)> for Entries<'a, K, V> { - #[inline] - fn next(&mut self) -> Option<(&'a K, &'a V)> { - self.inner.next() - } - #[inline] - fn size_hint(&self) -> (uint, Option<uint>) { - self.inner.size_hint() - } + #[inline] fn next(&mut self) -> Option<(&'a K, &'a V)> { self.inner.next() } + #[inline] fn size_hint(&self) -> (uint, Option<uint>) { self.inner.size_hint() } } -impl<'a, K, V> Iterator<(&'a K, &'a mut V)> for MutEntries<'a, K, V> { - #[inline] - fn next(&mut self) -> Option<(&'a K, &'a mut V)> { - self.inner.next() - } - #[inline] - fn size_hint(&self) -> (uint, Option<uint>) { - self.inner.size_hint() - } +impl<'a, K, V> Iterator<(&'a K, &'a mut V)> for IterMut<'a, K, V> { + #[inline] fn next(&mut self) -> Option<(&'a K, &'a mut V)> { self.inner.next() } + #[inline] fn size_hint(&self) -> (uint, Option<uint>) { self.inner.size_hint() } +} + +impl<K, V> Iterator<(K, V)> for IntoIter<K, V> { + #[inline] fn next(&mut self) -> Option<(K, V)> { self.inner.next() } + #[inline] fn size_hint(&self) -> (uint, Option<uint>) { self.inner.size_hint() } +} + +impl<'a, K, V> Iterator<&'a K> for Keys<'a, K, V> { + #[inline] fn next(&mut self) -> Option<(&'a K)> { self.inner.next() } + #[inline] fn size_hint(&self) -> (uint, Option<uint>) { self.inner.size_hint() } } -impl<K, V> Iterator<(K, V)> for MoveEntries<K, V> { +impl<'a, K, V> Iterator<&'a V> for Values<'a, K, V> { + #[inline] fn next(&mut self) -> Option<(&'a V)> { self.inner.next() } + #[inline] fn size_hint(&self) -> (uint, Option<uint>) { self.inner.size_hint() } +} + +impl<'a, K: 'a, V: 'a> Iterator<(K, V)> for Drain<'a, K, V> { #[inline] fn next(&mut self) -> Option<(K, V)> { self.inner.next() @@ -1350,21 +1412,18 @@ impl<K, V> Iterator<(K, V)> for MoveEntries<K, V> { impl<'a, K, V> OccupiedEntry<'a, K, V> { /// Gets a reference to the value in the entry pub fn get(&self) -> &V { - let (_, v) = self.elem.read(); - v + self.elem.read().1 } /// Gets a mutable reference to the value in the entry pub fn get_mut(&mut self) -> &mut V { - let (_, v) = self.elem.read_mut(); - v + self.elem.read_mut().1 } /// Converts the OccupiedEntry into a mutable reference to the value in the entry /// with a lifetime bound to the map itself pub fn into_mut(self) -> &'a mut V { - let (_, v) = self.elem.into_mut_refs(); - v + self.elem.into_mut_refs().1 } /// Sets the value of the entry, and returns the entry's old value @@ -1376,8 +1435,7 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { /// Takes the value out of the entry, and returns it pub fn take(self) -> V { - let (_, _, v) = self.elem.take(); - v + pop_internal(self.elem).1 } } @@ -1390,25 +1448,15 @@ impl<'a, K, V> VacantEntry<'a, K, V> { robin_hood(bucket, ib, self.hash, self.key, value) } NoElem(bucket) => { - let full = bucket.put(self.hash, self.key, value); - let (_, v) = full.into_mut_refs(); - v + bucket.put(self.hash, self.key, value).into_mut_refs().1 } } } } -/// HashMap keys iterator -pub type Keys<'a, K, V> = - iter::Map<'static, (&'a K, &'a V), &'a K, Entries<'a, K, V>>; - -/// HashMap values iterator -pub type Values<'a, K, V> = - iter::Map<'static, (&'a K, &'a V), &'a V, Entries<'a, K, V>>; - impl<K: Eq + Hash<S>, V, S, H: Hasher<S> + Default> FromIterator<(K, V)> for HashMap<K, V, H> { fn from_iter<T: Iterator<(K, V)>>(iter: T) -> HashMap<K, V, H> { - let (lower, _) = iter.size_hint(); + let lower = iter.size_hint().0; let mut map = HashMap::with_capacity_and_hasher(lower, Default::default()); map.extend(iter); map @@ -1428,11 +1476,11 @@ mod test_map { use prelude::*; use super::HashMap; - use super::{Occupied, Vacant}; - use cmp::Equiv; + use super::Entry::{Occupied, Vacant}; use hash; - use iter::{Iterator,range_inclusive,range_step_inclusive}; + use iter::{range_inclusive, range_step_inclusive}; use cell::RefCell; + use rand::{weak_rng, Rng}; struct KindaIntLike(int); @@ -1471,7 +1519,7 @@ mod test_map { assert_eq!(*m.get(&2).unwrap(), 4); } - local_data_key!(drop_vector: RefCell<Vec<int>>) + thread_local! { static DROP_VECTOR: RefCell<Vec<int>> = RefCell::new(Vec::new()) } #[deriving(Hash, PartialEq, Eq)] struct Dropable { @@ -1480,8 +1528,9 @@ mod test_map { impl Dropable { fn new(k: uint) -> Dropable { - let v = drop_vector.get().unwrap(); - v.borrow_mut().as_mut_slice()[k] += 1; + DROP_VECTOR.with(|slot| { + slot.borrow_mut()[k] += 1; + }); Dropable { k: k } } @@ -1489,8 +1538,9 @@ mod test_map { impl Drop for Dropable { fn drop(&mut self) { - let v = drop_vector.get().unwrap(); - v.borrow_mut().as_mut_slice()[self.k] -= 1; + DROP_VECTOR.with(|slot| { + slot.borrow_mut()[self.k] -= 1; + }); } } @@ -1502,16 +1552,18 @@ mod test_map { #[test] fn test_drops() { - drop_vector.replace(Some(RefCell::new(Vec::from_elem(200, 0i)))); + DROP_VECTOR.with(|slot| { + *slot.borrow_mut() = Vec::from_elem(200, 0i); + }); { let mut m = HashMap::new(); - let v = drop_vector.get().unwrap(); - for i in range(0u, 200) { - assert_eq!(v.borrow().as_slice()[i], 0); - } - drop(v); + DROP_VECTOR.with(|v| { + for i in range(0u, 200) { + assert_eq!(v.borrow()[i], 0); + } + }); for i in range(0u, 100) { let d1 = Dropable::new(i); @@ -1519,11 +1571,11 @@ mod test_map { m.insert(d1, d2); } - let v = drop_vector.get().unwrap(); - for i in range(0u, 200) { - assert_eq!(v.borrow().as_slice()[i], 1); - } - drop(v); + DROP_VECTOR.with(|v| { + for i in range(0u, 200) { + assert_eq!(v.borrow()[i], 1); + } + }); for i in range(0u, 50) { let k = Dropable::new(i); @@ -1531,41 +1583,46 @@ mod test_map { assert!(v.is_some()); - let v = drop_vector.get().unwrap(); - assert_eq!(v.borrow().as_slice()[i], 1); - assert_eq!(v.borrow().as_slice()[i+100], 1); + DROP_VECTOR.with(|v| { + assert_eq!(v.borrow()[i], 1); + assert_eq!(v.borrow()[i+100], 1); + }); } - let v = drop_vector.get().unwrap(); - for i in range(0u, 50) { - assert_eq!(v.borrow().as_slice()[i], 0); - assert_eq!(v.borrow().as_slice()[i+100], 0); - } + DROP_VECTOR.with(|v| { + for i in range(0u, 50) { + assert_eq!(v.borrow()[i], 0); + assert_eq!(v.borrow()[i+100], 0); + } - for i in range(50u, 100) { - assert_eq!(v.borrow().as_slice()[i], 1); - assert_eq!(v.borrow().as_slice()[i+100], 1); - } + for i in range(50u, 100) { + assert_eq!(v.borrow()[i], 1); + assert_eq!(v.borrow()[i+100], 1); + } + }); } - let v = drop_vector.get().unwrap(); - for i in range(0u, 200) { - assert_eq!(v.borrow().as_slice()[i], 0); - } + DROP_VECTOR.with(|v| { + for i in range(0u, 200) { + assert_eq!(v.borrow()[i], 0); + } + }); } #[test] fn test_move_iter_drops() { - drop_vector.replace(Some(RefCell::new(Vec::from_elem(200, 0i)))); + DROP_VECTOR.with(|v| { + *v.borrow_mut() = Vec::from_elem(200, 0i); + }); let hm = { let mut hm = HashMap::new(); - let v = drop_vector.get().unwrap(); - for i in range(0u, 200) { - assert_eq!(v.borrow().as_slice()[i], 0); - } - drop(v); + DROP_VECTOR.with(|v| { + for i in range(0u, 200) { + assert_eq!(v.borrow()[i], 0); + } + }); for i in range(0u, 100) { let d1 = Dropable::new(i); @@ -1573,11 +1630,11 @@ mod test_map { hm.insert(d1, d2); } - let v = drop_vector.get().unwrap(); - for i in range(0u, 200) { - assert_eq!(v.borrow().as_slice()[i], 1); - } - drop(v); + DROP_VECTOR.with(|v| { + for i in range(0u, 200) { + assert_eq!(v.borrow()[i], 1); + } + }); hm }; @@ -1588,31 +1645,33 @@ mod test_map { { let mut half = hm.into_iter().take(50); - let v = drop_vector.get().unwrap(); - for i in range(0u, 200) { - assert_eq!(v.borrow().as_slice()[i], 1); - } - drop(v); + DROP_VECTOR.with(|v| { + for i in range(0u, 200) { + assert_eq!(v.borrow()[i], 1); + } + }); for _ in half {} - let v = drop_vector.get().unwrap(); - let nk = range(0u, 100).filter(|&i| { - v.borrow().as_slice()[i] == 1 - }).count(); + DROP_VECTOR.with(|v| { + let nk = range(0u, 100).filter(|&i| { + v.borrow()[i] == 1 + }).count(); - let nv = range(0u, 100).filter(|&i| { - v.borrow().as_slice()[i+100] == 1 - }).count(); + let nv = range(0u, 100).filter(|&i| { + v.borrow()[i+100] == 1 + }).count(); - assert_eq!(nk, 50); - assert_eq!(nv, 50); + assert_eq!(nk, 50); + assert_eq!(nv, 50); + }); }; - let v = drop_vector.get().unwrap(); - for i in range(0u, 200) { - assert_eq!(v.borrow().as_slice()[i], 0); - } + DROP_VECTOR.with(|v| { + for i in range(0u, 200) { + assert_eq!(v.borrow()[i], 0); + } + }); } #[test] @@ -1859,8 +1918,8 @@ mod test_map { let map_str = format!("{}", map); - assert!(map_str == "{1: 2, 3: 4}".to_string() || map_str == "{3: 4, 1: 2}".to_string()); - assert_eq!(format!("{}", empty), "{}".to_string()); + assert!(map_str == "{1: 2, 3: 4}" || map_str == "{3: 4, 1: 2}"); + assert_eq!(format!("{}", empty), "{}"); } #[test] @@ -1882,7 +1941,7 @@ mod test_map { } #[test] - fn test_resize_policy() { + fn test_behavior_resize_policy() { let mut m = HashMap::new(); assert_eq!(m.len(), 0); @@ -1893,7 +1952,7 @@ mod test_map { m.remove(&0); assert!(m.is_empty()); let initial_cap = m.table.capacity(); - m.reserve(initial_cap * 2); + m.reserve(initial_cap); let cap = m.table.capacity(); assert_eq!(cap, initial_cap * 2); @@ -1923,15 +1982,55 @@ mod test_map { assert_eq!(m.table.capacity(), new_cap); } // A little more than one quarter full. - // Shrinking starts as we remove more elements: + m.shrink_to_fit(); + assert_eq!(m.table.capacity(), cap); + // again, a little more than half full for _ in range(0, cap / 2 - 1) { i -= 1; m.remove(&i); } + m.shrink_to_fit(); assert_eq!(m.len(), i); assert!(!m.is_empty()); - assert_eq!(m.table.capacity(), cap); + assert_eq!(m.table.capacity(), initial_cap); + } + + #[test] + fn test_reserve_shrink_to_fit() { + let mut m = HashMap::new(); + m.insert(0u, 0u); + m.remove(&0); + assert!(m.capacity() >= m.len()); + for i in range(0, 128) { + m.insert(i, i); + } + m.reserve(256); + + let usable_cap = m.capacity(); + for i in range(128, 128+256) { + m.insert(i, i); + assert_eq!(m.capacity(), usable_cap); + } + + for i in range(100, 128+256) { + assert_eq!(m.remove(&i), Some(i)); + } + m.shrink_to_fit(); + + assert_eq!(m.len(), 100); + assert!(!m.is_empty()); + assert!(m.capacity() >= m.len()); + + for i in range(0, 100) { + assert_eq!(m.remove(&i), Some(i)); + } + m.shrink_to_fit(); + m.insert(0, 0); + + assert_eq!(m.len(), 1); + assert!(m.capacity() >= m.len()); + assert_eq!(m.remove(&0), Some(0)); } #[test] @@ -2062,4 +2161,37 @@ mod test_map { assert_eq!(map.get(&10).unwrap(), &1000); assert_eq!(map.len(), 6); } + + #[test] + fn test_entry_take_doesnt_corrupt() { + // Test for #19292 + fn check(m: &HashMap<int, ()>) { + for k in m.keys() { + assert!(m.contains_key(k), + "{} is in keys() but not in the map?", k); + } + } + + let mut m = HashMap::new(); + let mut rng = weak_rng(); + + // Populate the map with some items. + for _ in range(0u, 50) { + let x = rng.gen_range(-10, 10); + m.insert(x, ()); + } + + for i in range(0u, 1000) { + let x = rng.gen_range(-10, 10); + match m.entry(x) { + Vacant(_) => {}, + Occupied(e) => { + println!("{}: remove {}", i, x); + e.take(); + }, + } + + check(&m); + } + } } diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs index 2fbcb464358..6d83d5510b3 100644 --- a/src/libstd/collections/hash/set.rs +++ b/src/libstd/collections/hash/set.rs @@ -11,22 +11,19 @@ // ignore-lexer-test FIXME #15883 use borrow::BorrowFrom; +use clone::Clone; use cmp::{Eq, Equiv, PartialEq}; use core::kinds::Sized; use default::Default; use fmt::Show; use fmt; use hash::{Hash, Hasher, RandomSipHasher}; -use iter::{Iterator, FromIterator, FilterMap, Chain, Repeat, Zip, Extend}; -use iter; -use option::{Some, None}; -use result::{Ok, Err}; - -use super::map::{HashMap, Entries, MoveEntries, INITIAL_CAPACITY}; - -// FIXME(conventions): implement BitOr, BitAnd, BitXor, and Sub -// FIXME(conventions): update capacity management to match other collections (no auto-shrink) +use iter::{Iterator, IteratorExt, IteratorCloneExt, FromIterator, Map, Chain, Extend}; +use ops::{BitOr, BitAnd, BitXor, Sub}; +use option::Option::{Some, None, mod}; +use result::Result::{Ok, Err}; +use super::map::{mod, HashMap, Keys, INITIAL_CAPACITY}; // Future Optimization (FIXME!) // ============================= @@ -172,7 +169,28 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S>> HashSet<T, H> { HashSet { map: HashMap::with_capacity_and_hasher(capacity, hasher) } } - /// Reserve space for at least `n` elements in the hash table. + /// Returns the number of elements the set can hold without reallocating. + /// + /// # Example + /// + /// ``` + /// use std::collections::HashSet; + /// let set: HashSet<int> = HashSet::with_capacity(100); + /// assert!(set.capacity() >= 100); + /// ``` + #[inline] + #[unstable = "matches collection reform specification, waiting for dust to settle"] + pub fn capacity(&self) -> uint { + self.map.capacity() + } + + /// Reserves capacity for at least `additional` more elements to be inserted + /// in the `HashSet`. The collection may reserve more space to avoid + /// frequent reallocations. + /// + /// # Panics + /// + /// Panics if the new allocation size overflows `uint`. /// /// # Example /// @@ -181,8 +199,30 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S>> HashSet<T, H> { /// let mut set: HashSet<int> = HashSet::new(); /// set.reserve(10); /// ``` - pub fn reserve(&mut self, n: uint) { - self.map.reserve(n) + #[unstable = "matches collection reform specification, waiting for dust to settle"] + pub fn reserve(&mut self, additional: uint) { + self.map.reserve(additional) + } + + /// Shrinks the capacity of the set as much as possible. It will drop + /// down as much as possible while maintaining the internal rules + /// and possibly leaving some space in accordance with the resize policy. + /// + /// # Example + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let mut set: HashSet<int> = HashSet::with_capacity(100); + /// set.insert(1); + /// set.insert(2); + /// assert!(set.capacity() >= 100); + /// set.shrink_to_fit(); + /// assert!(set.capacity() >= 2); + /// ``` + #[unstable = "matches collection reform specification, waiting for dust to settle"] + pub fn shrink_to_fit(&mut self) { + self.map.shrink_to_fit() } /// Deprecated: use `contains` and `BorrowFrom`. @@ -209,8 +249,8 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S>> HashSet<T, H> { /// } /// ``` #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn iter<'a>(&'a self) -> SetItems<'a, T> { - self.map.keys() + pub fn iter<'a>(&'a self) -> Iter<'a, T> { + Iter { iter: self.map.keys() } } /// Creates a consuming iterator, that is, one that moves each value out @@ -234,8 +274,11 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S>> HashSet<T, H> { /// } /// ``` #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn into_iter(self) -> SetMoveItems<T> { - self.map.into_iter().map(|(k, _)| k) + pub fn into_iter(self) -> IntoIter<T> { + fn first<A, B>((a, _): (A, B)) -> A { a } + let first: fn((T, ())) -> T = first; + + IntoIter { iter: self.map.into_iter().map(first) } } /// Visit the values representing the difference. @@ -261,11 +304,11 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S>> HashSet<T, H> { /// assert_eq!(diff, [4i].iter().map(|&x| x).collect()); /// ``` #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn difference<'a>(&'a self, other: &'a HashSet<T, H>) -> SetAlgebraItems<'a, T, H> { - Repeat::new(other).zip(self.iter()) - .filter_map(|(other, elt)| { - if !other.contains(elt) { Some(elt) } else { None } - }) + pub fn difference<'a>(&'a self, other: &'a HashSet<T, H>) -> Difference<'a, T, H> { + Difference { + iter: self.iter(), + other: other, + } } /// Visit the values representing the symmetric difference. @@ -290,8 +333,8 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S>> HashSet<T, H> { /// ``` #[unstable = "matches collection reform specification, waiting for dust to settle"] pub fn symmetric_difference<'a>(&'a self, other: &'a HashSet<T, H>) - -> Chain<SetAlgebraItems<'a, T, H>, SetAlgebraItems<'a, T, H>> { - self.difference(other).chain(other.difference(self)) + -> SymmetricDifference<'a, T, H> { + SymmetricDifference { iter: self.difference(other).chain(other.difference(self)) } } /// Visit the values representing the intersection. @@ -312,12 +355,11 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S>> HashSet<T, H> { /// assert_eq!(diff, [2i, 3].iter().map(|&x| x).collect()); /// ``` #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn intersection<'a>(&'a self, other: &'a HashSet<T, H>) - -> SetAlgebraItems<'a, T, H> { - Repeat::new(other).zip(self.iter()) - .filter_map(|(other, elt)| { - if other.contains(elt) { Some(elt) } else { None } - }) + pub fn intersection<'a>(&'a self, other: &'a HashSet<T, H>) -> Intersection<'a, T, H> { + Intersection { + iter: self.iter(), + other: other, + } } /// Visit the values representing the union. @@ -338,9 +380,8 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S>> HashSet<T, H> { /// assert_eq!(diff, [1i, 2, 3, 4].iter().map(|&x| x).collect()); /// ``` #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn union<'a>(&'a self, other: &'a HashSet<T, H>) - -> Chain<SetItems<'a, T>, SetAlgebraItems<'a, T, H>> { - self.iter().chain(other.difference(self)) + pub fn union<'a>(&'a self, other: &'a HashSet<T, H>) -> Union<'a, T, H> { + Union { iter: self.iter().chain(other.difference(self)) } } /// Return the number of elements in the set @@ -373,6 +414,16 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S>> HashSet<T, H> { #[unstable = "matches collection reform specification, waiting for dust to settle"] pub fn is_empty(&self) -> bool { self.map.len() == 0 } + /// Clears the set, returning all elements in an iterator. + #[inline] + #[unstable = "matches collection reform specification, waiting for dust to settle"] + pub fn drain(&mut self) -> Drain<T> { + fn first<A, B>((a, _): (A, B)) -> A { a } + let first: fn((T, ())) -> T = first; // coerce to fn pointer + + Drain { iter: self.map.drain().map(first) } + } + /// Clears the set, removing all values. /// /// # Example @@ -546,7 +597,7 @@ impl<T: Eq + Hash<S> + fmt::Show, S, H: Hasher<S>> fmt::Show for HashSet<T, H> { impl<T: Eq + Hash<S>, S, H: Hasher<S> + Default> FromIterator<T> for HashSet<T, H> { fn from_iter<I: Iterator<T>>(iter: I) -> HashSet<T, H> { - let (lower, _) = iter.size_hint(); + let lower = iter.size_hint().0; let mut set = HashSet::with_capacity_and_hasher(lower, Default::default()); set.extend(iter); set @@ -561,33 +612,241 @@ impl<T: Eq + Hash<S>, S, H: Hasher<S> + Default> Extend<T> for HashSet<T, H> { } } +#[stable] impl<T: Eq + Hash<S>, S, H: Hasher<S> + Default> Default for HashSet<T, H> { + #[stable] fn default() -> HashSet<T, H> { HashSet::with_hasher(Default::default()) } } +#[unstable = "matches collection reform specification, waiting for dust to settle"] +impl<'a, 'b, T: Eq + Hash<S> + Clone, S, H: Hasher<S> + Default> +BitOr<&'b HashSet<T, H>, HashSet<T, H>> for &'a HashSet<T, H> { + /// Returns the union of `self` and `rhs` as a new `HashSet<T, H>`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let a: HashSet<int> = vec![1, 2, 3].into_iter().collect(); + /// let b: HashSet<int> = vec![3, 4, 5].into_iter().collect(); + /// + /// let set: HashSet<int> = &a | &b; + /// + /// let mut i = 0; + /// let expected = [1, 2, 3, 4, 5]; + /// for x in set.iter() { + /// assert!(expected.contains(x)); + /// i += 1; + /// } + /// assert_eq!(i, expected.len()); + /// ``` + fn bitor(self, rhs: &HashSet<T, H>) -> HashSet<T, H> { + self.union(rhs).cloned().collect() + } +} + +#[unstable = "matches collection reform specification, waiting for dust to settle"] +impl<'a, 'b, T: Eq + Hash<S> + Clone, S, H: Hasher<S> + Default> +BitAnd<&'b HashSet<T, H>, HashSet<T, H>> for &'a HashSet<T, H> { + /// Returns the intersection of `self` and `rhs` as a new `HashSet<T, H>`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let a: HashSet<int> = vec![1, 2, 3].into_iter().collect(); + /// let b: HashSet<int> = vec![2, 3, 4].into_iter().collect(); + /// + /// let set: HashSet<int> = &a & &b; + /// + /// let mut i = 0; + /// let expected = [2, 3]; + /// for x in set.iter() { + /// assert!(expected.contains(x)); + /// i += 1; + /// } + /// assert_eq!(i, expected.len()); + /// ``` + fn bitand(self, rhs: &HashSet<T, H>) -> HashSet<T, H> { + self.intersection(rhs).cloned().collect() + } +} + +#[unstable = "matches collection reform specification, waiting for dust to settle"] +impl<'a, 'b, T: Eq + Hash<S> + Clone, S, H: Hasher<S> + Default> +BitXor<&'b HashSet<T, H>, HashSet<T, H>> for &'a HashSet<T, H> { + /// Returns the symmetric difference of `self` and `rhs` as a new `HashSet<T, H>`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let a: HashSet<int> = vec![1, 2, 3].into_iter().collect(); + /// let b: HashSet<int> = vec![3, 4, 5].into_iter().collect(); + /// + /// let set: HashSet<int> = &a ^ &b; + /// + /// let mut i = 0; + /// let expected = [1, 2, 4, 5]; + /// for x in set.iter() { + /// assert!(expected.contains(x)); + /// i += 1; + /// } + /// assert_eq!(i, expected.len()); + /// ``` + fn bitxor(self, rhs: &HashSet<T, H>) -> HashSet<T, H> { + self.symmetric_difference(rhs).cloned().collect() + } +} + +#[unstable = "matches collection reform specification, waiting for dust to settle"] +impl<'a, 'b, T: Eq + Hash<S> + Clone, S, H: Hasher<S> + Default> +Sub<&'b HashSet<T, H>, HashSet<T, H>> for &'a HashSet<T, H> { + /// Returns the difference of `self` and `rhs` as a new `HashSet<T, H>`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashSet; + /// + /// let a: HashSet<int> = vec![1, 2, 3].into_iter().collect(); + /// let b: HashSet<int> = vec![3, 4, 5].into_iter().collect(); + /// + /// let set: HashSet<int> = &a - &b; + /// + /// let mut i = 0; + /// let expected = [1, 2]; + /// for x in set.iter() { + /// assert!(expected.contains(x)); + /// i += 1; + /// } + /// assert_eq!(i, expected.len()); + /// ``` + fn sub(self, rhs: &HashSet<T, H>) -> HashSet<T, H> { + self.difference(rhs).cloned().collect() + } +} + /// HashSet iterator -pub type SetItems<'a, K> = - iter::Map<'static, (&'a K, &'a ()), &'a K, Entries<'a, K, ()>>; +pub struct Iter<'a, K: 'a> { + iter: Keys<'a, K, ()> +} /// HashSet move iterator -pub type SetMoveItems<K> = - iter::Map<'static, (K, ()), K, MoveEntries<K, ()>>; +pub struct IntoIter<K> { + iter: Map<(K, ()), K, map::IntoIter<K, ()>, fn((K, ())) -> K> +} + +/// HashSet drain iterator +pub struct Drain<'a, K: 'a> { + iter: Map<(K, ()), K, map::Drain<'a, K, ()>, fn((K, ())) -> K>, +} + +/// Intersection iterator +pub struct Intersection<'a, T: 'a, H: 'a> { + // iterator of the first set + iter: Iter<'a, T>, + // the second set + other: &'a HashSet<T, H>, +} + +/// Difference iterator +pub struct Difference<'a, T: 'a, H: 'a> { + // iterator of the first set + iter: Iter<'a, T>, + // the second set + other: &'a HashSet<T, H>, +} -// `Repeat` is used to feed the filter closure an explicit capture -// of a reference to the other set -/// Set operations iterator -pub type SetAlgebraItems<'a, T, H> = - FilterMap<'static, (&'a HashSet<T, H>, &'a T), &'a T, - Zip<Repeat<&'a HashSet<T, H>>, SetItems<'a, T>>>; +/// Symmetric difference iterator. +pub struct SymmetricDifference<'a, T: 'a, H: 'a> { + iter: Chain<Difference<'a, T, H>, Difference<'a, T, H>> +} + +/// Set union iterator. +pub struct Union<'a, T: 'a, H: 'a> { + iter: Chain<Iter<'a, T>, Difference<'a, T, H>> +} + +impl<'a, K> Iterator<&'a K> for Iter<'a, K> { + fn next(&mut self) -> Option<&'a K> { self.iter.next() } + fn size_hint(&self) -> (uint, Option<uint>) { self.iter.size_hint() } +} + +impl<K> Iterator<K> for IntoIter<K> { + fn next(&mut self) -> Option<K> { self.iter.next() } + fn size_hint(&self) -> (uint, Option<uint>) { self.iter.size_hint() } +} + +impl<'a, K: 'a> Iterator<K> for Drain<'a, K> { + fn next(&mut self) -> Option<K> { self.iter.next() } + fn size_hint(&self) -> (uint, Option<uint>) { self.iter.size_hint() } +} + +impl<'a, T, S, H> Iterator<&'a T> for Intersection<'a, T, H> + where T: Eq + Hash<S>, H: Hasher<S> +{ + fn next(&mut self) -> Option<&'a T> { + loop { + match self.iter.next() { + None => return None, + Some(elt) => if self.other.contains(elt) { + return Some(elt) + }, + } + } + } + + fn size_hint(&self) -> (uint, Option<uint>) { + let (_, upper) = self.iter.size_hint(); + (0, upper) + } +} + +impl<'a, T, S, H> Iterator<&'a T> for Difference<'a, T, H> + where T: Eq + Hash<S>, H: Hasher<S> +{ + fn next(&mut self) -> Option<&'a T> { + loop { + match self.iter.next() { + None => return None, + Some(elt) => if !self.other.contains(elt) { + return Some(elt) + }, + } + } + } + + fn size_hint(&self) -> (uint, Option<uint>) { + let (_, upper) = self.iter.size_hint(); + (0, upper) + } +} + +impl<'a, T, S, H> Iterator<&'a T> for SymmetricDifference<'a, T, H> + where T: Eq + Hash<S>, H: Hasher<S> +{ + fn next(&mut self) -> Option<&'a T> { self.iter.next() } + fn size_hint(&self) -> (uint, Option<uint>) { self.iter.size_hint() } +} + +impl<'a, T, S, H> Iterator<&'a T> for Union<'a, T, H> + where T: Eq + Hash<S>, H: Hasher<S> +{ + fn next(&mut self) -> Option<&'a T> { self.iter.next() } + fn size_hint(&self) -> (uint, Option<uint>) { self.iter.size_hint() } +} #[cfg(test)] mod test_set { use prelude::*; use super::HashSet; - use slice::PartialEqSlicePrelude; #[test] fn test_disjoint() { @@ -785,7 +1044,7 @@ mod test_set { }; let v = hs.into_iter().collect::<Vec<char>>(); - assert!(['a', 'b'][] == v.as_slice() || ['b', 'a'][] == v.as_slice()); + assert!(['a', 'b'] == v || ['b', 'a'] == v); } #[test] @@ -820,7 +1079,44 @@ mod test_set { let set_str = format!("{}", set); - assert!(set_str == "{1, 2}".to_string() || set_str == "{2, 1}".to_string()); - assert_eq!(format!("{}", empty), "{}".to_string()); + assert!(set_str == "{1, 2}" || set_str == "{2, 1}"); + assert_eq!(format!("{}", empty), "{}"); + } + + #[test] + fn test_trivial_drain() { + let mut s = HashSet::<int>::new(); + for _ in s.drain() {} + assert!(s.is_empty()); + drop(s); + + let mut s = HashSet::<int>::new(); + drop(s.drain()); + assert!(s.is_empty()); + } + + #[test] + fn test_drain() { + let mut s: HashSet<int> = range(1, 100).collect(); + + // try this a bunch of times to make sure we don't screw up internal state. + for _ in range(0i, 20) { + assert_eq!(s.len(), 99); + + { + let mut last_i = 0; + let mut d = s.drain(); + for (i, x) in d.by_ref().take(50).enumerate() { + last_i = i; + assert!(x != 0); + } + assert_eq!(last_i, 49); + } + + for _ in s.iter() { panic!("s should be empty!"); } + + // reset to try again. + s.extend(range(1, 100)); + } } } diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index f41ccea0aaf..8f2152c5a9d 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -10,18 +10,19 @@ // // ignore-lexer-test FIXME #15883 -pub use self::BucketState::*; +use self::BucketState::*; use clone::Clone; use cmp; use hash::{Hash, Hasher}; use iter::{Iterator, count}; -use kinds::{Sized, marker}; +use kinds::{Copy, Sized, marker}; use mem::{min_align_of, size_of}; use mem; use num::{Int, UnsignedInt}; use ops::{Deref, DerefMut, Drop}; -use option::{Some, None, Option}; +use option::Option; +use option::Option::{Some, None}; use ptr::{RawPtr, copy_nonoverlapping_memory, zero_memory}; use ptr; use rt::heap::{allocate, deallocate}; @@ -80,12 +81,16 @@ struct RawBucket<K, V> { val: *mut V } +impl<K,V> Copy for RawBucket<K,V> {} + pub struct Bucket<K, V, M> { raw: RawBucket<K, V>, idx: uint, table: M } +impl<K,V,M:Copy> Copy for Bucket<K,V,M> {} + pub struct EmptyBucket<K, V, M> { raw: RawBucket<K, V>, idx: uint, @@ -119,7 +124,7 @@ struct GapThenFull<K, V, M> { /// A hash that is not zero, since we use a hash of zero to represent empty /// buckets. -#[deriving(PartialEq)] +#[deriving(PartialEq, Copy)] pub struct SafeHash { hash: u64, } @@ -206,7 +211,7 @@ impl<K, V, M> Bucket<K, V, M> { } impl<K, V, M: Deref<RawTable<K, V>>> Bucket<K, V, M> { - pub fn new(table: M, hash: &SafeHash) -> Bucket<K, V, M> { + pub fn new(table: M, hash: SafeHash) -> Bucket<K, V, M> { Bucket::at_index(table, hash.inspect() as uint) } @@ -659,17 +664,17 @@ impl<K, V> RawTable<K, V> { } } - pub fn iter_mut(&mut self) -> MutEntries<K, V> { - MutEntries { + pub fn iter_mut(&mut self) -> IterMut<K, V> { + IterMut { iter: self.raw_buckets(), elems_left: self.size(), } } - pub fn into_iter(self) -> MoveEntries<K, V> { + pub fn into_iter(self) -> IntoIter<K, V> { let RawBuckets { raw, hashes_end, .. } = self.raw_buckets(); // Replace the marker regardless of lifetime bounds on parameters. - MoveEntries { + IntoIter { iter: RawBuckets { raw: raw, hashes_end: hashes_end, @@ -679,6 +684,19 @@ impl<K, V> RawTable<K, V> { } } + pub fn drain(&mut self) -> Drain<K, V> { + let RawBuckets { raw, hashes_end, .. } = self.raw_buckets(); + // Replace the marker regardless of lifetime bounds on parameters. + Drain { + iter: RawBuckets { + raw: raw, + hashes_end: hashes_end, + marker: marker::ContravariantLifetime::<'static>, + }, + table: self, + } + } + /// Returns an iterator that copies out each entry. Used while the table /// is being dropped. unsafe fn rev_move_buckets(&mut self) -> RevMoveBuckets<K, V> { @@ -758,17 +776,23 @@ pub struct Entries<'a, K: 'a, V: 'a> { } /// Iterator over mutable references to entries in a table. -pub struct MutEntries<'a, K: 'a, V: 'a> { +pub struct IterMut<'a, K: 'a, V: 'a> { iter: RawBuckets<'a, K, V>, elems_left: uint, } /// Iterator over the entries in a table, consuming the table. -pub struct MoveEntries<K, V> { +pub struct IntoIter<K, V> { table: RawTable<K, V>, iter: RawBuckets<'static, K, V> } +/// Iterator over the entries in a table, clearing the table. +pub struct Drain<'a, K: 'a, V: 'a> { + table: &'a mut RawTable<K, V>, + iter: RawBuckets<'static, K, V>, +} + impl<'a, K, V> Iterator<(&'a K, &'a V)> for Entries<'a, K, V> { fn next(&mut self) -> Option<(&'a K, &'a V)> { self.iter.next().map(|bucket| { @@ -785,7 +809,7 @@ impl<'a, K, V> Iterator<(&'a K, &'a V)> for Entries<'a, K, V> { } } -impl<'a, K, V> Iterator<(&'a K, &'a mut V)> for MutEntries<'a, K, V> { +impl<'a, K, V> Iterator<(&'a K, &'a mut V)> for IterMut<'a, K, V> { fn next(&mut self) -> Option<(&'a K, &'a mut V)> { self.iter.next().map(|bucket| { self.elems_left -= 1; @@ -801,7 +825,7 @@ impl<'a, K, V> Iterator<(&'a K, &'a mut V)> for MutEntries<'a, K, V> { } } -impl<K, V> Iterator<(SafeHash, K, V)> for MoveEntries<K, V> { +impl<K, V> Iterator<(SafeHash, K, V)> for IntoIter<K, V> { fn next(&mut self) -> Option<(SafeHash, K, V)> { self.iter.next().map(|bucket| { self.table.size -= 1; @@ -823,6 +847,36 @@ impl<K, V> Iterator<(SafeHash, K, V)> for MoveEntries<K, V> { } } +impl<'a, K: 'a, V: 'a> Iterator<(SafeHash, K, V)> for Drain<'a, K, V> { + #[inline] + fn next(&mut self) -> Option<(SafeHash, K, V)> { + self.iter.next().map(|bucket| { + self.table.size -= 1; + unsafe { + ( + SafeHash { + hash: ptr::replace(bucket.hash, EMPTY_BUCKET), + }, + ptr::read(bucket.key as *const K), + ptr::read(bucket.val as *const V) + ) + } + }) + } + + fn size_hint(&self) -> (uint, Option<uint>) { + let size = self.table.size(); + (size, Some(size)) + } +} + +#[unsafe_destructor] +impl<'a, K: 'a, V: 'a> Drop for Drain<'a, K, V> { + fn drop(&mut self) { + for _ in *self {} + } +} + impl<K: Clone, V: Clone> Clone for RawTable<K, V> { fn clone(&self) -> RawTable<K, V> { unsafe { diff --git a/src/libstd/collections/lru_cache.rs b/src/libstd/collections/lru_cache.rs deleted file mode 100644 index 94bea37d187..00000000000 --- a/src/libstd/collections/lru_cache.rs +++ /dev/null @@ -1,470 +0,0 @@ -// 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. - - -//! A cache that holds a limited number of key-value pairs. When the -//! capacity of the cache is exceeded, the least-recently-used -//! (where "used" means a look-up or putting the pair into the cache) -//! pair is automatically removed. -//! -//! # Example -//! -//! ```rust -//! use std::collections::LruCache; -//! -//! let mut cache: LruCache<int, int> = LruCache::new(2); -//! cache.insert(1, 10); -//! cache.insert(2, 20); -//! cache.insert(3, 30); -//! assert!(cache.get(&1).is_none()); -//! assert_eq!(*cache.get(&2).unwrap(), 20); -//! assert_eq!(*cache.get(&3).unwrap(), 30); -//! -//! cache.insert(2, 22); -//! assert_eq!(*cache.get(&2).unwrap(), 22); -//! -//! cache.insert(6, 60); -//! assert!(cache.get(&3).is_none()); -//! -//! cache.set_capacity(1); -//! assert!(cache.get(&2).is_none()); -//! ``` - -use cmp::{PartialEq, Eq}; -use collections::HashMap; -use fmt; -use hash::Hash; -use iter::{range, Iterator, Extend}; -use mem; -use ops::Drop; -use option::{Some, None, Option}; -use boxed::Box; -use ptr; -use result::{Ok, Err}; - -// FIXME(conventions): implement iterators? -// FIXME(conventions): implement indexing? - -struct KeyRef<K> { k: *const K } - -struct LruEntry<K, V> { - next: *mut LruEntry<K, V>, - prev: *mut LruEntry<K, V>, - key: K, - value: V, -} - -/// An LRU Cache. -pub struct LruCache<K, V> { - map: HashMap<KeyRef<K>, Box<LruEntry<K, V>>>, - max_size: uint, - head: *mut LruEntry<K, V>, -} - -impl<S, K: Hash<S>> Hash<S> for KeyRef<K> { - fn hash(&self, state: &mut S) { - unsafe { (*self.k).hash(state) } - } -} - -impl<K: PartialEq> PartialEq for KeyRef<K> { - fn eq(&self, other: &KeyRef<K>) -> bool { - unsafe{ (*self.k).eq(&*other.k) } - } -} - -impl<K: Eq> Eq for KeyRef<K> {} - -impl<K, V> LruEntry<K, V> { - fn new(k: K, v: V) -> LruEntry<K, V> { - LruEntry { - key: k, - value: v, - next: ptr::null_mut(), - prev: ptr::null_mut(), - } - } -} - -impl<K: Hash + Eq, V> LruCache<K, V> { - /// Create an LRU Cache that holds at most `capacity` items. - /// - /// # Example - /// - /// ``` - /// use std::collections::LruCache; - /// let mut cache: LruCache<int, &str> = LruCache::new(10); - /// ``` - #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn new(capacity: uint) -> LruCache<K, V> { - let cache = LruCache { - map: HashMap::new(), - max_size: capacity, - head: unsafe{ mem::transmute(box mem::uninitialized::<LruEntry<K, V>>()) }, - }; - unsafe { - (*cache.head).next = cache.head; - (*cache.head).prev = cache.head; - } - return cache; - } - - /// Deprecated: Replaced with `insert`. - #[deprecated = "Replaced with `insert`"] - pub fn put(&mut self, k: K, v: V) { - self.insert(k, v); - } - - /// Inserts a key-value pair into the cache. If the key already existed, the old value is - /// returned. - /// - /// # Example - /// - /// ``` - /// use std::collections::LruCache; - /// let mut cache = LruCache::new(2); - /// - /// cache.insert(1i, "a"); - /// cache.insert(2, "b"); - /// assert_eq!(cache.get(&1), Some(&"a")); - /// assert_eq!(cache.get(&2), Some(&"b")); - /// ``` - #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn insert(&mut self, k: K, v: V) -> Option<V> { - let (node_ptr, node_opt, old_val) = match self.map.get_mut(&KeyRef{k: &k}) { - Some(node) => { - let old_val = mem::replace(&mut node.value, v); - let node_ptr: *mut LruEntry<K, V> = &mut **node; - (node_ptr, None, Some(old_val)) - } - None => { - let mut node = box LruEntry::new(k, v); - let node_ptr: *mut LruEntry<K, V> = &mut *node; - (node_ptr, Some(node), None) - } - }; - match node_opt { - None => { - // Existing node, just update LRU position - self.detach(node_ptr); - self.attach(node_ptr); - } - Some(node) => { - let keyref = unsafe { &(*node_ptr).key }; - self.map.insert(KeyRef{k: keyref}, node); - self.attach(node_ptr); - if self.len() > self.capacity() { - self.remove_lru(); - } - } - } - old_val - } - - /// Return a value corresponding to the key in the cache. - /// - /// # Example - /// - /// ``` - /// use std::collections::LruCache; - /// let mut cache = LruCache::new(2); - /// - /// cache.insert(1i, "a"); - /// cache.insert(2, "b"); - /// cache.insert(2, "c"); - /// cache.insert(3, "d"); - /// - /// assert_eq!(cache.get(&1), None); - /// assert_eq!(cache.get(&2), Some(&"c")); - /// ``` - #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn get(&mut self, k: &K) -> Option<&V> { - let (value, node_ptr_opt) = match self.map.get_mut(&KeyRef{k: k}) { - None => (None, None), - Some(node) => { - let node_ptr: *mut LruEntry<K, V> = &mut **node; - (Some(unsafe { &(*node_ptr).value }), Some(node_ptr)) - } - }; - match node_ptr_opt { - None => (), - Some(node_ptr) => { - self.detach(node_ptr); - self.attach(node_ptr); - } - } - return value; - } - - /// Deprecated: Renamed to `remove`. - #[deprecated = "Renamed to `remove`"] - pub fn pop(&mut self, k: &K) -> Option<V> { - self.remove(k) - } - - /// Remove and return a value corresponding to the key from the cache. - /// - /// # Example - /// - /// ``` - /// use std::collections::LruCache; - /// let mut cache = LruCache::new(2); - /// - /// cache.insert(2i, "a"); - /// - /// assert_eq!(cache.remove(&1), None); - /// assert_eq!(cache.remove(&2), Some("a")); - /// assert_eq!(cache.remove(&2), None); - /// assert_eq!(cache.len(), 0); - /// ``` - #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn remove(&mut self, k: &K) -> Option<V> { - match self.map.remove(&KeyRef{k: k}) { - None => None, - Some(lru_entry) => Some(lru_entry.value) - } - } - - /// Return the maximum number of key-value pairs the cache can hold. - /// - /// # Example - /// - /// ``` - /// use std::collections::LruCache; - /// let mut cache: LruCache<int, &str> = LruCache::new(2); - /// assert_eq!(cache.capacity(), 2); - /// ``` - #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn capacity(&self) -> uint { - self.max_size - } - - /// Deprecated: Renamed to `set_capacity`. - #[deprecated = "Renamed to `set_capacity`"] - pub fn change_capacity(&mut self, capacity: uint) { - self.set_capacity(capacity) - } - - /// Change the number of key-value pairs the cache can hold. Remove - /// least-recently-used key-value pairs if necessary. - /// - /// # Example - /// - /// ``` - /// use std::collections::LruCache; - /// let mut cache = LruCache::new(2); - /// - /// cache.insert(1i, "a"); - /// cache.insert(2, "b"); - /// cache.insert(3, "c"); - /// - /// assert_eq!(cache.get(&1), None); - /// assert_eq!(cache.get(&2), Some(&"b")); - /// assert_eq!(cache.get(&3), Some(&"c")); - /// - /// cache.set_capacity(3); - /// cache.insert(1i, "a"); - /// cache.insert(2, "b"); - /// - /// assert_eq!(cache.get(&1), Some(&"a")); - /// assert_eq!(cache.get(&2), Some(&"b")); - /// assert_eq!(cache.get(&3), Some(&"c")); - /// - /// cache.set_capacity(1); - /// - /// assert_eq!(cache.get(&1), None); - /// assert_eq!(cache.get(&2), None); - /// assert_eq!(cache.get(&3), Some(&"c")); - /// ``` - #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn set_capacity(&mut self, capacity: uint) { - for _ in range(capacity, self.len()) { - self.remove_lru(); - } - self.max_size = capacity; - } - - #[inline] - fn remove_lru(&mut self) { - if self.len() > 0 { - let lru = unsafe { (*self.head).prev }; - self.detach(lru); - self.map.remove(&KeyRef{k: unsafe { &(*lru).key }}); - } - } - - #[inline] - fn detach(&mut self, node: *mut LruEntry<K, V>) { - unsafe { - (*(*node).prev).next = (*node).next; - (*(*node).next).prev = (*node).prev; - } - } - - #[inline] - fn attach(&mut self, node: *mut LruEntry<K, V>) { - unsafe { - (*node).next = (*self.head).next; - (*node).prev = self.head; - (*self.head).next = node; - (*(*node).next).prev = node; - } - } - - /// Return the number of key-value pairs in the cache. - #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn len(&self) -> uint { self.map.len() } - - /// Returns whether the cache is currently empty. - #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn is_empty(&self) -> bool { self.len() == 0 } - - /// Clear the cache of all key-value pairs. - #[unstable = "matches collection reform specification, waiting for dust to settle"] - pub fn clear(&mut self) { self.map.clear(); } - -} - -impl<K: Hash + Eq, V> Extend<(K, V)> for LruCache<K, V> { - fn extend<T: Iterator<(K, V)>>(&mut self, mut iter: T) { - for (k, v) in iter{ - self.insert(k, v); - } - } -} - -impl<A: fmt::Show + Hash + Eq, B: fmt::Show> fmt::Show for LruCache<A, B> { - /// Return a string that lists the key-value pairs from most-recently - /// used to least-recently used. - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(write!(f, "{{")); - let mut cur = self.head; - for i in range(0, self.len()) { - if i > 0 { try!(write!(f, ", ")) } - unsafe { - cur = (*cur).next; - try!(write!(f, "{}", (*cur).key)); - } - try!(write!(f, ": ")); - unsafe { - try!(write!(f, "{}", (*cur).value)); - } - } - write!(f, r"}}") - } -} - -#[unsafe_destructor] -impl<K, V> Drop for LruCache<K, V> { - fn drop(&mut self) { - unsafe { - let node: Box<LruEntry<K, V>> = mem::transmute(self.head); - // Prevent compiler from trying to drop the un-initialized field in the sigil node. - let box internal_node = node; - let LruEntry { next: _, prev: _, key: k, value: v } = internal_node; - mem::forget(k); - mem::forget(v); - } - } -} - -#[cfg(test)] -mod tests { - use prelude::*; - use super::LruCache; - - fn assert_opt_eq<V: PartialEq>(opt: Option<&V>, v: V) { - assert!(opt.is_some()); - assert!(opt.unwrap() == &v); - } - - #[test] - fn test_put_and_get() { - let mut cache: LruCache<int, int> = LruCache::new(2); - cache.insert(1, 10); - cache.insert(2, 20); - assert_opt_eq(cache.get(&1), 10); - assert_opt_eq(cache.get(&2), 20); - assert_eq!(cache.len(), 2); - } - - #[test] - fn test_put_update() { - let mut cache: LruCache<String, Vec<u8>> = LruCache::new(1); - cache.insert("1".to_string(), vec![10, 10]); - cache.insert("1".to_string(), vec![10, 19]); - assert_opt_eq(cache.get(&"1".to_string()), vec![10, 19]); - assert_eq!(cache.len(), 1); - } - - #[test] - fn test_expire_lru() { - let mut cache: LruCache<String, String> = LruCache::new(2); - cache.insert("foo1".to_string(), "bar1".to_string()); - cache.insert("foo2".to_string(), "bar2".to_string()); - cache.insert("foo3".to_string(), "bar3".to_string()); - assert!(cache.get(&"foo1".to_string()).is_none()); - cache.insert("foo2".to_string(), "bar2update".to_string()); - cache.insert("foo4".to_string(), "bar4".to_string()); - assert!(cache.get(&"foo3".to_string()).is_none()); - } - - #[test] - fn test_pop() { - let mut cache: LruCache<int, int> = LruCache::new(2); - cache.insert(1, 10); - cache.insert(2, 20); - assert_eq!(cache.len(), 2); - let opt1 = cache.remove(&1); - assert!(opt1.is_some()); - assert_eq!(opt1.unwrap(), 10); - assert!(cache.get(&1).is_none()); - assert_eq!(cache.len(), 1); - } - - #[test] - fn test_change_capacity() { - let mut cache: LruCache<int, int> = LruCache::new(2); - assert_eq!(cache.capacity(), 2); - cache.insert(1, 10); - cache.insert(2, 20); - cache.set_capacity(1); - assert!(cache.get(&1).is_none()); - assert_eq!(cache.capacity(), 1); - } - - #[test] - fn test_to_string() { - let mut cache: LruCache<int, int> = LruCache::new(3); - cache.insert(1, 10); - cache.insert(2, 20); - cache.insert(3, 30); - assert_eq!(cache.to_string(), "{3: 30, 2: 20, 1: 10}".to_string()); - cache.insert(2, 22); - assert_eq!(cache.to_string(), "{2: 22, 3: 30, 1: 10}".to_string()); - cache.insert(6, 60); - assert_eq!(cache.to_string(), "{6: 60, 2: 22, 3: 30}".to_string()); - cache.get(&3); - assert_eq!(cache.to_string(), "{3: 30, 6: 60, 2: 22}".to_string()); - cache.set_capacity(2); - assert_eq!(cache.to_string(), "{3: 30, 6: 60}".to_string()); - } - - #[test] - fn test_clear() { - let mut cache: LruCache<int, int> = LruCache::new(2); - cache.insert(1, 10); - cache.insert(2, 20); - cache.clear(); - assert!(cache.get(&1).is_none()); - assert!(cache.get(&2).is_none()); - assert_eq!(cache.to_string(), "{}".to_string()); - } -} diff --git a/src/libstd/collections/mod.rs b/src/libstd/collections/mod.rs index 3419a3d98a1..0d44e6d869a 100644 --- a/src/libstd/collections/mod.rs +++ b/src/libstd/collections/mod.rs @@ -24,8 +24,8 @@ //! Rust's collections can be grouped into four major categories: //! //! * Sequences: `Vec`, `RingBuf`, `DList`, `BitV` -//! * Maps: `HashMap`, `BTreeMap`, `TreeMap`, `TrieMap`, `VecMap`, `LruCache` -//! * Sets: `HashSet`, `BTreeSet`, `TreeSet`, `TrieSet`, `BitVSet`, `EnumSet` +//! * Maps: `HashMap`, `BTreeMap`, `VecMap` +//! * Sets: `HashSet`, `BTreeSet`, `BitVSet` //! * Misc: `BinaryHeap` //! //! # When Should You Use Which Collection? @@ -64,16 +64,6 @@ //! * You want to be able to get all of the entries in order on-demand. //! * You want a sorted map. //! -//! ### Use a `TreeMap` when: -//! * You want a `BTreeMap`, but can't tolerate inconsistent performance. -//! * You want a `BTreeMap`, but have *very large* keys or values. -//! * You want a `BTreeMap`, but have keys that are expensive to compare. -//! * You want a `BTreeMap`, but you accept arbitrary untrusted inputs. -//! -//! ### Use a `TrieMap` when: -//! * You want a `HashMap`, but with many potentially large `uint` keys. -//! * You want a `BTreeMap`, but with potentially large `uint` keys. -//! //! ### Use a `VecMap` when: //! * You want a `HashMap` but with known to be small `uint` keys. //! * You want a `BTreeMap`, but with known to be small `uint` keys. @@ -90,18 +80,11 @@ //! ### Use a `BitVSet` when: //! * You want a `VecSet`. //! -//! ### Use an `EnumSet` when: -//! * You want a C-like enum, stored in a single `uint`. -//! //! ### Use a `BinaryHeap` when: //! * You want to store a bunch of elements, but only ever want to process the "biggest" //! or "most important" one at any given time. //! * You want a priority queue. //! -//! ### Use an `LruCache` when: -//! * You want a cache that discards infrequently used items when it becomes full. -//! * You want a least-recently-used cache. -//! //! # Correct and Efficient Usage of Collections //! //! Of course, knowing which collection is the right one for the job doesn't instantly @@ -329,15 +312,21 @@ #![experimental] pub use core_collections::{BinaryHeap, Bitv, BitvSet, BTreeMap, BTreeSet}; -pub use core_collections::{DList, EnumSet, RingBuf}; -pub use core_collections::{TreeMap, TreeSet, TrieMap, TrieSet, VecMap}; +pub use core_collections::{DList, RingBuf, VecMap}; -pub use core_collections::{binary_heap, bitv, bitv_set, btree_map, btree_set, dlist, enum_set}; -pub use core_collections::{ring_buf, tree_map, tree_set, trie_map, trie_set, vec_map}; +/// Deprecated: Moved to collect-rs: https://github.com/Gankro/collect-rs/ +#[deprecated = "Moved to collect-rs: https://github.com/Gankro/collect-rs/"] +pub use core_collections::EnumSet; + +pub use core_collections::{binary_heap, bitv, bitv_set, btree_map, btree_set}; +pub use core_collections::{dlist, ring_buf, vec_map}; + +/// Deprecated: Moved to collect-rs: https://github.com/Gankro/collect-rs/ +#[deprecated = "Moved to collect-rs: https://github.com/Gankro/collect-rs/"] +pub use core_collections::enum_set; pub use self::hash_map::HashMap; pub use self::hash_set::HashSet; -pub use self::lru_cache::LruCache; mod hash; @@ -350,5 +339,3 @@ pub mod hash_set { //! A hashset pub use super::hash::set::*; } - -pub mod lru_cache; diff --git a/src/libstd/comm/blocking.rs b/src/libstd/comm/blocking.rs new file mode 100644 index 00000000000..c477acd70aa --- /dev/null +++ b/src/libstd/comm/blocking.rs @@ -0,0 +1,83 @@ +// Copyright 2014 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. + +//! Generic support for building blocking abstractions. + +use thread::Thread; +use sync::atomic::{AtomicBool, INIT_ATOMIC_BOOL, Ordering}; +use sync::Arc; +use kinds::marker::{NoSend, NoSync}; +use mem; +use clone::Clone; + +struct Inner { + thread: Thread, + woken: AtomicBool, +} + +#[deriving(Clone)] +pub struct SignalToken { + inner: Arc<Inner>, +} + +pub struct WaitToken { + inner: Arc<Inner>, + no_send: NoSend, + no_sync: NoSync, +} + +pub fn tokens() -> (WaitToken, SignalToken) { + let inner = Arc::new(Inner { + thread: Thread::current(), + woken: INIT_ATOMIC_BOOL, + }); + let wait_token = WaitToken { + inner: inner.clone(), + no_send: NoSend, + no_sync: NoSync, + }; + let signal_token = SignalToken { + inner: inner + }; + (wait_token, signal_token) +} + +impl SignalToken { + pub fn signal(&self) -> bool { + let wake = !self.inner.woken.compare_and_swap(false, true, Ordering::SeqCst); + if wake { + self.inner.thread.unpark(); + } + wake + } + + /// Convert to an unsafe uint value. Useful for storing in a pipe's state + /// flag. + #[inline] + pub unsafe fn cast_to_uint(self) -> uint { + mem::transmute(self.inner) + } + + /// Convert from an unsafe uint value. Useful for retrieving a pipe's state + /// flag. + #[inline] + pub unsafe fn cast_from_uint(signal_ptr: uint) -> SignalToken { + SignalToken { inner: mem::transmute(signal_ptr) } + } + +} + +impl WaitToken { + pub fn wait(self) { + while !self.inner.woken.load(Ordering::SeqCst) { + Thread::park() + } + } +} diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs new file mode 100644 index 00000000000..7352cdfbfe7 --- /dev/null +++ b/src/libstd/comm/mod.rs @@ -0,0 +1,2028 @@ +// Copyright 2013-2014 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. + +//! Communication primitives for concurrent tasks +//! +//! Rust makes it very difficult to share data among tasks to prevent race +//! conditions and to improve parallelism, but there is often a need for +//! communication between concurrent tasks. The primitives defined in this +//! module are the building blocks for synchronization in rust. +//! +//! This module provides message-based communication over channels, concretely +//! defined among three types: +//! +//! * `Sender` +//! * `SyncSender` +//! * `Receiver` +//! +//! A `Sender` or `SyncSender` is used to send data to a `Receiver`. Both +//! senders are clone-able such that many tasks can send simultaneously to one +//! receiver. These channels are *task blocking*, not *thread blocking*. This +//! means that if one task is blocked on a channel, other tasks can continue to +//! make progress. +//! +//! Rust channels come in one of two flavors: +//! +//! 1. An asynchronous, infinitely buffered channel. The `channel()` function +//! will return a `(Sender, Receiver)` tuple where all sends will be +//! **asynchronous** (they never block). The channel conceptually has an +//! infinite buffer. +//! +//! 2. A synchronous, bounded channel. The `sync_channel()` function will return +//! a `(SyncSender, Receiver)` tuple where the storage for pending messages +//! is a pre-allocated buffer of a fixed size. All sends will be +//! **synchronous** by blocking until there is buffer space available. Note +//! that a bound of 0 is allowed, causing the channel to become a +//! "rendezvous" channel where each sender atomically hands off a message to +//! a receiver. +//! +//! ## Panic Propagation +//! +//! In addition to being a core primitive for communicating in rust, channels +//! are the points at which panics are propagated among tasks. Whenever the one +//! half of channel is closed, the other half will have its next operation +//! `panic!`. The purpose of this is to allow propagation of panics among tasks +//! that are linked to one another via channels. +//! +//! There are methods on both of senders and receivers to perform their +//! respective operations without panicking, however. +//! +//! # Example +//! +//! Simple usage: +//! +//! ``` +//! use std::thread::Thread; +//! +//! // Create a simple streaming channel +//! let (tx, rx) = channel(); +//! Thread::spawn(move|| { +//! tx.send(10i); +//! }).detach(); +//! assert_eq!(rx.recv(), 10i); +//! ``` +//! +//! Shared usage: +//! +//! ``` +//! use std::thread::Thread; +//! +//! // Create a shared channel that can be sent along from many threads +//! // where tx is the sending half (tx for transmission), and rx is the receiving +//! // half (rx for receiving). +//! let (tx, rx) = channel(); +//! for i in range(0i, 10i) { +//! let tx = tx.clone(); +//! Thread::spawn(move|| { +//! tx.send(i); +//! }).detach() +//! } +//! +//! for _ in range(0i, 10i) { +//! let j = rx.recv(); +//! assert!(0 <= j && j < 10); +//! } +//! ``` +//! +//! Propagating panics: +//! +//! ```should_fail +//! // The call to recv() will panic!() because the channel has already hung +//! // up (or been deallocated) +//! let (tx, rx) = channel::<int>(); +//! drop(tx); +//! rx.recv(); +//! ``` +//! +//! Synchronous channels: +//! +//! ``` +//! use std::thread::Thread; +//! +//! let (tx, rx) = sync_channel::<int>(0); +//! Thread::spawn(move|| { +//! // This will wait for the parent task to start receiving +//! tx.send(53); +//! }).detach(); +//! rx.recv(); +//! ``` +//! +//! Reading from a channel with a timeout requires to use a Timer together +//! with the channel. You can use the select! macro to select either and +//! handle the timeout case. This first example will break out of the loop +//! after 10 seconds no matter what: +//! +//! ```no_run +//! use std::io::timer::Timer; +//! use std::time::Duration; +//! +//! let (tx, rx) = channel::<int>(); +//! let mut timer = Timer::new().unwrap(); +//! let timeout = timer.oneshot(Duration::seconds(10)); +//! +//! loop { +//! select! { +//! val = rx.recv() => println!("Received {}", val), +//! () = timeout.recv() => { +//! println!("timed out, total time was more than 10 seconds"); +//! break; +//! } +//! } +//! } +//! ``` +//! +//! This second example is more costly since it allocates a new timer every +//! time a message is received, but it allows you to timeout after the channel +//! has been inactive for 5 seconds: +//! +//! ```no_run +//! use std::io::timer::Timer; +//! use std::time::Duration; +//! +//! let (tx, rx) = channel::<int>(); +//! let mut timer = Timer::new().unwrap(); +//! +//! loop { +//! let timeout = timer.oneshot(Duration::seconds(5)); +//! +//! select! { +//! val = rx.recv() => println!("Received {}", val), +//! () = timeout.recv() => { +//! println!("timed out, no message received in 5 seconds"); +//! break; +//! } +//! } +//! } +//! ``` + +// A description of how Rust's channel implementation works +// +// Channels are supposed to be the basic building block for all other +// concurrent primitives that are used in Rust. As a result, the channel type +// needs to be highly optimized, flexible, and broad enough for use everywhere. +// +// The choice of implementation of all channels is to be built on lock-free data +// structures. The channels themselves are then consequently also lock-free data +// structures. As always with lock-free code, this is a very "here be dragons" +// territory, especially because I'm unaware of any academic papers that have +// gone into great length about channels of these flavors. +// +// ## Flavors of channels +// +// From the perspective of a consumer of this library, there is only one flavor +// of channel. This channel can be used as a stream and cloned to allow multiple +// senders. Under the hood, however, there are actually three flavors of +// channels in play. +// +// * Oneshots - these channels are highly optimized for the one-send use case. +// They contain as few atomics as possible and involve one and +// exactly one allocation. +// * Streams - these channels are optimized for the non-shared use case. They +// use a different concurrent queue that is more tailored for this +// use case. The initial allocation of this flavor of channel is not +// optimized. +// * Shared - this is the most general form of channel that this module offers, +// a channel with multiple senders. This type is as optimized as it +// can be, but the previous two types mentioned are much faster for +// their use-cases. +// +// ## Concurrent queues +// +// The basic idea of Rust's Sender/Receiver types is that send() never blocks, but +// recv() obviously blocks. This means that under the hood there must be some +// shared and concurrent queue holding all of the actual data. +// +// With two flavors of channels, two flavors of queues are also used. We have +// chosen to use queues from a well-known author that are abbreviated as SPSC +// and MPSC (single producer, single consumer and multiple producer, single +// consumer). SPSC queues are used for streams while MPSC queues are used for +// shared channels. +// +// ### SPSC optimizations +// +// The SPSC queue found online is essentially a linked list of nodes where one +// half of the nodes are the "queue of data" and the other half of nodes are a +// cache of unused nodes. The unused nodes are used such that an allocation is +// not required on every push() and a free doesn't need to happen on every +// pop(). +// +// As found online, however, the cache of nodes is of an infinite size. This +// means that if a channel at one point in its life had 50k items in the queue, +// then the queue will always have the capacity for 50k items. I believed that +// this was an unnecessary limitation of the implementation, so I have altered +// the queue to optionally have a bound on the cache size. +// +// By default, streams will have an unbounded SPSC queue with a small-ish cache +// size. The hope is that the cache is still large enough to have very fast +// send() operations while not too large such that millions of channels can +// coexist at once. +// +// ### MPSC optimizations +// +// Right now the MPSC queue has not been optimized. Like the SPSC queue, it uses +// a linked list under the hood to earn its unboundedness, but I have not put +// forth much effort into having a cache of nodes similar to the SPSC queue. +// +// For now, I believe that this is "ok" because shared channels are not the most +// common type, but soon we may wish to revisit this queue choice and determine +// another candidate for backend storage of shared channels. +// +// ## Overview of the Implementation +// +// Now that there's a little background on the concurrent queues used, it's +// worth going into much more detail about the channels themselves. The basic +// pseudocode for a send/recv are: +// +// +// send(t) recv() +// queue.push(t) return if queue.pop() +// if increment() == -1 deschedule { +// wakeup() if decrement() > 0 +// cancel_deschedule() +// } +// queue.pop() +// +// As mentioned before, there are no locks in this implementation, only atomic +// instructions are used. +// +// ### The internal atomic counter +// +// Every channel has a shared counter with each half to keep track of the size +// of the queue. This counter is used to abort descheduling by the receiver and +// to know when to wake up on the sending side. +// +// As seen in the pseudocode, senders will increment this count and receivers +// will decrement the count. The theory behind this is that if a sender sees a +// -1 count, it will wake up the receiver, and if the receiver sees a 1+ count, +// then it doesn't need to block. +// +// The recv() method has a beginning call to pop(), and if successful, it needs +// to decrement the count. It is a crucial implementation detail that this +// decrement does *not* happen to the shared counter. If this were the case, +// then it would be possible for the counter to be very negative when there were +// no receivers waiting, in which case the senders would have to determine when +// it was actually appropriate to wake up a receiver. +// +// Instead, the "steal count" is kept track of separately (not atomically +// because it's only used by receivers), and then the decrement() call when +// descheduling will lump in all of the recent steals into one large decrement. +// +// The implication of this is that if a sender sees a -1 count, then there's +// guaranteed to be a waiter waiting! +// +// ## Native Implementation +// +// A major goal of these channels is to work seamlessly on and off the runtime. +// All of the previous race conditions have been worded in terms of +// scheduler-isms (which is obviously not available without the runtime). +// +// For now, native usage of channels (off the runtime) will fall back onto +// mutexes/cond vars for descheduling/atomic decisions. The no-contention path +// is still entirely lock-free, the "deschedule" blocks above are surrounded by +// a mutex and the "wakeup" blocks involve grabbing a mutex and signaling on a +// condition variable. +// +// ## Select +// +// Being able to support selection over channels has greatly influenced this +// design, and not only does selection need to work inside the runtime, but also +// outside the runtime. +// +// The implementation is fairly straightforward. The goal of select() is not to +// return some data, but only to return which channel can receive data without +// blocking. The implementation is essentially the entire blocking procedure +// followed by an increment as soon as its woken up. The cancellation procedure +// involves an increment and swapping out of to_wake to acquire ownership of the +// task to unblock. +// +// Sadly this current implementation requires multiple allocations, so I have +// seen the throughput of select() be much worse than it should be. I do not +// believe that there is anything fundamental that needs to change about these +// channels, however, in order to support a more efficient select(). +// +// # Conclusion +// +// And now that you've seen all the races that I found and attempted to fix, +// here's the code for you to find some more! + +use core::prelude::*; + +pub use self::TryRecvError::*; +pub use self::TrySendError::*; +use self::Flavor::*; + +use alloc::arc::Arc; +use core::kinds::marker; +use core::mem; +use core::cell::UnsafeCell; + +pub use self::select::{Select, Handle}; +use self::select::StartResult; +use self::select::StartResult::*; +use self::blocking::SignalToken; + +macro_rules! test { + { fn $name:ident() $b:block $(#[$a:meta])*} => ( + mod $name { + #![allow(unused_imports)] + + use prelude::*; + use rt; + + use comm::*; + use super::*; + use thread::Thread; + + $(#[$a])* #[test] fn f() { $b } + } + ) +} + +mod blocking; +mod oneshot; +mod select; +mod shared; +mod stream; +mod sync; +mod mpsc_queue; +mod spsc_queue; + +/// The receiving-half of Rust's channel type. This half can only be owned by +/// one task +#[unstable] +pub struct Receiver<T> { + inner: UnsafeCell<Flavor<T>>, + // can't share in an arc + _marker: marker::NoSync, +} + +/// An iterator over messages on a receiver, this iterator will block +/// whenever `next` is called, waiting for a new message, and `None` will be +/// returned when the corresponding channel has hung up. +#[unstable] +pub struct Messages<'a, T:'a> { + rx: &'a Receiver<T> +} + +/// The sending-half of Rust's asynchronous channel type. This half can only be +/// owned by one task, but it can be cloned to send to other tasks. +#[unstable] +pub struct Sender<T> { + inner: UnsafeCell<Flavor<T>>, + // can't share in an arc + _marker: marker::NoSync, +} + +/// The sending-half of Rust's synchronous channel type. This half can only be +/// owned by one task, but it can be cloned to send to other tasks. +#[unstable = "this type may be renamed, but it will always exist"] +pub struct SyncSender<T> { + inner: Arc<UnsafeCell<sync::Packet<T>>>, + // can't share in an arc + _marker: marker::NoSync, +} + +/// This enumeration is the list of the possible reasons that try_recv could not +/// return data when called. +#[deriving(PartialEq, Clone, Copy, Show)] +#[experimental = "this is likely to be removed in changing try_recv()"] +pub enum TryRecvError { + /// This channel is currently empty, but the sender(s) have not yet + /// disconnected, so data may yet become available. + Empty, + /// This channel's sending half has become disconnected, and there will + /// never be any more data received on this channel + Disconnected, +} + +/// This enumeration is the list of the possible error outcomes for the +/// `SyncSender::try_send` method. +#[deriving(PartialEq, Clone, Show)] +#[experimental = "this is likely to be removed in changing try_send()"] +pub enum TrySendError<T> { + /// The data could not be sent on the channel because it would require that + /// the callee block to send the data. + /// + /// If this is a buffered channel, then the buffer is full at this time. If + /// this is not a buffered channel, then there is no receiver available to + /// acquire the data. + Full(T), + /// This channel's receiving half has disconnected, so the data could not be + /// sent. The data is returned back to the callee in this case. + RecvDisconnected(T), +} + +enum Flavor<T> { + Oneshot(Arc<UnsafeCell<oneshot::Packet<T>>>), + Stream(Arc<UnsafeCell<stream::Packet<T>>>), + Shared(Arc<UnsafeCell<shared::Packet<T>>>), + Sync(Arc<UnsafeCell<sync::Packet<T>>>), +} + +#[doc(hidden)] +trait UnsafeFlavor<T> { + fn inner_unsafe<'a>(&'a self) -> &'a UnsafeCell<Flavor<T>>; + unsafe fn inner_mut<'a>(&'a self) -> &'a mut Flavor<T> { + &mut *self.inner_unsafe().get() + } + unsafe fn inner<'a>(&'a self) -> &'a Flavor<T> { + &*self.inner_unsafe().get() + } +} +impl<T> UnsafeFlavor<T> for Sender<T> { + fn inner_unsafe<'a>(&'a self) -> &'a UnsafeCell<Flavor<T>> { + &self.inner + } +} +impl<T> UnsafeFlavor<T> for Receiver<T> { + fn inner_unsafe<'a>(&'a self) -> &'a UnsafeCell<Flavor<T>> { + &self.inner + } +} + +/// Creates a new asynchronous channel, returning the sender/receiver halves. +/// +/// All data sent on the sender will become available on the receiver, and no +/// send will block the calling task (this channel has an "infinite buffer"). +/// +/// # Example +/// +/// ``` +/// use std::thread::Thread; +/// +/// // tx is is the sending half (tx for transmission), and rx is the receiving +/// // half (rx for receiving). +/// let (tx, rx) = channel(); +/// +/// // Spawn off an expensive computation +/// Thread::spawn(move|| { +/// # fn expensive_computation() {} +/// tx.send(expensive_computation()); +/// }).detach(); +/// +/// // Do some useful work for awhile +/// +/// // Let's see what that answer was +/// println!("{}", rx.recv()); +/// ``` +#[unstable] +pub fn channel<T: Send>() -> (Sender<T>, Receiver<T>) { + let a = Arc::new(UnsafeCell::new(oneshot::Packet::new())); + (Sender::new(Oneshot(a.clone())), Receiver::new(Oneshot(a))) +} + +/// Creates a new synchronous, bounded channel. +/// +/// Like asynchronous channels, the `Receiver` will block until a message +/// becomes available. These channels differ greatly in the semantics of the +/// sender from asynchronous channels, however. +/// +/// This channel has an internal buffer on which messages will be queued. When +/// the internal buffer becomes full, future sends will *block* waiting for the +/// buffer to open up. Note that a buffer size of 0 is valid, in which case this +/// becomes "rendezvous channel" where each send will not return until a recv +/// is paired with it. +/// +/// As with asynchronous channels, all senders will panic in `send` if the +/// `Receiver` has been destroyed. +/// +/// # Example +/// +/// ``` +/// use std::thread::Thread; +/// +/// let (tx, rx) = sync_channel(1); +/// +/// // this returns immediately +/// tx.send(1i); +/// +/// Thread::spawn(move|| { +/// // this will block until the previous message has been received +/// tx.send(2i); +/// }).detach(); +/// +/// assert_eq!(rx.recv(), 1i); +/// assert_eq!(rx.recv(), 2i); +/// ``` +#[unstable = "this function may be renamed to more accurately reflect the type \ + of channel that is is creating"] +pub fn sync_channel<T: Send>(bound: uint) -> (SyncSender<T>, Receiver<T>) { + let a = Arc::new(UnsafeCell::new(sync::Packet::new(bound))); + (SyncSender::new(a.clone()), Receiver::new(Sync(a))) +} + +//////////////////////////////////////////////////////////////////////////////// +// Sender +//////////////////////////////////////////////////////////////////////////////// + +impl<T: Send> Sender<T> { + fn new(inner: Flavor<T>) -> Sender<T> { + Sender { + inner: UnsafeCell::new(inner), + _marker: marker::NoSync, + } + } + + /// Sends a value along this channel to be received by the corresponding + /// receiver. + /// + /// Rust channels are infinitely buffered so this method will never block. + /// + /// # Panics + /// + /// This function will panic if the other end of the channel has hung up. + /// This means that if the corresponding receiver has fallen out of scope, + /// this function will trigger a panic message saying that a message is + /// being sent on a closed channel. + /// + /// Note that if this function does *not* panic, it does not mean that the + /// data will be successfully received. All sends are placed into a queue, + /// so it is possible for a send to succeed (the other end is alive), but + /// then the other end could immediately disconnect. + /// + /// The purpose of this functionality is to propagate panics among tasks. + /// If a panic is not desired, then consider using the `send_opt` method + #[experimental = "this function is being considered candidate for removal \ + to adhere to the general guidelines of rust"] + pub fn send(&self, t: T) { + if self.send_opt(t).is_err() { + panic!("sending on a closed channel"); + } + } + + /// Attempts to send a value on this channel, returning it back if it could + /// not be sent. + /// + /// A successful send occurs when it is determined that the other end of + /// the channel has not hung up already. An unsuccessful send would be one + /// where the corresponding receiver has already been deallocated. Note + /// that a return value of `Err` means that the data will never be + /// received, but a return value of `Ok` does *not* mean that the data + /// will be received. It is possible for the corresponding receiver to + /// hang up immediately after this function returns `Ok`. + /// + /// Like `send`, this method will never block. + /// + /// # Panics + /// + /// This method will never panic, it will return the message back to the + /// caller if the other end is disconnected + /// + /// # Example + /// + /// ``` + /// let (tx, rx) = channel(); + /// + /// // This send is always successful + /// assert_eq!(tx.send_opt(1i), Ok(())); + /// + /// // This send will fail because the receiver is gone + /// drop(rx); + /// assert_eq!(tx.send_opt(1i), Err(1)); + /// ``` + #[unstable = "this function may be renamed to send() in the future"] + pub fn send_opt(&self, t: T) -> Result<(), T> { + let (new_inner, ret) = match *unsafe { self.inner() } { + Oneshot(ref p) => { + unsafe { + let p = p.get(); + if !(*p).sent() { + return (*p).send(t); + } else { + let a = Arc::new(UnsafeCell::new(stream::Packet::new())); + match (*p).upgrade(Receiver::new(Stream(a.clone()))) { + oneshot::UpSuccess => { + let ret = (*a.get()).send(t); + (a, ret) + } + oneshot::UpDisconnected => (a, Err(t)), + oneshot::UpWoke(token) => { + // This send cannot panic because the thread is + // asleep (we're looking at it), so the receiver + // can't go away. + (*a.get()).send(t).ok().unwrap(); + token.signal(); + (a, Ok(())) + } + } + } + } + } + Stream(ref p) => return unsafe { (*p.get()).send(t) }, + Shared(ref p) => return unsafe { (*p.get()).send(t) }, + Sync(..) => unreachable!(), + }; + + unsafe { + let tmp = Sender::new(Stream(new_inner)); + mem::swap(self.inner_mut(), tmp.inner_mut()); + } + return ret; + } +} + +#[stable] +impl<T: Send> Clone for Sender<T> { + fn clone(&self) -> Sender<T> { + let (packet, sleeper, guard) = match *unsafe { self.inner() } { + Oneshot(ref p) => { + let a = Arc::new(UnsafeCell::new(shared::Packet::new())); + unsafe { + let guard = (*a.get()).postinit_lock(); + match (*p.get()).upgrade(Receiver::new(Shared(a.clone()))) { + oneshot::UpSuccess | + oneshot::UpDisconnected => (a, None, guard), + oneshot::UpWoke(task) => (a, Some(task), guard) + } + } + } + Stream(ref p) => { + let a = Arc::new(UnsafeCell::new(shared::Packet::new())); + unsafe { + let guard = (*a.get()).postinit_lock(); + match (*p.get()).upgrade(Receiver::new(Shared(a.clone()))) { + stream::UpSuccess | + stream::UpDisconnected => (a, None, guard), + stream::UpWoke(task) => (a, Some(task), guard), + } + } + } + Shared(ref p) => { + unsafe { (*p.get()).clone_chan(); } + return Sender::new(Shared(p.clone())); + } + Sync(..) => unreachable!(), + }; + + unsafe { + (*packet.get()).inherit_blocker(sleeper, guard); + + let tmp = Sender::new(Shared(packet.clone())); + mem::swap(self.inner_mut(), tmp.inner_mut()); + } + Sender::new(Shared(packet)) + } +} + +#[unsafe_destructor] +impl<T: Send> Drop for Sender<T> { + fn drop(&mut self) { + match *unsafe { self.inner_mut() } { + Oneshot(ref mut p) => unsafe { (*p.get()).drop_chan(); }, + Stream(ref mut p) => unsafe { (*p.get()).drop_chan(); }, + Shared(ref mut p) => unsafe { (*p.get()).drop_chan(); }, + Sync(..) => unreachable!(), + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// SyncSender +//////////////////////////////////////////////////////////////////////////////// + +impl<T: Send> SyncSender<T> { + fn new(inner: Arc<UnsafeCell<sync::Packet<T>>>) -> SyncSender<T> { + SyncSender { inner: inner, _marker: marker::NoSync } + } + + /// Sends a value on this synchronous channel. + /// + /// This function will *block* until space in the internal buffer becomes + /// available or a receiver is available to hand off the message to. + /// + /// Note that a successful send does *not* guarantee that the receiver will + /// ever see the data if there is a buffer on this channel. Messages may be + /// enqueued in the internal buffer for the receiver to receive at a later + /// time. If the buffer size is 0, however, it can be guaranteed that the + /// receiver has indeed received the data if this function returns success. + /// + /// # Panics + /// + /// Similarly to `Sender::send`, this function will panic if the + /// corresponding `Receiver` for this channel has disconnected. This + /// behavior is used to propagate panics among tasks. + /// + /// If a panic is not desired, you can achieve the same semantics with the + /// `SyncSender::send_opt` method which will not panic if the receiver + /// disconnects. + #[experimental = "this function is being considered candidate for removal \ + to adhere to the general guidelines of rust"] + pub fn send(&self, t: T) { + if self.send_opt(t).is_err() { + panic!("sending on a closed channel"); + } + } + + /// Send a value on a channel, returning it back if the receiver + /// disconnected + /// + /// This method will *block* to send the value `t` on the channel, but if + /// the value could not be sent due to the receiver disconnecting, the value + /// is returned back to the callee. This function is similar to `try_send`, + /// except that it will block if the channel is currently full. + /// + /// # Panics + /// + /// This function cannot panic. + #[unstable = "this function may be renamed to send() in the future"] + pub fn send_opt(&self, t: T) -> Result<(), T> { + unsafe { (*self.inner.get()).send(t) } + } + + /// Attempts to send a value on this channel without blocking. + /// + /// This method differs from `send_opt` by returning immediately if the + /// channel's buffer is full or no receiver is waiting to acquire some + /// data. Compared with `send_opt`, this function has two failure cases + /// instead of one (one for disconnection, one for a full buffer). + /// + /// See `SyncSender::send` for notes about guarantees of whether the + /// receiver has received the data or not if this function is successful. + /// + /// # Panics + /// + /// This function cannot panic + #[unstable = "the return type of this function is candidate for \ + modification"] + pub fn try_send(&self, t: T) -> Result<(), TrySendError<T>> { + unsafe { (*self.inner.get()).try_send(t) } + } +} + +#[stable] +impl<T: Send> Clone for SyncSender<T> { + fn clone(&self) -> SyncSender<T> { + unsafe { (*self.inner.get()).clone_chan(); } + return SyncSender::new(self.inner.clone()); + } +} + +#[unsafe_destructor] +impl<T: Send> Drop for SyncSender<T> { + fn drop(&mut self) { + unsafe { (*self.inner.get()).drop_chan(); } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Receiver +//////////////////////////////////////////////////////////////////////////////// + +impl<T: Send> Receiver<T> { + fn new(inner: Flavor<T>) -> Receiver<T> { + Receiver { inner: UnsafeCell::new(inner), _marker: marker::NoSync } + } + + /// Blocks waiting for a value on this receiver + /// + /// This function will block if necessary to wait for a corresponding send + /// on the channel from its paired `Sender` structure. This receiver will + /// be woken up when data is ready, and the data will be returned. + /// + /// # Panics + /// + /// Similar to channels, this method will trigger a task panic if the + /// other end of the channel has hung up (been deallocated). The purpose of + /// this is to propagate panics among tasks. + /// + /// If a panic is not desired, then there are two options: + /// + /// * If blocking is still desired, the `recv_opt` method will return `None` + /// when the other end hangs up + /// + /// * If blocking is not desired, then the `try_recv` method will attempt to + /// peek at a value on this receiver. + #[experimental = "this function is being considered candidate for removal \ + to adhere to the general guidelines of rust"] + pub fn recv(&self) -> T { + match self.recv_opt() { + Ok(t) => t, + Err(()) => panic!("receiving on a closed channel"), + } + } + + /// Attempts to return a pending value on this receiver without blocking + /// + /// This method will never block the caller in order to wait for data to + /// become available. Instead, this will always return immediately with a + /// possible option of pending data on the channel. + /// + /// This is useful for a flavor of "optimistic check" before deciding to + /// block on a receiver. + /// + /// # Panics + /// + /// This function cannot panic. + #[unstable = "the return type of this function may be altered"] + pub fn try_recv(&self) -> Result<T, TryRecvError> { + loop { + let new_port = match *unsafe { self.inner() } { + Oneshot(ref p) => { + match unsafe { (*p.get()).try_recv() } { + Ok(t) => return Ok(t), + Err(oneshot::Empty) => return Err(Empty), + Err(oneshot::Disconnected) => return Err(Disconnected), + Err(oneshot::Upgraded(rx)) => rx, + } + } + Stream(ref p) => { + match unsafe { (*p.get()).try_recv() } { + Ok(t) => return Ok(t), + Err(stream::Empty) => return Err(Empty), + Err(stream::Disconnected) => return Err(Disconnected), + Err(stream::Upgraded(rx)) => rx, + } + } + Shared(ref p) => { + match unsafe { (*p.get()).try_recv() } { + Ok(t) => return Ok(t), + Err(shared::Empty) => return Err(Empty), + Err(shared::Disconnected) => return Err(Disconnected), + } + } + Sync(ref p) => { + match unsafe { (*p.get()).try_recv() } { + Ok(t) => return Ok(t), + Err(sync::Empty) => return Err(Empty), + Err(sync::Disconnected) => return Err(Disconnected), + } + } + }; + unsafe { + mem::swap(self.inner_mut(), + new_port.inner_mut()); + } + } + } + + /// Attempt to wait for a value on this receiver, but does not panic if the + /// corresponding channel has hung up. + /// + /// This implementation of iterators for ports will always block if there is + /// not data available on the receiver, but it will not panic in the case + /// that the channel has been deallocated. + /// + /// In other words, this function has the same semantics as the `recv` + /// method except for the panic aspect. + /// + /// If the channel has hung up, then `Err` is returned. Otherwise `Ok` of + /// the value found on the receiver is returned. + #[unstable = "this function may be renamed to recv()"] + pub fn recv_opt(&self) -> Result<T, ()> { + loop { + let new_port = match *unsafe { self.inner() } { + Oneshot(ref p) => { + match unsafe { (*p.get()).recv() } { + Ok(t) => return Ok(t), + Err(oneshot::Empty) => return unreachable!(), + Err(oneshot::Disconnected) => return Err(()), + Err(oneshot::Upgraded(rx)) => rx, + } + } + Stream(ref p) => { + match unsafe { (*p.get()).recv() } { + Ok(t) => return Ok(t), + Err(stream::Empty) => return unreachable!(), + Err(stream::Disconnected) => return Err(()), + Err(stream::Upgraded(rx)) => rx, + } + } + Shared(ref p) => { + match unsafe { (*p.get()).recv() } { + Ok(t) => return Ok(t), + Err(shared::Empty) => return unreachable!(), + Err(shared::Disconnected) => return Err(()), + } + } + Sync(ref p) => return unsafe { (*p.get()).recv() } + }; + unsafe { + mem::swap(self.inner_mut(), new_port.inner_mut()); + } + } + } + + /// Returns an iterator that will block waiting for messages, but never + /// `panic!`. It will return `None` when the channel has hung up. + #[unstable] + pub fn iter<'a>(&'a self) -> Messages<'a, T> { + Messages { rx: self } + } +} + +impl<T: Send> select::Packet for Receiver<T> { + fn can_recv(&self) -> bool { + loop { + let new_port = match *unsafe { self.inner() } { + Oneshot(ref p) => { + match unsafe { (*p.get()).can_recv() } { + Ok(ret) => return ret, + Err(upgrade) => upgrade, + } + } + Stream(ref p) => { + match unsafe { (*p.get()).can_recv() } { + Ok(ret) => return ret, + Err(upgrade) => upgrade, + } + } + Shared(ref p) => { + return unsafe { (*p.get()).can_recv() }; + } + Sync(ref p) => { + return unsafe { (*p.get()).can_recv() }; + } + }; + unsafe { + mem::swap(self.inner_mut(), + new_port.inner_mut()); + } + } + } + + fn start_selection(&self, mut token: SignalToken) -> StartResult { + loop { + let (t, new_port) = match *unsafe { self.inner() } { + Oneshot(ref p) => { + match unsafe { (*p.get()).start_selection(token) } { + oneshot::SelSuccess => return Installed, + oneshot::SelCanceled => return Abort, + oneshot::SelUpgraded(t, rx) => (t, rx), + } + } + Stream(ref p) => { + match unsafe { (*p.get()).start_selection(token) } { + stream::SelSuccess => return Installed, + stream::SelCanceled => return Abort, + stream::SelUpgraded(t, rx) => (t, rx), + } + } + Shared(ref p) => { + return unsafe { (*p.get()).start_selection(token) }; + } + Sync(ref p) => { + return unsafe { (*p.get()).start_selection(token) }; + } + }; + token = t; + unsafe { + mem::swap(self.inner_mut(), new_port.inner_mut()); + } + } + } + + fn abort_selection(&self) -> bool { + let mut was_upgrade = false; + loop { + let result = match *unsafe { self.inner() } { + Oneshot(ref p) => unsafe { (*p.get()).abort_selection() }, + Stream(ref p) => unsafe { + (*p.get()).abort_selection(was_upgrade) + }, + Shared(ref p) => return unsafe { + (*p.get()).abort_selection(was_upgrade) + }, + Sync(ref p) => return unsafe { + (*p.get()).abort_selection() + }, + }; + let new_port = match result { Ok(b) => return b, Err(p) => p }; + was_upgrade = true; + unsafe { + mem::swap(self.inner_mut(), + new_port.inner_mut()); + } + } + } +} + +#[unstable] +impl<'a, T: Send> Iterator<T> for Messages<'a, T> { + fn next(&mut self) -> Option<T> { self.rx.recv_opt().ok() } +} + +#[unsafe_destructor] +impl<T: Send> Drop for Receiver<T> { + fn drop(&mut self) { + match *unsafe { self.inner_mut() } { + Oneshot(ref mut p) => unsafe { (*p.get()).drop_port(); }, + Stream(ref mut p) => unsafe { (*p.get()).drop_port(); }, + Shared(ref mut p) => unsafe { (*p.get()).drop_port(); }, + Sync(ref mut p) => unsafe { (*p.get()).drop_port(); }, + } + } +} + +#[cfg(test)] +mod test { + use prelude::*; + + use os; + use super::*; + + pub fn stress_factor() -> uint { + match os::getenv("RUST_TEST_STRESS") { + Some(val) => from_str::<uint>(val.as_slice()).unwrap(), + None => 1, + } + } + + test! { fn smoke() { + let (tx, rx) = channel::<int>(); + tx.send(1); + assert_eq!(rx.recv(), 1); + } } + + test! { fn drop_full() { + let (tx, _rx) = channel(); + tx.send(box 1i); + } } + + test! { fn drop_full_shared() { + let (tx, _rx) = channel(); + drop(tx.clone()); + drop(tx.clone()); + tx.send(box 1i); + } } + + test! { fn smoke_shared() { + let (tx, rx) = channel::<int>(); + tx.send(1); + assert_eq!(rx.recv(), 1); + let tx = tx.clone(); + tx.send(1); + assert_eq!(rx.recv(), 1); + } } + + test! { fn smoke_threads() { + let (tx, rx) = channel::<int>(); + spawn(move|| { + tx.send(1); + }); + assert_eq!(rx.recv(), 1); + } } + + test! { fn smoke_port_gone() { + let (tx, rx) = channel::<int>(); + drop(rx); + tx.send(1); + } #[should_fail] } + + test! { fn smoke_shared_port_gone() { + let (tx, rx) = channel::<int>(); + drop(rx); + tx.send(1); + } #[should_fail] } + + test! { fn smoke_shared_port_gone2() { + let (tx, rx) = channel::<int>(); + drop(rx); + let tx2 = tx.clone(); + drop(tx); + tx2.send(1); + } #[should_fail] } + + test! { fn port_gone_concurrent() { + let (tx, rx) = channel::<int>(); + spawn(move|| { + rx.recv(); + }); + loop { tx.send(1) } + } #[should_fail] } + + test! { fn port_gone_concurrent_shared() { + let (tx, rx) = channel::<int>(); + let tx2 = tx.clone(); + spawn(move|| { + rx.recv(); + }); + loop { + tx.send(1); + tx2.send(1); + } + } #[should_fail] } + + test! { fn smoke_chan_gone() { + let (tx, rx) = channel::<int>(); + drop(tx); + rx.recv(); + } #[should_fail] } + + test! { fn smoke_chan_gone_shared() { + let (tx, rx) = channel::<()>(); + let tx2 = tx.clone(); + drop(tx); + drop(tx2); + rx.recv(); + } #[should_fail] } + + test! { fn chan_gone_concurrent() { + let (tx, rx) = channel::<int>(); + spawn(move|| { + tx.send(1); + tx.send(1); + }); + loop { rx.recv(); } + } #[should_fail] } + + test! { fn stress() { + let (tx, rx) = channel::<int>(); + spawn(move|| { + for _ in range(0u, 10000) { tx.send(1i); } + }); + for _ in range(0u, 10000) { + assert_eq!(rx.recv(), 1); + } + } } + + test! { fn stress_shared() { + static AMT: uint = 10000; + static NTHREADS: uint = 8; + let (tx, rx) = channel::<int>(); + let (dtx, drx) = channel::<()>(); + + spawn(move|| { + for _ in range(0, AMT * NTHREADS) { + assert_eq!(rx.recv(), 1); + } + match rx.try_recv() { + Ok(..) => panic!(), + _ => {} + } + dtx.send(()); + }); + + for _ in range(0, NTHREADS) { + let tx = tx.clone(); + spawn(move|| { + for _ in range(0, AMT) { tx.send(1); } + }); + } + drop(tx); + drx.recv(); + } } + + #[test] + fn send_from_outside_runtime() { + let (tx1, rx1) = channel::<()>(); + let (tx2, rx2) = channel::<int>(); + let (tx3, rx3) = channel::<()>(); + let tx4 = tx3.clone(); + spawn(move|| { + tx1.send(()); + for _ in range(0i, 40) { + assert_eq!(rx2.recv(), 1); + } + tx3.send(()); + }); + rx1.recv(); + spawn(move|| { + for _ in range(0i, 40) { + tx2.send(1); + } + tx4.send(()); + }); + rx3.recv(); + rx3.recv(); + } + + #[test] + fn recv_from_outside_runtime() { + let (tx, rx) = channel::<int>(); + let (dtx, drx) = channel(); + spawn(move|| { + for _ in range(0i, 40) { + assert_eq!(rx.recv(), 1); + } + dtx.send(()); + }); + for _ in range(0u, 40) { + tx.send(1); + } + drx.recv(); + } + + #[test] + fn no_runtime() { + let (tx1, rx1) = channel::<int>(); + let (tx2, rx2) = channel::<int>(); + let (tx3, rx3) = channel::<()>(); + let tx4 = tx3.clone(); + spawn(move|| { + assert_eq!(rx1.recv(), 1); + tx2.send(2); + tx4.send(()); + }); + spawn(move|| { + tx1.send(1); + assert_eq!(rx2.recv(), 2); + tx3.send(()); + }); + rx3.recv(); + rx3.recv(); + } + + test! { fn oneshot_single_thread_close_port_first() { + // Simple test of closing without sending + let (_tx, rx) = channel::<int>(); + drop(rx); + } } + + test! { fn oneshot_single_thread_close_chan_first() { + // Simple test of closing without sending + let (tx, _rx) = channel::<int>(); + drop(tx); + } } + + test! { fn oneshot_single_thread_send_port_close() { + // Testing that the sender cleans up the payload if receiver is closed + let (tx, rx) = channel::<Box<int>>(); + drop(rx); + tx.send(box 0); + } #[should_fail] } + + test! { fn oneshot_single_thread_recv_chan_close() { + // Receiving on a closed chan will panic + let res = Thread::spawn(move|| { + let (tx, rx) = channel::<int>(); + drop(tx); + rx.recv(); + }).join(); + // What is our res? + assert!(res.is_err()); + } } + + test! { fn oneshot_single_thread_send_then_recv() { + let (tx, rx) = channel::<Box<int>>(); + tx.send(box 10); + assert!(rx.recv() == box 10); + } } + + test! { fn oneshot_single_thread_try_send_open() { + let (tx, rx) = channel::<int>(); + assert!(tx.send_opt(10).is_ok()); + assert!(rx.recv() == 10); + } } + + test! { fn oneshot_single_thread_try_send_closed() { + let (tx, rx) = channel::<int>(); + drop(rx); + assert!(tx.send_opt(10).is_err()); + } } + + test! { fn oneshot_single_thread_try_recv_open() { + let (tx, rx) = channel::<int>(); + tx.send(10); + assert!(rx.recv_opt() == Ok(10)); + } } + + test! { fn oneshot_single_thread_try_recv_closed() { + let (tx, rx) = channel::<int>(); + drop(tx); + assert!(rx.recv_opt() == Err(())); + } } + + test! { fn oneshot_single_thread_peek_data() { + let (tx, rx) = channel::<int>(); + assert_eq!(rx.try_recv(), Err(Empty)); + tx.send(10); + assert_eq!(rx.try_recv(), Ok(10)); + } } + + test! { fn oneshot_single_thread_peek_close() { + let (tx, rx) = channel::<int>(); + drop(tx); + assert_eq!(rx.try_recv(), Err(Disconnected)); + assert_eq!(rx.try_recv(), Err(Disconnected)); + } } + + test! { fn oneshot_single_thread_peek_open() { + let (_tx, rx) = channel::<int>(); + assert_eq!(rx.try_recv(), Err(Empty)); + } } + + test! { fn oneshot_multi_task_recv_then_send() { + let (tx, rx) = channel::<Box<int>>(); + spawn(move|| { + assert!(rx.recv() == box 10); + }); + + tx.send(box 10); + } } + + test! { fn oneshot_multi_task_recv_then_close() { + let (tx, rx) = channel::<Box<int>>(); + spawn(move|| { + drop(tx); + }); + let res = Thread::spawn(move|| { + assert!(rx.recv() == box 10); + }).join(); + assert!(res.is_err()); + } } + + test! { fn oneshot_multi_thread_close_stress() { + for _ in range(0, stress_factor()) { + let (tx, rx) = channel::<int>(); + spawn(move|| { + drop(rx); + }); + drop(tx); + } + } } + + test! { fn oneshot_multi_thread_send_close_stress() { + for _ in range(0, stress_factor()) { + let (tx, rx) = channel::<int>(); + spawn(move|| { + drop(rx); + }); + let _ = Thread::spawn(move|| { + tx.send(1); + }).join(); + } + } } + + test! { fn oneshot_multi_thread_recv_close_stress() { + for _ in range(0, stress_factor()) { + let (tx, rx) = channel::<int>(); + spawn(move|| { + let res = Thread::spawn(move|| { + rx.recv(); + }).join(); + assert!(res.is_err()); + }); + spawn(move|| { + spawn(move|| { + drop(tx); + }); + }); + } + } } + + test! { fn oneshot_multi_thread_send_recv_stress() { + for _ in range(0, stress_factor()) { + let (tx, rx) = channel(); + spawn(move|| { + tx.send(box 10i); + }); + spawn(move|| { + assert!(rx.recv() == box 10i); + }); + } + } } + + test! { fn stream_send_recv_stress() { + for _ in range(0, stress_factor()) { + let (tx, rx) = channel(); + + send(tx, 0); + recv(rx, 0); + + fn send(tx: Sender<Box<int>>, i: int) { + if i == 10 { return } + + spawn(move|| { + tx.send(box i); + send(tx, i + 1); + }); + } + + fn recv(rx: Receiver<Box<int>>, i: int) { + if i == 10 { return } + + spawn(move|| { + assert!(rx.recv() == box i); + recv(rx, i + 1); + }); + } + } + } } + + test! { fn recv_a_lot() { + // Regression test that we don't run out of stack in scheduler context + let (tx, rx) = channel(); + for _ in range(0i, 10000) { tx.send(()); } + for _ in range(0i, 10000) { rx.recv(); } + } } + + test! { fn shared_chan_stress() { + let (tx, rx) = channel(); + let total = stress_factor() + 100; + for _ in range(0, total) { + let tx = tx.clone(); + spawn(move|| { + tx.send(()); + }); + } + + for _ in range(0, total) { + rx.recv(); + } + } } + + test! { fn test_nested_recv_iter() { + let (tx, rx) = channel::<int>(); + let (total_tx, total_rx) = channel::<int>(); + + spawn(move|| { + let mut acc = 0; + for x in rx.iter() { + acc += x; + } + total_tx.send(acc); + }); + + tx.send(3); + tx.send(1); + tx.send(2); + drop(tx); + assert_eq!(total_rx.recv(), 6); + } } + + test! { fn test_recv_iter_break() { + let (tx, rx) = channel::<int>(); + let (count_tx, count_rx) = channel(); + + spawn(move|| { + let mut count = 0; + for x in rx.iter() { + if count >= 3 { + break; + } else { + count += x; + } + } + count_tx.send(count); + }); + + tx.send(2); + tx.send(2); + tx.send(2); + let _ = tx.send_opt(2); + drop(tx); + assert_eq!(count_rx.recv(), 4); + } } + + test! { fn try_recv_states() { + let (tx1, rx1) = channel::<int>(); + let (tx2, rx2) = channel::<()>(); + let (tx3, rx3) = channel::<()>(); + spawn(move|| { + rx2.recv(); + tx1.send(1); + tx3.send(()); + rx2.recv(); + drop(tx1); + tx3.send(()); + }); + + assert_eq!(rx1.try_recv(), Err(Empty)); + tx2.send(()); + rx3.recv(); + assert_eq!(rx1.try_recv(), Ok(1)); + assert_eq!(rx1.try_recv(), Err(Empty)); + tx2.send(()); + rx3.recv(); + assert_eq!(rx1.try_recv(), Err(Disconnected)); + } } + + // This bug used to end up in a livelock inside of the Receiver destructor + // because the internal state of the Shared packet was corrupted + test! { fn destroy_upgraded_shared_port_when_sender_still_active() { + let (tx, rx) = channel(); + let (tx2, rx2) = channel(); + spawn(move|| { + rx.recv(); // wait on a oneshot + drop(rx); // destroy a shared + tx2.send(()); + }); + // make sure the other task has gone to sleep + for _ in range(0u, 5000) { Thread::yield_now(); } + + // upgrade to a shared chan and send a message + let t = tx.clone(); + drop(tx); + t.send(()); + + // wait for the child task to exit before we exit + rx2.recv(); + }} +} + +#[cfg(test)] +mod sync_tests { + use prelude::*; + use os; + + pub fn stress_factor() -> uint { + match os::getenv("RUST_TEST_STRESS") { + Some(val) => from_str::<uint>(val.as_slice()).unwrap(), + None => 1, + } + } + + test! { fn smoke() { + let (tx, rx) = sync_channel::<int>(1); + tx.send(1); + assert_eq!(rx.recv(), 1); + } } + + test! { fn drop_full() { + let (tx, _rx) = sync_channel(1); + tx.send(box 1i); + } } + + test! { fn smoke_shared() { + let (tx, rx) = sync_channel::<int>(1); + tx.send(1); + assert_eq!(rx.recv(), 1); + let tx = tx.clone(); + tx.send(1); + assert_eq!(rx.recv(), 1); + } } + + test! { fn smoke_threads() { + let (tx, rx) = sync_channel::<int>(0); + spawn(move|| { + tx.send(1); + }); + assert_eq!(rx.recv(), 1); + } } + + test! { fn smoke_port_gone() { + let (tx, rx) = sync_channel::<int>(0); + drop(rx); + tx.send(1); + } #[should_fail] } + + test! { fn smoke_shared_port_gone2() { + let (tx, rx) = sync_channel::<int>(0); + drop(rx); + let tx2 = tx.clone(); + drop(tx); + tx2.send(1); + } #[should_fail] } + + test! { fn port_gone_concurrent() { + let (tx, rx) = sync_channel::<int>(0); + spawn(move|| { + rx.recv(); + }); + loop { tx.send(1) } + } #[should_fail] } + + test! { fn port_gone_concurrent_shared() { + let (tx, rx) = sync_channel::<int>(0); + let tx2 = tx.clone(); + spawn(move|| { + rx.recv(); + }); + loop { + tx.send(1); + tx2.send(1); + } + } #[should_fail] } + + test! { fn smoke_chan_gone() { + let (tx, rx) = sync_channel::<int>(0); + drop(tx); + rx.recv(); + } #[should_fail] } + + test! { fn smoke_chan_gone_shared() { + let (tx, rx) = sync_channel::<()>(0); + let tx2 = tx.clone(); + drop(tx); + drop(tx2); + rx.recv(); + } #[should_fail] } + + test! { fn chan_gone_concurrent() { + let (tx, rx) = sync_channel::<int>(0); + spawn(move|| { + tx.send(1); + tx.send(1); + }); + loop { rx.recv(); } + } #[should_fail] } + + test! { fn stress() { + let (tx, rx) = sync_channel::<int>(0); + spawn(move|| { + for _ in range(0u, 10000) { tx.send(1); } + }); + for _ in range(0u, 10000) { + assert_eq!(rx.recv(), 1); + } + } } + + test! { fn stress_shared() { + static AMT: uint = 1000; + static NTHREADS: uint = 8; + let (tx, rx) = sync_channel::<int>(0); + let (dtx, drx) = sync_channel::<()>(0); + + spawn(move|| { + for _ in range(0, AMT * NTHREADS) { + assert_eq!(rx.recv(), 1); + } + match rx.try_recv() { + Ok(..) => panic!(), + _ => {} + } + dtx.send(()); + }); + + for _ in range(0, NTHREADS) { + let tx = tx.clone(); + spawn(move|| { + for _ in range(0, AMT) { tx.send(1); } + }); + } + drop(tx); + drx.recv(); + } } + + test! { fn oneshot_single_thread_close_port_first() { + // Simple test of closing without sending + let (_tx, rx) = sync_channel::<int>(0); + drop(rx); + } } + + test! { fn oneshot_single_thread_close_chan_first() { + // Simple test of closing without sending + let (tx, _rx) = sync_channel::<int>(0); + drop(tx); + } } + + test! { fn oneshot_single_thread_send_port_close() { + // Testing that the sender cleans up the payload if receiver is closed + let (tx, rx) = sync_channel::<Box<int>>(0); + drop(rx); + tx.send(box 0); + } #[should_fail] } + + test! { fn oneshot_single_thread_recv_chan_close() { + // Receiving on a closed chan will panic + let res = Thread::spawn(move|| { + let (tx, rx) = sync_channel::<int>(0); + drop(tx); + rx.recv(); + }).join(); + // What is our res? + assert!(res.is_err()); + } } + + test! { fn oneshot_single_thread_send_then_recv() { + let (tx, rx) = sync_channel::<Box<int>>(1); + tx.send(box 10); + assert!(rx.recv() == box 10); + } } + + test! { fn oneshot_single_thread_try_send_open() { + let (tx, rx) = sync_channel::<int>(1); + assert_eq!(tx.try_send(10), Ok(())); + assert!(rx.recv() == 10); + } } + + test! { fn oneshot_single_thread_try_send_closed() { + let (tx, rx) = sync_channel::<int>(0); + drop(rx); + assert_eq!(tx.try_send(10), Err(RecvDisconnected(10))); + } } + + test! { fn oneshot_single_thread_try_send_closed2() { + let (tx, _rx) = sync_channel::<int>(0); + assert_eq!(tx.try_send(10), Err(Full(10))); + } } + + test! { fn oneshot_single_thread_try_recv_open() { + let (tx, rx) = sync_channel::<int>(1); + tx.send(10); + assert!(rx.recv_opt() == Ok(10)); + } } + + test! { fn oneshot_single_thread_try_recv_closed() { + let (tx, rx) = sync_channel::<int>(0); + drop(tx); + assert!(rx.recv_opt() == Err(())); + } } + + test! { fn oneshot_single_thread_peek_data() { + let (tx, rx) = sync_channel::<int>(1); + assert_eq!(rx.try_recv(), Err(Empty)); + tx.send(10); + assert_eq!(rx.try_recv(), Ok(10)); + } } + + test! { fn oneshot_single_thread_peek_close() { + let (tx, rx) = sync_channel::<int>(0); + drop(tx); + assert_eq!(rx.try_recv(), Err(Disconnected)); + assert_eq!(rx.try_recv(), Err(Disconnected)); + } } + + test! { fn oneshot_single_thread_peek_open() { + let (_tx, rx) = sync_channel::<int>(0); + assert_eq!(rx.try_recv(), Err(Empty)); + } } + + test! { fn oneshot_multi_task_recv_then_send() { + let (tx, rx) = sync_channel::<Box<int>>(0); + spawn(move|| { + assert!(rx.recv() == box 10); + }); + + tx.send(box 10); + } } + + test! { fn oneshot_multi_task_recv_then_close() { + let (tx, rx) = sync_channel::<Box<int>>(0); + spawn(move|| { + drop(tx); + }); + let res = Thread::spawn(move|| { + assert!(rx.recv() == box 10); + }).join(); + assert!(res.is_err()); + } } + + test! { fn oneshot_multi_thread_close_stress() { + for _ in range(0, stress_factor()) { + let (tx, rx) = sync_channel::<int>(0); + spawn(move|| { + drop(rx); + }); + drop(tx); + } + } } + + test! { fn oneshot_multi_thread_send_close_stress() { + for _ in range(0, stress_factor()) { + let (tx, rx) = sync_channel::<int>(0); + spawn(move|| { + drop(rx); + }); + let _ = Thread::spawn(move || { + tx.send(1); + }).join(); + } + } } + + test! { fn oneshot_multi_thread_recv_close_stress() { + for _ in range(0, stress_factor()) { + let (tx, rx) = sync_channel::<int>(0); + spawn(move|| { + let res = Thread::spawn(move|| { + rx.recv(); + }).join(); + assert!(res.is_err()); + }); + spawn(move|| { + spawn(move|| { + drop(tx); + }); + }); + } + } } + + test! { fn oneshot_multi_thread_send_recv_stress() { + for _ in range(0, stress_factor()) { + let (tx, rx) = sync_channel::<Box<int>>(0); + spawn(move|| { + tx.send(box 10i); + }); + spawn(move|| { + assert!(rx.recv() == box 10i); + }); + } + } } + + test! { fn stream_send_recv_stress() { + for _ in range(0, stress_factor()) { + let (tx, rx) = sync_channel::<Box<int>>(0); + + send(tx, 0); + recv(rx, 0); + + fn send(tx: SyncSender<Box<int>>, i: int) { + if i == 10 { return } + + spawn(move|| { + tx.send(box i); + send(tx, i + 1); + }); + } + + fn recv(rx: Receiver<Box<int>>, i: int) { + if i == 10 { return } + + spawn(move|| { + assert!(rx.recv() == box i); + recv(rx, i + 1); + }); + } + } + } } + + test! { fn recv_a_lot() { + // Regression test that we don't run out of stack in scheduler context + let (tx, rx) = sync_channel(10000); + for _ in range(0u, 10000) { tx.send(()); } + for _ in range(0u, 10000) { rx.recv(); } + } } + + test! { fn shared_chan_stress() { + let (tx, rx) = sync_channel(0); + let total = stress_factor() + 100; + for _ in range(0, total) { + let tx = tx.clone(); + spawn(move|| { + tx.send(()); + }); + } + + for _ in range(0, total) { + rx.recv(); + } + } } + + test! { fn test_nested_recv_iter() { + let (tx, rx) = sync_channel::<int>(0); + let (total_tx, total_rx) = sync_channel::<int>(0); + + spawn(move|| { + let mut acc = 0; + for x in rx.iter() { + acc += x; + } + total_tx.send(acc); + }); + + tx.send(3); + tx.send(1); + tx.send(2); + drop(tx); + assert_eq!(total_rx.recv(), 6); + } } + + test! { fn test_recv_iter_break() { + let (tx, rx) = sync_channel::<int>(0); + let (count_tx, count_rx) = sync_channel(0); + + spawn(move|| { + let mut count = 0; + for x in rx.iter() { + if count >= 3 { + break; + } else { + count += x; + } + } + count_tx.send(count); + }); + + tx.send(2); + tx.send(2); + tx.send(2); + let _ = tx.try_send(2); + drop(tx); + assert_eq!(count_rx.recv(), 4); + } } + + test! { fn try_recv_states() { + let (tx1, rx1) = sync_channel::<int>(1); + let (tx2, rx2) = sync_channel::<()>(1); + let (tx3, rx3) = sync_channel::<()>(1); + spawn(move|| { + rx2.recv(); + tx1.send(1); + tx3.send(()); + rx2.recv(); + drop(tx1); + tx3.send(()); + }); + + assert_eq!(rx1.try_recv(), Err(Empty)); + tx2.send(()); + rx3.recv(); + assert_eq!(rx1.try_recv(), Ok(1)); + assert_eq!(rx1.try_recv(), Err(Empty)); + tx2.send(()); + rx3.recv(); + assert_eq!(rx1.try_recv(), Err(Disconnected)); + } } + + // This bug used to end up in a livelock inside of the Receiver destructor + // because the internal state of the Shared packet was corrupted + test! { fn destroy_upgraded_shared_port_when_sender_still_active() { + let (tx, rx) = sync_channel::<()>(0); + let (tx2, rx2) = sync_channel::<()>(0); + spawn(move|| { + rx.recv(); // wait on a oneshot + drop(rx); // destroy a shared + tx2.send(()); + }); + // make sure the other task has gone to sleep + for _ in range(0u, 5000) { Thread::yield_now(); } + + // upgrade to a shared chan and send a message + let t = tx.clone(); + drop(tx); + t.send(()); + + // wait for the child task to exit before we exit + rx2.recv(); + } } + + test! { fn send_opt1() { + let (tx, rx) = sync_channel::<int>(0); + spawn(move|| { rx.recv(); }); + assert_eq!(tx.send_opt(1), Ok(())); + } } + + test! { fn send_opt2() { + let (tx, rx) = sync_channel::<int>(0); + spawn(move|| { drop(rx); }); + assert_eq!(tx.send_opt(1), Err(1)); + } } + + test! { fn send_opt3() { + let (tx, rx) = sync_channel::<int>(1); + assert_eq!(tx.send_opt(1), Ok(())); + spawn(move|| { drop(rx); }); + assert_eq!(tx.send_opt(1), Err(1)); + } } + + test! { fn send_opt4() { + let (tx, rx) = sync_channel::<int>(0); + let tx2 = tx.clone(); + let (done, donerx) = channel(); + let done2 = done.clone(); + spawn(move|| { + assert_eq!(tx.send_opt(1), Err(1)); + done.send(()); + }); + spawn(move|| { + assert_eq!(tx2.send_opt(2), Err(2)); + done2.send(()); + }); + drop(rx); + donerx.recv(); + donerx.recv(); + } } + + test! { fn try_send1() { + let (tx, _rx) = sync_channel::<int>(0); + assert_eq!(tx.try_send(1), Err(Full(1))); + } } + + test! { fn try_send2() { + let (tx, _rx) = sync_channel::<int>(1); + assert_eq!(tx.try_send(1), Ok(())); + assert_eq!(tx.try_send(1), Err(Full(1))); + } } + + test! { fn try_send3() { + let (tx, rx) = sync_channel::<int>(1); + assert_eq!(tx.try_send(1), Ok(())); + drop(rx); + assert_eq!(tx.try_send(1), Err(RecvDisconnected(1))); + } } + + test! { fn try_send4() { + let (tx, rx) = sync_channel::<int>(0); + spawn(move|| { + for _ in range(0u, 1000) { Thread::yield_now(); } + assert_eq!(tx.try_send(1), Ok(())); + }); + assert_eq!(rx.recv(), 1); + } #[ignore(reason = "flaky on libnative")] } + + test! { fn issue_15761() { + fn repro() { + let (tx1, rx1) = sync_channel::<()>(3); + let (tx2, rx2) = sync_channel::<()>(3); + + spawn(move|| { + rx1.recv(); + tx2.try_send(()).unwrap(); + }); + + tx1.try_send(()).unwrap(); + rx2.recv(); + } + + for _ in range(0u, 100) { + repro() + } + } } +} diff --git a/src/libstd/comm/mpsc_queue.rs b/src/libstd/comm/mpsc_queue.rs new file mode 100644 index 00000000000..db4e3eac449 --- /dev/null +++ b/src/libstd/comm/mpsc_queue.rs @@ -0,0 +1,201 @@ +/* Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing official + * policies, either expressed or implied, of Dmitry Vyukov. + */ + +//! A mostly lock-free multi-producer, single consumer queue. +//! +//! This module contains an implementation of a concurrent MPSC queue. This +//! queue can be used to share data between tasks, and is also used as the +//! building block of channels in rust. +//! +//! Note that the current implementation of this queue has a caveat of the `pop` +//! method, and see the method for more information about it. Due to this +//! caveat, this queue may not be appropriate for all use-cases. + +#![experimental] + +// http://www.1024cores.net/home/lock-free-algorithms +// /queues/non-intrusive-mpsc-node-based-queue + +pub use self::PopResult::*; + +use core::prelude::*; + +use alloc::boxed::Box; +use core::mem; +use core::cell::UnsafeCell; + +use sync::atomic::{AtomicPtr, Release, Acquire, AcqRel, Relaxed}; + +/// A result of the `pop` function. +pub enum PopResult<T> { + /// Some data has been popped + Data(T), + /// The queue is empty + Empty, + /// The queue is in an inconsistent state. Popping data should succeed, but + /// some pushers have yet to make enough progress in order allow a pop to + /// succeed. It is recommended that a pop() occur "in the near future" in + /// order to see if the sender has made progress or not + Inconsistent, +} + +struct Node<T> { + next: AtomicPtr<Node<T>>, + value: Option<T>, +} + +/// The multi-producer single-consumer structure. This is not cloneable, but it +/// may be safely shared so long as it is guaranteed that there is only one +/// popper at a time (many pushers are allowed). +pub struct Queue<T> { + head: AtomicPtr<Node<T>>, + tail: UnsafeCell<*mut Node<T>>, +} + +impl<T> Node<T> { + unsafe fn new(v: Option<T>) -> *mut Node<T> { + mem::transmute(box Node { + next: AtomicPtr::new(0 as *mut Node<T>), + value: v, + }) + } +} + +impl<T: Send> Queue<T> { + /// Creates a new queue that is safe to share among multiple producers and + /// one consumer. + pub fn new() -> Queue<T> { + let stub = unsafe { Node::new(None) }; + Queue { + head: AtomicPtr::new(stub), + tail: UnsafeCell::new(stub), + } + } + + /// Pushes a new value onto this queue. + pub fn push(&self, t: T) { + unsafe { + let n = Node::new(Some(t)); + let prev = self.head.swap(n, AcqRel); + (*prev).next.store(n, Release); + } + } + + /// Pops some data from this queue. + /// + /// Note that the current implementation means that this function cannot + /// return `Option<T>`. It is possible for this queue to be in an + /// inconsistent state where many pushes have succeeded and completely + /// finished, but pops cannot return `Some(t)`. This inconsistent state + /// happens when a pusher is pre-empted at an inopportune moment. + /// + /// This inconsistent state means that this queue does indeed have data, but + /// it does not currently have access to it at this time. + pub fn pop(&self) -> PopResult<T> { + unsafe { + let tail = *self.tail.get(); + let next = (*tail).next.load(Acquire); + + if !next.is_null() { + *self.tail.get() = next; + assert!((*tail).value.is_none()); + assert!((*next).value.is_some()); + let ret = (*next).value.take().unwrap(); + let _: Box<Node<T>> = mem::transmute(tail); + return Data(ret); + } + + if self.head.load(Acquire) == tail {Empty} else {Inconsistent} + } + } +} + +#[unsafe_destructor] +impl<T: Send> Drop for Queue<T> { + fn drop(&mut self) { + unsafe { + let mut cur = *self.tail.get(); + while !cur.is_null() { + let next = (*cur).next.load(Relaxed); + let _: Box<Node<T>> = mem::transmute(cur); + cur = next; + } + } + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + + use alloc::arc::Arc; + + use super::{Queue, Data, Empty, Inconsistent}; + + #[test] + fn test_full() { + let q = Queue::new(); + q.push(box 1i); + q.push(box 2i); + } + + #[test] + fn test() { + let nthreads = 8u; + let nmsgs = 1000u; + let q = Queue::new(); + match q.pop() { + Empty => {} + Inconsistent | Data(..) => panic!() + } + let (tx, rx) = channel(); + let q = Arc::new(q); + + for _ in range(0, nthreads) { + let tx = tx.clone(); + let q = q.clone(); + spawn(move|| { + for i in range(0, nmsgs) { + q.push(i); + } + tx.send(()); + }); + } + + let mut i = 0u; + while i < nthreads * nmsgs { + match q.pop() { + Empty | Inconsistent => {}, + Data(_) => { i += 1 } + } + } + drop(tx); + for _ in range(0, nthreads) { + rx.recv(); + } + } +} diff --git a/src/libstd/comm/oneshot.rs b/src/libstd/comm/oneshot.rs new file mode 100644 index 00000000000..9c5a6518845 --- /dev/null +++ b/src/libstd/comm/oneshot.rs @@ -0,0 +1,375 @@ +// Copyright 2014 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. + +/// Oneshot channels/ports +/// +/// This is the initial flavor of channels/ports used for comm module. This is +/// an optimization for the one-use case of a channel. The major optimization of +/// this type is to have one and exactly one allocation when the chan/port pair +/// is created. +/// +/// Another possible optimization would be to not use an Arc box because +/// in theory we know when the shared packet can be deallocated (no real need +/// for the atomic reference counting), but I was having trouble how to destroy +/// the data early in a drop of a Port. +/// +/// # Implementation +/// +/// Oneshots are implemented around one atomic uint variable. This variable +/// indicates both the state of the port/chan but also contains any tasks +/// blocked on the port. All atomic operations happen on this one word. +/// +/// In order to upgrade a oneshot channel, an upgrade is considered a disconnect +/// on behalf of the channel side of things (it can be mentally thought of as +/// consuming the port). This upgrade is then also stored in the shared packet. +/// The one caveat to consider is that when a port sees a disconnected channel +/// it must check for data because there is no "data plus upgrade" state. + +pub use self::Failure::*; +pub use self::UpgradeResult::*; +pub use self::SelectionResult::*; +use self::MyUpgrade::*; + +use core::prelude::*; + +use comm::Receiver; +use comm::blocking::{mod, SignalToken}; +use core::mem; +use sync::atomic; + +// Various states you can find a port in. +const EMPTY: uint = 0; // initial state: no data, no blocked reciever +const DATA: uint = 1; // data ready for receiver to take +const DISCONNECTED: uint = 2; // channel is disconnected OR upgraded +// Any other value represents a pointer to a SignalToken value. The +// protocol ensures that when the state moves *to* a pointer, +// ownership of the token is given to the packet, and when the state +// moves *from* a pointer, ownership of the token is transferred to +// whoever changed the state. + +pub struct Packet<T> { + // Internal state of the chan/port pair (stores the blocked task as well) + state: atomic::AtomicUint, + // One-shot data slot location + data: Option<T>, + // when used for the second time, a oneshot channel must be upgraded, and + // this contains the slot for the upgrade + upgrade: MyUpgrade<T>, +} + +pub enum Failure<T> { + Empty, + Disconnected, + Upgraded(Receiver<T>), +} + +pub enum UpgradeResult { + UpSuccess, + UpDisconnected, + UpWoke(SignalToken), +} + +pub enum SelectionResult<T> { + SelCanceled, + SelUpgraded(SignalToken, Receiver<T>), + SelSuccess, +} + +enum MyUpgrade<T> { + NothingSent, + SendUsed, + GoUp(Receiver<T>), +} + +impl<T: Send> Packet<T> { + pub fn new() -> Packet<T> { + Packet { + data: None, + upgrade: NothingSent, + state: atomic::AtomicUint::new(EMPTY), + } + } + + pub fn send(&mut self, t: T) -> Result<(), T> { + // Sanity check + match self.upgrade { + NothingSent => {} + _ => panic!("sending on a oneshot that's already sent on "), + } + assert!(self.data.is_none()); + self.data = Some(t); + self.upgrade = SendUsed; + + match self.state.swap(DATA, atomic::SeqCst) { + // Sent the data, no one was waiting + EMPTY => Ok(()), + + // Couldn't send the data, the port hung up first. Return the data + // back up the stack. + DISCONNECTED => { + Err(self.data.take().unwrap()) + } + + // Not possible, these are one-use channels + DATA => unreachable!(), + + // There is a thread waiting on the other end. We leave the 'DATA' + // state inside so it'll pick it up on the other end. + ptr => unsafe { + SignalToken::cast_from_uint(ptr).signal(); + Ok(()) + } + } + } + + // Just tests whether this channel has been sent on or not, this is only + // safe to use from the sender. + pub fn sent(&self) -> bool { + match self.upgrade { + NothingSent => false, + _ => true, + } + } + + pub fn recv(&mut self) -> Result<T, Failure<T>> { + // Attempt to not block the task (it's a little expensive). If it looks + // like we're not empty, then immediately go through to `try_recv`. + if self.state.load(atomic::SeqCst) == EMPTY { + let (wait_token, signal_token) = blocking::tokens(); + let ptr = unsafe { signal_token.cast_to_uint() }; + + // race with senders to enter the blocking state + if self.state.compare_and_swap(EMPTY, ptr, atomic::SeqCst) == EMPTY { + wait_token.wait(); + debug_assert!(self.state.load(atomic::SeqCst) != EMPTY); + } else { + // drop the signal token, since we never blocked + drop(unsafe { SignalToken::cast_from_uint(ptr) }); + } + } + + self.try_recv() + } + + pub fn try_recv(&mut self) -> Result<T, Failure<T>> { + match self.state.load(atomic::SeqCst) { + EMPTY => Err(Empty), + + // We saw some data on the channel, but the channel can be used + // again to send us an upgrade. As a result, we need to re-insert + // into the channel that there's no data available (otherwise we'll + // just see DATA next time). This is done as a cmpxchg because if + // the state changes under our feet we'd rather just see that state + // change. + DATA => { + self.state.compare_and_swap(DATA, EMPTY, atomic::SeqCst); + match self.data.take() { + Some(data) => Ok(data), + None => unreachable!(), + } + } + + // There's no guarantee that we receive before an upgrade happens, + // and an upgrade flags the channel as disconnected, so when we see + // this we first need to check if there's data available and *then* + // we go through and process the upgrade. + DISCONNECTED => { + match self.data.take() { + Some(data) => Ok(data), + None => { + match mem::replace(&mut self.upgrade, SendUsed) { + SendUsed | NothingSent => Err(Disconnected), + GoUp(upgrade) => Err(Upgraded(upgrade)) + } + } + } + } + + // We are the sole receiver; there cannot be a blocking + // receiver already. + _ => unreachable!() + } + } + + // Returns whether the upgrade was completed. If the upgrade wasn't + // completed, then the port couldn't get sent to the other half (it will + // never receive it). + pub fn upgrade(&mut self, up: Receiver<T>) -> UpgradeResult { + let prev = match self.upgrade { + NothingSent => NothingSent, + SendUsed => SendUsed, + _ => panic!("upgrading again"), + }; + self.upgrade = GoUp(up); + + match self.state.swap(DISCONNECTED, atomic::SeqCst) { + // If the channel is empty or has data on it, then we're good to go. + // Senders will check the data before the upgrade (in case we + // plastered over the DATA state). + DATA | EMPTY => UpSuccess, + + // If the other end is already disconnected, then we failed the + // upgrade. Be sure to trash the port we were given. + DISCONNECTED => { self.upgrade = prev; UpDisconnected } + + // If someone's waiting, we gotta wake them up + ptr => UpWoke(unsafe { SignalToken::cast_from_uint(ptr) }) + } + } + + pub fn drop_chan(&mut self) { + match self.state.swap(DISCONNECTED, atomic::SeqCst) { + DATA | DISCONNECTED | EMPTY => {} + + // If someone's waiting, we gotta wake them up + ptr => unsafe { + SignalToken::cast_from_uint(ptr).signal(); + } + } + } + + pub fn drop_port(&mut self) { + match self.state.swap(DISCONNECTED, atomic::SeqCst) { + // An empty channel has nothing to do, and a remotely disconnected + // channel also has nothing to do b/c we're about to run the drop + // glue + DISCONNECTED | EMPTY => {} + + // There's data on the channel, so make sure we destroy it promptly. + // This is why not using an arc is a little difficult (need the box + // to stay valid while we take the data). + DATA => { self.data.take().unwrap(); } + + // We're the only ones that can block on this port + _ => unreachable!() + } + } + + //////////////////////////////////////////////////////////////////////////// + // select implementation + //////////////////////////////////////////////////////////////////////////// + + // If Ok, the value is whether this port has data, if Err, then the upgraded + // port needs to be checked instead of this one. + pub fn can_recv(&mut self) -> Result<bool, Receiver<T>> { + match self.state.load(atomic::SeqCst) { + EMPTY => Ok(false), // Welp, we tried + DATA => Ok(true), // we have some un-acquired data + DISCONNECTED if self.data.is_some() => Ok(true), // we have data + DISCONNECTED => { + match mem::replace(&mut self.upgrade, SendUsed) { + // The other end sent us an upgrade, so we need to + // propagate upwards whether the upgrade can receive + // data + GoUp(upgrade) => Err(upgrade), + + // If the other end disconnected without sending an + // upgrade, then we have data to receive (the channel is + // disconnected). + up => { self.upgrade = up; Ok(true) } + } + } + _ => unreachable!(), // we're the "one blocker" + } + } + + // Attempts to start selection on this port. This can either succeed, fail + // because there is data, or fail because there is an upgrade pending. + pub fn start_selection(&mut self, token: SignalToken) -> SelectionResult<T> { + let ptr = unsafe { token.cast_to_uint() }; + match self.state.compare_and_swap(EMPTY, ptr, atomic::SeqCst) { + EMPTY => SelSuccess, + DATA => { + drop(unsafe { SignalToken::cast_from_uint(ptr) }); + SelCanceled + } + DISCONNECTED if self.data.is_some() => { + drop(unsafe { SignalToken::cast_from_uint(ptr) }); + SelCanceled + } + DISCONNECTED => { + match mem::replace(&mut self.upgrade, SendUsed) { + // The other end sent us an upgrade, so we need to + // propagate upwards whether the upgrade can receive + // data + GoUp(upgrade) => { + SelUpgraded(unsafe { SignalToken::cast_from_uint(ptr) }, upgrade) + } + + // If the other end disconnected without sending an + // upgrade, then we have data to receive (the channel is + // disconnected). + up => { + self.upgrade = up; + drop(unsafe { SignalToken::cast_from_uint(ptr) }); + SelCanceled + } + } + } + _ => unreachable!(), // we're the "one blocker" + } + } + + // Remove a previous selecting task from this port. This ensures that the + // blocked task will no longer be visible to any other threads. + // + // The return value indicates whether there's data on this port. + pub fn abort_selection(&mut self) -> Result<bool, Receiver<T>> { + let state = match self.state.load(atomic::SeqCst) { + // Each of these states means that no further activity will happen + // with regard to abortion selection + s @ EMPTY | + s @ DATA | + s @ DISCONNECTED => s, + + // If we've got a blocked task, then use an atomic to gain ownership + // of it (may fail) + ptr => self.state.compare_and_swap(ptr, EMPTY, atomic::SeqCst) + }; + + // Now that we've got ownership of our state, figure out what to do + // about it. + match state { + EMPTY => unreachable!(), + // our task used for select was stolen + DATA => Ok(true), + + // If the other end has hung up, then we have complete ownership + // of the port. First, check if there was data waiting for us. This + // is possible if the other end sent something and then hung up. + // + // We then need to check to see if there was an upgrade requested, + // and if so, the upgraded port needs to have its selection aborted. + DISCONNECTED => { + if self.data.is_some() { + Ok(true) + } else { + match mem::replace(&mut self.upgrade, SendUsed) { + GoUp(port) => Err(port), + _ => Ok(true), + } + } + } + + // We woke ourselves up from select. + ptr => unsafe { + drop(SignalToken::cast_from_uint(ptr)); + Ok(false) + } + } + } +} + +#[unsafe_destructor] +impl<T: Send> Drop for Packet<T> { + fn drop(&mut self) { + assert_eq!(self.state.load(atomic::SeqCst), DISCONNECTED); + } +} diff --git a/src/libstd/comm/select.rs b/src/libstd/comm/select.rs new file mode 100644 index 00000000000..690b5861c22 --- /dev/null +++ b/src/libstd/comm/select.rs @@ -0,0 +1,721 @@ +// Copyright 2013-2014 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. + +//! Selection over an array of receivers +//! +//! This module contains the implementation machinery necessary for selecting +//! over a number of receivers. One large goal of this module is to provide an +//! efficient interface to selecting over any receiver of any type. +//! +//! This is achieved through an architecture of a "receiver set" in which +//! receivers are added to a set and then the entire set is waited on at once. +//! The set can be waited on multiple times to prevent re-adding each receiver +//! to the set. +//! +//! Usage of this module is currently encouraged to go through the use of the +//! `select!` macro. This macro allows naturally binding of variables to the +//! received values of receivers in a much more natural syntax then usage of the +//! `Select` structure directly. +//! +//! # Example +//! +//! ```rust +//! let (tx1, rx1) = channel(); +//! let (tx2, rx2) = channel(); +//! +//! tx1.send(1i); +//! tx2.send(2i); +//! +//! select! { +//! val = rx1.recv() => { +//! assert_eq!(val, 1i); +//! }, +//! val = rx2.recv() => { +//! assert_eq!(val, 2i); +//! } +//! } +//! ``` + +#![allow(dead_code)] +#![experimental = "This implementation, while likely sufficient, is unsafe and \ + likely to be error prone. At some point in the future this \ + module will likely be replaced, and it is currently \ + unknown how much API breakage that will cause. The ability \ + to select over a number of channels will remain forever, \ + but no guarantees beyond this are being made"] + + +use core::prelude::*; + +use core::cell::Cell; +use core::kinds::marker; +use core::mem; +use core::uint; + +use comm::Receiver; +use comm::blocking::{mod, SignalToken}; + +/// The "receiver set" of the select interface. This structure is used to manage +/// a set of receivers which are being selected over. +pub struct Select { + head: *mut Handle<'static, ()>, + tail: *mut Handle<'static, ()>, + next_id: Cell<uint>, + marker1: marker::NoSend, +} + +/// A handle to a receiver which is currently a member of a `Select` set of +/// receivers. This handle is used to keep the receiver in the set as well as +/// interact with the underlying receiver. +pub struct Handle<'rx, T:'rx> { + /// The ID of this handle, used to compare against the return value of + /// `Select::wait()` + id: uint, + selector: &'rx Select, + next: *mut Handle<'static, ()>, + prev: *mut Handle<'static, ()>, + added: bool, + packet: &'rx (Packet+'rx), + + // due to our fun transmutes, we be sure to place this at the end. (nothing + // previous relies on T) + rx: &'rx Receiver<T>, +} + +struct Packets { cur: *mut Handle<'static, ()> } + +#[doc(hidden)] +#[deriving(PartialEq)] +pub enum StartResult { + Installed, + Abort, +} + +#[doc(hidden)] +pub trait Packet { + fn can_recv(&self) -> bool; + fn start_selection(&self, token: SignalToken) -> StartResult; + fn abort_selection(&self) -> bool; +} + +impl Select { + /// Creates a new selection structure. This set is initially empty and + /// `wait` will panic!() if called. + /// + /// Usage of this struct directly can sometimes be burdensome, and usage is + /// rather much easier through the `select!` macro. + pub fn new() -> Select { + Select { + marker1: marker::NoSend, + head: 0 as *mut Handle<'static, ()>, + tail: 0 as *mut Handle<'static, ()>, + next_id: Cell::new(1), + } + } + + /// Creates a new handle into this receiver set for a new receiver. Note + /// that this does *not* add the receiver to the receiver set, for that you + /// must call the `add` method on the handle itself. + pub fn handle<'a, T: Send>(&'a self, rx: &'a Receiver<T>) -> Handle<'a, T> { + let id = self.next_id.get(); + self.next_id.set(id + 1); + Handle { + id: id, + selector: self, + next: 0 as *mut Handle<'static, ()>, + prev: 0 as *mut Handle<'static, ()>, + added: false, + rx: rx, + packet: rx, + } + } + + /// Waits for an event on this receiver set. The returned value is *not* an + /// index, but rather an id. This id can be queried against any active + /// `Handle` structures (each one has an `id` method). The handle with + /// the matching `id` will have some sort of event available on it. The + /// event could either be that data is available or the corresponding + /// channel has been closed. + pub fn wait(&self) -> uint { + self.wait2(true) + } + + /// Helper method for skipping the preflight checks during testing + fn wait2(&self, do_preflight_checks: bool) -> uint { + // Note that this is currently an inefficient implementation. We in + // theory have knowledge about all receivers in the set ahead of time, + // so this method shouldn't really have to iterate over all of them yet + // again. The idea with this "receiver set" interface is to get the + // interface right this time around, and later this implementation can + // be optimized. + // + // This implementation can be summarized by: + // + // fn select(receivers) { + // if any receiver ready { return ready index } + // deschedule { + // block on all receivers + // } + // unblock on all receivers + // return ready index + // } + // + // Most notably, the iterations over all of the receivers shouldn't be + // necessary. + unsafe { + // Stage 1: preflight checks. Look for any packets ready to receive + if do_preflight_checks { + for handle in self.iter() { + if (*handle).packet.can_recv() { + return (*handle).id(); + } + } + } + + // Stage 2: begin the blocking process + // + // Create a number of signal tokens, and install each one + // sequentially until one fails. If one fails, then abort the + // selection on the already-installed tokens. + let (wait_token, signal_token) = blocking::tokens(); + for (i, handle) in self.iter().enumerate() { + match (*handle).packet.start_selection(signal_token.clone()) { + StartResult::Installed => {} + StartResult::Abort => { + // Go back and abort the already-begun selections + for handle in self.iter().take(i) { + (*handle).packet.abort_selection(); + } + return (*handle).id; + } + } + } + + // Stage 3: no messages available, actually block + wait_token.wait(); + + // Stage 4: there *must* be message available; find it. + // + // Abort the selection process on each receiver. If the abort + // process returns `true`, then that means that the receiver is + // ready to receive some data. Note that this also means that the + // receiver may have yet to have fully read the `to_wake` field and + // woken us up (although the wakeup is guaranteed to fail). + // + // This situation happens in the window of where a sender invokes + // increment(), sees -1, and then decides to wake up the task. After + // all this is done, the sending thread will set `selecting` to + // `false`. Until this is done, we cannot return. If we were to + // return, then a sender could wake up a receiver which has gone + // back to sleep after this call to `select`. + // + // Note that it is a "fairly small window" in which an increment() + // views that it should wake a thread up until the `selecting` bit + // is set to false. For now, the implementation currently just spins + // in a yield loop. This is very distasteful, but this + // implementation is already nowhere near what it should ideally be. + // A rewrite should focus on avoiding a yield loop, and for now this + // implementation is tying us over to a more efficient "don't + // iterate over everything every time" implementation. + let mut ready_id = uint::MAX; + for handle in self.iter() { + if (*handle).packet.abort_selection() { + ready_id = (*handle).id; + } + } + + // We must have found a ready receiver + assert!(ready_id != uint::MAX); + return ready_id; + } + } + + fn iter(&self) -> Packets { Packets { cur: self.head } } +} + +impl<'rx, T: Send> Handle<'rx, T> { + /// Retrieve the id of this handle. + #[inline] + pub fn id(&self) -> uint { self.id } + + /// Receive a value on the underlying receiver. Has the same semantics as + /// `Receiver.recv` + pub fn recv(&mut self) -> T { self.rx.recv() } + /// Block to receive a value on the underlying receiver, returning `Some` on + /// success or `None` if the channel disconnects. This function has the same + /// semantics as `Receiver.recv_opt` + pub fn recv_opt(&mut self) -> Result<T, ()> { self.rx.recv_opt() } + + /// Adds this handle to the receiver set that the handle was created from. This + /// method can be called multiple times, but it has no effect if `add` was + /// called previously. + /// + /// This method is unsafe because it requires that the `Handle` is not moved + /// while it is added to the `Select` set. + pub unsafe fn add(&mut self) { + if self.added { return } + let selector: &mut Select = mem::transmute(&*self.selector); + let me: *mut Handle<'static, ()> = mem::transmute(&*self); + + if selector.head.is_null() { + selector.head = me; + selector.tail = me; + } else { + (*me).prev = selector.tail; + assert!((*me).next.is_null()); + (*selector.tail).next = me; + selector.tail = me; + } + self.added = true; + } + + /// Removes this handle from the `Select` set. This method is unsafe because + /// it has no guarantee that the `Handle` was not moved since `add` was + /// called. + pub unsafe fn remove(&mut self) { + if !self.added { return } + + let selector: &mut Select = mem::transmute(&*self.selector); + let me: *mut Handle<'static, ()> = mem::transmute(&*self); + + if self.prev.is_null() { + assert_eq!(selector.head, me); + selector.head = self.next; + } else { + (*self.prev).next = self.next; + } + if self.next.is_null() { + assert_eq!(selector.tail, me); + selector.tail = self.prev; + } else { + (*self.next).prev = self.prev; + } + + self.next = 0 as *mut Handle<'static, ()>; + self.prev = 0 as *mut Handle<'static, ()>; + + self.added = false; + } +} + +#[unsafe_destructor] +impl Drop for Select { + fn drop(&mut self) { + assert!(self.head.is_null()); + assert!(self.tail.is_null()); + } +} + +#[unsafe_destructor] +impl<'rx, T: Send> Drop for Handle<'rx, T> { + fn drop(&mut self) { + unsafe { self.remove() } + } +} + +impl Iterator<*mut Handle<'static, ()>> for Packets { + fn next(&mut self) -> Option<*mut Handle<'static, ()>> { + if self.cur.is_null() { + None + } else { + let ret = Some(self.cur); + unsafe { self.cur = (*self.cur).next; } + ret + } + } +} + +#[cfg(test)] +#[allow(unused_imports)] +mod test { + use prelude::*; + + use super::*; + + // Don't use the libstd version so we can pull in the right Select structure + // (std::comm points at the wrong one) + macro_rules! select { + ( + $($name:pat = $rx:ident.$meth:ident() => $code:expr),+ + ) => ({ + use comm::Select; + let sel = Select::new(); + $( let mut $rx = sel.handle(&$rx); )+ + unsafe { + $( $rx.add(); )+ + } + let ret = sel.wait(); + $( if ret == $rx.id() { let $name = $rx.$meth(); $code } else )+ + { unreachable!() } + }) + } + + test! { fn smoke() { + let (tx1, rx1) = channel::<int>(); + let (tx2, rx2) = channel::<int>(); + tx1.send(1); + select! { + foo = rx1.recv() => { assert_eq!(foo, 1); }, + _bar = rx2.recv() => { panic!() } + } + tx2.send(2); + select! { + _foo = rx1.recv() => { panic!() }, + bar = rx2.recv() => { assert_eq!(bar, 2) } + } + drop(tx1); + select! { + foo = rx1.recv_opt() => { assert_eq!(foo, Err(())); }, + _bar = rx2.recv() => { panic!() } + } + drop(tx2); + select! { + bar = rx2.recv_opt() => { assert_eq!(bar, Err(())); } + } + } } + + test! { fn smoke2() { + let (_tx1, rx1) = channel::<int>(); + let (_tx2, rx2) = channel::<int>(); + let (_tx3, rx3) = channel::<int>(); + let (_tx4, rx4) = channel::<int>(); + let (tx5, rx5) = channel::<int>(); + tx5.send(4); + select! { + _foo = rx1.recv() => { panic!("1") }, + _foo = rx2.recv() => { panic!("2") }, + _foo = rx3.recv() => { panic!("3") }, + _foo = rx4.recv() => { panic!("4") }, + foo = rx5.recv() => { assert_eq!(foo, 4); } + } + } } + + test! { fn closed() { + let (_tx1, rx1) = channel::<int>(); + let (tx2, rx2) = channel::<int>(); + drop(tx2); + + select! { + _a1 = rx1.recv_opt() => { panic!() }, + a2 = rx2.recv_opt() => { assert_eq!(a2, Err(())); } + } + } } + + test! { fn unblocks() { + let (tx1, rx1) = channel::<int>(); + let (_tx2, rx2) = channel::<int>(); + let (tx3, rx3) = channel::<int>(); + + spawn(move|| { + for _ in range(0u, 20) { Thread::yield_now(); } + tx1.send(1); + rx3.recv(); + for _ in range(0u, 20) { Thread::yield_now(); } + }); + + select! { + a = rx1.recv() => { assert_eq!(a, 1); }, + _b = rx2.recv() => { panic!() } + } + tx3.send(1); + select! { + a = rx1.recv_opt() => { assert_eq!(a, Err(())); }, + _b = rx2.recv() => { panic!() } + } + } } + + test! { fn both_ready() { + let (tx1, rx1) = channel::<int>(); + let (tx2, rx2) = channel::<int>(); + let (tx3, rx3) = channel::<()>(); + + spawn(move|| { + for _ in range(0u, 20) { Thread::yield_now(); } + tx1.send(1); + tx2.send(2); + rx3.recv(); + }); + + select! { + a = rx1.recv() => { assert_eq!(a, 1); }, + a = rx2.recv() => { assert_eq!(a, 2); } + } + select! { + a = rx1.recv() => { assert_eq!(a, 1); }, + a = rx2.recv() => { assert_eq!(a, 2); } + } + assert_eq!(rx1.try_recv(), Err(Empty)); + assert_eq!(rx2.try_recv(), Err(Empty)); + tx3.send(()); + } } + + test! { fn stress() { + static AMT: int = 10000; + let (tx1, rx1) = channel::<int>(); + let (tx2, rx2) = channel::<int>(); + let (tx3, rx3) = channel::<()>(); + + spawn(move|| { + for i in range(0, AMT) { + if i % 2 == 0 { + tx1.send(i); + } else { + tx2.send(i); + } + rx3.recv(); + } + }); + + for i in range(0, AMT) { + select! { + i1 = rx1.recv() => { assert!(i % 2 == 0 && i == i1); }, + i2 = rx2.recv() => { assert!(i % 2 == 1 && i == i2); } + } + tx3.send(()); + } + } } + + test! { fn cloning() { + let (tx1, rx1) = channel::<int>(); + let (_tx2, rx2) = channel::<int>(); + let (tx3, rx3) = channel::<()>(); + + spawn(move|| { + rx3.recv(); + tx1.clone(); + assert_eq!(rx3.try_recv(), Err(Empty)); + tx1.send(2); + rx3.recv(); + }); + + tx3.send(()); + select! { + _i1 = rx1.recv() => {}, + _i2 = rx2.recv() => panic!() + } + tx3.send(()); + } } + + test! { fn cloning2() { + let (tx1, rx1) = channel::<int>(); + let (_tx2, rx2) = channel::<int>(); + let (tx3, rx3) = channel::<()>(); + + spawn(move|| { + rx3.recv(); + tx1.clone(); + assert_eq!(rx3.try_recv(), Err(Empty)); + tx1.send(2); + rx3.recv(); + }); + + tx3.send(()); + select! { + _i1 = rx1.recv() => {}, + _i2 = rx2.recv() => panic!() + } + tx3.send(()); + } } + + test! { fn cloning3() { + let (tx1, rx1) = channel::<()>(); + let (tx2, rx2) = channel::<()>(); + let (tx3, rx3) = channel::<()>(); + spawn(move|| { + let s = Select::new(); + let mut h1 = s.handle(&rx1); + let mut h2 = s.handle(&rx2); + unsafe { h2.add(); } + unsafe { h1.add(); } + assert_eq!(s.wait(), h2.id); + tx3.send(()); + }); + + for _ in range(0u, 1000) { Thread::yield_now(); } + drop(tx1.clone()); + tx2.send(()); + rx3.recv(); + } } + + test! { fn preflight1() { + let (tx, rx) = channel(); + tx.send(()); + select! { + () = rx.recv() => {} + } + } } + + test! { fn preflight2() { + let (tx, rx) = channel(); + tx.send(()); + tx.send(()); + select! { + () = rx.recv() => {} + } + } } + + test! { fn preflight3() { + let (tx, rx) = channel(); + drop(tx.clone()); + tx.send(()); + select! { + () = rx.recv() => {} + } + } } + + test! { fn preflight4() { + let (tx, rx) = channel(); + tx.send(()); + let s = Select::new(); + let mut h = s.handle(&rx); + unsafe { h.add(); } + assert_eq!(s.wait2(false), h.id); + } } + + test! { fn preflight5() { + let (tx, rx) = channel(); + tx.send(()); + tx.send(()); + let s = Select::new(); + let mut h = s.handle(&rx); + unsafe { h.add(); } + assert_eq!(s.wait2(false), h.id); + } } + + test! { fn preflight6() { + let (tx, rx) = channel(); + drop(tx.clone()); + tx.send(()); + let s = Select::new(); + let mut h = s.handle(&rx); + unsafe { h.add(); } + assert_eq!(s.wait2(false), h.id); + } } + + test! { fn preflight7() { + let (tx, rx) = channel::<()>(); + drop(tx); + let s = Select::new(); + let mut h = s.handle(&rx); + unsafe { h.add(); } + assert_eq!(s.wait2(false), h.id); + } } + + test! { fn preflight8() { + let (tx, rx) = channel(); + tx.send(()); + drop(tx); + rx.recv(); + let s = Select::new(); + let mut h = s.handle(&rx); + unsafe { h.add(); } + assert_eq!(s.wait2(false), h.id); + } } + + test! { fn preflight9() { + let (tx, rx) = channel(); + drop(tx.clone()); + tx.send(()); + drop(tx); + rx.recv(); + let s = Select::new(); + let mut h = s.handle(&rx); + unsafe { h.add(); } + assert_eq!(s.wait2(false), h.id); + } } + + test! { fn oneshot_data_waiting() { + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + spawn(move|| { + select! { + () = rx1.recv() => {} + } + tx2.send(()); + }); + + for _ in range(0u, 100) { Thread::yield_now() } + tx1.send(()); + rx2.recv(); + } } + + test! { fn stream_data_waiting() { + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + tx1.send(()); + tx1.send(()); + rx1.recv(); + rx1.recv(); + spawn(move|| { + select! { + () = rx1.recv() => {} + } + tx2.send(()); + }); + + for _ in range(0u, 100) { Thread::yield_now() } + tx1.send(()); + rx2.recv(); + } } + + test! { fn shared_data_waiting() { + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + drop(tx1.clone()); + tx1.send(()); + rx1.recv(); + spawn(move|| { + select! { + () = rx1.recv() => {} + } + tx2.send(()); + }); + + for _ in range(0u, 100) { Thread::yield_now() } + tx1.send(()); + rx2.recv(); + } } + + test! { fn sync1() { + let (tx, rx) = sync_channel::<int>(1); + tx.send(1); + select! { + n = rx.recv() => { assert_eq!(n, 1); } + } + } } + + test! { fn sync2() { + let (tx, rx) = sync_channel::<int>(0); + spawn(move|| { + for _ in range(0u, 100) { Thread::yield_now() } + tx.send(1); + }); + select! { + n = rx.recv() => { assert_eq!(n, 1); } + } + } } + + test! { fn sync3() { + let (tx1, rx1) = sync_channel::<int>(0); + let (tx2, rx2): (Sender<int>, Receiver<int>) = channel(); + spawn(move|| { tx1.send(1); }); + spawn(move|| { tx2.send(2); }); + select! { + n = rx1.recv() => { + assert_eq!(n, 1); + assert_eq!(rx2.recv(), 2); + }, + n = rx2.recv() => { + assert_eq!(n, 2); + assert_eq!(rx1.recv(), 1); + } + } + } } +} diff --git a/src/libstd/comm/shared.rs b/src/libstd/comm/shared.rs new file mode 100644 index 00000000000..1022694e634 --- /dev/null +++ b/src/libstd/comm/shared.rs @@ -0,0 +1,486 @@ +// Copyright 2014 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. + +/// Shared channels +/// +/// This is the flavor of channels which are not necessarily optimized for any +/// particular use case, but are the most general in how they are used. Shared +/// channels are cloneable allowing for multiple senders. +/// +/// High level implementation details can be found in the comment of the parent +/// module. You'll also note that the implementation of the shared and stream +/// channels are quite similar, and this is no coincidence! + +pub use self::Failure::*; + +use core::prelude::*; + +use core::cmp; +use core::int; + +use sync::{atomic, Mutex, MutexGuard}; +use comm::mpsc_queue as mpsc; +use comm::blocking::{mod, SignalToken}; +use comm::select::StartResult; +use comm::select::StartResult::*; +use thread::Thread; + +const DISCONNECTED: int = int::MIN; +const FUDGE: int = 1024; +#[cfg(test)] +const MAX_STEALS: int = 5; +#[cfg(not(test))] +const MAX_STEALS: int = 1 << 20; + +pub struct Packet<T> { + queue: mpsc::Queue<T>, + cnt: atomic::AtomicInt, // How many items are on this channel + steals: int, // How many times has a port received without blocking? + to_wake: atomic::AtomicUint, // SignalToken for wake up + + // The number of channels which are currently using this packet. + channels: atomic::AtomicInt, + + // See the discussion in Port::drop and the channel send methods for what + // these are used for + port_dropped: atomic::AtomicBool, + sender_drain: atomic::AtomicInt, + + // this lock protects various portions of this implementation during + // select() + select_lock: Mutex<()>, +} + +pub enum Failure { + Empty, + Disconnected, +} + +impl<T: Send> Packet<T> { + // Creation of a packet *must* be followed by a call to postinit_lock + // and later by inherit_blocker + pub fn new() -> Packet<T> { + let p = Packet { + queue: mpsc::Queue::new(), + cnt: atomic::AtomicInt::new(0), + steals: 0, + to_wake: atomic::AtomicUint::new(0), + channels: atomic::AtomicInt::new(2), + port_dropped: atomic::AtomicBool::new(false), + sender_drain: atomic::AtomicInt::new(0), + select_lock: Mutex::new(()), + }; + return p; + } + + // This function should be used after newly created Packet + // was wrapped with an Arc + // In other case mutex data will be duplicated while cloning + // and that could cause problems on platforms where it is + // represented by opaque data structure + pub fn postinit_lock(&self) -> MutexGuard<()> { + self.select_lock.lock() + } + + // This function is used at the creation of a shared packet to inherit a + // previously blocked task. This is done to prevent spurious wakeups of + // tasks in select(). + // + // This can only be called at channel-creation time + pub fn inherit_blocker(&mut self, + token: Option<SignalToken>, + guard: MutexGuard<()>) { + token.map(|token| { + assert_eq!(self.cnt.load(atomic::SeqCst), 0); + assert_eq!(self.to_wake.load(atomic::SeqCst), 0); + self.to_wake.store(unsafe { token.cast_to_uint() }, atomic::SeqCst); + self.cnt.store(-1, atomic::SeqCst); + + // This store is a little sketchy. What's happening here is that + // we're transferring a blocker from a oneshot or stream channel to + // this shared channel. In doing so, we never spuriously wake them + // up and rather only wake them up at the appropriate time. This + // implementation of shared channels assumes that any blocking + // recv() will undo the increment of steals performed in try_recv() + // once the recv is complete. This thread that we're inheriting, + // however, is not in the middle of recv. Hence, the first time we + // wake them up, they're going to wake up from their old port, move + // on to the upgraded port, and then call the block recv() function. + // + // When calling this function, they'll find there's data immediately + // available, counting it as a steal. This in fact wasn't a steal + // because we appropriately blocked them waiting for data. + // + // To offset this bad increment, we initially set the steal count to + // -1. You'll find some special code in abort_selection() as well to + // ensure that this -1 steal count doesn't escape too far. + self.steals = -1; + }); + + // When the shared packet is constructed, we grabbed this lock. The + // purpose of this lock is to ensure that abort_selection() doesn't + // interfere with this method. After we unlock this lock, we're + // signifying that we're done modifying self.cnt and self.to_wake and + // the port is ready for the world to continue using it. + drop(guard); + } + + pub fn send(&mut self, t: T) -> Result<(), T> { + // See Port::drop for what's going on + if self.port_dropped.load(atomic::SeqCst) { return Err(t) } + + // Note that the multiple sender case is a little trickier + // semantically than the single sender case. The logic for + // incrementing is "add and if disconnected store disconnected". + // This could end up leading some senders to believe that there + // wasn't a disconnect if in fact there was a disconnect. This means + // that while one thread is attempting to re-store the disconnected + // states, other threads could walk through merrily incrementing + // this very-negative disconnected count. To prevent senders from + // spuriously attempting to send when the channels is actually + // disconnected, the count has a ranged check here. + // + // This is also done for another reason. Remember that the return + // value of this function is: + // + // `true` == the data *may* be received, this essentially has no + // meaning + // `false` == the data will *never* be received, this has a lot of + // meaning + // + // In the SPSC case, we have a check of 'queue.is_empty()' to see + // whether the data was actually received, but this same condition + // means nothing in a multi-producer context. As a result, this + // preflight check serves as the definitive "this will never be + // received". Once we get beyond this check, we have permanently + // entered the realm of "this may be received" + if self.cnt.load(atomic::SeqCst) < DISCONNECTED + FUDGE { + return Err(t) + } + + self.queue.push(t); + match self.cnt.fetch_add(1, atomic::SeqCst) { + -1 => { + self.take_to_wake().signal(); + } + + // In this case, we have possibly failed to send our data, and + // we need to consider re-popping the data in order to fully + // destroy it. We must arbitrate among the multiple senders, + // however, because the queues that we're using are + // single-consumer queues. In order to do this, all exiting + // pushers will use an atomic count in order to count those + // flowing through. Pushers who see 0 are required to drain as + // much as possible, and then can only exit when they are the + // only pusher (otherwise they must try again). + n if n < DISCONNECTED + FUDGE => { + // see the comment in 'try' for a shared channel for why this + // window of "not disconnected" is ok. + self.cnt.store(DISCONNECTED, atomic::SeqCst); + + if self.sender_drain.fetch_add(1, atomic::SeqCst) == 0 { + loop { + // drain the queue, for info on the thread yield see the + // discussion in try_recv + loop { + match self.queue.pop() { + mpsc::Data(..) => {} + mpsc::Empty => break, + mpsc::Inconsistent => Thread::yield_now(), + } + } + // maybe we're done, if we're not the last ones + // here, then we need to go try again. + if self.sender_drain.fetch_sub(1, atomic::SeqCst) == 1 { + break + } + } + + // At this point, there may still be data on the queue, + // but only if the count hasn't been incremented and + // some other sender hasn't finished pushing data just + // yet. That sender in question will drain its own data. + } + } + + // Can't make any assumptions about this case like in the SPSC case. + _ => {} + } + + Ok(()) + } + + pub fn recv(&mut self) -> Result<T, Failure> { + // This code is essentially the exact same as that found in the stream + // case (see stream.rs) + match self.try_recv() { + Err(Empty) => {} + data => return data, + } + + let (wait_token, signal_token) = blocking::tokens(); + if self.decrement(signal_token) == Installed { + wait_token.wait() + } + + match self.try_recv() { + data @ Ok(..) => { self.steals -= 1; data } + data => data, + } + } + + // Essentially the exact same thing as the stream decrement function. + // Returns true if blocking should proceed. + fn decrement(&mut self, token: SignalToken) -> StartResult { + assert_eq!(self.to_wake.load(atomic::SeqCst), 0); + let ptr = unsafe { token.cast_to_uint() }; + self.to_wake.store(ptr, atomic::SeqCst); + + let steals = self.steals; + self.steals = 0; + + match self.cnt.fetch_sub(1 + steals, atomic::SeqCst) { + DISCONNECTED => { self.cnt.store(DISCONNECTED, atomic::SeqCst); } + // If we factor in our steals and notice that the channel has no + // data, we successfully sleep + n => { + assert!(n >= 0); + if n - steals <= 0 { return Installed } + } + } + + self.to_wake.store(0, atomic::SeqCst); + drop(unsafe { SignalToken::cast_from_uint(ptr) }); + Abort + } + + pub fn try_recv(&mut self) -> Result<T, Failure> { + let ret = match self.queue.pop() { + mpsc::Data(t) => Some(t), + mpsc::Empty => None, + + // This is a bit of an interesting case. The channel is reported as + // having data available, but our pop() has failed due to the queue + // being in an inconsistent state. This means that there is some + // pusher somewhere which has yet to complete, but we are guaranteed + // that a pop will eventually succeed. In this case, we spin in a + // yield loop because the remote sender should finish their enqueue + // operation "very quickly". + // + // Avoiding this yield loop would require a different queue + // abstraction which provides the guarantee that after M pushes have + // succeeded, at least M pops will succeed. The current queues + // guarantee that if there are N active pushes, you can pop N times + // once all N have finished. + mpsc::Inconsistent => { + let data; + loop { + Thread::yield_now(); + match self.queue.pop() { + mpsc::Data(t) => { data = t; break } + mpsc::Empty => panic!("inconsistent => empty"), + mpsc::Inconsistent => {} + } + } + Some(data) + } + }; + match ret { + // See the discussion in the stream implementation for why we + // might decrement steals. + Some(data) => { + if self.steals > MAX_STEALS { + match self.cnt.swap(0, atomic::SeqCst) { + DISCONNECTED => { + self.cnt.store(DISCONNECTED, atomic::SeqCst); + } + n => { + let m = cmp::min(n, self.steals); + self.steals -= m; + self.bump(n - m); + } + } + assert!(self.steals >= 0); + } + self.steals += 1; + Ok(data) + } + + // See the discussion in the stream implementation for why we try + // again. + None => { + match self.cnt.load(atomic::SeqCst) { + n if n != DISCONNECTED => Err(Empty), + _ => { + match self.queue.pop() { + mpsc::Data(t) => Ok(t), + mpsc::Empty => Err(Disconnected), + // with no senders, an inconsistency is impossible. + mpsc::Inconsistent => unreachable!(), + } + } + } + } + } + } + + // Prepares this shared packet for a channel clone, essentially just bumping + // a refcount. + pub fn clone_chan(&mut self) { + self.channels.fetch_add(1, atomic::SeqCst); + } + + // Decrement the reference count on a channel. This is called whenever a + // Chan is dropped and may end up waking up a receiver. It's the receiver's + // responsibility on the other end to figure out that we've disconnected. + pub fn drop_chan(&mut self) { + match self.channels.fetch_sub(1, atomic::SeqCst) { + 1 => {} + n if n > 1 => return, + n => panic!("bad number of channels left {}", n), + } + + match self.cnt.swap(DISCONNECTED, atomic::SeqCst) { + -1 => { self.take_to_wake().signal(); } + DISCONNECTED => {} + n => { assert!(n >= 0); } + } + } + + // See the long discussion inside of stream.rs for why the queue is drained, + // and why it is done in this fashion. + pub fn drop_port(&mut self) { + self.port_dropped.store(true, atomic::SeqCst); + let mut steals = self.steals; + while { + let cnt = self.cnt.compare_and_swap(steals, DISCONNECTED, atomic::SeqCst); + cnt != DISCONNECTED && cnt != steals + } { + // See the discussion in 'try_recv' for why we yield + // control of this thread. + loop { + match self.queue.pop() { + mpsc::Data(..) => { steals += 1; } + mpsc::Empty | mpsc::Inconsistent => break, + } + } + } + } + + // Consumes ownership of the 'to_wake' field. + fn take_to_wake(&mut self) -> SignalToken { + let ptr = self.to_wake.load(atomic::SeqCst); + self.to_wake.store(0, atomic::SeqCst); + assert!(ptr != 0); + unsafe { SignalToken::cast_from_uint(ptr) } + } + + //////////////////////////////////////////////////////////////////////////// + // select implementation + //////////////////////////////////////////////////////////////////////////// + + // Helper function for select, tests whether this port can receive without + // blocking (obviously not an atomic decision). + // + // This is different than the stream version because there's no need to peek + // at the queue, we can just look at the local count. + pub fn can_recv(&mut self) -> bool { + let cnt = self.cnt.load(atomic::SeqCst); + cnt == DISCONNECTED || cnt - self.steals > 0 + } + + // increment the count on the channel (used for selection) + fn bump(&mut self, amt: int) -> int { + match self.cnt.fetch_add(amt, atomic::SeqCst) { + DISCONNECTED => { + self.cnt.store(DISCONNECTED, atomic::SeqCst); + DISCONNECTED + } + n => n + } + } + + // Inserts the signal token for selection on this port, returning true if + // blocking should proceed. + // + // The code here is the same as in stream.rs, except that it doesn't need to + // peek at the channel to see if an upgrade is pending. + pub fn start_selection(&mut self, token: SignalToken) -> StartResult { + match self.decrement(token) { + Installed => Installed, + Abort => { + let prev = self.bump(1); + assert!(prev == DISCONNECTED || prev >= 0); + Abort + } + } + } + + // Cancels a previous task waiting on this port, returning whether there's + // data on the port. + // + // This is similar to the stream implementation (hence fewer comments), but + // uses a different value for the "steals" variable. + pub fn abort_selection(&mut self, _was_upgrade: bool) -> bool { + // Before we do anything else, we bounce on this lock. The reason for + // doing this is to ensure that any upgrade-in-progress is gone and + // done with. Without this bounce, we can race with inherit_blocker + // about looking at and dealing with to_wake. Once we have acquired the + // lock, we are guaranteed that inherit_blocker is done. + { + let _guard = self.select_lock.lock(); + } + + // Like the stream implementation, we want to make sure that the count + // on the channel goes non-negative. We don't know how negative the + // stream currently is, so instead of using a steal value of 1, we load + // the channel count and figure out what we should do to make it + // positive. + let steals = { + let cnt = self.cnt.load(atomic::SeqCst); + if cnt < 0 && cnt != DISCONNECTED {-cnt} else {0} + }; + let prev = self.bump(steals + 1); + + if prev == DISCONNECTED { + assert_eq!(self.to_wake.load(atomic::SeqCst), 0); + true + } else { + let cur = prev + steals + 1; + assert!(cur >= 0); + if prev < 0 { + drop(self.take_to_wake()); + } else { + while self.to_wake.load(atomic::SeqCst) != 0 { + Thread::yield_now(); + } + } + // if the number of steals is -1, it was the pre-emptive -1 steal + // count from when we inherited a blocker. This is fine because + // we're just going to overwrite it with a real value. + assert!(self.steals == 0 || self.steals == -1); + self.steals = steals; + prev >= 0 + } + } +} + +#[unsafe_destructor] +impl<T: Send> Drop for Packet<T> { + fn drop(&mut self) { + // Note that this load is not only an assert for correctness about + // disconnection, but also a proper fence before the read of + // `to_wake`, so this assert cannot be removed with also removing + // the `to_wake` assert. + assert_eq!(self.cnt.load(atomic::SeqCst), DISCONNECTED); + assert_eq!(self.to_wake.load(atomic::SeqCst), 0); + assert_eq!(self.channels.load(atomic::SeqCst), 0); + } +} diff --git a/src/libstd/comm/spsc_queue.rs b/src/libstd/comm/spsc_queue.rs new file mode 100644 index 00000000000..db8fff772a4 --- /dev/null +++ b/src/libstd/comm/spsc_queue.rs @@ -0,0 +1,337 @@ +/* Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing official + * policies, either expressed or implied, of Dmitry Vyukov. + */ + +// http://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue + +//! A single-producer single-consumer concurrent queue +//! +//! This module contains the implementation of an SPSC queue which can be used +//! concurrently between two tasks. This data structure is safe to use and +//! enforces the semantics that there is one pusher and one popper. + +#![experimental] + +use core::prelude::*; + +use alloc::boxed::Box; +use core::mem; +use core::cell::UnsafeCell; + +use sync::atomic::{AtomicPtr, Relaxed, AtomicUint, Acquire, Release}; + +// Node within the linked list queue of messages to send +struct Node<T> { + // FIXME: this could be an uninitialized T if we're careful enough, and + // that would reduce memory usage (and be a bit faster). + // is it worth it? + value: Option<T>, // nullable for re-use of nodes + next: AtomicPtr<Node<T>>, // next node in the queue +} + +/// The single-producer single-consumer queue. This structure is not cloneable, +/// but it can be safely shared in an Arc if it is guaranteed that there +/// is only one popper and one pusher touching the queue at any one point in +/// time. +pub struct Queue<T> { + // consumer fields + tail: UnsafeCell<*mut Node<T>>, // where to pop from + tail_prev: AtomicPtr<Node<T>>, // where to pop from + + // producer fields + head: UnsafeCell<*mut Node<T>>, // where to push to + first: UnsafeCell<*mut Node<T>>, // where to get new nodes from + tail_copy: UnsafeCell<*mut Node<T>>, // between first/tail + + // Cache maintenance fields. Additions and subtractions are stored + // separately in order to allow them to use nonatomic addition/subtraction. + cache_bound: uint, + cache_additions: AtomicUint, + cache_subtractions: AtomicUint, +} + +impl<T: Send> Node<T> { + fn new() -> *mut Node<T> { + unsafe { + mem::transmute(box Node { + value: None, + next: AtomicPtr::new(0 as *mut Node<T>), + }) + } + } +} + +impl<T: Send> Queue<T> { + /// Creates a new queue. + /// + /// This is unsafe as the type system doesn't enforce a single + /// consumer-producer relationship. It also allows the consumer to `pop` + /// items while there is a `peek` active due to all methods having a + /// non-mutable receiver. + /// + /// # Arguments + /// + /// * `bound` - This queue implementation is implemented with a linked + /// list, and this means that a push is always a malloc. In + /// order to amortize this cost, an internal cache of nodes is + /// maintained to prevent a malloc from always being + /// necessary. This bound is the limit on the size of the + /// cache (if desired). If the value is 0, then the cache has + /// no bound. Otherwise, the cache will never grow larger than + /// `bound` (although the queue itself could be much larger. + pub unsafe fn new(bound: uint) -> Queue<T> { + let n1 = Node::new(); + let n2 = Node::new(); + (*n1).next.store(n2, Relaxed); + Queue { + tail: UnsafeCell::new(n2), + tail_prev: AtomicPtr::new(n1), + head: UnsafeCell::new(n2), + first: UnsafeCell::new(n1), + tail_copy: UnsafeCell::new(n1), + cache_bound: bound, + cache_additions: AtomicUint::new(0), + cache_subtractions: AtomicUint::new(0), + } + } + + /// Pushes a new value onto this queue. Note that to use this function + /// safely, it must be externally guaranteed that there is only one pusher. + pub fn push(&self, t: T) { + unsafe { + // Acquire a node (which either uses a cached one or allocates a new + // one), and then append this to the 'head' node. + let n = self.alloc(); + assert!((*n).value.is_none()); + (*n).value = Some(t); + (*n).next.store(0 as *mut Node<T>, Relaxed); + (**self.head.get()).next.store(n, Release); + *self.head.get() = n; + } + } + + unsafe fn alloc(&self) -> *mut Node<T> { + // First try to see if we can consume the 'first' node for our uses. + // We try to avoid as many atomic instructions as possible here, so + // the addition to cache_subtractions is not atomic (plus we're the + // only one subtracting from the cache). + if *self.first.get() != *self.tail_copy.get() { + if self.cache_bound > 0 { + let b = self.cache_subtractions.load(Relaxed); + self.cache_subtractions.store(b + 1, Relaxed); + } + let ret = *self.first.get(); + *self.first.get() = (*ret).next.load(Relaxed); + return ret; + } + // If the above fails, then update our copy of the tail and try + // again. + *self.tail_copy.get() = self.tail_prev.load(Acquire); + if *self.first.get() != *self.tail_copy.get() { + if self.cache_bound > 0 { + let b = self.cache_subtractions.load(Relaxed); + self.cache_subtractions.store(b + 1, Relaxed); + } + let ret = *self.first.get(); + *self.first.get() = (*ret).next.load(Relaxed); + return ret; + } + // If all of that fails, then we have to allocate a new node + // (there's nothing in the node cache). + Node::new() + } + + /// Attempts to pop a value from this queue. Remember that to use this type + /// safely you must ensure that there is only one popper at a time. + pub fn pop(&self) -> Option<T> { + unsafe { + // The `tail` node is not actually a used node, but rather a + // sentinel from where we should start popping from. Hence, look at + // tail's next field and see if we can use it. If we do a pop, then + // the current tail node is a candidate for going into the cache. + let tail = *self.tail.get(); + let next = (*tail).next.load(Acquire); + if next.is_null() { return None } + assert!((*next).value.is_some()); + let ret = (*next).value.take(); + + *self.tail.get() = next; + if self.cache_bound == 0 { + self.tail_prev.store(tail, Release); + } else { + // FIXME: this is dubious with overflow. + let additions = self.cache_additions.load(Relaxed); + let subtractions = self.cache_subtractions.load(Relaxed); + let size = additions - subtractions; + + if size < self.cache_bound { + self.tail_prev.store(tail, Release); + self.cache_additions.store(additions + 1, Relaxed); + } else { + (*self.tail_prev.load(Relaxed)).next.store(next, Relaxed); + // We have successfully erased all references to 'tail', so + // now we can safely drop it. + let _: Box<Node<T>> = mem::transmute(tail); + } + } + return ret; + } + } + + /// Attempts to peek at the head of the queue, returning `None` if the queue + /// has no data currently + /// + /// # Warning + /// The reference returned is invalid if it is not used before the consumer + /// pops the value off the queue. If the producer then pushes another value + /// onto the queue, it will overwrite the value pointed to by the reference. + pub fn peek<'a>(&'a self) -> Option<&'a mut T> { + // This is essentially the same as above with all the popping bits + // stripped out. + unsafe { + let tail = *self.tail.get(); + let next = (*tail).next.load(Acquire); + if next.is_null() { return None } + return (*next).value.as_mut(); + } + } +} + +#[unsafe_destructor] +impl<T: Send> Drop for Queue<T> { + fn drop(&mut self) { + unsafe { + let mut cur = *self.first.get(); + while !cur.is_null() { + let next = (*cur).next.load(Relaxed); + let _n: Box<Node<T>> = mem::transmute(cur); + cur = next; + } + } + } +} + +#[cfg(test)] +mod test { + use prelude::*; + + use sync::Arc; + use super::Queue; + + #[test] + fn smoke() { + unsafe { + let queue = Queue::new(0); + queue.push(1i); + queue.push(2); + assert_eq!(queue.pop(), Some(1i)); + assert_eq!(queue.pop(), Some(2)); + assert_eq!(queue.pop(), None); + queue.push(3); + queue.push(4); + assert_eq!(queue.pop(), Some(3)); + assert_eq!(queue.pop(), Some(4)); + assert_eq!(queue.pop(), None); + } + } + + #[test] + fn peek() { + unsafe { + let queue = Queue::new(0); + queue.push(vec![1i]); + + // Ensure the borrowchecker works + match queue.peek() { + Some(vec) => match vec.as_slice() { + // Note that `pop` is not allowed here due to borrow + [1] => {} + _ => return + }, + None => unreachable!() + } + + queue.pop(); + } + } + + #[test] + fn drop_full() { + unsafe { + let q = Queue::new(0); + q.push(box 1i); + q.push(box 2i); + } + } + + #[test] + fn smoke_bound() { + unsafe { + let q = Queue::new(0); + q.push(1i); + q.push(2); + assert_eq!(q.pop(), Some(1)); + assert_eq!(q.pop(), Some(2)); + assert_eq!(q.pop(), None); + q.push(3); + q.push(4); + assert_eq!(q.pop(), Some(3)); + assert_eq!(q.pop(), Some(4)); + assert_eq!(q.pop(), None); + } + } + + #[test] + fn stress() { + unsafe { + stress_bound(0); + stress_bound(1); + } + + unsafe fn stress_bound(bound: uint) { + let q = Arc::new(Queue::new(bound)); + + let (tx, rx) = channel(); + let q2 = q.clone(); + spawn(move|| { + for _ in range(0u, 100000) { + loop { + match q2.pop() { + Some(1i) => break, + Some(_) => panic!(), + None => {} + } + } + } + tx.send(()); + }); + for _ in range(0i, 100000) { + q.push(1); + } + rx.recv(); + } + } +} diff --git a/src/libstd/comm/stream.rs b/src/libstd/comm/stream.rs new file mode 100644 index 00000000000..b68f626060e --- /dev/null +++ b/src/libstd/comm/stream.rs @@ -0,0 +1,484 @@ +// Copyright 2014 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. + +/// Stream channels +/// +/// This is the flavor of channels which are optimized for one sender and one +/// receiver. The sender will be upgraded to a shared channel if the channel is +/// cloned. +/// +/// High level implementation details can be found in the comment of the parent +/// module. + +pub use self::Failure::*; +pub use self::UpgradeResult::*; +pub use self::SelectionResult::*; +use self::Message::*; + +use core::prelude::*; + +use core::cmp; +use core::int; +use thread::Thread; + +use sync::atomic; +use comm::spsc_queue as spsc; +use comm::Receiver; +use comm::blocking::{mod, SignalToken}; + +const DISCONNECTED: int = int::MIN; +#[cfg(test)] +const MAX_STEALS: int = 5; +#[cfg(not(test))] +const MAX_STEALS: int = 1 << 20; + +pub struct Packet<T> { + queue: spsc::Queue<Message<T>>, // internal queue for all message + + cnt: atomic::AtomicInt, // How many items are on this channel + steals: int, // How many times has a port received without blocking? + to_wake: atomic::AtomicUint, // SignalToken for the blocked thread to wake up + + port_dropped: atomic::AtomicBool, // flag if the channel has been destroyed. +} + +pub enum Failure<T> { + Empty, + Disconnected, + Upgraded(Receiver<T>), +} + +pub enum UpgradeResult { + UpSuccess, + UpDisconnected, + UpWoke(SignalToken), +} + +pub enum SelectionResult<T> { + SelSuccess, + SelCanceled, + SelUpgraded(SignalToken, Receiver<T>), +} + +// Any message could contain an "upgrade request" to a new shared port, so the +// internal queue it's a queue of T, but rather Message<T> +enum Message<T> { + Data(T), + GoUp(Receiver<T>), +} + +impl<T: Send> Packet<T> { + pub fn new() -> Packet<T> { + Packet { + queue: unsafe { spsc::Queue::new(128) }, + + cnt: atomic::AtomicInt::new(0), + steals: 0, + to_wake: atomic::AtomicUint::new(0), + + port_dropped: atomic::AtomicBool::new(false), + } + } + + pub fn send(&mut self, t: T) -> Result<(), T> { + // If the other port has deterministically gone away, then definitely + // must return the data back up the stack. Otherwise, the data is + // considered as being sent. + if self.port_dropped.load(atomic::SeqCst) { return Err(t) } + + match self.do_send(Data(t)) { + UpSuccess | UpDisconnected => {}, + UpWoke(token) => { token.signal(); } + } + Ok(()) + } + + pub fn upgrade(&mut self, up: Receiver<T>) -> UpgradeResult { + // If the port has gone away, then there's no need to proceed any + // further. + if self.port_dropped.load(atomic::SeqCst) { return UpDisconnected } + + self.do_send(GoUp(up)) + } + + fn do_send(&mut self, t: Message<T>) -> UpgradeResult { + self.queue.push(t); + match self.cnt.fetch_add(1, atomic::SeqCst) { + // As described in the mod's doc comment, -1 == wakeup + -1 => UpWoke(self.take_to_wake()), + // As as described before, SPSC queues must be >= -2 + -2 => UpSuccess, + + // Be sure to preserve the disconnected state, and the return value + // in this case is going to be whether our data was received or not. + // This manifests itself on whether we have an empty queue or not. + // + // Primarily, are required to drain the queue here because the port + // will never remove this data. We can only have at most one item to + // drain (the port drains the rest). + DISCONNECTED => { + self.cnt.store(DISCONNECTED, atomic::SeqCst); + let first = self.queue.pop(); + let second = self.queue.pop(); + assert!(second.is_none()); + + match first { + Some(..) => UpSuccess, // we failed to send the data + None => UpDisconnected, // we successfully sent data + } + } + + // Otherwise we just sent some data on a non-waiting queue, so just + // make sure the world is sane and carry on! + n => { assert!(n >= 0); UpSuccess } + } + } + + // Consumes ownership of the 'to_wake' field. + fn take_to_wake(&mut self) -> SignalToken { + let ptr = self.to_wake.load(atomic::SeqCst); + self.to_wake.store(0, atomic::SeqCst); + assert!(ptr != 0); + unsafe { SignalToken::cast_from_uint(ptr) } + } + + // Decrements the count on the channel for a sleeper, returning the sleeper + // back if it shouldn't sleep. Note that this is the location where we take + // steals into account. + fn decrement(&mut self, token: SignalToken) -> Result<(), SignalToken> { + assert_eq!(self.to_wake.load(atomic::SeqCst), 0); + let ptr = unsafe { token.cast_to_uint() }; + self.to_wake.store(ptr, atomic::SeqCst); + + let steals = self.steals; + self.steals = 0; + + match self.cnt.fetch_sub(1 + steals, atomic::SeqCst) { + DISCONNECTED => { self.cnt.store(DISCONNECTED, atomic::SeqCst); } + // If we factor in our steals and notice that the channel has no + // data, we successfully sleep + n => { + assert!(n >= 0); + if n - steals <= 0 { return Ok(()) } + } + } + + self.to_wake.store(0, atomic::SeqCst); + Err(unsafe { SignalToken::cast_from_uint(ptr) }) + } + + pub fn recv(&mut self) -> Result<T, Failure<T>> { + // Optimistic preflight check (scheduling is expensive). + match self.try_recv() { + Err(Empty) => {} + data => return data, + } + + // Welp, our channel has no data. Deschedule the current task and + // initiate the blocking protocol. + let (wait_token, signal_token) = blocking::tokens(); + if self.decrement(signal_token).is_ok() { + wait_token.wait() + } + + match self.try_recv() { + // Messages which actually popped from the queue shouldn't count as + // a steal, so offset the decrement here (we already have our + // "steal" factored into the channel count above). + data @ Ok(..) | + data @ Err(Upgraded(..)) => { + self.steals -= 1; + data + } + + data => data, + } + } + + pub fn try_recv(&mut self) -> Result<T, Failure<T>> { + match self.queue.pop() { + // If we stole some data, record to that effect (this will be + // factored into cnt later on). + // + // Note that we don't allow steals to grow without bound in order to + // prevent eventual overflow of either steals or cnt as an overflow + // would have catastrophic results. Sometimes, steals > cnt, but + // other times cnt > steals, so we don't know the relation between + // steals and cnt. This code path is executed only rarely, so we do + // a pretty slow operation, of swapping 0 into cnt, taking steals + // down as much as possible (without going negative), and then + // adding back in whatever we couldn't factor into steals. + Some(data) => { + if self.steals > MAX_STEALS { + match self.cnt.swap(0, atomic::SeqCst) { + DISCONNECTED => { + self.cnt.store(DISCONNECTED, atomic::SeqCst); + } + n => { + let m = cmp::min(n, self.steals); + self.steals -= m; + self.bump(n - m); + } + } + assert!(self.steals >= 0); + } + self.steals += 1; + match data { + Data(t) => Ok(t), + GoUp(up) => Err(Upgraded(up)), + } + } + + None => { + match self.cnt.load(atomic::SeqCst) { + n if n != DISCONNECTED => Err(Empty), + + // This is a little bit of a tricky case. We failed to pop + // data above, and then we have viewed that the channel is + // disconnected. In this window more data could have been + // sent on the channel. It doesn't really make sense to + // return that the channel is disconnected when there's + // actually data on it, so be extra sure there's no data by + // popping one more time. + // + // We can ignore steals because the other end is + // disconnected and we'll never need to really factor in our + // steals again. + _ => { + match self.queue.pop() { + Some(Data(t)) => Ok(t), + Some(GoUp(up)) => Err(Upgraded(up)), + None => Err(Disconnected), + } + } + } + } + } + } + + pub fn drop_chan(&mut self) { + // Dropping a channel is pretty simple, we just flag it as disconnected + // and then wakeup a blocker if there is one. + match self.cnt.swap(DISCONNECTED, atomic::SeqCst) { + -1 => { self.take_to_wake().signal(); } + DISCONNECTED => {} + n => { assert!(n >= 0); } + } + } + + pub fn drop_port(&mut self) { + // Dropping a port seems like a fairly trivial thing. In theory all we + // need to do is flag that we're disconnected and then everything else + // can take over (we don't have anyone to wake up). + // + // The catch for Ports is that we want to drop the entire contents of + // the queue. There are multiple reasons for having this property, the + // largest of which is that if another chan is waiting in this channel + // (but not received yet), then waiting on that port will cause a + // deadlock. + // + // So if we accept that we must now destroy the entire contents of the + // queue, this code may make a bit more sense. The tricky part is that + // we can't let any in-flight sends go un-dropped, we have to make sure + // *everything* is dropped and nothing new will come onto the channel. + + // The first thing we do is set a flag saying that we're done for. All + // sends are gated on this flag, so we're immediately guaranteed that + // there are a bounded number of active sends that we'll have to deal + // with. + self.port_dropped.store(true, atomic::SeqCst); + + // Now that we're guaranteed to deal with a bounded number of senders, + // we need to drain the queue. This draining process happens atomically + // with respect to the "count" of the channel. If the count is nonzero + // (with steals taken into account), then there must be data on the + // channel. In this case we drain everything and then try again. We will + // continue to fail while active senders send data while we're dropping + // data, but eventually we're guaranteed to break out of this loop + // (because there is a bounded number of senders). + let mut steals = self.steals; + while { + let cnt = self.cnt.compare_and_swap( + steals, DISCONNECTED, atomic::SeqCst); + cnt != DISCONNECTED && cnt != steals + } { + loop { + match self.queue.pop() { + Some(..) => { steals += 1; } + None => break + } + } + } + + // At this point in time, we have gated all future senders from sending, + // and we have flagged the channel as being disconnected. The senders + // still have some responsibility, however, because some sends may not + // complete until after we flag the disconnection. There are more + // details in the sending methods that see DISCONNECTED + } + + //////////////////////////////////////////////////////////////////////////// + // select implementation + //////////////////////////////////////////////////////////////////////////// + + // Tests to see whether this port can receive without blocking. If Ok is + // returned, then that's the answer. If Err is returned, then the returned + // port needs to be queried instead (an upgrade happened) + pub fn can_recv(&mut self) -> Result<bool, Receiver<T>> { + // We peek at the queue to see if there's anything on it, and we use + // this return value to determine if we should pop from the queue and + // upgrade this channel immediately. If it looks like we've got an + // upgrade pending, then go through the whole recv rigamarole to update + // the internal state. + match self.queue.peek() { + Some(&GoUp(..)) => { + match self.recv() { + Err(Upgraded(port)) => Err(port), + _ => unreachable!(), + } + } + Some(..) => Ok(true), + None => Ok(false) + } + } + + // increment the count on the channel (used for selection) + fn bump(&mut self, amt: int) -> int { + match self.cnt.fetch_add(amt, atomic::SeqCst) { + DISCONNECTED => { + self.cnt.store(DISCONNECTED, atomic::SeqCst); + DISCONNECTED + } + n => n + } + } + + // Attempts to start selecting on this port. Like a oneshot, this can fail + // immediately because of an upgrade. + pub fn start_selection(&mut self, token: SignalToken) -> SelectionResult<T> { + match self.decrement(token) { + Ok(()) => SelSuccess, + Err(token) => { + let ret = match self.queue.peek() { + Some(&GoUp(..)) => { + match self.queue.pop() { + Some(GoUp(port)) => SelUpgraded(token, port), + _ => unreachable!(), + } + } + Some(..) => SelCanceled, + None => SelCanceled, + }; + // Undo our decrement above, and we should be guaranteed that the + // previous value is positive because we're not going to sleep + let prev = self.bump(1); + assert!(prev == DISCONNECTED || prev >= 0); + return ret; + } + } + } + + // Removes a previous task from being blocked in this port + pub fn abort_selection(&mut self, + was_upgrade: bool) -> Result<bool, Receiver<T>> { + // If we're aborting selection after upgrading from a oneshot, then + // we're guarantee that no one is waiting. The only way that we could + // have seen the upgrade is if data was actually sent on the channel + // half again. For us, this means that there is guaranteed to be data on + // this channel. Furthermore, we're guaranteed that there was no + // start_selection previously, so there's no need to modify `self.cnt` + // at all. + // + // Hence, because of these invariants, we immediately return `Ok(true)`. + // Note that the data may not actually be sent on the channel just yet. + // The other end could have flagged the upgrade but not sent data to + // this end. This is fine because we know it's a small bounded windows + // of time until the data is actually sent. + if was_upgrade { + assert_eq!(self.steals, 0); + assert_eq!(self.to_wake.load(atomic::SeqCst), 0); + return Ok(true) + } + + // We want to make sure that the count on the channel goes non-negative, + // and in the stream case we can have at most one steal, so just assume + // that we had one steal. + let steals = 1; + let prev = self.bump(steals + 1); + + // If we were previously disconnected, then we know for sure that there + // is no task in to_wake, so just keep going + let has_data = if prev == DISCONNECTED { + assert_eq!(self.to_wake.load(atomic::SeqCst), 0); + true // there is data, that data is that we're disconnected + } else { + let cur = prev + steals + 1; + assert!(cur >= 0); + + // If the previous count was negative, then we just made things go + // positive, hence we passed the -1 boundary and we're responsible + // for removing the to_wake() field and trashing it. + // + // If the previous count was positive then we're in a tougher + // situation. A possible race is that a sender just incremented + // through -1 (meaning it's going to try to wake a task up), but it + // hasn't yet read the to_wake. In order to prevent a future recv() + // from waking up too early (this sender picking up the plastered + // over to_wake), we spin loop here waiting for to_wake to be 0. + // Note that this entire select() implementation needs an overhaul, + // and this is *not* the worst part of it, so this is not done as a + // final solution but rather out of necessity for now to get + // something working. + if prev < 0 { + drop(self.take_to_wake()); + } else { + while self.to_wake.load(atomic::SeqCst) != 0 { + Thread::yield_now(); + } + } + assert_eq!(self.steals, 0); + self.steals = steals; + + // if we were previously positive, then there's surely data to + // receive + prev >= 0 + }; + + // Now that we've determined that this queue "has data", we peek at the + // queue to see if the data is an upgrade or not. If it's an upgrade, + // then we need to destroy this port and abort selection on the + // upgraded port. + if has_data { + match self.queue.peek() { + Some(&GoUp(..)) => { + match self.queue.pop() { + Some(GoUp(port)) => Err(port), + _ => unreachable!(), + } + } + _ => Ok(true), + } + } else { + Ok(false) + } + } +} + +#[unsafe_destructor] +impl<T: Send> Drop for Packet<T> { + fn drop(&mut self) { + // Note that this load is not only an assert for correctness about + // disconnection, but also a proper fence before the read of + // `to_wake`, so this assert cannot be removed with also removing + // the `to_wake` assert. + assert_eq!(self.cnt.load(atomic::SeqCst), DISCONNECTED); + assert_eq!(self.to_wake.load(atomic::SeqCst), 0); + } +} diff --git a/src/libstd/comm/sync.rs b/src/libstd/comm/sync.rs new file mode 100644 index 00000000000..f75186e70e3 --- /dev/null +++ b/src/libstd/comm/sync.rs @@ -0,0 +1,475 @@ +// Copyright 2014 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. + +/// Synchronous channels/ports +/// +/// This channel implementation differs significantly from the asynchronous +/// implementations found next to it (oneshot/stream/share). This is an +/// implementation of a synchronous, bounded buffer channel. +/// +/// Each channel is created with some amount of backing buffer, and sends will +/// *block* until buffer space becomes available. A buffer size of 0 is valid, +/// which means that every successful send is paired with a successful recv. +/// +/// This flavor of channels defines a new `send_opt` method for channels which +/// is the method by which a message is sent but the task does not panic if it +/// cannot be delivered. +/// +/// Another major difference is that send() will *always* return back the data +/// if it couldn't be sent. This is because it is deterministically known when +/// the data is received and when it is not received. +/// +/// Implementation-wise, it can all be summed up with "use a mutex plus some +/// logic". The mutex used here is an OS native mutex, meaning that no user code +/// is run inside of the mutex (to prevent context switching). This +/// implementation shares almost all code for the buffered and unbuffered cases +/// of a synchronous channel. There are a few branches for the unbuffered case, +/// but they're mostly just relevant to blocking senders. + +use core::prelude::*; + +pub use self::Failure::*; +use self::Blocker::*; + +use vec::Vec; +use core::mem; + +use sync::{atomic, Mutex, MutexGuard}; +use comm::blocking::{mod, WaitToken, SignalToken}; +use comm::select::StartResult::{mod, Installed, Abort}; + +pub struct Packet<T> { + /// Only field outside of the mutex. Just done for kicks, but mainly because + /// the other shared channel already had the code implemented + channels: atomic::AtomicUint, + + lock: Mutex<State<T>>, +} + +struct State<T> { + disconnected: bool, // Is the channel disconnected yet? + queue: Queue, // queue of senders waiting to send data + blocker: Blocker, // currently blocked task on this channel + buf: Buffer<T>, // storage for buffered messages + cap: uint, // capacity of this channel + + /// A curious flag used to indicate whether a sender failed or succeeded in + /// blocking. This is used to transmit information back to the task that it + /// must dequeue its message from the buffer because it was not received. + /// This is only relevant in the 0-buffer case. This obviously cannot be + /// safely constructed, but it's guaranteed to always have a valid pointer + /// value. + canceled: Option<&'static mut bool>, +} + +/// Possible flavors of threads who can be blocked on this channel. +enum Blocker { + BlockedSender(SignalToken), + BlockedReceiver(SignalToken), + NoneBlocked +} + +/// Simple queue for threading tasks together. Nodes are stack-allocated, so +/// this structure is not safe at all +struct Queue { + head: *mut Node, + tail: *mut Node, +} + +struct Node { + token: Option<SignalToken>, + next: *mut Node, +} + +/// A simple ring-buffer +struct Buffer<T> { + buf: Vec<Option<T>>, + start: uint, + size: uint, +} + +#[deriving(Show)] +pub enum Failure { + Empty, + Disconnected, +} + +/// Atomically blocks the current thread, placing it into `slot`, unlocking `lock` +/// in the meantime. This re-locks the mutex upon returning. +fn wait<'a, 'b, T: Send>(lock: &'a Mutex<State<T>>, + mut guard: MutexGuard<'b, State<T>>, + f: fn(SignalToken) -> Blocker) + -> MutexGuard<'a, State<T>> +{ + let (wait_token, signal_token) = blocking::tokens(); + match mem::replace(&mut guard.blocker, f(signal_token)) { + NoneBlocked => {} + _ => unreachable!(), + } + drop(guard); // unlock + wait_token.wait(); // block + lock.lock() // relock +} + +/// Wakes up a thread, dropping the lock at the correct time +fn wakeup<T>(token: SignalToken, guard: MutexGuard<State<T>>) { + // We need to be careful to wake up the waiting task *outside* of the mutex + // in case it incurs a context switch. + drop(guard); + token.signal(); +} + +impl<T: Send> Packet<T> { + pub fn new(cap: uint) -> Packet<T> { + Packet { + channels: atomic::AtomicUint::new(1), + lock: Mutex::new(State { + disconnected: false, + blocker: NoneBlocked, + cap: cap, + canceled: None, + queue: Queue { + head: 0 as *mut Node, + tail: 0 as *mut Node, + }, + buf: Buffer { + buf: Vec::from_fn(cap + if cap == 0 {1} else {0}, |_| None), + start: 0, + size: 0, + }, + }), + } + } + + // wait until a send slot is available, returning locked access to + // the channel state. + fn acquire_send_slot(&self) -> MutexGuard<State<T>> { + let mut node = Node { token: None, next: 0 as *mut Node }; + loop { + let mut guard = self.lock.lock(); + // are we ready to go? + if guard.disconnected || guard.buf.size() < guard.buf.cap() { + return guard; + } + // no room; actually block + let wait_token = guard.queue.enqueue(&mut node); + drop(guard); + wait_token.wait(); + } + } + + pub fn send(&self, t: T) -> Result<(), T> { + let mut guard = self.acquire_send_slot(); + if guard.disconnected { return Err(t) } + guard.buf.enqueue(t); + + match mem::replace(&mut guard.blocker, NoneBlocked) { + // if our capacity is 0, then we need to wait for a receiver to be + // available to take our data. After waiting, we check again to make + // sure the port didn't go away in the meantime. If it did, we need + // to hand back our data. + NoneBlocked if guard.cap == 0 => { + let mut canceled = false; + assert!(guard.canceled.is_none()); + guard.canceled = Some(unsafe { mem::transmute(&mut canceled) }); + let mut guard = wait(&self.lock, guard, BlockedSender); + if canceled {Err(guard.buf.dequeue())} else {Ok(())} + } + + // success, we buffered some data + NoneBlocked => Ok(()), + + // success, someone's about to receive our buffered data. + BlockedReceiver(token) => { wakeup(token, guard); Ok(()) } + + BlockedSender(..) => panic!("lolwut"), + } + } + + pub fn try_send(&self, t: T) -> Result<(), super::TrySendError<T>> { + let mut guard = self.lock.lock(); + if guard.disconnected { + Err(super::RecvDisconnected(t)) + } else if guard.buf.size() == guard.buf.cap() { + Err(super::Full(t)) + } else if guard.cap == 0 { + // With capacity 0, even though we have buffer space we can't + // transfer the data unless there's a receiver waiting. + match mem::replace(&mut guard.blocker, NoneBlocked) { + NoneBlocked => Err(super::Full(t)), + BlockedSender(..) => unreachable!(), + BlockedReceiver(token) => { + guard.buf.enqueue(t); + wakeup(token, guard); + Ok(()) + } + } + } else { + // If the buffer has some space and the capacity isn't 0, then we + // just enqueue the data for later retrieval, ensuring to wake up + // any blocked receiver if there is one. + assert!(guard.buf.size() < guard.buf.cap()); + guard.buf.enqueue(t); + match mem::replace(&mut guard.blocker, NoneBlocked) { + BlockedReceiver(token) => wakeup(token, guard), + NoneBlocked => {} + BlockedSender(..) => unreachable!(), + } + Ok(()) + } + } + + // Receives a message from this channel + // + // When reading this, remember that there can only ever be one receiver at + // time. + pub fn recv(&self) -> Result<T, ()> { + let mut guard = self.lock.lock(); + + // Wait for the buffer to have something in it. No need for a while loop + // because we're the only receiver. + let mut waited = false; + if !guard.disconnected && guard.buf.size() == 0 { + guard = wait(&self.lock, guard, BlockedReceiver); + waited = true; + } + if guard.disconnected && guard.buf.size() == 0 { return Err(()) } + + // Pick up the data, wake up our neighbors, and carry on + assert!(guard.buf.size() > 0); + let ret = guard.buf.dequeue(); + self.wakeup_senders(waited, guard); + return Ok(ret); + } + + pub fn try_recv(&self) -> Result<T, Failure> { + let mut guard = self.lock.lock(); + + // Easy cases first + if guard.disconnected { return Err(Disconnected) } + if guard.buf.size() == 0 { return Err(Empty) } + + // Be sure to wake up neighbors + let ret = Ok(guard.buf.dequeue()); + self.wakeup_senders(false, guard); + + return ret; + } + + // Wake up pending senders after some data has been received + // + // * `waited` - flag if the receiver blocked to receive some data, or if it + // just picked up some data on the way out + // * `guard` - the lock guard that is held over this channel's lock + fn wakeup_senders(&self, waited: bool, mut guard: MutexGuard<State<T>>) { + let pending_sender1: Option<SignalToken> = guard.queue.dequeue(); + + // If this is a no-buffer channel (cap == 0), then if we didn't wait we + // need to ACK the sender. If we waited, then the sender waking us up + // was already the ACK. + let pending_sender2 = if guard.cap == 0 && !waited { + match mem::replace(&mut guard.blocker, NoneBlocked) { + NoneBlocked => None, + BlockedReceiver(..) => unreachable!(), + BlockedSender(token) => { + guard.canceled.take(); + Some(token) + } + } + } else { + None + }; + mem::drop(guard); + + // only outside of the lock do we wake up the pending tasks + pending_sender1.map(|t| t.signal()); + pending_sender2.map(|t| t.signal()); + } + + // Prepares this shared packet for a channel clone, essentially just bumping + // a refcount. + pub fn clone_chan(&self) { + self.channels.fetch_add(1, atomic::SeqCst); + } + + pub fn drop_chan(&self) { + // Only flag the channel as disconnected if we're the last channel + match self.channels.fetch_sub(1, atomic::SeqCst) { + 1 => {} + _ => return + } + + // Not much to do other than wake up a receiver if one's there + let mut guard = self.lock.lock(); + if guard.disconnected { return } + guard.disconnected = true; + match mem::replace(&mut guard.blocker, NoneBlocked) { + NoneBlocked => {} + BlockedSender(..) => unreachable!(), + BlockedReceiver(token) => wakeup(token, guard), + } + } + + pub fn drop_port(&self) { + let mut guard = self.lock.lock(); + + if guard.disconnected { return } + guard.disconnected = true; + + // If the capacity is 0, then the sender may want its data back after + // we're disconnected. Otherwise it's now our responsibility to destroy + // the buffered data. As with many other portions of this code, this + // needs to be careful to destroy the data *outside* of the lock to + // prevent deadlock. + let _data = if guard.cap != 0 { + mem::replace(&mut guard.buf.buf, Vec::new()) + } else { + Vec::new() + }; + let mut queue = mem::replace(&mut guard.queue, Queue { + head: 0 as *mut Node, + tail: 0 as *mut Node, + }); + + let waiter = match mem::replace(&mut guard.blocker, NoneBlocked) { + NoneBlocked => None, + BlockedSender(token) => { + *guard.canceled.take().unwrap() = true; + Some(token) + } + BlockedReceiver(..) => unreachable!(), + }; + mem::drop(guard); + + loop { + match queue.dequeue() { + Some(token) => { token.signal(); } + None => break, + } + } + waiter.map(|t| t.signal()); + } + + //////////////////////////////////////////////////////////////////////////// + // select implementation + //////////////////////////////////////////////////////////////////////////// + + // If Ok, the value is whether this port has data, if Err, then the upgraded + // port needs to be checked instead of this one. + pub fn can_recv(&self) -> bool { + let guard = self.lock.lock(); + guard.disconnected || guard.buf.size() > 0 + } + + // Attempts to start selection on this port. This can either succeed or fail + // because there is data waiting. + pub fn start_selection(&self, token: SignalToken) -> StartResult { + let mut guard = self.lock.lock(); + if guard.disconnected || guard.buf.size() > 0 { + Abort + } else { + match mem::replace(&mut guard.blocker, BlockedReceiver(token)) { + NoneBlocked => {} + BlockedSender(..) => unreachable!(), + BlockedReceiver(..) => unreachable!(), + } + Installed + } + } + + // Remove a previous selecting task from this port. This ensures that the + // blocked task will no longer be visible to any other threads. + // + // The return value indicates whether there's data on this port. + pub fn abort_selection(&self) -> bool { + let mut guard = self.lock.lock(); + match mem::replace(&mut guard.blocker, NoneBlocked) { + NoneBlocked => true, + BlockedSender(token) => { + guard.blocker = BlockedSender(token); + true + } + BlockedReceiver(token) => { drop(token); false } + } + } +} + +#[unsafe_destructor] +impl<T: Send> Drop for Packet<T> { + fn drop(&mut self) { + assert_eq!(self.channels.load(atomic::SeqCst), 0); + let mut guard = self.lock.lock(); + assert!(guard.queue.dequeue().is_none()); + assert!(guard.canceled.is_none()); + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// Buffer, a simple ring buffer backed by Vec<T> +//////////////////////////////////////////////////////////////////////////////// + +impl<T> Buffer<T> { + fn enqueue(&mut self, t: T) { + let pos = (self.start + self.size) % self.buf.len(); + self.size += 1; + let prev = mem::replace(&mut self.buf[pos], Some(t)); + assert!(prev.is_none()); + } + + fn dequeue(&mut self) -> T { + let start = self.start; + self.size -= 1; + self.start = (self.start + 1) % self.buf.len(); + self.buf[start].take().unwrap() + } + + fn size(&self) -> uint { self.size } + fn cap(&self) -> uint { self.buf.len() } +} + +//////////////////////////////////////////////////////////////////////////////// +// Queue, a simple queue to enqueue tasks with (stack-allocated nodes) +//////////////////////////////////////////////////////////////////////////////// + +impl Queue { + fn enqueue(&mut self, node: &mut Node) -> WaitToken { + let (wait_token, signal_token) = blocking::tokens(); + node.token = Some(signal_token); + node.next = 0 as *mut Node; + + if self.tail.is_null() { + self.head = node as *mut Node; + self.tail = node as *mut Node; + } else { + unsafe { + (*self.tail).next = node as *mut Node; + self.tail = node as *mut Node; + } + } + + wait_token + } + + fn dequeue(&mut self) -> Option<SignalToken> { + if self.head.is_null() { + return None + } + let node = self.head; + self.head = unsafe { (*node).next }; + if self.head.is_null() { + self.tail = 0 as *mut Node; + } + unsafe { + (*node).next = 0 as *mut Node; + Some((*node).token.take().unwrap()) + } + } +} diff --git a/src/libstd/dynamic_lib.rs b/src/libstd/dynamic_lib.rs index 0f119d44485..368abe7cb12 100644 --- a/src/libstd/dynamic_lib.rs +++ b/src/libstd/dynamic_lib.rs @@ -8,32 +8,22 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! - -Dynamic library facilities. - -A simple wrapper over the platform's dynamic library facilities - -*/ +//! Dynamic library facilities. +//! +//! A simple wrapper over the platform's dynamic library facilities #![experimental] #![allow(missing_docs)] -use clone::Clone; -use c_str::ToCStr; -use iter::Iterator; +use prelude::*; use mem; -use ops::*; -use option::*; use os; -use path::{Path,GenericPath}; -use result::*; -use slice::{AsSlice,SlicePrelude}; use str; -use string::String; -use vec::Vec; -pub struct DynamicLibrary { handle: *mut u8 } +#[allow(missing_copy_implementations)] +pub struct DynamicLibrary { + handle: *mut u8 +} impl Drop for DynamicLibrary { fn drop(&mut self) { @@ -210,13 +200,12 @@ mod test { target_os = "freebsd", target_os = "dragonfly"))] pub mod dl { - pub use self::Rtld::*; + use self::Rtld::*; - use c_str::{CString, ToCStr}; + use prelude::*; + use c_str::CString; use libc; use ptr; - use result::*; - use string::String; pub unsafe fn open_external<T: ToCStr>(filename: T) -> *mut u8 { filename.with_c_str(|raw_name| { @@ -228,9 +217,11 @@ pub mod dl { dlopen(ptr::null(), Lazy as libc::c_int) as *mut u8 } - pub fn check_for_errors_in<T>(f: || -> T) -> Result<T, String> { - use rustrt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; - static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; + pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where + F: FnOnce() -> T, + { + use sync::{StaticMutex, MUTEX_INIT}; + static LOCK: StaticMutex = MUTEX_INIT; unsafe { // dlerror isn't thread safe, so we need to lock around this entire // sequence @@ -259,6 +250,7 @@ pub mod dl { dlclose(handle as *mut libc::c_void); () } + #[deriving(Copy)] pub enum Rtld { Lazy = 1, Now = 2, @@ -280,13 +272,15 @@ pub mod dl { #[cfg(target_os = "windows")] pub mod dl { use c_str::ToCStr; - use iter::Iterator; + use iter::IteratorExt; use libc; + use ops::FnOnce; use os; use ptr; - use result::{Ok, Err, Result}; - use slice::SlicePrelude; - use str::StrPrelude; + use result::Result; + use result::Result::{Ok, Err}; + use slice::SliceExt; + use str::StrExt; use str; use string::String; use vec::Vec; @@ -306,7 +300,9 @@ pub mod dl { handle as *mut u8 } - pub fn check_for_errors_in<T>(f: || -> T) -> Result<T, String> { + pub fn check_for_errors_in<T, F>(f: F) -> Result<T, String> where + F: FnOnce() -> T, + { unsafe { SetLastError(0); diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 82ad893f88a..cd7d9aacc90 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -78,9 +78,9 @@ //! } //! ``` -use option::{Option, None}; -use kinds::Send; -use string::String; +use prelude::*; + +use str::Utf8Error; /// Base functionality for all errors in Rust. pub trait Error: Send { @@ -106,3 +106,14 @@ impl<E> FromError<E> for E { err } } + +impl Error for Utf8Error { + fn description(&self) -> &str { + match *self { + Utf8Error::TooShort => "invalid utf-8: not enough bytes", + Utf8Error::InvalidByte(..) => "invalid utf-8: corrupt contents", + } + } + + fn detail(&self) -> Option<String> { Some(self.to_string()) } +} diff --git a/src/libstd/failure.rs b/src/libstd/failure.rs index c23e043c174..7010eae6dba 100644 --- a/src/libstd/failure.rs +++ b/src/libstd/failure.rs @@ -10,22 +10,22 @@ #![experimental] -use alloc::boxed::Box; +use prelude::*; + use any::{Any, AnyRefExt}; +use cell::RefCell; use fmt; -use io::{Writer, IoResult}; -use kinds::Send; -use option::{Some, None}; -use result::Ok; -use rt::backtrace; -use rustrt::{Stderr, Stdio}; -use rustrt::local::Local; -use rustrt::task::Task; -use str::Str; -use string::String; +use io::IoResult; +use rt::{backtrace, unwind}; +use rt::util::{Stderr, Stdio}; +use thread::Thread; // Defined in this module instead of io::stdio so that the unwinding -local_data_key!(pub local_stderr: Box<Writer + Send>) +thread_local! { + pub static LOCAL_STDERR: RefCell<Option<Box<Writer + Send>>> = { + RefCell::new(None) + } +} impl Writer for Stdio { fn write(&mut self, bytes: &[u8]) -> IoResult<()> { @@ -37,68 +37,44 @@ impl Writer for Stdio { } } -pub fn on_fail(obj: &Any + Send, file: &'static str, line: uint) { +pub fn on_fail(obj: &(Any+Send), file: &'static str, line: uint) { let msg = match obj.downcast_ref::<&'static str>() { Some(s) => *s, None => match obj.downcast_ref::<String>() { - Some(s) => s.as_slice(), + Some(s) => s[], None => "Box<Any>", } }; let mut err = Stderr; - - // It is assumed that all reasonable rust code will have a local task at - // all times. This means that this `exists` will return true almost all of - // the time. There are border cases, however, when the runtime has - // *almost* set up the local task, but hasn't quite gotten there yet. In - // order to get some better diagnostics, we print on panic and - // immediately abort the whole process if there is no local task - // available. - if !Local::exists(None::<Task>) { - let _ = writeln!(&mut err, "panicked at '{}', {}:{}", msg, file, line); - if backtrace::log_enabled() { - let _ = backtrace::write(&mut err); - } else { - let _ = writeln!(&mut err, "run with `RUST_BACKTRACE=1` to \ - see a backtrace"); - } - return - } - - // Peel the name out of local task so we can print it. We've got to be sure - // that the local task is in TLS while we're printing as I/O may occur. - let (name, unwinding) = { - let mut t = Local::borrow(None::<Task>); - (t.name.take(), t.unwinder.unwinding()) - }; - { - let n = name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>"); - - match local_stderr.replace(None) { - Some(mut stderr) => { - // FIXME: what to do when the task printing panics? - let _ = writeln!(stderr, - "task '{}' panicked at '{}', {}:{}\n", - n, msg, file, line); - if backtrace::log_enabled() { - let _ = backtrace::write(&mut *stderr); - } - local_stderr.replace(Some(stderr)); + let thread = Thread::current(); + let name = thread.name().unwrap_or("<unnamed>"); + let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take()); + match prev { + Some(mut stderr) => { + // FIXME: what to do when the thread printing panics? + let _ = writeln!(stderr, + "thread '{}' panicked at '{}', {}:{}\n", + name, msg, file, line); + if backtrace::log_enabled() { + let _ = backtrace::write(&mut *stderr); } - None => { - let _ = writeln!(&mut err, "task '{}' panicked at '{}', {}:{}", - n, msg, file, line); - if backtrace::log_enabled() { - let _ = backtrace::write(&mut err); - } + let mut s = Some(stderr); + LOCAL_STDERR.with(|slot| { + *slot.borrow_mut() = s.take(); + }); + } + None => { + let _ = writeln!(&mut err, "thread '{}' panicked at '{}', {}:{}", + name, msg, file, line); + if backtrace::log_enabled() { + let _ = backtrace::write(&mut err); } } + } - // If this is a double panic, make sure that we printed a backtrace - // for this panic. - if unwinding && !backtrace::log_enabled() { - let _ = backtrace::write(&mut err); - } + // If this is a double panic, make sure that we printed a backtrace + // for this panic. + if unwind::panicking() && !backtrace::log_enabled() { + let _ = backtrace::write(&mut err); } - Local::borrow(None::<Task>).name = name; } diff --git a/src/libstd/fmt.rs b/src/libstd/fmt.rs index 2482fe63028..d0c9df9d914 100644 --- a/src/libstd/fmt.rs +++ b/src/libstd/fmt.rs @@ -10,403 +10,403 @@ // // ignore-lexer-test FIXME #15679 -/*! - -Utilities for formatting and printing strings - -This module contains the runtime support for the `format!` syntax extension. -This macro is implemented in the compiler to emit calls to this module in order -to format arguments at runtime into strings and streams. - -The functions contained in this module should not normally be used in everyday -use cases of `format!`. The assumptions made by these functions are unsafe for -all inputs, and the compiler performs a large amount of validation on the -arguments to `format!` in order to ensure safety at runtime. While it is -possible to call these functions directly, it is not recommended to do so in the -general case. - -## Usage - -The `format!` macro is intended to be familiar to those coming from C's -printf/fprintf functions or Python's `str.format` function. In its current -revision, the `format!` macro returns a `String` type which is the result of -the formatting. In the future it will also be able to pass in a stream to -format arguments directly while performing minimal allocations. - -Some examples of the `format!` extension are: - -```rust -# fn main() { -format!("Hello"); // => "Hello" -format!("Hello, {}!", "world"); // => "Hello, world!" -format!("The number is {}", 1i); // => "The number is 1" -format!("{}", (3i, 4i)); // => "(3, 4)" -format!("{value}", value=4i); // => "4" -format!("{} {}", 1i, 2u); // => "1 2" -# } -``` - -From these, you can see that the first argument is a format string. It is -required by the compiler for this to be a string literal; it cannot be a -variable passed in (in order to perform validity checking). The compiler will -then parse the format string and determine if the list of arguments provided is -suitable to pass to this format string. - -### Positional parameters - -Each formatting argument is allowed to specify which value argument it's -referencing, and if omitted it is assumed to be "the next argument". For -example, the format string `{} {} {}` would take three parameters, and they -would be formatted in the same order as they're given. The format string -`{2} {1} {0}`, however, would format arguments in reverse order. - -Things can get a little tricky once you start intermingling the two types of -positional specifiers. The "next argument" specifier can be thought of as an -iterator over the argument. Each time a "next argument" specifier is seen, the -iterator advances. This leads to behavior like this: - -```rust -format!("{1} {} {0} {}", 1i, 2i); // => "2 1 1 2" -``` - -The internal iterator over the argument has not been advanced by the time the -first `{}` is seen, so it prints the first argument. Then upon reaching the -second `{}`, the iterator has advanced forward to the second argument. -Essentially, parameters which explicitly name their argument do not affect -parameters which do not name an argument in terms of positional specifiers. - -A format string is required to use all of its arguments, otherwise it is a -compile-time error. You may refer to the same argument more than once in the -format string, although it must always be referred to with the same type. - -### Named parameters - -Rust itself does not have a Python-like equivalent of named parameters to a -function, but the `format!` macro is a syntax extension which allows it to -leverage named parameters. Named parameters are listed at the end of the -argument list and have the syntax: - -```text -identifier '=' expression -``` - -For example, the following `format!` expressions all use named argument: - -```rust -# fn main() { -format!("{argument}", argument = "test"); // => "test" -format!("{name} {}", 1i, name = 2i); // => "2 1" -format!("{a} {c} {b}", a="a", b=(), c=3i); // => "a 3 ()" -# } -``` - -It is illegal to put positional parameters (those without names) after arguments -which have names. Like with positional parameters, it is illegal to provide -named parameters that are unused by the format string. - -### Argument types - -Each argument's type is dictated by the format string. It is a requirement that -every argument is only ever referred to by one type. For example, this is an -invalid format string: - -```text -{0:d} {0:s} -``` - -This is invalid because the first argument is both referred to as an integer as -well as a string. - -Because formatting is done via traits, there is no requirement that the -`d` format actually takes an `int`, but rather it simply requires a type which -ascribes to the `Signed` formatting trait. There are various parameters which do -require a particular type, however. Namely if the syntax `{:.*s}` is used, then -the number of characters to print from the string precedes the actual string and -must have the type `uint`. Although a `uint` can be printed with `{:u}`, it is -illegal to reference an argument as such. For example, this is another invalid -format string: - -```text -{:.*s} {0:u} -``` - -### Formatting traits - -When requesting that an argument be formatted with a particular type, you are -actually requesting that an argument ascribes to a particular trait. This allows -multiple actual types to be formatted via `{:d}` (like `i8` as well as `int`). -The current mapping of types to traits is: - -* *nothing* ⇒ `Show` -* `o` ⇒ `Octal` -* `x` ⇒ `LowerHex` -* `X` ⇒ `UpperHex` -* `p` ⇒ `Pointer` -* `b` ⇒ `Binary` -* `e` ⇒ `LowerExp` -* `E` ⇒ `UpperExp` - -What this means is that any type of argument which implements the -`std::fmt::Binary` trait can then be formatted with `{:b}`. Implementations are -provided for these traits for a number of primitive types by the standard -library as well. If no format is specified (as in `{}` or `{:6}`), then the -format trait used is the `Show` trait. This is one of the more commonly -implemented traits when formatting a custom type. - -When implementing a format trait for your own type, you will have to implement a -method of the signature: - -```rust -# use std; -# mod fmt { pub type Result = (); } -# struct T; -# trait SomeName<T> { -fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result; -# } -``` - -Your type will be passed as `self` by-reference, and then the function should -emit output into the `f.buf` stream. It is up to each format trait -implementation to correctly adhere to the requested formatting parameters. The -values of these parameters will be listed in the fields of the `Formatter` -struct. In order to help with this, the `Formatter` struct also provides some -helper methods. - -Additionally, the return value of this function is `fmt::Result` which is a -typedef to `Result<(), IoError>` (also known as `IoResult<()>`). Formatting -implementations should ensure that they return errors from `write!` correctly -(propagating errors upward). - -An example of implementing the formatting traits would look -like: - -```rust -use std::fmt; -use std::f64; -use std::num::Float; - -struct Vector2D { - x: int, - y: int, -} - -impl fmt::Show for Vector2D { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // The `f` value implements the `Writer` trait, which is what the - // write! macro is expecting. Note that this formatting ignores the - // various flags provided to format strings. - write!(f, "({}, {})", self.x, self.y) - } -} - -// Different traits allow different forms of output of a type. The meaning of -// this format is to print the magnitude of a vector. -impl fmt::Binary for Vector2D { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let magnitude = (self.x * self.x + self.y * self.y) as f64; - let magnitude = magnitude.sqrt(); - - // Respect the formatting flags by using the helper method - // `pad_integral` on the Formatter object. See the method documentation - // for details, and the function `pad` can be used to pad strings. - let decimals = f.precision().unwrap_or(3); - let string = f64::to_str_exact(magnitude, decimals); - f.pad_integral(true, "", string.as_bytes()) - } -} - -fn main() { - let myvector = Vector2D { x: 3, y: 4 }; - - println!("{}", myvector); // => "(3, 4)" - println!("{:10.3b}", myvector); // => " 5.000" -} -``` - -### Related macros - -There are a number of related macros in the `format!` family. The ones that are -currently implemented are: - -```ignore -format! // described above -write! // first argument is a &mut io::Writer, the destination -writeln! // same as write but appends a newline -print! // the format string is printed to the standard output -println! // same as print but appends a newline -format_args! // described below. -``` - - -#### `write!` - -This and `writeln` are two macros which are used to emit the format string to a -specified stream. This is used to prevent intermediate allocations of format -strings and instead directly write the output. Under the hood, this function is -actually invoking the `write` function defined in this module. Example usage is: - -```rust -# #![allow(unused_must_use)] -use std::io; - -let mut w = Vec::new(); -write!(&mut w as &mut io::Writer, "Hello {}!", "world"); -``` - -#### `print!` - -This and `println` emit their output to stdout. Similarly to the `write!` macro, -the goal of these macros is to avoid intermediate allocations when printing -output. Example usage is: - -```rust -print!("Hello {}!", "world"); -println!("I have a newline {}", "character at the end"); -``` - -#### `format_args!` -This is a curious macro which is used to safely pass around -an opaque object describing the format string. This object -does not require any heap allocations to create, and it only -references information on the stack. Under the hood, all of -the related macros are implemented in terms of this. First -off, some example usage is: - -``` -use std::fmt; -use std::io; - -# #[allow(unused_must_use)] -# fn main() { -format_args!(fmt::format, "this returns {}", "String"); - -let some_writer: &mut io::Writer = &mut io::stdout(); -format_args!(|args| { write!(some_writer, "{}", args) }, "print with a {}", "closure"); - -fn my_fmt_fn(args: &fmt::Arguments) { - write!(&mut io::stdout(), "{}", args); -} -format_args!(my_fmt_fn, "or a {} too", "function"); -# } -``` - -The first argument of the `format_args!` macro is a function (or closure) which -takes one argument of type `&fmt::Arguments`. This structure can then be -passed to the `write` and `format` functions inside this module in order to -process the format string. The goal of this macro is to even further prevent -intermediate allocations when dealing formatting strings. - -For example, a logging library could use the standard formatting syntax, but it -would internally pass around this structure until it has been determined where -output should go to. - -It is unsafe to programmatically create an instance of `fmt::Arguments` because -the operations performed when executing a format string require the compile-time -checks provided by the compiler. The `format_args!` macro is the only method of -safely creating these structures, but they can be unsafely created with the -constructor provided. - -## Syntax - -The syntax for the formatting language used is drawn from other languages, so it -should not be too alien. Arguments are formatted with python-like syntax, -meaning that arguments are surrounded by `{}` instead of the C-like `%`. The -actual grammar for the formatting syntax is: - -```text -format_string := <text> [ format <text> ] * -format := '{' [ argument ] [ ':' format_spec ] '}' -argument := integer | identifier - -format_spec := [[fill]align][sign]['#'][0][width]['.' precision][type] -fill := character -align := '<' | '^' | '>' -sign := '+' | '-' -width := count -precision := count | '*' -type := identifier | '' -count := parameter | integer -parameter := integer '$' -``` - -## Formatting Parameters - -Each argument being formatted can be transformed by a number of formatting -parameters (corresponding to `format_spec` in the syntax above). These -parameters affect the string representation of what's being formatted. This -syntax draws heavily from Python's, so it may seem a bit familiar. - -### Fill/Alignment - -The fill character is provided normally in conjunction with the `width` -parameter. This indicates that if the value being formatted is smaller than -`width` some extra characters will be printed around it. The extra characters -are specified by `fill`, and the alignment can be one of two options: - -* `<` - the argument is left-aligned in `width` columns -* `^` - the argument is center-aligned in `width` columns -* `>` - the argument is right-aligned in `width` columns - -### Sign/#/0 - -These can all be interpreted as flags for a particular formatter. - -* '+' - This is intended for numeric types and indicates that the sign should - always be printed. Positive signs are never printed by default, and the - negative sign is only printed by default for the `Signed` trait. This - flag indicates that the correct sign (+ or -) should always be printed. -* '-' - Currently not used -* '#' - This flag is indicates that the "alternate" form of printing should be - used. By default, this only applies to the integer formatting traits and - performs like: - * `x` - precedes the argument with a "0x" - * `X` - precedes the argument with a "0x" - * `t` - precedes the argument with a "0b" - * `o` - precedes the argument with a "0o" -* '0' - This is used to indicate for integer formats that the padding should - both be done with a `0` character as well as be sign-aware. A format - like `{:08d}` would yield `00000001` for the integer `1`, while the same - format would yield `-0000001` for the integer `-1`. Notice that the - negative version has one fewer zero than the positive version. - -### Width - -This is a parameter for the "minimum width" that the format should take up. If -the value's string does not fill up this many characters, then the padding -specified by fill/alignment will be used to take up the required space. - -The default fill/alignment for non-numerics is a space and left-aligned. The -defaults for numeric formatters is also a space but with right-alignment. If the -'0' flag is specified for numerics, then the implicit fill character is '0'. - -The value for the width can also be provided as a `uint` in the list of -parameters by using the `2$` syntax indicating that the second argument is a -`uint` specifying the width. - -### Precision - -For non-numeric types, this can be considered a "maximum width". If the -resulting string is longer than this width, then it is truncated down to this -many characters and only those are emitted. - -For integral types, this has no meaning currently. - -For floating-point types, this indicates how many digits after the decimal point -should be printed. - -## Escaping - -The literal characters `{` and `}` may be included in a string by preceding them -with the same character. For example, the `{` character is escaped with `{{` and -the `}` character is escaped with `}}`. - -*/ +//! Utilities for formatting and printing strings +//! +//! This module contains the runtime support for the `format!` syntax extension. +//! This macro is implemented in the compiler to emit calls to this module in +//! order to format arguments at runtime into strings and streams. +//! +//! The functions contained in this module should not normally be used in +//! everyday use cases of `format!`. The assumptions made by these functions are +//! unsafe for all inputs, and the compiler performs a large amount of +//! validation on the arguments to `format!` in order to ensure safety at +//! runtime. While it is possible to call these functions directly, it is not +//! recommended to do so in the general case. +//! +//! ## Usage +//! +//! The `format!` macro is intended to be familiar to those coming from C's +//! printf/fprintf functions or Python's `str.format` function. In its current +//! revision, the `format!` macro returns a `String` type which is the result of +//! the formatting. In the future it will also be able to pass in a stream to +//! format arguments directly while performing minimal allocations. +//! +//! Some examples of the `format!` extension are: +//! +//! ```rust +//! # fn main() { +//! format!("Hello"); // => "Hello" +//! format!("Hello, {}!", "world"); // => "Hello, world!" +//! format!("The number is {}", 1i); // => "The number is 1" +//! format!("{}", (3i, 4i)); // => "(3, 4)" +//! format!("{value}", value=4i); // => "4" +//! format!("{} {}", 1i, 2u); // => "1 2" +//! # } +//! ``` +//! +//! From these, you can see that the first argument is a format string. It is +//! required by the compiler for this to be a string literal; it cannot be a +//! variable passed in (in order to perform validity checking). The compiler +//! will then parse the format string and determine if the list of arguments +//! provided is suitable to pass to this format string. +//! +//! ### Positional parameters +//! +//! Each formatting argument is allowed to specify which value argument it's +//! referencing, and if omitted it is assumed to be "the next argument". For +//! example, the format string `{} {} {}` would take three parameters, and they +//! would be formatted in the same order as they're given. The format string +//! `{2} {1} {0}`, however, would format arguments in reverse order. +//! +//! Things can get a little tricky once you start intermingling the two types of +//! positional specifiers. The "next argument" specifier can be thought of as an +//! iterator over the argument. Each time a "next argument" specifier is seen, +//! the iterator advances. This leads to behavior like this: +//! +//! ```rust +//! format!("{1} {} {0} {}", 1i, 2i); // => "2 1 1 2" +//! ``` +//! +//! The internal iterator over the argument has not been advanced by the time +//! the first `{}` is seen, so it prints the first argument. Then upon reaching +//! the second `{}`, the iterator has advanced forward to the second argument. +//! Essentially, parameters which explicitly name their argument do not affect +//! parameters which do not name an argument in terms of positional specifiers. +//! +//! A format string is required to use all of its arguments, otherwise it is a +//! compile-time error. You may refer to the same argument more than once in the +//! format string, although it must always be referred to with the same type. +//! +//! ### Named parameters +//! +//! Rust itself does not have a Python-like equivalent of named parameters to a +//! function, but the `format!` macro is a syntax extension which allows it to +//! leverage named parameters. Named parameters are listed at the end of the +//! argument list and have the syntax: +//! +//! ```text +//! identifier '=' expression +//! ``` +//! +//! For example, the following `format!` expressions all use named argument: +//! +//! ```rust +//! # fn main() { +//! format!("{argument}", argument = "test"); // => "test" +//! format!("{name} {}", 1i, name = 2i); // => "2 1" +//! format!("{a} {c} {b}", a="a", b=(), c=3i); // => "a 3 ()" +//! # } +//! ``` +//! +//! It is illegal to put positional parameters (those without names) after +//! arguments which have names. Like with positional parameters, it is illegal +//! to provide named parameters that are unused by the format string. +//! +//! ### Argument types +//! +//! Each argument's type is dictated by the format string. It is a requirement +//! that every argument is only ever referred to by one type. For example, this +//! is an invalid format string: +//! +//! ```text +//! {0:x} {0:o} +//! ``` +//! +//! This is invalid because the first argument is both referred to as a +//! hexidecimal as well as an +//! octal. +//! +//! There are various parameters which do require a particular type, however. +//! Namely if the syntax `{:.*}` is used, then the number of characters to print +//! precedes the actual object being formatted, and the number of characters +//! must have the type `uint`. Although a `uint` can be printed with `{}`, it is +//! illegal to reference an argument as such. For example this is another +//! invalid format string: +//! +//! ```text +//! {:.*} {0} +//! ``` +//! +//! ### Formatting traits +//! +//! When requesting that an argument be formatted with a particular type, you +//! are actually requesting that an argument ascribes to a particular trait. +//! This allows multiple actual types to be formatted via `{:x}` (like `i8` as +//! well as `int`). The current mapping of types to traits is: +//! +//! * *nothing* ⇒ `Show` +//! * `o` ⇒ `Octal` +//! * `x` ⇒ `LowerHex` +//! * `X` ⇒ `UpperHex` +//! * `p` ⇒ `Pointer` +//! * `b` ⇒ `Binary` +//! * `e` ⇒ `LowerExp` +//! * `E` ⇒ `UpperExp` +//! +//! What this means is that any type of argument which implements the +//! `std::fmt::Binary` trait can then be formatted with `{:b}`. Implementations +//! are provided for these traits for a number of primitive types by the +//! standard library as well. If no format is specified (as in `{}` or `{:6}`), +//! then the format trait used is the `Show` trait. This is one of the more +//! commonly implemented traits when formatting a custom type. +//! +//! When implementing a format trait for your own type, you will have to +//! implement a method of the signature: +//! +//! ```rust +//! # use std::fmt; +//! # struct Foo; // our custom type +//! # impl fmt::Show for Foo { +//! fn fmt(&self, f: &mut std::fmt::Formatter) -> fmt::Result { +//! # write!(f, "testing, testing") +//! # } } +//! ``` +//! +//! Your type will be passed as `self` by-reference, and then the function +//! should emit output into the `f.buf` stream. It is up to each format trait +//! implementation to correctly adhere to the requested formatting parameters. +//! The values of these parameters will be listed in the fields of the +//! `Formatter` struct. In order to help with this, the `Formatter` struct also +//! provides some helper methods. +//! +//! Additionally, the return value of this function is `fmt::Result` which is a +//! typedef to `Result<(), IoError>` (also known as `IoResult<()>`). Formatting +//! implementations should ensure that they return errors from `write!` +//! correctly (propagating errors upward). +//! +//! An example of implementing the formatting traits would look +//! like: +//! +//! ```rust +//! use std::fmt; +//! use std::f64; +//! use std::num::Float; +//! +//! struct Vector2D { +//! x: int, +//! y: int, +//! } +//! +//! impl fmt::Show for Vector2D { +//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +//! // The `f` value implements the `Writer` trait, which is what the +//! // write! macro is expecting. Note that this formatting ignores the +//! // various flags provided to format strings. +//! write!(f, "({}, {})", self.x, self.y) +//! } +//! } +//! +//! // Different traits allow different forms of output of a type. The meaning +//! // of this format is to print the magnitude of a vector. +//! impl fmt::Binary for Vector2D { +//! fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { +//! let magnitude = (self.x * self.x + self.y * self.y) as f64; +//! let magnitude = magnitude.sqrt(); +//! +//! // Respect the formatting flags by using the helper method +//! // `pad_integral` on the Formatter object. See the method documentation +//! // for details, and the function `pad` can be used to pad strings. +//! let decimals = f.precision().unwrap_or(3); +//! let string = f64::to_str_exact(magnitude, decimals); +//! f.pad_integral(true, "", string.as_bytes()) +//! } +//! } +//! +//! fn main() { +//! let myvector = Vector2D { x: 3, y: 4 }; +//! +//! println!("{}", myvector); // => "(3, 4)" +//! println!("{:10.3b}", myvector); // => " 5.000" +//! } +//! ``` +//! +//! ### Related macros +//! +//! There are a number of related macros in the `format!` family. The ones that +//! are currently implemented are: +//! +//! ```ignore +//! format! // described above +//! write! // first argument is a &mut io::Writer, the destination +//! writeln! // same as write but appends a newline +//! print! // the format string is printed to the standard output +//! println! // same as print but appends a newline +//! format_args! // described below. +//! ``` +//! +//! #### `write!` +//! +//! This and `writeln` are two macros which are used to emit the format string +//! to a specified stream. This is used to prevent intermediate allocations of +//! format strings and instead directly write the output. Under the hood, this +//! function is actually invoking the `write` function defined in this module. +//! Example usage is: +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::io; +//! +//! let mut w = Vec::new(); +//! write!(&mut w as &mut io::Writer, "Hello {}!", "world"); +//! ``` +//! +//! #### `print!` +//! +//! This and `println` emit their output to stdout. Similarly to the `write!` +//! macro, the goal of these macros is to avoid intermediate allocations when +//! printing output. Example usage is: +//! +//! ```rust +//! print!("Hello {}!", "world"); +//! println!("I have a newline {}", "character at the end"); +//! ``` +//! +//! #### `format_args!` +//! This is a curious macro which is used to safely pass around +//! an opaque object describing the format string. This object +//! does not require any heap allocations to create, and it only +//! references information on the stack. Under the hood, all of +//! the related macros are implemented in terms of this. First +//! off, some example usage is: +//! +//! ``` +//! use std::fmt; +//! use std::io; +//! +//! # #[allow(unused_must_use)] +//! # fn main() { +//! format_args!(fmt::format, "this returns {}", "String"); +//! +//! let some_writer: &mut io::Writer = &mut io::stdout(); +//! format_args!(|args| { write!(some_writer, "{}", args) }, +//! "print with a {}", "closure"); +//! +//! fn my_fmt_fn(args: &fmt::Arguments) { +//! write!(&mut io::stdout(), "{}", args); +//! } +//! format_args!(my_fmt_fn, "or a {} too", "function"); +//! # } +//! ``` +//! +//! The first argument of the `format_args!` macro is a function (or closure) +//! which takes one argument of type `&fmt::Arguments`. This structure can then +//! be passed to the `write` and `format` functions inside this module in order +//! to process the format string. The goal of this macro is to even further +//! prevent intermediate allocations when dealing formatting strings. +//! +//! For example, a logging library could use the standard formatting syntax, but +//! it would internally pass around this structure until it has been determined +//! where output should go to. +//! +//! It is unsafe to programmatically create an instance of `fmt::Arguments` +//! because the operations performed when executing a format string require the +//! compile-time checks provided by the compiler. The `format_args!` macro is +//! the only method of safely creating these structures, but they can be +//! unsafely created with the constructor provided. +//! +//! ## Syntax +//! +//! The syntax for the formatting language used is drawn from other languages, +//! so it should not be too alien. Arguments are formatted with python-like +//! syntax, meaning that arguments are surrounded by `{}` instead of the C-like +//! `%`. The actual grammar for the formatting syntax is: +//! +//! ```text +//! format_string := <text> [ format <text> ] * +//! format := '{' [ argument ] [ ':' format_spec ] '}' +//! argument := integer | identifier +//! +//! format_spec := [[fill]align][sign]['#'][0][width]['.' precision][type] +//! fill := character +//! align := '<' | '^' | '>' +//! sign := '+' | '-' +//! width := count +//! precision := count | '*' +//! type := identifier | '' +//! count := parameter | integer +//! parameter := integer '$' +//! ``` +//! +//! ## Formatting Parameters +//! +//! Each argument being formatted can be transformed by a number of formatting +//! parameters (corresponding to `format_spec` in the syntax above). These +//! parameters affect the string representation of what's being formatted. This +//! syntax draws heavily from Python's, so it may seem a bit familiar. +//! +//! ### Fill/Alignment +//! +//! The fill character is provided normally in conjunction with the `width` +//! parameter. This indicates that if the value being formatted is smaller than +//! `width` some extra characters will be printed around it. The extra +//! characters are specified by `fill`, and the alignment can be one of two +//! options: +//! +//! * `<` - the argument is left-aligned in `width` columns +//! * `^` - the argument is center-aligned in `width` columns +//! * `>` - the argument is right-aligned in `width` columns +//! +//! ### Sign/#/0 +//! +//! These can all be interpreted as flags for a particular formatter. +//! +//! * '+' - This is intended for numeric types and indicates that the sign +//! should always be printed. Positive signs are never printed by +//! default, and the negative sign is only printed by default for the +//! `Signed` trait. This flag indicates that the correct sign (+ or -) +//! should always be printed. +//! * '-' - Currently not used +//! * '#' - This flag is indicates that the "alternate" form of printing should +//! be used. By default, this only applies to the integer formatting +//! traits and performs like: +//! * `x` - precedes the argument with a "0x" +//! * `X` - precedes the argument with a "0x" +//! * `t` - precedes the argument with a "0b" +//! * `o` - precedes the argument with a "0o" +//! * '0' - This is used to indicate for integer formats that the padding should +//! both be done with a `0` character as well as be sign-aware. A format +//! like `{:08d}` would yield `00000001` for the integer `1`, while the +//! same format would yield `-0000001` for the integer `-1`. Notice that +//! the negative version has one fewer zero than the positive version. +//! +//! ### Width +//! +//! This is a parameter for the "minimum width" that the format should take up. +//! If the value's string does not fill up this many characters, then the +//! padding specified by fill/alignment will be used to take up the required +//! space. +//! +//! The default fill/alignment for non-numerics is a space and left-aligned. The +//! defaults for numeric formatters is also a space but with right-alignment. If +//! the '0' flag is specified for numerics, then the implicit fill character is +//! '0'. +//! +//! The value for the width can also be provided as a `uint` in the list of +//! parameters by using the `2$` syntax indicating that the second argument is a +//! `uint` specifying the width. +//! +//! ### Precision +//! +//! For non-numeric types, this can be considered a "maximum width". If the +//! resulting string is longer than this width, then it is truncated down to +//! this many characters and only those are emitted. +//! +//! For integral types, this has no meaning currently. +//! +//! For floating-point types, this indicates how many digits after the decimal +//! point should be printed. +//! +//! ## Escaping +//! +//! The literal characters `{` and `}` may be included in a string by preceding +//! them with the same character. For example, the `{` character is escaped with +//! `{{` and the `}` character is escaped with `}}`. #![experimental] use io::Writer; use io; -use result::{Ok, Err}; +use result::Result::{Ok, Err}; use string; use vec::Vec; @@ -418,7 +418,7 @@ pub use core::fmt::Error; pub use core::fmt::{Argument, Arguments, write, radix, Radix, RadixFmt}; #[doc(hidden)] -pub use core::fmt::{argument, argumentstr, argumentuint}; +pub use core::fmt::{argument, argumentuint}; /// The format function takes a precompiled format string and a list of /// arguments, to return the resulting formatted string. diff --git a/src/libstd/hash.rs b/src/libstd/hash.rs index e4017ea5a47..52e3c718b2d 100644 --- a/src/libstd/hash.rs +++ b/src/libstd/hash.rs @@ -8,62 +8,60 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! - * Generic hashing support. - * - * This module provides a generic way to compute the hash of a value. The - * simplest way to make a type hashable is to use `#[deriving(Hash)]`: - * - * # Example - * - * ```rust - * use std::hash; - * use std::hash::Hash; - * - * #[deriving(Hash)] - * struct Person { - * id: uint, - * name: String, - * phone: u64, - * } - * - * let person1 = Person { id: 5, name: "Janet".to_string(), phone: 555_666_7777 }; - * let person2 = Person { id: 5, name: "Bob".to_string(), phone: 555_666_7777 }; - * - * assert!(hash::hash(&person1) != hash::hash(&person2)); - * ``` - * - * If you need more control over how a value is hashed, you need to implement - * the trait `Hash`: - * - * ```rust - * use std::hash; - * use std::hash::Hash; - * use std::hash::sip::SipState; - * - * struct Person { - * id: uint, - * name: String, - * phone: u64, - * } - * - * impl Hash for Person { - * fn hash(&self, state: &mut SipState) { - * self.id.hash(state); - * self.phone.hash(state); - * } - * } - * - * let person1 = Person { id: 5, name: "Janet".to_string(), phone: 555_666_7777 }; - * let person2 = Person { id: 5, name: "Bob".to_string(), phone: 555_666_7777 }; - * - * assert!(hash::hash(&person1) == hash::hash(&person2)); - * ``` - */ +//! Generic hashing support. +//! +//! This module provides a generic way to compute the hash of a value. The +//! simplest way to make a type hashable is to use `#[deriving(Hash)]`: +//! +//! # Example +//! +//! ```rust +//! use std::hash; +//! use std::hash::Hash; +//! +//! #[deriving(Hash)] +//! struct Person { +//! id: uint, +//! name: String, +//! phone: u64, +//! } +//! +//! let person1 = Person { id: 5, name: "Janet".to_string(), phone: 555_666_7777 }; +//! let person2 = Person { id: 5, name: "Bob".to_string(), phone: 555_666_7777 }; +//! +//! assert!(hash::hash(&person1) != hash::hash(&person2)); +//! ``` +//! +//! If you need more control over how a value is hashed, you need to implement +//! the trait `Hash`: +//! +//! ```rust +//! use std::hash; +//! use std::hash::Hash; +//! use std::hash::sip::SipState; +//! +//! struct Person { +//! id: uint, +//! name: String, +//! phone: u64, +//! } +//! +//! impl Hash for Person { +//! fn hash(&self, state: &mut SipState) { +//! self.id.hash(state); +//! self.phone.hash(state); +//! } +//! } +//! +//! let person1 = Person { id: 5, name: "Janet".to_string(), phone: 555_666_7777 }; +//! let person2 = Person { id: 5, name: "Bob".to_string(), phone: 555_666_7777 }; +//! +//! assert!(hash::hash(&person1) == hash::hash(&person2)); +//! ``` #![experimental] -pub use core_collections::hash::{Hash, Hasher, Writer, hash, sip}; +pub use core::hash::{Hash, Hasher, Writer, hash, sip}; use core::kinds::Sized; use default::Default; @@ -97,7 +95,9 @@ impl Hasher<sip::SipState> for RandomSipHasher { } } +#[stable] impl Default for RandomSipHasher { + #[stable] #[inline] fn default() -> RandomSipHasher { RandomSipHasher::new() diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 25e85f33aa5..9d9e8827571 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -14,11 +14,12 @@ use cmp; use io::{Reader, Writer, Stream, Buffer, DEFAULT_BUF_SIZE, IoResult}; -use iter::ExactSize; +use iter::ExactSizeIterator; use ops::Drop; -use option::{Some, None, Option}; -use result::{Ok, Err}; -use slice::{SlicePrelude}; +use option::Option; +use option::Option::{Some, None}; +use result::Result::{Ok, Err}; +use slice::{SliceExt}; use slice; use vec::Vec; @@ -75,15 +76,23 @@ impl<R: Reader> BufferedReader<R> { } /// Gets a reference to the underlying reader. + pub fn get_ref<'a>(&self) -> &R { &self.inner } + + /// Gets a mutable reference to the underlying reader. /// - /// This type does not expose the ability to get a mutable reference to the - /// underlying reader because that could possibly corrupt the buffer. - pub fn get_ref<'a>(&'a self) -> &'a R { &self.inner } + /// # Warning + /// + /// It is inadvisable to directly read from the underlying reader. + pub fn get_mut(&mut self) -> &mut R { &mut self.inner } /// Unwraps this `BufferedReader`, returning the underlying reader. /// /// Note that any leftover data in the internal buffer is lost. - pub fn unwrap(self) -> R { self.inner } + pub fn into_inner(self) -> R { self.inner } + + /// Deprecated, use into_inner() instead + #[deprecated = "renamed to into_inner()"] + pub fn unwrap(self) -> R { self.into_inner() } } impl<R: Reader> Buffer for BufferedReader<R> { @@ -172,19 +181,27 @@ impl<W: Writer> BufferedWriter<W> { } /// Gets a reference to the underlying writer. + pub fn get_ref(&self) -> &W { self.inner.as_ref().unwrap() } + + /// Gets a mutable reference to the underlying write. /// - /// This type does not expose the ability to get a mutable reference to the - /// underlying reader because that could possibly corrupt the buffer. - pub fn get_ref<'a>(&'a self) -> &'a W { self.inner.as_ref().unwrap() } + /// # Warning + /// + /// It is inadvisable to directly read from the underlying writer. + pub fn get_mut(&mut self) -> &mut W { self.inner.as_mut().unwrap() } /// Unwraps this `BufferedWriter`, returning the underlying writer. /// /// The buffer is flushed before returning the writer. - pub fn unwrap(mut self) -> W { + pub fn into_inner(mut self) -> W { // FIXME(#12628): is panicking the right thing to do if flushing panicks? self.flush_buf().unwrap(); self.inner.take().unwrap() } + + /// Deprecated, use into_inner() instead + #[deprecated = "renamed to into_inner()"] + pub fn unwrap(self) -> W { self.into_inner() } } impl<W: Writer> Writer for BufferedWriter<W> { @@ -244,7 +261,11 @@ impl<W: Writer> LineBufferedWriter<W> { /// Unwraps this `LineBufferedWriter`, returning the underlying writer. /// /// The internal buffer is flushed before returning the writer. - pub fn unwrap(self) -> W { self.inner.unwrap() } + pub fn into_inner(self) -> W { self.inner.into_inner() } + + /// Deprecated, use into_inner() instead + #[deprecated = "renamed to into_inner()"] + pub fn unwrap(self) -> W { self.into_inner() } } impl<W: Writer> Writer for LineBufferedWriter<W> { @@ -329,22 +350,34 @@ impl<S: Stream> BufferedStream<S> { } /// Gets a reference to the underlying stream. - /// - /// This type does not expose the ability to get a mutable reference to the - /// underlying reader because that could possibly corrupt the buffer. - pub fn get_ref<'a>(&'a self) -> &'a S { + pub fn get_ref(&self) -> &S { let InternalBufferedWriter(ref w) = self.inner.inner; w.get_ref() } + /// Gets a mutable reference to the underlying stream. + /// + /// # Warning + /// + /// It is inadvisable to read directly from or write directly to the + /// underlying stream. + pub fn get_mut(&mut self) -> &mut S { + let InternalBufferedWriter(ref mut w) = self.inner.inner; + w.get_mut() + } + /// Unwraps this `BufferedStream`, returning the underlying stream. /// /// The internal buffer is flushed before returning the stream. Any leftover /// data in the read buffer is lost. - pub fn unwrap(self) -> S { + pub fn into_inner(self) -> S { let InternalBufferedWriter(w) = self.inner.inner; - w.unwrap() + w.into_inner() } + + /// Deprecated, use into_inner() instead + #[deprecated = "renamed to into_inner()"] + pub fn unwrap(self) -> S { self.into_inner() } } impl<S: Stream> Buffer for BufferedStream<S> { @@ -374,9 +407,8 @@ mod test { use prelude::*; use super::*; use super::super::{IoResult, EndOfFile}; - use super::super::mem::{MemReader, BufReader}; + use super::super::mem::MemReader; use self::test::Bencher; - use str::StrPrelude; /// A type, free to create, primarily intended for benchmarking creation of /// wrappers that, just for construction, don't need a Reader/Writer that @@ -417,30 +449,30 @@ mod test { let nread = reader.read(&mut buf); assert_eq!(Ok(3), nread); let b: &[_] = &[5, 6, 7]; - assert_eq!(buf.as_slice(), b); + assert_eq!(buf, b); let mut buf = [0, 0]; let nread = reader.read(&mut buf); assert_eq!(Ok(2), nread); let b: &[_] = &[0, 1]; - assert_eq!(buf.as_slice(), b); + assert_eq!(buf, b); let mut buf = [0]; let nread = reader.read(&mut buf); assert_eq!(Ok(1), nread); let b: &[_] = &[2]; - assert_eq!(buf.as_slice(), b); + assert_eq!(buf, b); let mut buf = [0, 0, 0]; let nread = reader.read(&mut buf); assert_eq!(Ok(1), nread); let b: &[_] = &[3, 0, 0]; - assert_eq!(buf.as_slice(), b); + assert_eq!(buf, b); let nread = reader.read(&mut buf); assert_eq!(Ok(1), nread); let b: &[_] = &[4, 0, 0]; - assert_eq!(buf.as_slice(), b); + assert_eq!(buf, b); assert!(reader.read(&mut buf).is_err()); } @@ -594,14 +626,14 @@ mod test { #[test] fn read_char_buffered() { let buf = [195u8, 159u8]; - let mut reader = BufferedReader::with_capacity(1, BufReader::new(&buf)); + let mut reader = BufferedReader::with_capacity(1, buf[]); assert_eq!(reader.read_char(), Ok('ß')); } #[test] fn test_chars() { let buf = [195u8, 159u8, b'a']; - let mut reader = BufferedReader::with_capacity(1, BufReader::new(&buf)); + let mut reader = BufferedReader::with_capacity(1, buf[]); let mut it = reader.chars(); assert_eq!(it.next(), Some(Ok('ß'))); assert_eq!(it.next(), Some(Ok('a'))); diff --git a/src/libstd/io/comm_adapters.rs b/src/libstd/io/comm_adapters.rs index a90b6bbbb8e..3a18b0dc1b5 100644 --- a/src/libstd/io/comm_adapters.rs +++ b/src/libstd/io/comm_adapters.rs @@ -12,9 +12,9 @@ use clone::Clone; use cmp; use comm::{Sender, Receiver}; use io; -use option::{None, Some}; -use result::{Ok, Err}; -use slice::{bytes, CloneSliceAllocPrelude, SlicePrelude}; +use option::Option::{None, Some}; +use result::Result::{Ok, Err}; +use slice::{bytes, CloneSliceExt, SliceExt}; use super::{Buffer, Reader, Writer, IoResult}; use vec::Vec; @@ -132,6 +132,7 @@ impl ChanWriter { } } +#[stable] impl Clone for ChanWriter { fn clone(&self) -> ChanWriter { ChanWriter { tx: self.tx.clone() } @@ -156,18 +157,18 @@ mod test { use prelude::*; use super::*; use io; - use task; + use thread::Thread; #[test] fn test_rx_reader() { let (tx, rx) = channel(); - task::spawn(proc() { + Thread::spawn(move|| { tx.send(vec![1u8, 2u8]); tx.send(vec![]); tx.send(vec![3u8, 4u8]); tx.send(vec![5u8, 6u8]); tx.send(vec![7u8, 8u8]); - }); + }).detach(); let mut reader = ChanReader::new(rx); let mut buf = [0u8, ..3]; @@ -176,41 +177,41 @@ mod test { assert_eq!(Ok(3), reader.read(&mut buf)); let a: &[u8] = &[1,2,3]; - assert_eq!(a, buf.as_slice()); + assert_eq!(a, buf); assert_eq!(Ok(3), reader.read(&mut buf)); let a: &[u8] = &[4,5,6]; - assert_eq!(a, buf.as_slice()); + assert_eq!(a, buf); assert_eq!(Ok(2), reader.read(&mut buf)); let a: &[u8] = &[7,8,6]; - assert_eq!(a, buf.as_slice()); + assert_eq!(a, buf); match reader.read(buf.as_mut_slice()) { Ok(..) => panic!(), Err(e) => assert_eq!(e.kind, io::EndOfFile), } - assert_eq!(a, buf.as_slice()); + assert_eq!(a, buf); // Ensure it continues to panic in the same way. match reader.read(buf.as_mut_slice()) { Ok(..) => panic!(), Err(e) => assert_eq!(e.kind, io::EndOfFile), } - assert_eq!(a, buf.as_slice()); + assert_eq!(a, buf); } #[test] fn test_rx_buffer() { let (tx, rx) = channel(); - task::spawn(proc() { + Thread::spawn(move|| { tx.send(b"he".to_vec()); tx.send(b"llo wo".to_vec()); tx.send(b"".to_vec()); tx.send(b"rld\nhow ".to_vec()); tx.send(b"are you?".to_vec()); tx.send(b"".to_vec()); - }); + }).detach(); let mut reader = ChanReader::new(rx); @@ -229,7 +230,7 @@ mod test { writer.write_be_u32(42).unwrap(); let wanted = vec![0u8, 0u8, 0u8, 42u8]; - let got = match task::try(proc() { rx.recv() }) { + let got = match Thread::spawn(move|| { rx.recv() }).join() { Ok(got) => got, Err(_) => panic!(), }; diff --git a/src/libstd/io/extensions.rs b/src/libstd/io/extensions.rs index 4b2ffb4d559..c1f1a5b7869 100644 --- a/src/libstd/io/extensions.rs +++ b/src/libstd/io/extensions.rs @@ -19,10 +19,12 @@ use io::{IoError, IoResult, Reader}; use io; use iter::Iterator; use num::Int; -use option::{Option, Some, None}; +use ops::FnOnce; +use option::Option; +use option::Option::{Some, None}; use ptr::RawPtr; -use result::{Ok, Err}; -use slice::{SlicePrelude, AsSlice}; +use result::Result::{Ok, Err}; +use slice::{SliceExt, AsSlice}; /// An iterator that reads a single byte on each iteration, /// until `.read_byte()` returns `EndOfFile`. @@ -75,7 +77,9 @@ impl<'r, R: Reader> Iterator<IoResult<u8>> for Bytes<'r, R> { /// * `f`: A callback that receives the value. /// /// This function returns the value returned by the callback, for convenience. -pub fn u64_to_le_bytes<T>(n: u64, size: uint, f: |v: &[u8]| -> T) -> T { +pub fn u64_to_le_bytes<T, F>(n: u64, size: uint, f: F) -> T where + F: FnOnce(&[u8]) -> T, +{ use mem::transmute; // LLVM fails to properly optimize this when using shifts instead of the to_le* intrinsics @@ -114,7 +118,9 @@ pub fn u64_to_le_bytes<T>(n: u64, size: uint, f: |v: &[u8]| -> T) -> T { /// * `f`: A callback that receives the value. /// /// This function returns the value returned by the callback, for convenience. -pub fn u64_to_be_bytes<T>(n: u64, size: uint, f: |v: &[u8]| -> T) -> T { +pub fn u64_to_be_bytes<T, F>(n: u64, size: uint, f: F) -> T where + F: FnOnce(&[u8]) -> T, +{ use mem::transmute; // LLVM fails to properly optimize this when using shifts instead of the to_be* intrinsics @@ -150,7 +156,7 @@ pub fn u64_to_be_bytes<T>(n: u64, size: uint, f: |v: &[u8]| -> T) -> T { /// 32-bit value is parsed. pub fn u64_from_be_bytes(data: &[u8], start: uint, size: uint) -> u64 { use ptr::{copy_nonoverlapping_memory}; - use slice::SlicePrelude; + use slice::SliceExt; assert!(size <= 8u); @@ -505,7 +511,7 @@ mod bench { use self::test::Bencher; // why is this a macro? wouldn't an inlined function work just as well? - macro_rules! u64_from_be_bytes_bench_impl( + macro_rules! u64_from_be_bytes_bench_impl { ($b:expr, $size:expr, $stride:expr, $start_index:expr) => ({ use super::u64_from_be_bytes; @@ -520,7 +526,7 @@ mod bench { } }); }) - ) + } #[bench] fn u64_from_be_bytes_4_aligned(b: &mut Bencher) { diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs index cd4141e045c..4e736908c37 100644 --- a/src/libstd/io/fs.rs +++ b/src/libstd/io/fs.rs @@ -10,61 +10,61 @@ // // ignore-lexer-test FIXME #15679 -/*! Synchronous File I/O - -This module provides a set of functions and traits for working -with regular files & directories on a filesystem. - -At the top-level of the module are a set of freestanding functions, associated -with various filesystem operations. They all operate on `Path` objects. - -All operations in this module, including those as part of `File` et al -block the task during execution. In the event of failure, all functions/methods -will return an `IoResult` type with an `Err` value. - -Also included in this module is an implementation block on the `Path` object -defined in `std::path::Path`. The impl adds useful methods about inspecting the -metadata of a file. This includes getting the `stat` information, reading off -particular bits of it, etc. - -# Example - -```rust -# #![allow(unused_must_use)] -use std::io::fs::PathExtensions; -use std::io::{File, fs}; - -let path = Path::new("foo.txt"); - -// create the file, whether it exists or not -let mut file = File::create(&path); -file.write(b"foobar"); -# drop(file); - -// open the file in read-only mode -let mut file = File::open(&path); -file.read_to_end(); - -println!("{}", path.stat().unwrap().size); -# drop(file); -fs::unlink(&path); -``` - -*/ +//! Synchronous File I/O +//! +//! This module provides a set of functions and traits for working +//! with regular files & directories on a filesystem. +//! +//! At the top-level of the module are a set of freestanding functions, associated +//! with various filesystem operations. They all operate on `Path` objects. +//! +//! All operations in this module, including those as part of `File` et al +//! block the task during execution. In the event of failure, all functions/methods +//! will return an `IoResult` type with an `Err` value. +//! +//! Also included in this module is an implementation block on the `Path` object +//! defined in `std::path::Path`. The impl adds useful methods about inspecting the +//! metadata of a file. This includes getting the `stat` information, reading off +//! particular bits of it, etc. +//! +//! # Example +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::io::fs::PathExtensions; +//! use std::io::{File, fs}; +//! +//! let path = Path::new("foo.txt"); +//! +//! // create the file, whether it exists or not +//! let mut file = File::create(&path); +//! file.write(b"foobar"); +//! # drop(file); +//! +//! // open the file in read-only mode +//! let mut file = File::open(&path); +//! file.read_to_end(); +//! +//! println!("{}", path.stat().unwrap().size); +//! # drop(file); +//! fs::unlink(&path); +//! ``` use clone::Clone; use io::standard_error; -use io::{FilePermission, Write, Open, FileAccess, FileMode}; -use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader}; +use io::{FilePermission, Write, Open, FileAccess, FileMode, FileType}; +use io::{IoResult, IoError, InvalidInput}; +use io::{FileStat, SeekStyle, Seek, Writer, Reader}; use io::{Read, Truncate, ReadWrite, Append}; use io::UpdateIoError; use io; use iter::{Iterator, Extend}; -use option::{Some, None, Option}; +use option::Option; +use option::Option::{Some, None}; use path::{Path, GenericPath}; use path; -use result::{Err, Ok}; -use slice::SlicePrelude; +use result::Result::{Err, Ok}; +use slice::SliceExt; use string::String; use vec::Vec; @@ -88,8 +88,8 @@ pub struct File { last_nread: int, } -impl sys_common::AsFileDesc for File { - fn as_fd(&self) -> &fs_imp::FileDesc { +impl sys_common::AsInner<fs_imp::FileDesc> for File { + fn as_inner(&self) -> &fs_imp::FileDesc { &self.fd } } @@ -136,13 +136,26 @@ impl File { pub fn open_mode(path: &Path, mode: FileMode, access: FileAccess) -> IoResult<File> { - fs_imp::open(path, mode, access).map(|fd| { - File { - path: path.clone(), - fd: fd, - last_nread: -1 + fs_imp::open(path, mode, access).and_then(|fd| { + // On *BSD systems, we can open a directory as a file and read from it: + // fd=open("/tmp", O_RDONLY); read(fd, buf, N); + // due to an old tradition before the introduction of opendir(3). + // We explicitly reject it because there are few use cases. + if cfg!(not(any(windows, target_os = "linux", target_os = "android"))) && + try!(fd.fstat()).kind == FileType::Directory { + Err(IoError { + kind: InvalidInput, + desc: "is a directory", + detail: None + }) + } else { + Ok(File { + path: path.clone(), + fd: fd, + last_nread: -1 + }) } - }).update_err("couldn't open file", |e| { + }).update_err("couldn't open path as file", |e| { format!("{}; path={}; mode={}; access={}", e, path.display(), mode_string(mode), access_string(access)) }) @@ -187,7 +200,7 @@ impl File { .update_desc("couldn't create file") } - /// Returns the original path which was used to open this file. + /// Returns the original path that was used to open this file. pub fn path<'a>(&'a self) -> &'a Path { &self.path } @@ -202,7 +215,7 @@ impl File { } /// This function is similar to `fsync`, except that it may not synchronize - /// file metadata to the filesystem. This is intended for use case which + /// file metadata to the filesystem. This is intended for use cases that /// must synchronize content, but don't need the metadata on disk. The goal /// of this method is to reduce disk operations. pub fn datasync(&mut self) -> IoResult<()> { @@ -239,7 +252,7 @@ impl File { } /// Queries information about the underlying file. - pub fn stat(&mut self) -> IoResult<FileStat> { + pub fn stat(&self) -> IoResult<FileStat> { self.fd.fstat() .update_err("couldn't fstat file", |e| format!("{}; path={}", e, self.path.display())) @@ -443,7 +456,7 @@ pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> { /// # Error /// /// This function will return an error on failure. Failure conditions include -/// reading a file that does not exist or reading a file which is not a symlink. +/// reading a file that does not exist or reading a file that is not a symlink. pub fn readlink(path: &Path) -> IoResult<Path> { fs_imp::readlink(path) .update_err("couldn't resolve symlink for path", |e| @@ -533,7 +546,7 @@ pub fn readdir(path: &Path) -> IoResult<Vec<Path>> { |e| format!("{}; path={}", e, path.display())) } -/// Returns an iterator which will recursively walk the directory structure +/// Returns an iterator that will recursively walk the directory structure /// rooted at `path`. The path given will not be iterated over, and this will /// perform iteration in some top-down order. The contents of unreadable /// subdirectories are ignored. @@ -544,7 +557,7 @@ pub fn walk_dir(path: &Path) -> IoResult<Directories> { }) } -/// An iterator which walks over a directory +/// An iterator that walks over a directory pub struct Directories { stack: Vec<Path>, } @@ -592,7 +605,7 @@ pub fn mkdir_recursive(path: &Path, mode: FilePermission) -> IoResult<()> { match result { Err(mkdir_err) => { // already exists ? - if try!(stat(&curpath)).kind != io::TypeDirectory { + if try!(stat(&curpath)).kind != FileType::Directory { return Err(mkdir_err); } } @@ -638,7 +651,7 @@ pub fn rmdir_recursive(path: &Path) -> IoResult<()> { false => try!(update_err(lstat(&child), path)) }; - if child_type.kind == io::TypeDirectory { + if child_type.kind == FileType::Directory { rm_stack.push(child); has_child_dir = true; } else { @@ -772,13 +785,13 @@ impl PathExtensions for path::Path { } fn is_file(&self) -> bool { match self.stat() { - Ok(s) => s.kind == io::TypeFile, + Ok(s) => s.kind == FileType::RegularFile, Err(..) => false } } fn is_dir(&self) -> bool { match self.stat() { - Ok(s) => s.kind == io::TypeDirectory, + Ok(s) => s.kind == FileType::Directory, Err(..) => false } } @@ -806,29 +819,25 @@ fn access_string(access: FileAccess) -> &'static str { #[allow(unused_mut)] mod test { use prelude::*; - use io::{SeekSet, SeekCur, SeekEnd, Read, Open, ReadWrite}; + use io::{SeekSet, SeekCur, SeekEnd, Read, Open, ReadWrite, FileType}; use io; use str; use io::fs::*; - use path::Path; - use io; - use ops::Drop; - use str::StrPrelude; - macro_rules! check( ($e:expr) => ( + macro_rules! check { ($e:expr) => ( match $e { Ok(t) => t, Err(e) => panic!("{} failed with: {}", stringify!($e), e), } - ) ) + ) } - macro_rules! error( ($e:expr, $s:expr) => ( + macro_rules! error { ($e:expr, $s:expr) => ( match $e { Ok(_) => panic!("Unexpected success. Should've been: {}", $s), - Err(ref err) => assert!(err.to_string().as_slice().contains($s.as_slice()), + Err(ref err) => assert!(err.to_string().contains($s.as_slice()), format!("`{}` did not contain `{}`", err, $s)) } - ) ) + ) } pub struct TempDir(Path); @@ -888,7 +897,7 @@ mod test { let filename = &tmpdir.join("file_that_does_not_exist.txt"); let result = File::open_mode(filename, Open, Read); - error!(result, "couldn't open file"); + error!(result, "couldn't open path as file"); if cfg!(unix) { error!(result, "no such file or directory"); } @@ -983,7 +992,7 @@ mod test { } check!(unlink(filename)); let read_str = str::from_utf8(&read_mem).unwrap(); - assert!(read_str.as_slice() == final_msg.as_slice()); + assert!(read_str == final_msg); } #[test] @@ -1028,12 +1037,12 @@ mod test { fs.write(msg.as_bytes()).unwrap(); let fstat_res = check!(fs.stat()); - assert_eq!(fstat_res.kind, io::TypeFile); + assert_eq!(fstat_res.kind, FileType::RegularFile); } let stat_res_fn = check!(stat(filename)); - assert_eq!(stat_res_fn.kind, io::TypeFile); + assert_eq!(stat_res_fn.kind, FileType::RegularFile); let stat_res_meth = check!(filename.stat()); - assert_eq!(stat_res_meth.kind, io::TypeFile); + assert_eq!(stat_res_meth.kind, FileType::RegularFile); check!(unlink(filename)); } @@ -1043,9 +1052,9 @@ mod test { let filename = &tmpdir.join("file_stat_correct_on_is_dir"); check!(mkdir(filename, io::USER_RWX)); let stat_res_fn = check!(stat(filename)); - assert!(stat_res_fn.kind == io::TypeDirectory); + assert!(stat_res_fn.kind == FileType::Directory); let stat_res_meth = check!(filename.stat()); - assert!(stat_res_meth.kind == io::TypeDirectory); + assert!(stat_res_meth.kind == FileType::Directory); check!(rmdir(filename)); } @@ -1091,7 +1100,7 @@ mod test { let f = dir.join(format!("{}.txt", n)); let mut w = check!(File::create(&f)); let msg_str = format!("{}{}", prefix, n.to_string()); - let msg = msg_str.as_slice().as_bytes(); + let msg = msg_str.as_bytes(); check!(w.write(msg)); } let files = check!(readdir(dir)); @@ -1202,7 +1211,7 @@ mod test { assert!(dirpath.is_dir()); let mut filepath = dirpath; - filepath.push("unicode-file-\uac00\u4e00\u30fc\u4f60\u597d.rs"); + filepath.push("unicode-file-\u{ac00}\u{4e00}\u{30fc}\u{4f60}\u{597d}.rs"); check!(File::create(&filepath)); // ignore return; touch only assert!(!filepath.is_dir()); assert!(filepath.exists()); @@ -1315,8 +1324,8 @@ mod test { check!(File::create(&input).write("foobar".as_bytes())); check!(symlink(&input, &out)); if cfg!(not(windows)) { - assert_eq!(check!(lstat(&out)).kind, io::TypeSymlink); - assert_eq!(check!(out.lstat()).kind, io::TypeSymlink); + assert_eq!(check!(lstat(&out)).kind, FileType::Symlink); + assert_eq!(check!(out.lstat()).kind, FileType::Symlink); } assert_eq!(check!(stat(&out)).size, check!(stat(&input)).size); assert_eq!(check!(File::open(&out).read_to_end()), @@ -1350,8 +1359,8 @@ mod test { check!(File::create(&input).write("foobar".as_bytes())); check!(link(&input, &out)); if cfg!(not(windows)) { - assert_eq!(check!(lstat(&out)).kind, io::TypeFile); - assert_eq!(check!(out.lstat()).kind, io::TypeFile); + assert_eq!(check!(lstat(&out)).kind, FileType::RegularFile); + assert_eq!(check!(out.lstat()).kind, FileType::RegularFile); assert_eq!(check!(stat(&out)).unstable.nlink, 2); assert_eq!(check!(out.stat()).unstable.nlink, 2); } @@ -1530,7 +1539,7 @@ mod test { check!(File::create(&tmpdir.join("test")).write(&bytes)); let actual = check!(File::open(&tmpdir.join("test")).read_to_end()); - assert!(actual.as_slice() == &bytes); + assert!(actual == bytes.as_slice()); } #[test] diff --git a/src/libstd/io/mem.rs b/src/libstd/io/mem.rs index 21de6c2013d..431e11cf9ca 100644 --- a/src/libstd/io/mem.rs +++ b/src/libstd/io/mem.rs @@ -15,11 +15,11 @@ #![allow(deprecated)] use cmp::min; -use option::None; -use result::{Err, Ok}; +use option::Option::None; +use result::Result::{Err, Ok}; use io; use io::{Reader, Writer, Seek, Buffer, IoError, SeekStyle, IoResult}; -use slice::{mod, AsSlice, SlicePrelude}; +use slice::{mod, AsSlice, SliceExt}; use vec::Vec; const BUF_CAPACITY: uint = 128; @@ -62,7 +62,7 @@ impl Writer for Vec<u8> { /// let mut w = MemWriter::new(); /// w.write(&[0, 1, 2]); /// -/// assert_eq!(w.unwrap(), vec!(0, 1, 2)); +/// assert_eq!(w.into_inner(), vec!(0, 1, 2)); /// ``` #[deprecated = "use the Vec<u8> Writer implementation directly"] #[deriving(Clone)] @@ -95,7 +95,11 @@ impl MemWriter { /// Unwraps this `MemWriter`, returning the underlying buffer #[inline] - pub fn unwrap(self) -> Vec<u8> { self.buf } + pub fn into_inner(self) -> Vec<u8> { self.buf } + + /// Deprecated, use into_inner() instead + #[deprecated = "renamed to into_inner()"] + pub fn unwrap(self) -> Vec<u8> { self.into_inner() } } impl Writer for MemWriter { @@ -150,7 +154,11 @@ impl MemReader { /// Unwraps this `MemReader`, returning the underlying buffer #[inline] - pub fn unwrap(self) -> Vec<u8> { self.buf } + pub fn into_inner(self) -> Vec<u8> { self.buf } + + /// Deprecated, use into_inner() instead + #[deprecated = "renamed to into_inner()"] + pub fn unwrap(self) -> Vec<u8> { self.into_inner() } } impl Reader for MemReader { @@ -198,6 +206,41 @@ impl Buffer for MemReader { fn consume(&mut self, amt: uint) { self.pos += amt; } } +impl<'a> Reader for &'a [u8] { + #[inline] + fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { + if self.is_empty() { return Err(io::standard_error(io::EndOfFile)); } + + let write_len = min(buf.len(), self.len()); + { + let input = self[..write_len]; + let output = buf[mut ..write_len]; + slice::bytes::copy_memory(output, input); + } + + *self = self.slice_from(write_len); + + Ok(write_len) + } +} + +impl<'a> Buffer for &'a [u8] { + #[inline] + fn fill_buf(&mut self) -> IoResult<&[u8]> { + if self.is_empty() { + Err(io::standard_error(io::EndOfFile)) + } else { + Ok(*self) + } + } + + #[inline] + fn consume(&mut self, amt: uint) { + *self = self[amt..]; + } +} + + /// Writes to a fixed-size byte slice /// /// If a write will not fit in the buffer, it returns an error and does not @@ -225,7 +268,7 @@ impl<'a> BufWriter<'a> { /// Creates a new `BufWriter` which will wrap the specified buffer. The /// writer initially starts at position 0. #[inline] - pub fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> { + pub fn new(buf: &'a mut [u8]) -> BufWriter<'a> { BufWriter { buf: buf, pos: 0 @@ -235,20 +278,29 @@ impl<'a> BufWriter<'a> { impl<'a> Writer for BufWriter<'a> { #[inline] - fn write(&mut self, buf: &[u8]) -> IoResult<()> { - // return an error if the entire write does not fit in the buffer - let cap = if self.pos >= self.buf.len() { 0 } else { self.buf.len() - self.pos }; - if buf.len() > cap { - return Err(IoError { - kind: io::OtherIoError, - desc: "Trying to write past end of buffer", - detail: None - }) + fn write(&mut self, src: &[u8]) -> IoResult<()> { + let dst = self.buf[mut self.pos..]; + let dst_len = dst.len(); + + if dst_len == 0 { + return Err(io::standard_error(io::EndOfFile)); } - slice::bytes::copy_memory(self.buf[mut self.pos..], buf); - self.pos += buf.len(); - Ok(()) + let src_len = src.len(); + + if dst_len >= src_len { + slice::bytes::copy_memory(dst, src); + + self.pos += src_len; + + Ok(()) + } else { + slice::bytes::copy_memory(dst, src[..dst_len]); + + self.pos += dst_len; + + Err(io::standard_error(io::ShortWrite(dst_len))) + } } } @@ -259,7 +311,7 @@ impl<'a> Seek for BufWriter<'a> { #[inline] fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> { let new = try!(combine(style, self.pos, self.buf.len(), pos)); - self.pos = new as uint; + self.pos = min(new as uint, self.buf.len()); Ok(()) } } @@ -272,10 +324,10 @@ impl<'a> Seek for BufWriter<'a> { /// # #![allow(unused_must_use)] /// use std::io::BufReader; /// -/// let mut buf = [0, 1, 2, 3]; -/// let mut r = BufReader::new(&mut buf); +/// let buf = [0, 1, 2, 3]; +/// let mut r = BufReader::new(&buf); /// -/// assert_eq!(r.read_to_end().unwrap(), vec!(0, 1, 2, 3)); +/// assert_eq!(r.read_to_end().unwrap(), vec![0, 1, 2, 3]); /// ``` pub struct BufReader<'a> { buf: &'a [u8], @@ -285,7 +337,7 @@ pub struct BufReader<'a> { impl<'a> BufReader<'a> { /// Creates a new buffered reader which will read the specified buffer #[inline] - pub fn new<'a>(buf: &'a [u8]) -> BufReader<'a> { + pub fn new(buf: &'a [u8]) -> BufReader<'a> { BufReader { buf: buf, pos: 0 @@ -332,7 +384,7 @@ impl<'a> Seek for BufReader<'a> { impl<'a> Buffer for BufReader<'a> { #[inline] - fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { + fn fill_buf(&mut self) -> IoResult<&[u8]> { if self.pos < self.buf.len() { Ok(self.buf[self.pos..]) } else { @@ -346,13 +398,22 @@ impl<'a> Buffer for BufReader<'a> { #[cfg(test)] mod test { - extern crate test; + extern crate "test" as test_crate; use prelude::*; use super::*; use io::*; use io; - use self::test::Bencher; - use str::StrPrelude; + use self::test_crate::Bencher; + + #[test] + fn test_vec_writer() { + let mut writer = Vec::new(); + writer.write(&[0]).unwrap(); + writer.write(&[1, 2, 3]).unwrap(); + writer.write(&[4, 5, 6, 7]).unwrap(); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; + assert_eq!(writer.as_slice(), b); + } #[test] fn test_mem_writer() { @@ -366,7 +427,7 @@ mod test { #[test] fn test_buf_writer() { - let mut buf = [0 as u8, ..8]; + let mut buf = [0 as u8, ..9]; { let mut writer = BufWriter::new(&mut buf); assert_eq!(writer.tell(), Ok(0)); @@ -377,9 +438,12 @@ mod test { assert_eq!(writer.tell(), Ok(8)); writer.write(&[]).unwrap(); assert_eq!(writer.tell(), Ok(8)); + + assert_eq!(writer.write(&[8, 9]).unwrap_err().kind, io::ShortWrite(1)); + assert_eq!(writer.write(&[10]).unwrap_err().kind, io::EndOfFile); } - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7]; - assert_eq!(buf.as_slice(), b); + let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; + assert_eq!(buf, b); } #[test] @@ -408,7 +472,7 @@ mod test { } let b: &[_] = &[1, 3, 2, 0, 0, 0, 0, 4]; - assert_eq!(buf.as_slice(), b); + assert_eq!(buf, b); } #[test] @@ -419,7 +483,7 @@ mod test { match writer.write(&[0, 0]) { Ok(..) => panic!(), - Err(e) => assert_eq!(e.kind, io::OtherIoError), + Err(e) => assert_eq!(e.kind, io::ShortWrite(1)), } } @@ -433,12 +497,12 @@ mod test { assert_eq!(reader.read(&mut buf), Ok(1)); assert_eq!(reader.tell(), Ok(1)); let b: &[_] = &[0]; - assert_eq!(buf.as_slice(), b); + assert_eq!(buf, b); let mut buf = [0, ..4]; assert_eq!(reader.read(&mut buf), Ok(4)); assert_eq!(reader.tell(), Ok(5)); let b: &[_] = &[1, 2, 3, 4]; - assert_eq!(buf.as_slice(), b); + assert_eq!(buf, b); assert_eq!(reader.read(&mut buf), Ok(3)); let b: &[_] = &[5, 6, 7]; assert_eq!(buf[0..3], b); @@ -450,6 +514,32 @@ mod test { } #[test] + fn test_slice_reader() { + let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; + let mut reader = &mut in_buf.as_slice(); + let mut buf = []; + assert_eq!(reader.read(&mut buf), Ok(0)); + let mut buf = [0]; + assert_eq!(reader.read(&mut buf), Ok(1)); + assert_eq!(reader.len(), 7); + let b: &[_] = &[0]; + assert_eq!(buf.as_slice(), b); + let mut buf = [0, ..4]; + assert_eq!(reader.read(&mut buf), Ok(4)); + assert_eq!(reader.len(), 3); + let b: &[_] = &[1, 2, 3, 4]; + assert_eq!(buf.as_slice(), b); + assert_eq!(reader.read(&mut buf), Ok(3)); + let b: &[_] = &[5, 6, 7]; + assert_eq!(buf[0..3], b); + assert!(reader.read(&mut buf).is_err()); + let mut reader = &mut in_buf.as_slice(); + assert_eq!(reader.read_until(3).unwrap(), vec!(0, 1, 2, 3)); + assert_eq!(reader.read_until(3).unwrap(), vec!(4, 5, 6, 7)); + assert!(reader.read(&mut buf).is_err()); + } + + #[test] fn test_buf_reader() { let in_buf = vec![0, 1, 2, 3, 4, 5, 6, 7]; let mut reader = BufReader::new(in_buf.as_slice()); @@ -460,12 +550,12 @@ mod test { assert_eq!(reader.read(&mut buf), Ok(1)); assert_eq!(reader.tell(), Ok(1)); let b: &[_] = &[0]; - assert_eq!(buf.as_slice(), b); + assert_eq!(buf, b); let mut buf = [0, ..4]; assert_eq!(reader.read(&mut buf), Ok(4)); assert_eq!(reader.tell(), Ok(5)); let b: &[_] = &[1, 2, 3, 4]; - assert_eq!(buf.as_slice(), b); + assert_eq!(buf, b); assert_eq!(reader.read(&mut buf), Ok(3)); let b: &[_] = &[5, 6, 7]; assert_eq!(buf[0..3], b); @@ -501,7 +591,7 @@ mod test { writer.write_line("testing").unwrap(); writer.write_str("testing").unwrap(); let mut r = BufReader::new(writer.get_ref()); - assert_eq!(r.read_to_string().unwrap(), "testingtesting\ntesting".to_string()); + assert_eq!(r.read_to_string().unwrap(), "testingtesting\ntesting"); } #[test] @@ -511,7 +601,7 @@ mod test { writer.write_char('\n').unwrap(); writer.write_char('ệ').unwrap(); let mut r = BufReader::new(writer.get_ref()); - assert_eq!(r.read_to_string().unwrap(), "a\nệ".to_string()); + assert_eq!(r.read_to_string().unwrap(), "a\nệ"); } #[test] @@ -561,15 +651,15 @@ mod test { let mut buf = [0, ..3]; assert!(r.read_at_least(buf.len(), &mut buf).is_ok()); let b: &[_] = &[1, 2, 3]; - assert_eq!(buf.as_slice(), b); + assert_eq!(buf, b); assert!(r.read_at_least(0, buf[mut ..0]).is_ok()); - assert_eq!(buf.as_slice(), b); + assert_eq!(buf, b); assert!(r.read_at_least(buf.len(), &mut buf).is_ok()); let b: &[_] = &[4, 5, 6]; - assert_eq!(buf.as_slice(), b); + assert_eq!(buf, b); assert!(r.read_at_least(buf.len(), &mut buf).is_err()); let b: &[_] = &[7, 8, 6]; - assert_eq!(buf.as_slice(), b); + assert_eq!(buf, b); } fn do_bench_mem_writer(b: &mut Bencher, times: uint, len: uint) { @@ -666,7 +756,7 @@ mod test { for _i in range(0u, 10) { let mut buf = [0 as u8, .. 10]; rdr.read(&mut buf).unwrap(); - assert_eq!(buf.as_slice(), [5, .. 10].as_slice()); + assert_eq!(buf, [5, .. 10]); } } }); diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index 681400e9db5..233ad781093 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -16,207 +16,206 @@ // error handling -/*! I/O, including files, networking, timers, and processes - -`std::io` provides Rust's basic I/O types, -for reading and writing to files, TCP, UDP, -and other types of sockets and pipes, -manipulating the file system, spawning processes. - -# Examples - -Some examples of obvious things you might want to do - -* Read lines from stdin - - ```rust - use std::io; - - for line in io::stdin().lines() { - print!("{}", line.unwrap()); - } - ``` - -* Read a complete file - - ```rust - use std::io::File; - - let contents = File::open(&Path::new("message.txt")).read_to_end(); - ``` - -* Write a line to a file - - ```rust - # #![allow(unused_must_use)] - use std::io::File; - - let mut file = File::create(&Path::new("message.txt")); - file.write(b"hello, file!\n"); - # drop(file); - # ::std::io::fs::unlink(&Path::new("message.txt")); - ``` - -* Iterate over the lines of a file - - ```rust,no_run - use std::io::BufferedReader; - use std::io::File; - - let path = Path::new("message.txt"); - let mut file = BufferedReader::new(File::open(&path)); - for line in file.lines() { - print!("{}", line.unwrap()); - } - ``` - -* Pull the lines of a file into a vector of strings - - ```rust,no_run - use std::io::BufferedReader; - use std::io::File; - - let path = Path::new("message.txt"); - let mut file = BufferedReader::new(File::open(&path)); - let lines: Vec<String> = file.lines().map(|x| x.unwrap()).collect(); - ``` - -* Make a simple TCP client connection and request - - ```rust - # #![allow(unused_must_use)] - use std::io::TcpStream; - - # // connection doesn't fail if a server is running on 8080 - # // locally, we still want to be type checking this code, so lets - # // just stop it running (#11576) - # if false { - let mut socket = TcpStream::connect("127.0.0.1:8080").unwrap(); - socket.write(b"GET / HTTP/1.0\n\n"); - let response = socket.read_to_end(); - # } - ``` - -* Make a simple TCP server - - ```rust - # fn main() { } - # fn foo() { - # #![allow(dead_code)] - use std::io::{TcpListener, TcpStream}; - use std::io::{Acceptor, Listener}; - - let listener = TcpListener::bind("127.0.0.1:80"); - - // bind the listener to the specified address - let mut acceptor = listener.listen(); - - fn handle_client(mut stream: TcpStream) { - // ... - # &mut stream; // silence unused mutability/variable warning - } - // accept connections and process them, spawning a new tasks for each one - for stream in acceptor.incoming() { - match stream { - Err(e) => { /* connection failed */ } - Ok(stream) => spawn(proc() { - // connection succeeded - handle_client(stream) - }) - } - } - - // close the socket server - drop(acceptor); - # } - ``` - - -# Error Handling - -I/O is an area where nearly every operation can result in unexpected -errors. Errors should be painfully visible when they happen, and handling them -should be easy to work with. It should be convenient to handle specific I/O -errors, and it should also be convenient to not deal with I/O errors. - -Rust's I/O employs a combination of techniques to reduce boilerplate -while still providing feedback about errors. The basic strategy: - -* All I/O operations return `IoResult<T>` which is equivalent to - `Result<T, IoError>`. The `Result` type is defined in the `std::result` - module. -* If the `Result` type goes unused, then the compiler will by default emit a - warning about the unused result. This is because `Result` has the - `#[must_use]` attribute. -* Common traits are implemented for `IoResult`, e.g. - `impl<R: Reader> Reader for IoResult<R>`, so that error values do not have - to be 'unwrapped' before use. - -These features combine in the API to allow for expressions like -`File::create(&Path::new("diary.txt")).write(b"Met a girl.\n")` -without having to worry about whether "diary.txt" exists or whether -the write succeeds. As written, if either `new` or `write_line` -encounters an error then the result of the entire expression will -be an error. - -If you wanted to handle the error though you might write: - -```rust -# #![allow(unused_must_use)] -use std::io::File; - -match File::create(&Path::new("diary.txt")).write(b"Met a girl.\n") { - Ok(()) => (), // succeeded - Err(e) => println!("failed to write to my diary: {}", e), -} - -# ::std::io::fs::unlink(&Path::new("diary.txt")); -``` - -So what actually happens if `create` encounters an error? -It's important to know that what `new` returns is not a `File` -but an `IoResult<File>`. If the file does not open, then `new` will simply -return `Err(..)`. Because there is an implementation of `Writer` (the trait -required ultimately required for types to implement `write_line`) there is no -need to inspect or unwrap the `IoResult<File>` and we simply call `write_line` -on it. If `new` returned an `Err(..)` then the followup call to `write_line` -will also return an error. - -## `try!` - -Explicit pattern matching on `IoResult`s can get quite verbose, especially -when performing many I/O operations. Some examples (like those above) are -alleviated with extra methods implemented on `IoResult`, but others have more -complex interdependencies among each I/O operation. - -The `try!` macro from `std::macros` is provided as a method of early-return -inside `Result`-returning functions. It expands to an early-return on `Err` -and otherwise unwraps the contained `Ok` value. - -If you wanted to read several `u32`s from a file and return their product: - -```rust -use std::io::{File, IoResult}; - -fn file_product(p: &Path) -> IoResult<u32> { - let mut f = File::open(p); - let x1 = try!(f.read_le_u32()); - let x2 = try!(f.read_le_u32()); - - Ok(x1 * x2) -} - -match file_product(&Path::new("numbers.bin")) { - Ok(x) => println!("{}", x), - Err(e) => println!("Failed to read numbers!") -} -``` - -With `try!` in `file_product`, each `read_le_u32` need not be directly -concerned with error handling; instead its caller is responsible for -responding to errors that may occur while attempting to read the numbers. - -*/ +//! I/O, including files, networking, timers, and processes +//! +//! `std::io` provides Rust's basic I/O types, +//! for reading and writing to files, TCP, UDP, +//! and other types of sockets and pipes, +//! manipulating the file system, spawning processes. +//! +//! # Examples +//! +//! Some examples of obvious things you might want to do +//! +//! * Read lines from stdin +//! +//! ```rust +//! use std::io; +//! +//! for line in io::stdin().lock().lines() { +//! print!("{}", line.unwrap()); +//! } +//! ``` +//! +//! * Read a complete file +//! +//! ```rust +//! use std::io::File; +//! +//! let contents = File::open(&Path::new("message.txt")).read_to_end(); +//! ``` +//! +//! * Write a line to a file +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::io::File; +//! +//! let mut file = File::create(&Path::new("message.txt")); +//! file.write(b"hello, file!\n"); +//! # drop(file); +//! # ::std::io::fs::unlink(&Path::new("message.txt")); +//! ``` +//! +//! * Iterate over the lines of a file +//! +//! ```rust,no_run +//! use std::io::BufferedReader; +//! use std::io::File; +//! +//! let path = Path::new("message.txt"); +//! let mut file = BufferedReader::new(File::open(&path)); +//! for line in file.lines() { +//! print!("{}", line.unwrap()); +//! } +//! ``` +//! +//! * Pull the lines of a file into a vector of strings +//! +//! ```rust,no_run +//! use std::io::BufferedReader; +//! use std::io::File; +//! +//! let path = Path::new("message.txt"); +//! let mut file = BufferedReader::new(File::open(&path)); +//! let lines: Vec<String> = file.lines().map(|x| x.unwrap()).collect(); +//! ``` +//! +//! * Make a simple TCP client connection and request +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::io::TcpStream; +//! +//! # // connection doesn't fail if a server is running on 8080 +//! # // locally, we still want to be type checking this code, so lets +//! # // just stop it running (#11576) +//! # if false { +//! let mut socket = TcpStream::connect("127.0.0.1:8080").unwrap(); +//! socket.write(b"GET / HTTP/1.0\n\n"); +//! let response = socket.read_to_end(); +//! # } +//! ``` +//! +//! * Make a simple TCP server +//! +//! ```rust +//! # fn main() { } +//! # fn foo() { +//! # #![allow(dead_code)] +//! use std::io::{TcpListener, TcpStream}; +//! use std::io::{Acceptor, Listener}; +//! use std::thread::Thread; +//! +//! let listener = TcpListener::bind("127.0.0.1:80"); +//! +//! // bind the listener to the specified address +//! let mut acceptor = listener.listen(); +//! +//! fn handle_client(mut stream: TcpStream) { +//! // ... +//! # &mut stream; // silence unused mutability/variable warning +//! } +//! // accept connections and process them, spawning a new tasks for each one +//! for stream in acceptor.incoming() { +//! match stream { +//! Err(e) => { /* connection failed */ } +//! Ok(stream) => Thread::spawn(move|| { +//! // connection succeeded +//! handle_client(stream) +//! }).detach() +//! } +//! } +//! +//! // close the socket server +//! drop(acceptor); +//! # } +//! ``` +//! +//! +//! # Error Handling +//! +//! I/O is an area where nearly every operation can result in unexpected +//! errors. Errors should be painfully visible when they happen, and handling them +//! should be easy to work with. It should be convenient to handle specific I/O +//! errors, and it should also be convenient to not deal with I/O errors. +//! +//! Rust's I/O employs a combination of techniques to reduce boilerplate +//! while still providing feedback about errors. The basic strategy: +//! +//! * All I/O operations return `IoResult<T>` which is equivalent to +//! `Result<T, IoError>`. The `Result` type is defined in the `std::result` +//! module. +//! * If the `Result` type goes unused, then the compiler will by default emit a +//! warning about the unused result. This is because `Result` has the +//! `#[must_use]` attribute. +//! * Common traits are implemented for `IoResult`, e.g. +//! `impl<R: Reader> Reader for IoResult<R>`, so that error values do not have +//! to be 'unwrapped' before use. +//! +//! These features combine in the API to allow for expressions like +//! `File::create(&Path::new("diary.txt")).write(b"Met a girl.\n")` +//! without having to worry about whether "diary.txt" exists or whether +//! the write succeeds. As written, if either `new` or `write_line` +//! encounters an error then the result of the entire expression will +//! be an error. +//! +//! If you wanted to handle the error though you might write: +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::io::File; +//! +//! match File::create(&Path::new("diary.txt")).write(b"Met a girl.\n") { +//! Ok(()) => (), // succeeded +//! Err(e) => println!("failed to write to my diary: {}", e), +//! } +//! +//! # ::std::io::fs::unlink(&Path::new("diary.txt")); +//! ``` +//! +//! So what actually happens if `create` encounters an error? +//! It's important to know that what `new` returns is not a `File` +//! but an `IoResult<File>`. If the file does not open, then `new` will simply +//! return `Err(..)`. Because there is an implementation of `Writer` (the trait +//! required ultimately required for types to implement `write_line`) there is no +//! need to inspect or unwrap the `IoResult<File>` and we simply call `write_line` +//! on it. If `new` returned an `Err(..)` then the followup call to `write_line` +//! will also return an error. +//! +//! ## `try!` +//! +//! Explicit pattern matching on `IoResult`s can get quite verbose, especially +//! when performing many I/O operations. Some examples (like those above) are +//! alleviated with extra methods implemented on `IoResult`, but others have more +//! complex interdependencies among each I/O operation. +//! +//! The `try!` macro from `std::macros` is provided as a method of early-return +//! inside `Result`-returning functions. It expands to an early-return on `Err` +//! and otherwise unwraps the contained `Ok` value. +//! +//! If you wanted to read several `u32`s from a file and return their product: +//! +//! ```rust +//! use std::io::{File, IoResult}; +//! +//! fn file_product(p: &Path) -> IoResult<u32> { +//! let mut f = File::open(p); +//! let x1 = try!(f.read_le_u32()); +//! let x2 = try!(f.read_le_u32()); +//! +//! Ok(x1 * x2) +//! } +//! +//! match file_product(&Path::new("numbers.bin")) { +//! Ok(x) => println!("{}", x), +//! Err(e) => println!("Failed to read numbers!") +//! } +//! ``` +//! +//! With `try!` in `file_product`, each `read_le_u32` need not be directly +//! concerned with error handling; instead its caller is responsible for +//! responding to errors that may occur while attempting to read the numbers. #![experimental] #![deny(unused_must_use)] @@ -224,7 +223,6 @@ responding to errors that may occur while attempting to read the numbers. pub use self::SeekStyle::*; pub use self::FileMode::*; pub use self::FileAccess::*; -pub use self::FileType::*; pub use self::IoErrorKind::*; use char::Char; @@ -233,19 +231,22 @@ use default::Default; use error::{FromError, Error}; use fmt; use int; -use iter::Iterator; +use iter::{Iterator, IteratorExt}; use mem::transmute; -use ops::{BitOr, BitXor, BitAnd, Sub, Not}; -use option::{Option, Some, None}; +use ops::{BitOr, BitXor, BitAnd, Sub, Not, FnOnce}; +use option::Option; +use option::Option::{Some, None}; use os; use boxed::Box; -use result::{Ok, Err, Result}; +use result::Result; +use result::Result::{Ok, Err}; use sys; -use slice::{AsSlice, SlicePrelude}; -use str::{Str, StrPrelude}; +use slice::SliceExt; +use str::StrExt; use str; use string::String; use uint; +use unicode; use unicode::char::UnicodeChar; use vec::Vec; @@ -319,7 +320,7 @@ impl IoError { pub fn from_errno(errno: uint, detail: bool) -> IoError { let mut err = sys::decode_error(errno as i32); if detail && err.kind == OtherIoError { - err.detail = Some(os::error_string(errno).as_slice().chars() + err.detail = Some(os::error_string(errno).chars() .map(|c| c.to_lowercase()).collect()) } err @@ -366,7 +367,7 @@ impl FromError<IoError> for Box<Error> { } /// A list specifying general categories of I/O error. -#[deriving(PartialEq, Eq, Clone, Show)] +#[deriving(Copy, PartialEq, Eq, Clone, Show)] pub enum IoErrorKind { /// Any I/O error not part of this list. OtherIoError, @@ -424,18 +425,22 @@ pub enum IoErrorKind { /// A trait that lets you add a `detail` to an IoError easily trait UpdateIoError<T> { /// Returns an IoError with updated description and detail - fn update_err(self, desc: &'static str, detail: |&IoError| -> String) -> Self; + fn update_err<D>(self, desc: &'static str, detail: D) -> Self where + D: FnOnce(&IoError) -> String; /// Returns an IoError with updated detail - fn update_detail(self, detail: |&IoError| -> String) -> Self; + fn update_detail<D>(self, detail: D) -> Self where + D: FnOnce(&IoError) -> String; /// Returns an IoError with update description fn update_desc(self, desc: &'static str) -> Self; } impl<T> UpdateIoError<T> for IoResult<T> { - fn update_err(self, desc: &'static str, detail: |&IoError| -> String) -> IoResult<T> { - self.map_err(|mut e| { + fn update_err<D>(self, desc: &'static str, detail: D) -> IoResult<T> where + D: FnOnce(&IoError) -> String, + { + self.map_err(move |mut e| { let detail = detail(&e); e.desc = desc; e.detail = Some(detail); @@ -443,8 +448,10 @@ impl<T> UpdateIoError<T> for IoResult<T> { }) } - fn update_detail(self, detail: |&IoError| -> String) -> IoResult<T> { - self.map_err(|mut e| { e.detail = Some(detail(&e)); e }) + fn update_detail<D>(self, detail: D) -> IoResult<T> where + D: FnOnce(&IoError) -> String, + { + self.map_err(move |mut e| { e.detail = Some(detail(&e)); e }) } fn update_desc(self, desc: &'static str) -> IoResult<T> { @@ -911,7 +918,7 @@ impl<'a> Reader for Box<Reader+'a> { } } -impl<'a> Reader for &'a mut Reader+'a { +impl<'a> Reader for &'a mut (Reader+'a) { fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { (*self).read(buf) } } @@ -973,7 +980,7 @@ impl<'a, R: Reader> Reader for RefReader<'a, R> { } impl<'a, R: Buffer> Buffer for RefReader<'a, R> { - fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> { self.inner.fill_buf() } + fn fill_buf(&mut self) -> IoResult<&[u8]> { self.inner.fill_buf() } fn consume(&mut self, amt: uint) { self.inner.consume(amt) } } @@ -1279,7 +1286,7 @@ impl<'a> Writer for Box<Writer+'a> { } } -impl<'a> Writer for &'a mut Writer+'a { +impl<'a> Writer for &'a mut (Writer+'a) { #[inline] fn write(&mut self, buf: &[u8]) -> IoResult<()> { (**self).write(buf) } @@ -1416,10 +1423,10 @@ pub trait Buffer: Reader { /// # Example /// /// ```rust - /// use std::io; + /// use std::io::BufReader; /// - /// let mut reader = io::stdin(); - /// let input = reader.read_line().ok().unwrap_or("nothing".to_string()); + /// let mut reader = BufReader::new(b"hello\nworld"); + /// assert_eq!("hello\n", &*reader.read_line().unwrap()); /// ``` /// /// # Error @@ -1499,7 +1506,7 @@ pub trait Buffer: Reader { /// valid utf-8 encoded codepoint as the next few bytes in the stream. fn read_char(&mut self) -> IoResult<char> { let first_byte = try!(self.read_byte()); - let width = str::utf8_char_width(first_byte); + let width = unicode::str::utf8_char_width(first_byte); if width == 1 { return Ok(first_byte as char) } if width == 0 { return Err(standard_error(InvalidInput)) } // not utf8 let mut buf = [first_byte, 0, 0, 0]; @@ -1513,7 +1520,7 @@ pub trait Buffer: Reader { } } } - match str::from_utf8(buf[..width]) { + match str::from_utf8(buf[..width]).ok() { Some(s) => Ok(s.char_at(0)), None => Err(standard_error(InvalidInput)) } @@ -1552,6 +1559,7 @@ impl<T: Buffer> BufferPrelude for T { /// When seeking, the resulting cursor is offset from a base by the offset given /// to the `seek` function. The base used is specified by this enumeration. +#[deriving(Copy)] pub enum SeekStyle { /// Seek from the beginning of the stream SeekSet, @@ -1674,6 +1682,7 @@ pub fn standard_error(kind: IoErrorKind) -> IoError { /// A mode specifies how a file should be opened or created. These modes are /// passed to `File::open_mode` and are used to control where the file is /// positioned when it is initially opened. +#[deriving(Copy)] pub enum FileMode { /// Opens a file positioned at the beginning. Open, @@ -1685,6 +1694,7 @@ pub enum FileMode { /// Access permissions with which the file should be opened. `File`s /// opened with `Read` will return an error if written to. +#[deriving(Copy)] pub enum FileAccess { /// Read-only access, requests to write will result in an error Read, @@ -1695,25 +1705,25 @@ pub enum FileAccess { } /// Different kinds of files which can be identified by a call to stat -#[deriving(PartialEq, Show, Hash, Clone)] +#[deriving(Copy, PartialEq, Show, Hash, Clone)] pub enum FileType { /// This is a normal file, corresponding to `S_IFREG` - TypeFile, + RegularFile, /// This file is a directory, corresponding to `S_IFDIR` - TypeDirectory, + Directory, /// This file is a named pipe, corresponding to `S_IFIFO` - TypeNamedPipe, + NamedPipe, /// This file is a block device, corresponding to `S_IFBLK` - TypeBlockSpecial, + BlockSpecial, /// This file is a symbolic link to another file, corresponding to `S_IFLNK` - TypeSymlink, + Symlink, /// The type of this file is not recognized as one of the other categories - TypeUnknown, + Unknown, } /// A structure used to describe metadata information about a file. This @@ -1733,7 +1743,7 @@ pub enum FileType { /// println!("byte size: {}", info.size); /// # } /// ``` -#[deriving(Hash)] +#[deriving(Copy, Hash)] pub struct FileStat { /// The size of the file, in bytes pub size: u64, @@ -1772,7 +1782,7 @@ pub struct FileStat { /// structure. This information is not necessarily platform independent, and may /// have different meanings or no meaning at all on some platforms. #[unstable] -#[deriving(Hash)] +#[deriving(Copy, Hash)] pub struct UnstableFileStat { /// The ID of the device containing the file. pub device: u64, @@ -1890,7 +1900,10 @@ bitflags! { } } + +#[stable] impl Default for FilePermission { + #[stable] #[inline] fn default() -> FilePermission { FilePermission::empty() } } @@ -2008,14 +2021,14 @@ mod tests { fn test_show() { use super::*; - assert_eq!(format!("{}", USER_READ), "0400".to_string()); - assert_eq!(format!("{}", USER_FILE), "0644".to_string()); - assert_eq!(format!("{}", USER_EXEC), "0755".to_string()); - assert_eq!(format!("{}", USER_RWX), "0700".to_string()); - assert_eq!(format!("{}", GROUP_RWX), "0070".to_string()); - assert_eq!(format!("{}", OTHER_RWX), "0007".to_string()); - assert_eq!(format!("{}", ALL_PERMISSIONS), "0777".to_string()); - assert_eq!(format!("{}", USER_READ | USER_WRITE | OTHER_WRITE), "0602".to_string()); + assert_eq!(format!("{}", USER_READ), "0400"); + assert_eq!(format!("{}", USER_FILE), "0644"); + assert_eq!(format!("{}", USER_EXEC), "0755"); + assert_eq!(format!("{}", USER_RWX), "0700"); + assert_eq!(format!("{}", GROUP_RWX), "0070"); + assert_eq!(format!("{}", OTHER_RWX), "0007"); + assert_eq!(format!("{}", ALL_PERMISSIONS), "0777"); + assert_eq!(format!("{}", USER_READ | USER_WRITE | OTHER_WRITE), "0602"); } fn _ensure_buffer_is_object_safe<T: Buffer>(x: &T) -> &Buffer { diff --git a/src/libstd/io/net/addrinfo.rs b/src/libstd/io/net/addrinfo.rs index 13f602de03a..69ba64d856e 100644 --- a/src/libstd/io/net/addrinfo.rs +++ b/src/libstd/io/net/addrinfo.rs @@ -8,14 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! - -Synchronous DNS Resolution - -Contains the functionality to perform DNS resolution in a style related to -getaddrinfo() - -*/ +//! Synchronous DNS Resolution +//! +//! Contains the functionality to perform DNS resolution in a style related to +//! `getaddrinfo()` #![allow(missing_docs)] @@ -23,14 +19,16 @@ pub use self::SocketType::*; pub use self::Flag::*; pub use self::Protocol::*; -use iter::Iterator; +use iter::IteratorExt; use io::{IoResult}; use io::net::ip::{SocketAddr, IpAddr}; -use option::{Option, Some, None}; +use option::Option; +use option::Option::{Some, None}; use sys; use vec::Vec; /// Hints to the types of sockets that are desired when looking up hosts +#[deriving(Copy)] pub enum SocketType { Stream, Datagram, Raw } @@ -39,6 +37,7 @@ pub enum SocketType { /// to manipulate how a query is performed. /// /// The meaning of each of these flags can be found with `man -s 3 getaddrinfo` +#[deriving(Copy)] pub enum Flag { AddrConfig, All, @@ -51,6 +50,7 @@ pub enum Flag { /// A transport protocol associated with either a hint or a return value of /// `lookup` +#[deriving(Copy)] pub enum Protocol { TCP, UDP } @@ -60,6 +60,7 @@ pub enum Protocol { /// /// For details on these fields, see their corresponding definitions via /// `man -s 3 getaddrinfo` +#[deriving(Copy)] pub struct Hint { pub family: uint, pub socktype: Option<SocketType>, @@ -67,6 +68,7 @@ pub struct Hint { pub flags: uint, } +#[deriving(Copy)] pub struct Info { pub address: SocketAddr, pub family: uint, diff --git a/src/libstd/io/net/ip.rs b/src/libstd/io/net/ip.rs index d87768a0860..add986387da 100644 --- a/src/libstd/io/net/ip.rs +++ b/src/libstd/io/net/ip.rs @@ -20,16 +20,18 @@ pub use self::IpAddr::*; use fmt; use io::{mod, IoResult, IoError}; use io::net; -use iter::Iterator; -use option::{Option, None, Some}; -use result::{Ok, Err}; -use str::{FromStr, StrPrelude}; -use slice::{CloneSlicePrelude, SlicePrelude}; +use iter::{Iterator, IteratorExt}; +use ops::FnOnce; +use option::Option; +use option::Option::{None, Some}; +use result::Result::{Ok, Err}; +use slice::{CloneSliceExt, SliceExt}; +use str::{FromStr, StrExt}; use vec::Vec; pub type Port = u16; -#[deriving(PartialEq, Eq, Clone, Hash)] +#[deriving(Copy, PartialEq, Eq, Clone, Hash)] pub enum IpAddr { Ipv4Addr(u8, u8, u8, u8), Ipv6Addr(u16, u16, u16, u16, u16, u16, u16, u16) @@ -60,7 +62,7 @@ impl fmt::Show for IpAddr { } } -#[deriving(PartialEq, Eq, Clone, Hash)] +#[deriving(Copy, PartialEq, Eq, Clone, Hash)] pub struct SocketAddr { pub ip: IpAddr, pub port: Port, @@ -94,8 +96,9 @@ impl<'a> Parser<'a> { } // Commit only if parser returns Some - fn read_atomically<T>(&mut self, cb: |&mut Parser| -> Option<T>) - -> Option<T> { + fn read_atomically<T, F>(&mut self, cb: F) -> Option<T> where + F: FnOnce(&mut Parser) -> Option<T>, + { let pos = self.pos; let r = cb(self); if r.is_none() { @@ -105,9 +108,10 @@ impl<'a> Parser<'a> { } // Commit only if parser read till EOF - fn read_till_eof<T>(&mut self, cb: |&mut Parser| -> Option<T>) - -> Option<T> { - self.read_atomically(|p| { + fn read_till_eof<T, F>(&mut self, cb: F) -> Option<T> where + F: FnOnce(&mut Parser) -> Option<T>, + { + self.read_atomically(move |p| { match cb(p) { Some(x) => if p.is_eof() {Some(x)} else {None}, None => None, @@ -128,15 +132,16 @@ impl<'a> Parser<'a> { } // Apply 3 parsers sequentially - fn read_seq_3<A, - B, - C>( - &mut self, - pa: |&mut Parser| -> Option<A>, - pb: |&mut Parser| -> Option<B>, - pc: |&mut Parser| -> Option<C>) - -> Option<(A, B, C)> { - self.read_atomically(|p| { + fn read_seq_3<A, B, C, PA, PB, PC>(&mut self, + pa: PA, + pb: PB, + pc: PC) + -> Option<(A, B, C)> where + PA: FnOnce(&mut Parser) -> Option<A>, + PB: FnOnce(&mut Parser) -> Option<B>, + PC: FnOnce(&mut Parser) -> Option<C>, + { + self.read_atomically(move |p| { let a = pa(p); let b = if a.is_some() { pb(p) } else { None }; let c = if b.is_some() { pc(p) } else { None }; @@ -321,22 +326,22 @@ impl<'a> Parser<'a> { } fn read_socket_addr(&mut self) -> Option<SocketAddr> { - let ip_addr = |p: &mut Parser| { + let ip_addr = |&: p: &mut Parser| { let ipv4_p = |p: &mut Parser| p.read_ip_addr(); let ipv6_p = |p: &mut Parser| { - let open_br = |p: &mut Parser| p.read_given_char('['); - let ip_addr = |p: &mut Parser| p.read_ipv6_addr(); - let clos_br = |p: &mut Parser| p.read_given_char(']'); - p.read_seq_3::<char, IpAddr, char>(open_br, ip_addr, clos_br) + let open_br = |&: p: &mut Parser| p.read_given_char('['); + let ip_addr = |&: p: &mut Parser| p.read_ipv6_addr(); + let clos_br = |&: p: &mut Parser| p.read_given_char(']'); + p.read_seq_3::<char, IpAddr, char, _, _, _>(open_br, ip_addr, clos_br) .map(|t| match t { (_, ip, _) => ip }) }; p.read_or(&mut [ipv4_p, ipv6_p]) }; - let colon = |p: &mut Parser| p.read_given_char(':'); - let port = |p: &mut Parser| p.read_number(10, 5, 0x10000).map(|n| n as u16); + let colon = |&: p: &mut Parser| p.read_given_char(':'); + let port = |&: p: &mut Parser| p.read_number(10, 5, 0x10000).map(|n| n as u16); // host, colon, port - self.read_seq_3::<IpAddr, char, u16>(ip_addr, colon, port) + self.read_seq_3::<IpAddr, char, u16, _, _, _>(ip_addr, colon, port) .map(|t| match t { (ip, _, port) => SocketAddr { ip: ip, port: port } }) } } @@ -378,8 +383,8 @@ impl FromStr for SocketAddr { /// expected by its `FromStr` implementation or a string like `<host_name>:<port>` pair /// where `<port>` is a `u16` value. /// -/// For the former, `to_socker_addr_all` returns a vector with a single element corresponding -/// to that socker address. +/// For the former, `to_socket_addr_all` returns a vector with a single element corresponding +/// to that socket address. /// /// For the latter, it tries to resolve the host name and returns a vector of all IP addresses /// for the host name, each joined with the port. @@ -438,7 +443,7 @@ pub trait ToSocketAddr { /// Converts this object to all available socket address values. /// - /// Some values like host name string naturally corrrespond to multiple IP addresses. + /// Some values like host name string naturally correspond to multiple IP addresses. /// This method tries to return all available addresses corresponding to this object. /// /// By default this method delegates to `to_socket_addr` method, creating a singleton @@ -468,7 +473,7 @@ fn resolve_socket_addr(s: &str, p: u16) -> IoResult<Vec<SocketAddr>> { } fn parse_and_resolve_socket_addr(s: &str) -> IoResult<Vec<SocketAddr>> { - macro_rules! try_opt( + macro_rules! try_opt { ($e:expr, $msg:expr) => ( match $e { Some(r) => r, @@ -479,7 +484,7 @@ fn parse_and_resolve_socket_addr(s: &str) -> IoResult<Vec<SocketAddr>> { }) } ) - ) + } // split the string by ':' and convert the second part to u16 let mut parts_iter = s.rsplitn(2, ':'); @@ -640,10 +645,10 @@ mod test { #[test] fn ipv6_addr_to_string() { let a1 = Ipv6Addr(0, 0, 0, 0, 0, 0xffff, 0xc000, 0x280); - assert!(a1.to_string() == "::ffff:192.0.2.128".to_string() || - a1.to_string() == "::FFFF:192.0.2.128".to_string()); + assert!(a1.to_string() == "::ffff:192.0.2.128" || + a1.to_string() == "::FFFF:192.0.2.128"); assert_eq!(Ipv6Addr(8, 9, 10, 11, 12, 13, 14, 15).to_string(), - "8:9:a:b:c:d:e:f".to_string()); + "8:9:a:b:c:d:e:f"); } #[test] diff --git a/src/libstd/io/net/mod.rs b/src/libstd/io/net/mod.rs index 5b1747876d7..2056933e6df 100644 --- a/src/libstd/io/net/mod.rs +++ b/src/libstd/io/net/mod.rs @@ -11,8 +11,9 @@ //! Networking I/O use io::{IoError, IoResult, InvalidInput}; -use option::None; -use result::{Ok, Err}; +use ops::FnMut; +use option::Option::None; +use result::Result::{Ok, Err}; use self::ip::{SocketAddr, ToSocketAddr}; pub use self::addrinfo::get_host_addresses; @@ -23,8 +24,10 @@ pub mod udp; pub mod ip; pub mod pipe; -fn with_addresses<A: ToSocketAddr, T>(addr: A, action: |SocketAddr| -> IoResult<T>) - -> IoResult<T> { +fn with_addresses<A, T, F>(addr: A, mut action: F) -> IoResult<T> where + A: ToSocketAddr, + F: FnMut(SocketAddr) -> IoResult<T>, +{ const DEFAULT_ERROR: IoError = IoError { kind: InvalidInput, desc: "no addresses found for hostname", diff --git a/src/libstd/io/net/pipe.rs b/src/libstd/io/net/pipe.rs index 8e934d221d2..01eb33b44f9 100644 --- a/src/libstd/io/net/pipe.rs +++ b/src/libstd/io/net/pipe.rs @@ -8,19 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! - -Named pipes - -This module contains the ability to communicate over named pipes with -synchronous I/O. On windows, this corresponds to talking over a Named Pipe, -while on Unix it corresponds to UNIX domain sockets. - -These pipes are similar to TCP in the sense that you can have both a stream to a -server and a server itself. The server provided accepts other `UnixStream` -instances as clients. - -*/ +//! Named pipes +//! +//! This module contains the ability to communicate over named pipes with +//! synchronous I/O. On windows, this corresponds to talking over a Named Pipe, +//! while on Unix it corresponds to UNIX domain sockets. +//! +//! These pipes are similar to TCP in the sense that you can have both a stream to a +//! server and a server itself. The server provided accepts other `UnixStream` +//! instances as clients. #![allow(missing_docs)] @@ -33,6 +29,8 @@ use sys::pipe::UnixStream as UnixStreamImp; use sys::pipe::UnixListener as UnixListenerImp; use sys::pipe::UnixAcceptor as UnixAcceptorImp; +use sys_common; + /// A stream which communicates over a named pipe. pub struct UnixStream { inner: UnixStreamImp, @@ -145,6 +143,12 @@ impl Writer for UnixStream { } } +impl sys_common::AsInner<UnixStreamImp> for UnixStream { + fn as_inner(&self) -> &UnixStreamImp { + &self.inner + } +} + /// A value that can listen for incoming named pipe connection requests. pub struct UnixListener { /// The internal, opaque runtime Unix listener. @@ -186,6 +190,12 @@ impl Listener<UnixStream, UnixAcceptor> for UnixListener { } } +impl sys_common::AsInner<UnixListenerImp> for UnixListener { + fn as_inner(&self) -> &UnixListenerImp { + &self.inner + } +} + /// A value that can accept named pipe connections, returned from `listen()`. pub struct UnixAcceptor { /// The internal, opaque runtime Unix acceptor. @@ -247,6 +257,12 @@ impl Clone for UnixAcceptor { } } +impl sys_common::AsInner<UnixAcceptorImp> for UnixAcceptor { + fn as_inner(&self) -> &UnixAcceptorImp { + &self.inner + } +} + #[cfg(test)] #[allow(experimental)] mod tests { @@ -257,13 +273,16 @@ mod tests { use io::fs::PathExtensions; use time::Duration; - pub fn smalltest(server: proc(UnixStream):Send, client: proc(UnixStream):Send) { + pub fn smalltest<F,G>(server: F, client: G) + where F : FnOnce(UnixStream), F : Send, + G : FnOnce(UnixStream), G : Send + { let path1 = next_test_unix(); let path2 = path1.clone(); let mut acceptor = UnixListener::bind(&path1).listen(); - spawn(proc() { + spawn(move|| { match UnixStream::connect(&path2) { Ok(c) => client(c), Err(e) => panic!("failed connect: {}", e), @@ -305,11 +324,11 @@ mod tests { #[test] fn smoke() { - smalltest(proc(mut server) { + smalltest(move |mut server| { let mut buf = [0]; server.read(&mut buf).unwrap(); assert!(buf[0] == 99); - }, proc(mut client) { + }, move|mut client| { client.write(&[99]).unwrap(); }) } @@ -317,18 +336,18 @@ mod tests { #[cfg_attr(windows, ignore)] // FIXME(#12516) #[test] fn read_eof() { - smalltest(proc(mut server) { + smalltest(move|mut server| { let mut buf = [0]; assert!(server.read(&mut buf).is_err()); assert!(server.read(&mut buf).is_err()); - }, proc(_client) { + }, move|_client| { // drop the client }) } #[test] fn write_begone() { - smalltest(proc(mut server) { + smalltest(move|mut server| { let buf = [0]; loop { match server.write(&buf) { @@ -342,7 +361,7 @@ mod tests { } } } - }, proc(_client) { + }, move|_client| { // drop the client }) } @@ -358,7 +377,7 @@ mod tests { Err(e) => panic!("failed listen: {}", e), }; - spawn(proc() { + spawn(move|| { for _ in range(0u, times) { let mut stream = UnixStream::connect(&path2); match stream.write(&[100]) { @@ -392,7 +411,7 @@ mod tests { let addr = next_test_unix(); let mut acceptor = UnixListener::bind(&addr).listen(); - spawn(proc() { + spawn(move|| { let mut s = UnixStream::connect(&addr); let mut buf = [0, 0]; debug!("client reading"); @@ -408,7 +427,7 @@ mod tests { let (tx1, rx1) = channel(); let (tx2, rx2) = channel(); - spawn(proc() { + spawn(move|| { let mut s2 = s2; rx1.recv(); debug!("writer writing"); @@ -431,7 +450,7 @@ mod tests { let (tx1, rx) = channel(); let tx2 = tx1.clone(); - spawn(proc() { + spawn(move|| { let mut s = UnixStream::connect(&addr); s.write(&[1]).unwrap(); rx.recv(); @@ -443,7 +462,7 @@ mod tests { let s2 = s1.clone(); let (done, rx) = channel(); - spawn(proc() { + spawn(move|| { let mut s2 = s2; let mut buf = [0, 0]; s2.read(&mut buf).unwrap(); @@ -462,7 +481,7 @@ mod tests { let addr = next_test_unix(); let mut acceptor = UnixListener::bind(&addr).listen(); - spawn(proc() { + spawn(move|| { let mut s = UnixStream::connect(&addr); let buf = &mut [0, 1]; s.read(buf).unwrap(); @@ -473,7 +492,7 @@ mod tests { let s2 = s1.clone(); let (tx, rx) = channel(); - spawn(proc() { + spawn(move|| { let mut s2 = s2; s2.write(&[1]).unwrap(); tx.send(()); @@ -520,7 +539,7 @@ mod tests { // continue to receive any pending connections. let (tx, rx) = channel(); let addr2 = addr.clone(); - spawn(proc() { + spawn(move|| { tx.send(UnixStream::connect(&addr2).unwrap()); }); let l = rx.recv(); @@ -530,7 +549,7 @@ mod tests { Err(ref e) if e.kind == TimedOut => {} Err(e) => panic!("error: {}", e), } - ::task::deschedule(); + ::thread::Thread::yield_now(); if i == 1000 { panic!("should have a pending connection") } } drop(l); @@ -538,7 +557,7 @@ mod tests { // Unset the timeout and make sure that this always blocks. a.set_timeout(None); let addr2 = addr.clone(); - spawn(proc() { + spawn(move|| { drop(UnixStream::connect(&addr2).unwrap()); }); a.accept().unwrap(); @@ -576,7 +595,7 @@ mod tests { let addr = next_test_unix(); let a = UnixListener::bind(&addr).listen().unwrap(); let (_tx, rx) = channel::<()>(); - spawn(proc() { + spawn(move|| { let mut a = a; let _s = a.accept().unwrap(); let _ = rx.recv_opt(); @@ -613,7 +632,7 @@ mod tests { let addr = next_test_unix(); let a = UnixListener::bind(&addr).listen().unwrap(); let (_tx, rx) = channel::<()>(); - spawn(proc() { + spawn(move|| { let mut a = a; let _s = a.accept().unwrap(); let _ = rx.recv_opt(); @@ -622,7 +641,7 @@ mod tests { let mut s = UnixStream::connect(&addr).unwrap(); let s2 = s.clone(); let (tx, rx) = channel(); - spawn(proc() { + spawn(move|| { let mut s2 = s2; assert!(s2.read(&mut [0]).is_err()); tx.send(()); @@ -639,7 +658,7 @@ mod tests { let addr = next_test_unix(); let mut a = UnixListener::bind(&addr).listen().unwrap(); let (tx, rx) = channel::<()>(); - spawn(proc() { + spawn(move|| { let mut s = UnixStream::connect(&addr).unwrap(); rx.recv(); assert!(s.write(&[0]).is_ok()); @@ -677,7 +696,7 @@ mod tests { let addr = next_test_unix(); let mut a = UnixListener::bind(&addr).listen().unwrap(); let (tx, rx) = channel::<()>(); - spawn(proc() { + spawn(move|| { let mut s = UnixStream::connect(&addr).unwrap(); rx.recv(); let mut amt = 0; @@ -706,7 +725,7 @@ mod tests { let addr = next_test_unix(); let mut a = UnixListener::bind(&addr).listen().unwrap(); let (tx, rx) = channel::<()>(); - spawn(proc() { + spawn(move|| { let mut s = UnixStream::connect(&addr).unwrap(); rx.recv(); assert!(s.write(&[0]).is_ok()); @@ -733,7 +752,7 @@ mod tests { let addr = next_test_unix(); let mut a = UnixListener::bind(&addr).listen().unwrap(); let (tx, rx) = channel::<()>(); - spawn(proc() { + spawn(move|| { let mut s = UnixStream::connect(&addr).unwrap(); rx.recv(); assert!(s.write(&[0]).is_ok()); @@ -743,7 +762,7 @@ mod tests { let mut s = a.accept().unwrap(); let s2 = s.clone(); let (tx2, rx2) = channel(); - spawn(proc() { + spawn(move|| { let mut s2 = s2; assert!(s2.read(&mut [0]).is_ok()); tx2.send(()); @@ -765,10 +784,10 @@ mod tests { let mut a2 = a.clone(); let addr2 = addr.clone(); - spawn(proc() { + spawn(move|| { let _ = UnixStream::connect(&addr2); }); - spawn(proc() { + spawn(move|| { let _ = UnixStream::connect(&addr); }); @@ -788,14 +807,14 @@ mod tests { let (tx, rx) = channel(); let tx2 = tx.clone(); - spawn(proc() { let mut a = a; tx.send(a.accept()) }); - spawn(proc() { let mut a = a2; tx2.send(a.accept()) }); + spawn(move|| { let mut a = a; tx.send(a.accept()) }); + spawn(move|| { let mut a = a2; tx2.send(a.accept()) }); let addr2 = addr.clone(); - spawn(proc() { + spawn(move|| { let _ = UnixStream::connect(&addr2); }); - spawn(proc() { + spawn(move|| { let _ = UnixStream::connect(&addr); }); @@ -821,7 +840,7 @@ mod tests { let mut a2 = a.clone(); let (tx, rx) = channel(); - spawn(proc() { + spawn(move|| { let mut a = a; tx.send(a.accept()); }); diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index cab54d82e1c..6adb5387f2e 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -19,33 +19,39 @@ use clone::Clone; use io::IoResult; -use iter::Iterator; -use result::Err; +use result::Result::Err; use io::net::ip::{SocketAddr, ToSocketAddr}; use io::{Reader, Writer, Listener, Acceptor}; use io::{standard_error, TimedOut}; -use option::{None, Some, Option}; +use option::Option; +use option::Option::{None, Some}; use time::Duration; use sys::tcp::TcpStream as TcpStreamImp; use sys::tcp::TcpListener as TcpListenerImp; use sys::tcp::TcpAcceptor as TcpAcceptorImp; +use sys_common; + /// A structure which represents a TCP stream between a local socket and a /// remote socket. /// +/// The socket will be closed when the value is dropped. +/// /// # Example /// /// ```no_run -/// # #![allow(unused_must_use)] /// use std::io::TcpStream; /// -/// let mut stream = TcpStream::connect("127.0.0.1:34254"); +/// { +/// let mut stream = TcpStream::connect("127.0.0.1:34254"); +/// +/// // ignore the Result +/// let _ = stream.write(&[1]); /// -/// stream.write(&[1]); -/// let mut buf = [0]; -/// stream.read(&mut buf); -/// drop(stream); // close the connection +/// let mut buf = [0]; +/// let _ = stream.read(&mut buf); // ignore here too +/// } // the stream is closed here /// ``` pub struct TcpStream { inner: TcpStreamImp, @@ -130,16 +136,17 @@ impl TcpStream { /// use std::io::timer; /// use std::io::TcpStream; /// use std::time::Duration; + /// use std::thread::Thread; /// /// let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap(); /// let stream2 = stream.clone(); /// - /// spawn(proc() { + /// Thread::spawn(move|| { /// // close this stream after one second /// timer::sleep(Duration::seconds(1)); /// let mut stream = stream2; /// stream.close_read(); - /// }); + /// }).detach(); /// /// // wait for some data, will get canceled after one second /// let mut buf = [0]; @@ -256,6 +263,12 @@ impl Writer for TcpStream { } } +impl sys_common::AsInner<TcpStreamImp> for TcpStream { + fn as_inner(&self) -> &TcpStreamImp { + &self.inner + } +} + /// A structure representing a socket server. This listener is used to create a /// `TcpAcceptor` which can be used to accept sockets on a local port. /// @@ -267,6 +280,7 @@ impl Writer for TcpStream { /// # #![allow(dead_code)] /// use std::io::{TcpListener, TcpStream}; /// use std::io::{Acceptor, Listener}; +/// use std::thread::Thread; /// /// let listener = TcpListener::bind("127.0.0.1:80"); /// @@ -281,10 +295,10 @@ impl Writer for TcpStream { /// for stream in acceptor.incoming() { /// match stream { /// Err(e) => { /* connection failed */ } -/// Ok(stream) => spawn(proc() { +/// Ok(stream) => Thread::spawn(move|| { /// // connection succeeded /// handle_client(stream) -/// }) +/// }).detach() /// } /// } /// @@ -305,7 +319,7 @@ impl TcpListener { /// to this listener. The port allocated can be queried via the /// `socket_name` function. /// - /// The address type can be any implementor of `ToSocketAddr` trait. See its + /// The address type can be any implementer of `ToSocketAddr` trait. See its /// documentation for concrete examples. pub fn bind<A: ToSocketAddr>(addr: A) -> IoResult<TcpListener> { super::with_addresses(addr, |addr| { @@ -325,6 +339,12 @@ impl Listener<TcpStream, TcpAcceptor> for TcpListener { } } +impl sys_common::AsInner<TcpListenerImp> for TcpListener { + fn as_inner(&self) -> &TcpListenerImp { + &self.inner + } +} + /// The accepting half of a TCP socket server. This structure is created through /// a `TcpListener`'s `listen` method, and this object can be used to accept new /// `TcpStream` instances. @@ -398,11 +418,12 @@ impl TcpAcceptor { /// ``` /// # #![allow(experimental)] /// use std::io::{TcpListener, Listener, Acceptor, EndOfFile}; + /// use std::thread::Thread; /// /// let mut a = TcpListener::bind("127.0.0.1:8482").listen().unwrap(); /// let a2 = a.clone(); /// - /// spawn(proc() { + /// Thread::spawn(move|| { /// let mut a2 = a2; /// for socket in a2.incoming() { /// match socket { @@ -411,7 +432,7 @@ impl TcpAcceptor { /// Err(e) => panic!("unexpected error: {}", e), /// } /// } - /// }); + /// }).detach(); /// /// # fn wait_for_sigint() {} /// // Now that our accept loop is running, wait for the program to be @@ -452,6 +473,12 @@ impl Clone for TcpAcceptor { } } +impl sys_common::AsInner<TcpAcceptorImp> for TcpAcceptor { + fn as_inner(&self) -> &TcpAcceptorImp { + &self.inner + } +} + #[cfg(test)] #[allow(experimental)] mod test { @@ -485,7 +512,7 @@ mod test { let listener = TcpListener::bind(socket_addr); let mut acceptor = listener.listen(); - spawn(proc() { + spawn(move|| { let mut stream = TcpStream::connect(("localhost", socket_addr.port)); stream.write(&[144]).unwrap(); }); @@ -501,7 +528,7 @@ mod test { let addr = next_test_ip4(); let mut acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { let mut stream = TcpStream::connect(("localhost", addr.port)); stream.write(&[64]).unwrap(); }); @@ -517,7 +544,7 @@ mod test { let addr = next_test_ip4(); let mut acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { let mut stream = TcpStream::connect(("127.0.0.1", addr.port)); stream.write(&[44]).unwrap(); }); @@ -533,7 +560,7 @@ mod test { let addr = next_test_ip6(); let mut acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { let mut stream = TcpStream::connect(("::1", addr.port)); stream.write(&[66]).unwrap(); }); @@ -549,7 +576,7 @@ mod test { let addr = next_test_ip4(); let mut acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { let mut stream = TcpStream::connect(addr); stream.write(&[99]).unwrap(); }); @@ -565,7 +592,7 @@ mod test { let addr = next_test_ip6(); let mut acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { let mut stream = TcpStream::connect(addr); stream.write(&[99]).unwrap(); }); @@ -581,7 +608,7 @@ mod test { let addr = next_test_ip4(); let mut acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { let _stream = TcpStream::connect(addr); // Close }); @@ -597,7 +624,7 @@ mod test { let addr = next_test_ip6(); let mut acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { let _stream = TcpStream::connect(addr); // Close }); @@ -613,7 +640,7 @@ mod test { let addr = next_test_ip4(); let mut acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { let _stream = TcpStream::connect(addr); // Close }); @@ -637,7 +664,7 @@ mod test { let addr = next_test_ip6(); let mut acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { let _stream = TcpStream::connect(addr); // Close }); @@ -662,7 +689,7 @@ mod test { let mut acceptor = TcpListener::bind(addr).listen(); let (tx, rx) = channel(); - spawn(proc() { + spawn(move|| { drop(TcpStream::connect(addr)); tx.send(()); }); @@ -687,7 +714,7 @@ mod test { let mut acceptor = TcpListener::bind(addr).listen(); let (tx, rx) = channel(); - spawn(proc() { + spawn(move|| { drop(TcpStream::connect(addr)); tx.send(()); }); @@ -712,7 +739,7 @@ mod test { let max = 10u; let mut acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { for _ in range(0, max) { let mut stream = TcpStream::connect(addr); stream.write(&[99]).unwrap(); @@ -732,7 +759,7 @@ mod test { let max = 10u; let mut acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { for _ in range(0, max) { let mut stream = TcpStream::connect(addr); stream.write(&[99]).unwrap(); @@ -752,11 +779,11 @@ mod test { static MAX: int = 10; let acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { let mut acceptor = acceptor; for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) { // Start another task to handle the connection - spawn(proc() { + spawn(move|| { let mut stream = stream; let mut buf = [0]; stream.read(&mut buf).unwrap(); @@ -771,7 +798,7 @@ mod test { fn connect(i: int, addr: SocketAddr) { if i == MAX { return } - spawn(proc() { + spawn(move|| { debug!("connecting"); let mut stream = TcpStream::connect(addr); // Connect again before writing @@ -788,11 +815,11 @@ mod test { static MAX: int = 10; let acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { let mut acceptor = acceptor; for (i, stream) in acceptor.incoming().enumerate().take(MAX as uint) { // Start another task to handle the connection - spawn(proc() { + spawn(move|| { let mut stream = stream; let mut buf = [0]; stream.read(&mut buf).unwrap(); @@ -807,7 +834,7 @@ mod test { fn connect(i: int, addr: SocketAddr) { if i == MAX { return } - spawn(proc() { + spawn(move|| { debug!("connecting"); let mut stream = TcpStream::connect(addr); // Connect again before writing @@ -824,11 +851,11 @@ mod test { let addr = next_test_ip4(); let acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { let mut acceptor = acceptor; for stream in acceptor.incoming().take(MAX as uint) { // Start another task to handle the connection - spawn(proc() { + spawn(move|| { let mut stream = stream; let mut buf = [0]; stream.read(&mut buf).unwrap(); @@ -843,7 +870,7 @@ mod test { fn connect(i: int, addr: SocketAddr) { if i == MAX { return } - spawn(proc() { + spawn(move|| { debug!("connecting"); let mut stream = TcpStream::connect(addr); // Connect again before writing @@ -860,11 +887,11 @@ mod test { let addr = next_test_ip6(); let acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { let mut acceptor = acceptor; for stream in acceptor.incoming().take(MAX as uint) { // Start another task to handle the connection - spawn(proc() { + spawn(move|| { let mut stream = stream; let mut buf = [0]; stream.read(&mut buf).unwrap(); @@ -879,7 +906,7 @@ mod test { fn connect(i: int, addr: SocketAddr) { if i == MAX { return } - spawn(proc() { + spawn(move|| { debug!("connecting"); let mut stream = TcpStream::connect(addr); // Connect again before writing @@ -902,7 +929,7 @@ mod test { pub fn peer_name(addr: SocketAddr) { let acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { let mut acceptor = acceptor; acceptor.accept().unwrap(); }); @@ -937,7 +964,7 @@ mod test { fn partial_read() { let addr = next_test_ip4(); let (tx, rx) = channel(); - spawn(proc() { + spawn(move|| { let mut srv = TcpListener::bind(addr).listen().unwrap(); tx.send(()); let mut cl = srv.accept().unwrap(); @@ -974,7 +1001,7 @@ mod test { let addr = next_test_ip4(); let (tx, rx) = channel(); - spawn(proc() { + spawn(move|| { rx.recv(); let _stream = TcpStream::connect(addr).unwrap(); // Close @@ -999,7 +1026,7 @@ mod test { let addr = next_test_ip4(); let mut acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { let mut s = TcpStream::connect(addr); let mut buf = [0, 0]; assert_eq!(s.read(&mut buf), Ok(1)); @@ -1012,7 +1039,7 @@ mod test { let (tx1, rx1) = channel(); let (tx2, rx2) = channel(); - spawn(proc() { + spawn(move|| { let mut s2 = s2; rx1.recv(); s2.write(&[1]).unwrap(); @@ -1031,7 +1058,7 @@ mod test { let (tx1, rx) = channel(); let tx2 = tx1.clone(); - spawn(proc() { + spawn(move|| { let mut s = TcpStream::connect(addr); s.write(&[1]).unwrap(); rx.recv(); @@ -1043,7 +1070,7 @@ mod test { let s2 = s1.clone(); let (done, rx) = channel(); - spawn(proc() { + spawn(move|| { let mut s2 = s2; let mut buf = [0, 0]; s2.read(&mut buf).unwrap(); @@ -1062,7 +1089,7 @@ mod test { let addr = next_test_ip4(); let mut acceptor = TcpListener::bind(addr).listen(); - spawn(proc() { + spawn(move|| { let mut s = TcpStream::connect(addr); let mut buf = [0, 1]; s.read(&mut buf).unwrap(); @@ -1073,7 +1100,7 @@ mod test { let s2 = s1.clone(); let (done, rx) = channel(); - spawn(proc() { + spawn(move|| { let mut s2 = s2; s2.write(&[1]).unwrap(); done.send(()); @@ -1087,7 +1114,7 @@ mod test { fn shutdown_smoke() { let addr = next_test_ip4(); let a = TcpListener::bind(addr).unwrap().listen(); - spawn(proc() { + spawn(move|| { let mut a = a; let mut c = a.accept().unwrap(); assert_eq!(c.read_to_end(), Ok(vec!())); @@ -1121,7 +1148,7 @@ mod test { // flakiness. if !cfg!(target_os = "freebsd") { let (tx, rx) = channel(); - spawn(proc() { + spawn(move|| { tx.send(TcpStream::connect(addr).unwrap()); }); let _l = rx.recv(); @@ -1131,14 +1158,14 @@ mod test { Err(ref e) if e.kind == TimedOut => {} Err(e) => panic!("error: {}", e), } - ::task::deschedule(); + ::thread::Thread::yield_now(); if i == 1000 { panic!("should have a pending connection") } } } // Unset the timeout and make sure that this always blocks. a.set_timeout(None); - spawn(proc() { + spawn(move|| { drop(TcpStream::connect(addr).unwrap()); }); a.accept().unwrap(); @@ -1149,7 +1176,7 @@ mod test { let addr = next_test_ip4(); let a = TcpListener::bind(addr).listen().unwrap(); let (_tx, rx) = channel::<()>(); - spawn(proc() { + spawn(move|| { let mut a = a; let _s = a.accept().unwrap(); let _ = rx.recv_opt(); @@ -1186,7 +1213,7 @@ mod test { let addr = next_test_ip4(); let a = TcpListener::bind(addr).listen().unwrap(); let (_tx, rx) = channel::<()>(); - spawn(proc() { + spawn(move|| { let mut a = a; let _s = a.accept().unwrap(); let _ = rx.recv_opt(); @@ -1195,7 +1222,7 @@ mod test { let mut s = TcpStream::connect(addr).unwrap(); let s2 = s.clone(); let (tx, rx) = channel(); - spawn(proc() { + spawn(move|| { let mut s2 = s2; assert!(s2.read(&mut [0]).is_err()); tx.send(()); @@ -1212,7 +1239,7 @@ mod test { let addr = next_test_ip6(); let mut a = TcpListener::bind(addr).listen().unwrap(); let (tx, rx) = channel::<()>(); - spawn(proc() { + spawn(move|| { let mut s = TcpStream::connect(addr).unwrap(); rx.recv(); assert!(s.write(&[0]).is_ok()); @@ -1245,7 +1272,7 @@ mod test { let addr = next_test_ip6(); let mut a = TcpListener::bind(addr).listen().unwrap(); let (tx, rx) = channel::<()>(); - spawn(proc() { + spawn(move|| { let mut s = TcpStream::connect(addr).unwrap(); rx.recv(); let mut amt = 0; @@ -1274,7 +1301,7 @@ mod test { let addr = next_test_ip6(); let mut a = TcpListener::bind(addr).listen().unwrap(); let (tx, rx) = channel::<()>(); - spawn(proc() { + spawn(move|| { let mut s = TcpStream::connect(addr).unwrap(); rx.recv(); assert!(s.write(&[0]).is_ok()); @@ -1302,7 +1329,7 @@ mod test { let addr = next_test_ip6(); let mut a = TcpListener::bind(addr).listen().unwrap(); let (tx, rx) = channel::<()>(); - spawn(proc() { + spawn(move|| { let mut s = TcpStream::connect(addr).unwrap(); rx.recv(); assert_eq!(s.write(&[0]), Ok(())); @@ -1312,7 +1339,7 @@ mod test { let mut s = a.accept().unwrap(); let s2 = s.clone(); let (tx2, rx2) = channel(); - spawn(proc() { + spawn(move|| { let mut s2 = s2; assert_eq!(s2.read(&mut [0]), Ok(1)); tx2.send(()); @@ -1335,7 +1362,7 @@ mod test { let (tx, rx) = channel(); let (txdone, rxdone) = channel(); let txdone2 = txdone.clone(); - spawn(proc() { + spawn(move|| { let mut tcp = TcpStream::connect(addr).unwrap(); rx.recv(); tcp.write_u8(0).unwrap(); @@ -1346,7 +1373,7 @@ mod test { let tcp = accept.accept().unwrap(); let tcp2 = tcp.clone(); let txdone3 = txdone.clone(); - spawn(proc() { + spawn(move|| { let mut tcp2 = tcp2; tcp2.read_u8().unwrap(); txdone3.send(()); @@ -1354,7 +1381,7 @@ mod test { // Try to ensure that the reading clone is indeed reading for _ in range(0i, 50) { - ::task::deschedule(); + ::thread::Thread::yield_now(); } // clone the handle again while it's reading, then let it finish the @@ -1372,10 +1399,10 @@ mod test { let mut a = l.listen().unwrap(); let mut a2 = a.clone(); - spawn(proc() { + spawn(move|| { let _ = TcpStream::connect(addr); }); - spawn(proc() { + spawn(move|| { let _ = TcpStream::connect(addr); }); @@ -1393,13 +1420,13 @@ mod test { let (tx, rx) = channel(); let tx2 = tx.clone(); - spawn(proc() { let mut a = a; tx.send(a.accept()) }); - spawn(proc() { let mut a = a2; tx2.send(a.accept()) }); + spawn(move|| { let mut a = a; tx.send(a.accept()) }); + spawn(move|| { let mut a = a2; tx2.send(a.accept()) }); - spawn(proc() { + spawn(move|| { let _ = TcpStream::connect(addr); }); - spawn(proc() { + spawn(move|| { let _ = TcpStream::connect(addr); }); @@ -1425,7 +1452,7 @@ mod test { let mut a2 = a.clone(); let (tx, rx) = channel(); - spawn(proc() { + spawn(move|| { let mut a = a; tx.send(a.accept()); }); diff --git a/src/libstd/io/net/udp.rs b/src/libstd/io/net/udp.rs index 567e7da0c00..f462143faf4 100644 --- a/src/libstd/io/net/udp.rs +++ b/src/libstd/io/net/udp.rs @@ -18,9 +18,11 @@ use clone::Clone; use io::net::ip::{SocketAddr, IpAddr, ToSocketAddr}; use io::{Reader, Writer, IoResult}; +use ops::FnOnce; use option::Option; -use result::{Ok, Err}; +use result::Result::{Ok, Err}; use sys::udp::UdpSocket as UdpSocketImp; +use sys_common; /// A User Datagram Protocol socket. /// @@ -80,7 +82,7 @@ impl UdpSocket { /// Sends data on the socket to the given address. Returns nothing on /// success. /// - /// Address type can be any implementor of `ToSocketAddr` trait. See its + /// Address type can be any implementer of `ToSocketAddr` trait. See its /// documentation for concrete examples. pub fn send_to<A: ToSocketAddr>(&mut self, buf: &[u8], addr: A) -> IoResult<()> { super::with_addresses(addr, |addr| self.inner.send_to(buf, addr)) @@ -184,6 +186,12 @@ impl Clone for UdpSocket { } } +impl sys_common::AsInner<UdpSocketImp> for UdpSocket { + fn as_inner(&self) -> &UdpSocketImp { + &self.inner + } +} + /// A type that allows convenient usage of a UDP stream connected to one /// address via the `Reader` and `Writer` traits. /// @@ -203,7 +211,9 @@ impl UdpStream { /// Allows access to the underlying UDP socket owned by this stream. This /// is useful to, for example, use the socket to send data to hosts other /// than the one that this stream is connected to. - pub fn as_socket<T>(&mut self, f: |&mut UdpSocket| -> T) -> T { + pub fn as_socket<T, F>(&mut self, f: F) -> T where + F: FnOnce(&mut UdpSocket) -> T, + { f(&mut self.socket) } @@ -262,7 +272,7 @@ mod test { let (tx1, rx1) = channel(); let (tx2, rx2) = channel(); - spawn(proc() { + spawn(move|| { match UdpSocket::bind(client_ip) { Ok(ref mut client) => { rx1.recv(); @@ -297,7 +307,7 @@ mod test { let client_ip = next_test_ip6(); let (tx, rx) = channel::<()>(); - spawn(proc() { + spawn(move|| { match UdpSocket::bind(client_ip) { Ok(ref mut client) => { rx.recv(); @@ -333,7 +343,7 @@ mod test { let (tx1, rx1) = channel(); let (tx2, rx2) = channel(); - spawn(proc() { + spawn(move|| { let send_as = |ip, val: &[u8]| { match UdpSocket::bind(ip) { Ok(client) => { @@ -377,7 +387,7 @@ mod test { let (tx1, rx1) = channel(); let (tx2, rx2) = channel(); - spawn(proc() { + spawn(move|| { match UdpSocket::bind(client_ip) { Ok(client) => { let client = box client; @@ -439,7 +449,7 @@ mod test { let mut sock1 = UdpSocket::bind(addr1).unwrap(); let sock2 = UdpSocket::bind(addr2).unwrap(); - spawn(proc() { + spawn(move|| { let mut sock2 = sock2; let mut buf = [0, 0]; assert_eq!(sock2.recv_from(&mut buf), Ok((1, addr1))); @@ -451,7 +461,7 @@ mod test { let (tx1, rx1) = channel(); let (tx2, rx2) = channel(); - spawn(proc() { + spawn(move|| { let mut sock3 = sock3; rx1.recv(); sock3.send_to(&[1], addr2).unwrap(); @@ -472,7 +482,7 @@ mod test { let (tx1, rx) = channel(); let tx2 = tx1.clone(); - spawn(proc() { + spawn(move|| { let mut sock2 = sock2; sock2.send_to(&[1], addr1).unwrap(); rx.recv(); @@ -483,7 +493,7 @@ mod test { let sock3 = sock1.clone(); let (done, rx) = channel(); - spawn(proc() { + spawn(move|| { let mut sock3 = sock3; let mut buf = [0, 0]; sock3.recv_from(&mut buf).unwrap(); @@ -507,7 +517,7 @@ mod test { let (tx, rx) = channel(); let (serv_tx, serv_rx) = channel(); - spawn(proc() { + spawn(move|| { let mut sock2 = sock2; let mut buf = [0, 1]; @@ -523,7 +533,7 @@ mod test { let (done, rx) = channel(); let tx2 = tx.clone(); - spawn(proc() { + spawn(move|| { let mut sock3 = sock3; match sock3.send_to(&[1], addr2) { Ok(..) => { let _ = tx2.send_opt(()); } @@ -547,11 +557,12 @@ mod test { let addr1 = next_test_ip4(); let addr2 = next_test_ip4(); let mut a = UdpSocket::bind(addr1).unwrap(); + let a2 = UdpSocket::bind(addr2).unwrap(); let (tx, rx) = channel(); let (tx2, rx2) = channel(); - spawn(proc() { - let mut a = UdpSocket::bind(addr2).unwrap(); + spawn(move|| { + let mut a = a2; assert_eq!(a.recv_from(&mut [0]), Ok((1, addr1))); assert_eq!(a.send_to(&[0], addr1), Ok(())); rx.recv(); diff --git a/src/libstd/io/pipe.rs b/src/libstd/io/pipe.rs index 8c20ea08863..73a893c4f2d 100644 --- a/src/libstd/io/pipe.rs +++ b/src/libstd/io/pipe.rs @@ -86,8 +86,8 @@ impl PipeStream { } } -impl sys_common::AsFileDesc for PipeStream { - fn as_fd(&self) -> &sys::fs::FileDesc { +impl sys_common::AsInner<sys::fs::FileDesc> for PipeStream { + fn as_inner(&self) -> &sys::fs::FileDesc { &*self.inner } } @@ -123,7 +123,7 @@ mod test { let out = PipeStream::open(writer); let mut input = PipeStream::open(reader); let (tx, rx) = channel(); - spawn(proc() { + spawn(move|| { let mut out = out; out.write(&[10]).unwrap(); rx.recv(); // don't close the pipe until the other read has finished diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs index d4d24c1e12f..4a0a3936424 100644 --- a/src/libstd/io/process.rs +++ b/src/libstd/io/process.rs @@ -30,6 +30,7 @@ use hash::Hash; use std::hash::sip::SipState; use io::pipe::{PipeStream, PipePair}; use path::BytesContainer; +use thread::Thread; use sys; use sys::fs::FileDesc; @@ -236,8 +237,8 @@ impl Command { // if the env is currently just inheriting from the parent's, // materialize the parent's env into a hashtable. self.env = Some(os::env_as_bytes().into_iter() - .map(|(k, v)| (EnvKey(k.as_slice().to_c_str()), - v.as_slice().to_c_str())) + .map(|(k, v)| (EnvKey(k.to_c_str()), + v.to_c_str())) .collect()); self.env.as_mut().unwrap() } @@ -460,7 +461,7 @@ pub struct ProcessOutput { } /// Describes what to do with a standard io stream for a child process. -#[deriving(Clone)] +#[deriving(Clone, Copy)] pub enum StdioContainer { /// This stream will be ignored. This is the equivalent of attaching the /// stream to `/dev/null` @@ -482,7 +483,7 @@ pub enum StdioContainer { /// Describes the result of a process after it has terminated. /// Note that Windows have no signals, so the result is usually ExitStatus. -#[deriving(PartialEq, Eq, Clone)] +#[deriving(PartialEq, Eq, Clone, Copy)] pub enum ProcessExit { /// Normal termination with an exit status. ExitStatus(int), @@ -689,10 +690,12 @@ impl Process { fn read(stream: Option<io::PipeStream>) -> Receiver<IoResult<Vec<u8>>> { let (tx, rx) = channel(); match stream { - Some(stream) => spawn(proc() { - let mut stream = stream; - tx.send(stream.read_to_end()) - }), + Some(stream) => { + Thread::spawn(move |:| { + let mut stream = stream; + tx.send(stream.read_to_end()) + }).detach(); + } None => tx.send(Ok(Vec::new())) } rx @@ -810,7 +813,7 @@ mod tests { fn stdout_works() { let mut cmd = Command::new("echo"); cmd.arg("foobar").stdout(CreatePipe(false, true)); - assert_eq!(run_output(cmd), "foobar\n".to_string()); + assert_eq!(run_output(cmd), "foobar\n"); } #[cfg(all(unix, not(target_os="android")))] @@ -820,7 +823,7 @@ mod tests { cmd.arg("-c").arg("pwd") .cwd(&Path::new("/")) .stdout(CreatePipe(false, true)); - assert_eq!(run_output(cmd), "/\n".to_string()); + assert_eq!(run_output(cmd), "/\n"); } #[cfg(all(unix, not(target_os="android")))] @@ -835,7 +838,7 @@ mod tests { drop(p.stdin.take()); let out = read_all(p.stdout.as_mut().unwrap() as &mut Reader); assert!(p.wait().unwrap().success()); - assert_eq!(out, "foobar\n".to_string()); + assert_eq!(out, "foobar\n"); } #[cfg(not(target_os="android"))] @@ -900,7 +903,7 @@ mod tests { let output_str = str::from_utf8(output.as_slice()).unwrap(); assert!(status.success()); - assert_eq!(output_str.trim().to_string(), "hello".to_string()); + assert_eq!(output_str.trim().to_string(), "hello"); // FIXME #7224 if !running_on_valgrind() { assert_eq!(error, Vec::new()); @@ -941,7 +944,7 @@ mod tests { let output_str = str::from_utf8(output.as_slice()).unwrap(); assert!(status.success()); - assert_eq!(output_str.trim().to_string(), "hello".to_string()); + assert_eq!(output_str.trim().to_string(), "hello"); // FIXME #7224 if !running_on_valgrind() { assert_eq!(error, Vec::new()); @@ -973,7 +976,7 @@ mod tests { let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap(); let parent_dir = os::getcwd().unwrap(); - let child_dir = Path::new(output.as_slice().trim()); + let child_dir = Path::new(output.trim()); let parent_stat = parent_dir.stat().unwrap(); let child_stat = child_dir.stat().unwrap(); @@ -991,7 +994,7 @@ mod tests { let prog = pwd_cmd().cwd(&parent_dir).spawn().unwrap(); let output = String::from_utf8(prog.wait_with_output().unwrap().output).unwrap(); - let child_dir = Path::new(output.as_slice().trim()); + let child_dir = Path::new(output.trim()); let parent_stat = parent_dir.stat().unwrap(); let child_stat = child_dir.stat().unwrap(); @@ -1031,8 +1034,7 @@ mod tests { for &(ref k, ref v) in r.iter() { // don't check windows magical empty-named variables assert!(k.is_empty() || - output.as_slice() - .contains(format!("{}={}", *k, *v).as_slice()), + output.contains(format!("{}={}", *k, *v).as_slice()), "output doesn't contain `{}={}`\n{}", k, v, output); } @@ -1050,12 +1052,10 @@ mod tests { for &(ref k, ref v) in r.iter() { // don't check android RANDOM variables if *k != "RANDOM".to_string() { - assert!(output.as_slice() - .contains(format!("{}={}", + assert!(output.contains(format!("{}={}", *k, *v).as_slice()) || - output.as_slice() - .contains(format!("{}=\'{}\'", + output.contains(format!("{}=\'{}\'", *k, *v).as_slice())); } @@ -1082,9 +1082,9 @@ mod tests { let prog = env_cmd().env_set_all(new_env.as_slice()).spawn().unwrap(); let result = prog.wait_with_output().unwrap(); - let output = String::from_utf8_lossy(result.output.as_slice()).into_string(); + let output = String::from_utf8_lossy(result.output.as_slice()).to_string(); - assert!(output.as_slice().contains("RUN_TEST_NEW_ENV=123"), + assert!(output.contains("RUN_TEST_NEW_ENV=123"), "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output); } @@ -1092,9 +1092,9 @@ mod tests { fn test_add_to_env() { let prog = env_cmd().env("RUN_TEST_NEW_ENV", "123").spawn().unwrap(); let result = prog.wait_with_output().unwrap(); - let output = String::from_utf8_lossy(result.output.as_slice()).into_string(); + let output = String::from_utf8_lossy(result.output.as_slice()).to_string(); - assert!(output.as_slice().contains("RUN_TEST_NEW_ENV=123"), + assert!(output.contains("RUN_TEST_NEW_ENV=123"), "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output); } @@ -1154,14 +1154,14 @@ mod tests { fn wait_timeout2() { let (tx, rx) = channel(); let tx2 = tx.clone(); - spawn(proc() { + spawn(move|| { let mut p = sleeper(); p.set_timeout(Some(10)); assert_eq!(p.wait().err().unwrap().kind, TimedOut); p.signal_kill().unwrap(); tx.send(()); }); - spawn(proc() { + spawn(move|| { let mut p = sleeper(); p.set_timeout(Some(10)); assert_eq!(p.wait().err().unwrap().kind, TimedOut); diff --git a/src/libstd/io/result.rs b/src/libstd/io/result.rs index 305bcf9ecbc..32965d23971 100644 --- a/src/libstd/io/result.rs +++ b/src/libstd/io/result.rs @@ -15,7 +15,7 @@ //! as a `Reader` without unwrapping the result first. use clone::Clone; -use result::{Ok, Err}; +use result::Result::{Ok, Err}; use super::{Reader, Writer, Listener, Acceptor, Seek, SeekStyle, IoResult}; impl<W: Writer> Writer for IoResult<W> { diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 7374668a69d..1c5ceaf2450 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -8,44 +8,47 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! Non-blocking access to stdin, stdout, and stderr. - -This module provides bindings to the local event loop's TTY interface, using it -to offer synchronous but non-blocking versions of stdio. These handles can be -inspected for information about terminal dimensions or for related information -about the stream or terminal to which it is attached. - -# Example - -```rust -# #![allow(unused_must_use)] -use std::io; - -let mut out = io::stdout(); -out.write(b"Hello, world!"); -``` - -*/ +//! Non-blocking access to stdin, stdout, and stderr. +//! +//! This module provides bindings to the local event loop's TTY interface, using it +//! to offer synchronous but non-blocking versions of stdio. These handles can be +//! inspected for information about terminal dimensions or for related information +//! about the stream or terminal to which it is attached. +//! +//! # Example +//! +//! ```rust +//! # #![allow(unused_must_use)] +//! use std::io; +//! +//! let mut out = io::stdout(); +//! out.write(b"Hello, world!"); +//! ``` use self::StdSource::*; -use failure::local_stderr; +use boxed::Box; +use cell::RefCell; +use clone::Clone; +use failure::LOCAL_STDERR; use fmt; -use io::{Reader, Writer, IoResult, IoError, OtherIoError, +use io::{Reader, Writer, IoResult, IoError, OtherIoError, Buffer, standard_error, EndOfFile, LineBufferedWriter, BufferedReader}; -use iter::Iterator; use kinds::Send; use libc; -use option::{Option, Some, None}; -use boxed::Box; +use mem; +use option::Option; +use option::Option::{Some, None}; +use ops::{Deref, DerefMut, FnOnce}; +use result::Result::{Ok, Err}; +use rt; +use slice::SliceExt; +use str::StrExt; +use string::String; use sys::{fs, tty}; -use result::{Ok, Err}; -use rustrt; -use rustrt::local::Local; -use rustrt::task::Task; -use slice::SlicePrelude; -use str::StrPrelude; +use sync::{Arc, Mutex, MutexGuard, Once, ONCE_INIT}; use uint; +use vec::Vec; // And so begins the tale of acquiring a uv handle to a stdio stream on all // platforms in all situations. Our story begins by splitting the world into two @@ -80,37 +83,156 @@ enum StdSource { File(fs::FileDesc), } -fn src<T>(fd: libc::c_int, _readable: bool, f: |StdSource| -> T) -> T { +fn src<T, F>(fd: libc::c_int, _readable: bool, f: F) -> T where + F: FnOnce(StdSource) -> T, +{ match tty::TTY::new(fd) { Ok(tty) => f(TTY(tty)), Err(_) => f(File(fs::FileDesc::new(fd, false))), } } -local_data_key!(local_stdout: Box<Writer + Send>) +thread_local! { + static LOCAL_STDOUT: RefCell<Option<Box<Writer + Send>>> = { + RefCell::new(None) + } +} -/// Creates a new non-blocking handle to the stdin of the current process. -/// -/// The returned handled is buffered by default with a `BufferedReader`. If -/// buffered access is not desired, the `stdin_raw` function is provided to -/// provided unbuffered access to stdin. +/// A synchronized wrapper around a buffered reader from stdin +#[deriving(Clone)] +pub struct StdinReader { + inner: Arc<Mutex<BufferedReader<StdReader>>>, +} + +/// A guard for exclusive access to `StdinReader`'s internal `BufferedReader`. +pub struct StdinReaderGuard<'a> { + inner: MutexGuard<'a, BufferedReader<StdReader>>, +} + +impl<'a> Deref<BufferedReader<StdReader>> for StdinReaderGuard<'a> { + fn deref(&self) -> &BufferedReader<StdReader> { + &*self.inner + } +} + +impl<'a> DerefMut<BufferedReader<StdReader>> for StdinReaderGuard<'a> { + fn deref_mut(&mut self) -> &mut BufferedReader<StdReader> { + &mut *self.inner + } +} + +impl StdinReader { + /// Locks the `StdinReader`, granting the calling thread exclusive access + /// to the underlying `BufferedReader`. + /// + /// This provides access to methods like `chars` and `lines`. + /// + /// # Examples + /// + /// ```rust + /// use std::io; + /// + /// for line in io::stdin().lock().lines() { + /// println!("{}", line.unwrap()); + /// } + /// ``` + pub fn lock<'a>(&'a mut self) -> StdinReaderGuard<'a> { + StdinReaderGuard { + inner: self.inner.lock() + } + } + + /// Like `Buffer::read_line`. + /// + /// The read is performed atomically - concurrent read calls in other + /// threads will not interleave with this one. + pub fn read_line(&mut self) -> IoResult<String> { + self.inner.lock().read_line() + } + + /// Like `Buffer::read_until`. + /// + /// The read is performed atomically - concurrent read calls in other + /// threads will not interleave with this one. + pub fn read_until(&mut self, byte: u8) -> IoResult<Vec<u8>> { + self.inner.lock().read_until(byte) + } + + /// Like `Buffer::read_char`. + /// + /// The read is performed atomically - concurrent read calls in other + /// threads will not interleave with this one. + pub fn read_char(&mut self) -> IoResult<char> { + self.inner.lock().read_char() + } +} + +impl Reader for StdinReader { + fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { + self.inner.lock().read(buf) + } + + // We have to manually delegate all of these because the default impls call + // read more than once and we don't want those calls to interleave (or + // incur the costs of repeated locking). + + fn read_at_least(&mut self, min: uint, buf: &mut [u8]) -> IoResult<uint> { + self.inner.lock().read_at_least(min, buf) + } + + fn push_at_least(&mut self, min: uint, len: uint, buf: &mut Vec<u8>) -> IoResult<uint> { + self.inner.lock().push_at_least(min, len, buf) + } + + fn read_to_end(&mut self) -> IoResult<Vec<u8>> { + self.inner.lock().read_to_end() + } + + fn read_le_uint_n(&mut self, nbytes: uint) -> IoResult<u64> { + self.inner.lock().read_le_uint_n(nbytes) + } + + fn read_be_uint_n(&mut self, nbytes: uint) -> IoResult<u64> { + self.inner.lock().read_be_uint_n(nbytes) + } +} + +/// Creates a new handle to the stdin of the current process. /// -/// Care should be taken when creating multiple handles to the stdin of a -/// process. Because this is a buffered reader by default, it's possible for -/// pending input to be unconsumed in one reader and unavailable to other -/// readers. It is recommended that only one handle at a time is created for the -/// stdin of a process. +/// The returned handle is a wrapper around a global `BufferedReader` shared +/// by all threads. If buffered access is not desired, the `stdin_raw` function +/// is provided to provided unbuffered access to stdin. /// /// See `stdout()` for more notes about this function. -pub fn stdin() -> BufferedReader<StdReader> { - // The default buffer capacity is 64k, but apparently windows doesn't like - // 64k reads on stdin. See #13304 for details, but the idea is that on - // windows we use a slightly smaller buffer that's been seen to be - // acceptable. - if cfg!(windows) { - BufferedReader::with_capacity(8 * 1024, stdin_raw()) - } else { - BufferedReader::new(stdin_raw()) +pub fn stdin() -> StdinReader { + // We're following the same strategy as kimundi's lazy_static library + static mut STDIN: *const StdinReader = 0 as *const StdinReader; + static ONCE: Once = ONCE_INIT; + + unsafe { + ONCE.doit(|| { + // The default buffer capacity is 64k, but apparently windows doesn't like + // 64k reads on stdin. See #13304 for details, but the idea is that on + // windows we use a slightly smaller buffer that's been seen to be + // acceptable. + let stdin = if cfg!(windows) { + BufferedReader::with_capacity(8 * 1024, stdin_raw()) + } else { + BufferedReader::new(stdin_raw()) + }; + let stdin = StdinReader { + inner: Arc::new(Mutex::new(stdin)) + }; + STDIN = mem::transmute(box stdin); + + // Make sure to free it at exit + rt::at_exit(|| { + mem::transmute::<_, Box<StdinReader>>(STDIN); + STDIN = 0 as *const _; + }); + }); + + (*STDIN).clone() } } @@ -167,7 +289,10 @@ pub fn stderr_raw() -> StdWriter { /// Note that this does not need to be called for all new tasks; the default /// output handle is to the process's stdout stream. pub fn set_stdout(stdout: Box<Writer + Send>) -> Option<Box<Writer + Send>> { - local_stdout.replace(Some(stdout)).and_then(|mut s| { + let mut new = Some(stdout); + LOCAL_STDOUT.with(|slot| { + mem::replace(&mut *slot.borrow_mut(), new.take()) + }).and_then(|mut s| { let _ = s.flush(); Some(s) }) @@ -182,7 +307,10 @@ pub fn set_stdout(stdout: Box<Writer + Send>) -> Option<Box<Writer + Send>> { /// Note that this does not need to be called for all new tasks; the default /// output handle is to the process's stderr stream. pub fn set_stderr(stderr: Box<Writer + Send>) -> Option<Box<Writer + Send>> { - local_stderr.replace(Some(stderr)).and_then(|mut s| { + let mut new = Some(stderr); + LOCAL_STDERR.with(|slot| { + mem::replace(&mut *slot.borrow_mut(), new.take()) + }).and_then(|mut s| { let _ = s.flush(); Some(s) }) @@ -199,17 +327,16 @@ pub fn set_stderr(stderr: Box<Writer + Send>) -> Option<Box<Writer + Send>> { // }) // }) fn with_task_stdout(f: |&mut Writer| -> IoResult<()>) { - let result = if Local::exists(None::<Task>) { - let mut my_stdout = local_stdout.replace(None).unwrap_or_else(|| { - box stdout() as Box<Writer + Send> - }); - let result = f(&mut *my_stdout); - local_stdout.replace(Some(my_stdout)); - result - } else { - let mut io = rustrt::Stdout; - f(&mut io as &mut Writer) - }; + let mut my_stdout = LOCAL_STDOUT.with(|slot| { + slot.borrow_mut().take() + }).unwrap_or_else(|| { + box stdout() as Box<Writer + Send> + }); + let result = f(&mut *my_stdout); + let mut var = Some(my_stdout); + LOCAL_STDOUT.with(|slot| { + *slot.borrow_mut() = var.take(); + }); match result { Ok(()) => {} Err(e) => panic!("failed printing to stdout: {}", e), @@ -399,25 +526,24 @@ mod tests { let (tx, rx) = channel(); let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx)); - spawn(proc() { + spawn(move|| { set_stdout(box w); println!("hello!"); }); - assert_eq!(r.read_to_string().unwrap(), "hello!\n".to_string()); + assert_eq!(r.read_to_string().unwrap(), "hello!\n"); } #[test] fn capture_stderr() { - use realstd::comm::channel; - use realstd::io::{ChanReader, ChanWriter, Reader}; + use io::{ChanReader, ChanWriter, Reader}; let (tx, rx) = channel(); let (mut r, w) = (ChanReader::new(rx), ChanWriter::new(tx)); - spawn(proc() { - ::realstd::io::stdio::set_stderr(box w); + spawn(move|| { + set_stderr(box w); panic!("my special message"); }); let s = r.read_to_string().unwrap(); - assert!(s.as_slice().contains("my special message")); + assert!(s.contains("my special message")); } } diff --git a/src/libstd/io/tempfile.rs b/src/libstd/io/tempfile.rs index a232231733d..c2b4d5a1fa9 100644 --- a/src/libstd/io/tempfile.rs +++ b/src/libstd/io/tempfile.rs @@ -14,14 +14,65 @@ use io::{fs, IoResult}; use io; use libc; use ops::Drop; -use option::{Option, None, Some}; +use option::Option; +use option::Option::{None, Some}; use os; use path::{Path, GenericPath}; -use result::{Ok, Err}; +use result::Result::{Ok, Err}; use sync::atomic; /// A wrapper for a path to temporary directory implementing automatic /// scope-based deletion. +/// +/// # Examples +/// +/// ```no_run +/// use std::io::TempDir; +/// +/// { +/// // create a temporary directory +/// let tmpdir = match TempDir::new("mysuffix") { +/// Ok(dir) => dir, +/// Err(e) => panic!("couldn't create temporary directory: {}", e) +/// }; +/// +/// // get the path of the temporary directory without affecting the wrapper +/// let tmppath = tmpdir.path(); +/// +/// println!("The path of temporary directory is {}", tmppath.display()); +/// +/// // the temporary directory is automatically removed when tmpdir goes +/// // out of scope at the end of the block +/// } +/// { +/// // create a temporary directory, this time using a custom path +/// let tmpdir = match TempDir::new_in(&Path::new("/tmp/best/custom/path"), "mysuffix") { +/// Ok(dir) => dir, +/// Err(e) => panic!("couldn't create temporary directory: {}", e) +/// }; +/// +/// // get the path of the temporary directory and disable automatic deletion in the wrapper +/// let tmppath = tmpdir.into_inner(); +/// +/// println!("The path of the not-so-temporary directory is {}", tmppath.display()); +/// +/// // the temporary directory is not removed here +/// // because the directory is detached from the wrapper +/// } +/// { +/// // create a temporary directory +/// let tmpdir = match TempDir::new("mysuffix") { +/// Ok(dir) => dir, +/// Err(e) => panic!("couldn't create temporary directory: {}", e) +/// }; +/// +/// // close the temporary directory manually and check the result +/// match tmpdir.close() { +/// Ok(_) => println!("success!"), +/// Err(e) => panic!("couldn't remove temporary directory: {}", e) +/// }; +/// } +/// ``` pub struct TempDir { path: Option<Path>, disarmed: bool @@ -73,11 +124,15 @@ impl TempDir { /// Unwrap the wrapped `std::path::Path` from the `TempDir` wrapper. /// This discards the wrapper so that the automatic deletion of the /// temporary directory is prevented. - pub fn unwrap(self) -> Path { + pub fn into_inner(self) -> Path { let mut tmpdir = self; tmpdir.path.take().unwrap() } + /// Deprecated, use into_inner() instead + #[deprecated = "renamed to into_inner()"] + pub fn unwrap(self) -> Path { self.into_inner() } + /// Access the wrapped `std::path::Path` to the temporary directory. pub fn path<'a>(&'a self) -> &'a Path { self.path.as_ref().unwrap() diff --git a/src/libstd/io/test.rs b/src/libstd/io/test.rs index a153ead2a38..af56735021e 100644 --- a/src/libstd/io/test.rs +++ b/src/libstd/io/test.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! Various utility functions useful for writing I/O tests */ +//! Various utility functions useful for writing I/O tests #![macro_escape] @@ -95,17 +95,14 @@ pub fn raise_fd_limit() { unsafe { darwin_fd_limit::raise_fd_limit() } } +/// darwin_fd_limit exists to work around an issue where launchctl on Mac OS X defaults the rlimit +/// maxfiles to 256/unlimited. The default soft limit of 256 ends up being far too low for our +/// multithreaded scheduler testing, depending on the number of cores available. +/// +/// This fixes issue #7772. #[cfg(target_os="macos")] #[allow(non_camel_case_types)] mod darwin_fd_limit { - /*! - * darwin_fd_limit exists to work around an issue where launchctl on Mac OS X defaults the - * rlimit maxfiles to 256/unlimited. The default soft limit of 256 ends up being far too low - * for our multithreaded scheduler testing, depending on the number of cores available. - * - * This fixes issue #7772. - */ - use libc; type rlim_t = libc::uint64_t; #[repr(C)] diff --git a/src/libstd/io/timer.rs b/src/libstd/io/timer.rs index ec588f13478..953effe4345 100644 --- a/src/libstd/io/timer.rs +++ b/src/libstd/io/timer.rs @@ -8,14 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! - -Synchronous Timers - -This module exposes the functionality to create timers, block the current task, -and create receivers which will receive notifications after a period of time. - -*/ +//! Synchronous Timers +//! +//! This module exposes the functionality to create timers, block the current task, +//! and create receivers which will receive notifications after a period of time. // FIXME: These functions take Durations but only pass ms to the backend impls. @@ -229,11 +225,11 @@ fn in_ms_u64(d: Duration) -> u64 { #[cfg(test)] mod test { - use super::*; - use time::Duration; - use task::spawn; use prelude::*; + use super::Timer; + use time::Duration; + #[test] fn test_io_timer_sleep_simple() { let mut timer = Timer::new().unwrap(); @@ -361,7 +357,7 @@ mod test { let mut timer = Timer::new().unwrap(); let timer_rx = timer.periodic(Duration::milliseconds(1000)); - spawn(proc() { + spawn(move|| { let _ = timer_rx.recv_opt(); }); @@ -375,7 +371,7 @@ mod test { let mut timer = Timer::new().unwrap(); let timer_rx = timer.periodic(Duration::milliseconds(1000)); - spawn(proc() { + spawn(move|| { let _ = timer_rx.recv_opt(); }); @@ -388,7 +384,7 @@ mod test { let mut timer = Timer::new().unwrap(); let timer_rx = timer.periodic(Duration::milliseconds(1000)); - spawn(proc() { + spawn(move|| { let _ = timer_rx.recv_opt(); }); diff --git a/src/libstd/io/util.rs b/src/libstd/io/util.rs index 4d491beb87b..18fabcbd1a2 100644 --- a/src/libstd/io/util.rs +++ b/src/libstd/io/util.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! Utility implementations of Reader and Writer */ +//! Utility implementations of Reader and Writer use prelude::*; use cmp; @@ -28,7 +28,11 @@ impl<R: Reader> LimitReader<R> { } /// Consumes the `LimitReader`, returning the underlying `Reader`. - pub fn unwrap(self) -> R { self.inner } + pub fn into_inner(self) -> R { self.inner } + + /// Deprecated, use into_inner() instead + #[deprecated = "renamed to into_inner"] + pub fn unwrap(self) -> R { self.into_inner() } /// Returns the number of bytes that can be read before the `LimitReader` /// will return EOF. @@ -77,6 +81,7 @@ impl<R: Buffer> Buffer for LimitReader<R> { } /// A `Writer` which ignores bytes written to it, like /dev/null. +#[deriving(Copy)] pub struct NullWriter; impl Writer for NullWriter { @@ -85,6 +90,7 @@ impl Writer for NullWriter { } /// A `Reader` which returns an infinite stream of 0 bytes, like /dev/zero. +#[deriving(Copy)] pub struct ZeroReader; impl Reader for ZeroReader { @@ -105,6 +111,7 @@ impl Buffer for ZeroReader { } /// A `Reader` which is always at EOF, like /dev/null. +#[deriving(Copy)] pub struct NullReader; impl Reader for NullReader { @@ -207,10 +214,14 @@ impl<R: Reader, W: Writer> TeeReader<R, W> { /// Consumes the `TeeReader`, returning the underlying `Reader` and /// `Writer`. - pub fn unwrap(self) -> (R, W) { + pub fn into_inner(self) -> (R, W) { let TeeReader { reader, writer } = self; (reader, writer) } + + /// Deprecated, use into_inner() instead + #[deprecated = "renamed to into_inner"] + pub fn unwrap(self) -> (R, W) { self.into_inner() } } impl<R: Reader, W: Writer> Reader for TeeReader<R, W> { @@ -265,7 +276,7 @@ impl<T: Iterator<u8>> Reader for IterReader<T> { #[cfg(test)] mod test { - use io::{MemReader, BufReader, ByRefReader}; + use io::{MemReader, ByRefReader}; use io; use boxed::Box; use super::*; @@ -387,8 +398,7 @@ mod test { #[test] fn limit_reader_buffer() { - let data = "0123456789\n0123456789\n"; - let mut r = BufReader::new(data.as_bytes()); + let r = &mut b"0123456789\n0123456789\n"; { let mut r = LimitReader::new(r.by_ref(), 3); assert_eq!(r.read_line(), Ok("012".to_string())); diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index b35c49efdd8..8274baeacfa 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -96,8 +96,6 @@ #![crate_name = "std"] #![unstable] -#![comment = "The Rust standard library"] -#![license = "MIT/ASL2"] #![crate_type = "rlib"] #![crate_type = "dylib"] #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", @@ -106,9 +104,9 @@ html_playground_url = "http://play.rust-lang.org/")] #![allow(unknown_features)] -#![feature(macro_rules, globs, linkage)] +#![feature(macro_rules, globs, linkage, thread_local, asm)] #![feature(default_type_params, phase, lang_items, unsafe_destructor)] -#![feature(import_shadowing, slicing_syntax)] +#![feature(slicing_syntax, unboxed_closures)] // Don't link to std. We are std. #![no_std] @@ -124,9 +122,7 @@ extern crate unicode; extern crate core; extern crate "collections" as core_collections; extern crate "rand" as core_rand; -extern crate "sync" as core_sync; extern crate libc; -extern crate rustrt; // Make std testable by not duplicating lang items. See #2912 #[cfg(test)] extern crate "std" as realstd; @@ -139,7 +135,6 @@ extern crate rustrt; // NB: These reexports are in the order they should be listed in rustdoc pub use core::any; -pub use core::bool; pub use core::borrow; pub use core::cell; pub use core::clone; @@ -154,14 +149,10 @@ pub use core::mem; pub use core::ptr; pub use core::raw; pub use core::simd; -pub use core::tuple; -// FIXME #15320: primitive documentation needs top-level modules, this -// should be `std::tuple::unit`. -pub use core::unit; pub use core::result; pub use core::option; -pub use alloc::boxed; +#[cfg(not(test))] pub use alloc::boxed; pub use alloc::rc; pub use core_collections::slice; @@ -169,13 +160,8 @@ pub use core_collections::str; pub use core_collections::string; pub use core_collections::vec; -pub use rustrt::c_str; -pub use rustrt::local_data; - pub use unicode::char; -pub use core_sync::comm; - /* Exported macros */ pub mod macros; @@ -209,35 +195,38 @@ pub mod prelude; #[path = "num/f32.rs"] pub mod f32; #[path = "num/f64.rs"] pub mod f64; -pub mod rand; - pub mod ascii; - -pub mod time; +pub mod thunk; /* Common traits */ pub mod error; pub mod num; +/* Runtime and platform support */ + +pub mod thread_local; +pub mod c_str; +pub mod c_vec; +pub mod dynamic_lib; +pub mod fmt; +pub mod io; +pub mod os; +pub mod path; +pub mod rand; +pub mod time; + /* Common data structures */ pub mod collections; pub mod hash; -/* Tasks and communication */ +/* Threads and communication */ pub mod task; +pub mod thread; pub mod sync; - -/* Runtime and platform support */ - -pub mod c_vec; -pub mod dynamic_lib; -pub mod os; -pub mod io; -pub mod path; -pub mod fmt; +pub mod comm; #[cfg(unix)] #[path = "sys/unix/mod.rs"] mod sys; @@ -249,6 +238,12 @@ pub mod fmt; pub mod rt; mod failure; +// Documentation for primitive types + +mod bool; +mod unit; +mod tuple; + // A curious inner-module that's not exported that contains the binding // 'std' so that macro-expanded references to std::error and such // can be resolved within libstd. @@ -263,10 +258,12 @@ mod std { pub use error; // used for try!() pub use fmt; // used for any formatting strings pub use io; // used for println!() - pub use local_data; // used for local_data_key!() pub use option; // used for bitflags!{} pub use rt; // used for panic!() pub use vec; // used for vec![] + pub use cell; // used for tls! + pub use thread_local; // used for thread_local! + pub use kinds; // used for tls! // The test runner calls ::std::os::args() but really wants realstd #[cfg(test)] pub use realstd::os as os; @@ -276,4 +273,5 @@ mod std { pub use slice; pub use boxed; // used for vec![] + } diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 18c109d92a0..3d03b5324b9 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -37,7 +37,7 @@ /// panic!("this is a {} {message}", "fancy", message = "message"); /// ``` #[macro_export] -macro_rules! panic( +macro_rules! panic { () => ({ panic!("explicit panic") }); @@ -70,7 +70,7 @@ macro_rules! panic( } format_args!(_run_fmt, $fmt, $($arg)*) }); -) +} /// Ensure that a boolean expression is `true` at runtime. /// @@ -93,7 +93,7 @@ macro_rules! panic( /// assert!(a + b == 30, "a = {}, b = {}", a, b); /// ``` #[macro_export] -macro_rules! assert( +macro_rules! assert { ($cond:expr) => ( if !$cond { panic!(concat!("assertion failed: ", stringify!($cond))) @@ -104,7 +104,7 @@ macro_rules! assert( panic!($($arg),+) } ); -) +} /// Asserts that two expressions are equal to each other, testing equality in /// both directions. @@ -119,20 +119,20 @@ macro_rules! assert( /// assert_eq!(a, b); /// ``` #[macro_export] -macro_rules! assert_eq( - ($given:expr , $expected:expr) => ({ - match (&($given), &($expected)) { - (given_val, expected_val) => { +macro_rules! assert_eq { + ($left:expr , $right:expr) => ({ + match (&($left), &($right)) { + (left_val, right_val) => { // check both directions of equality.... - if !((*given_val == *expected_val) && - (*expected_val == *given_val)) { + if !((*left_val == *right_val) && + (*right_val == *left_val)) { panic!("assertion failed: `(left == right) && (right == left)` \ - (left: `{}`, right: `{}`)", *given_val, *expected_val) + (left: `{}`, right: `{}`)", *left_val, *right_val) } } } }) -) +} /// Ensure that a boolean expression is `true` at runtime. /// @@ -160,9 +160,9 @@ macro_rules! assert_eq( /// debug_assert!(a + b == 30, "a = {}, b = {}", a, b); /// ``` #[macro_export] -macro_rules! debug_assert( +macro_rules! debug_assert { ($($arg:tt)*) => (if cfg!(not(ndebug)) { assert!($($arg)*); }) -) +} /// Asserts that two expressions are equal to each other, testing equality in /// both directions. @@ -182,35 +182,51 @@ macro_rules! debug_assert( /// debug_assert_eq!(a, b); /// ``` #[macro_export] -macro_rules! debug_assert_eq( +macro_rules! debug_assert_eq { ($($arg:tt)*) => (if cfg!(not(ndebug)) { assert_eq!($($arg)*); }) -) +} -/// A utility macro for indicating unreachable code. It will panic if -/// executed. This is occasionally useful to put after loops that never -/// terminate normally, but instead directly return from a function. +/// A utility macro for indicating unreachable code. /// -/// # Example +/// This is useful any time that the compiler can't determine that some code is unreachable. For +/// example: +/// +/// * Match arms with guard conditions. +/// * Loops that dynamically terminate. +/// * Iterators that dynamically terminate. +/// +/// # Panics +/// +/// This will always panic. +/// +/// # Examples /// -/// ```{.rust} -/// struct Item { weight: uint } -/// -/// fn choose_weighted_item(v: &[Item]) -> Item { -/// assert!(!v.is_empty()); -/// let mut so_far = 0u; -/// for item in v.iter() { -/// so_far += item.weight; -/// if so_far > 100 { -/// return *item; -/// } +/// Match arms: +/// +/// ```rust +/// fn foo(x: Option<int>) { +/// match x { +/// Some(n) if n >= 0 => println!("Some(Non-negative)"), +/// Some(n) if n < 0 => println!("Some(Negative)"), +/// Some(_) => unreachable!(), // compile error if commented out +/// None => println!("None") +/// } +/// } +/// ``` +/// +/// Iterators: +/// +/// ```rust +/// fn divide_by_three(x: u32) -> u32 { // one of the poorest implementations of x/3 +/// for i in std::iter::count(0_u32, 1) { +/// if 3*i < i { panic!("u32 overflow"); } +/// if x < 3*i { return i-1; } /// } -/// // The above loop always returns, so we must hint to the -/// // type checker that it isn't possible to get down here /// unreachable!(); /// } /// ``` #[macro_export] -macro_rules! unreachable( +macro_rules! unreachable { () => ({ panic!("internal error: entered unreachable code") }); @@ -220,14 +236,14 @@ macro_rules! unreachable( ($fmt:expr, $($arg:tt)*) => ({ panic!(concat!("internal error: entered unreachable code: ", $fmt), $($arg)*) }); -) +} /// A standardised placeholder for marking unfinished code. It panics with the /// message `"not yet implemented"` when executed. #[macro_export] -macro_rules! unimplemented( +macro_rules! unimplemented { () => (panic!("not yet implemented")) -) +} /// Use the syntax described in `std::fmt` to create a value of type `String`. /// See `std::fmt` for more information. @@ -241,11 +257,11 @@ macro_rules! unimplemented( /// ``` #[macro_export] #[stable] -macro_rules! format( +macro_rules! format { ($($arg:tt)*) => ( format_args!(::std::fmt::format, $($arg)*) ) -) +} /// Use the `format!` syntax to write data into a buffer of type `&mut Writer`. /// See `std::fmt` for more information. @@ -261,30 +277,30 @@ macro_rules! format( /// ``` #[macro_export] #[stable] -macro_rules! write( +macro_rules! write { ($dst:expr, $($arg:tt)*) => ({ let dst = &mut *$dst; format_args!(|args| { dst.write_fmt(args) }, $($arg)*) }) -) +} /// Equivalent to the `write!` macro, except that a newline is appended after /// the message is written. #[macro_export] #[stable] -macro_rules! writeln( +macro_rules! writeln { ($dst:expr, $fmt:expr $($arg:tt)*) => ( write!($dst, concat!($fmt, "\n") $($arg)*) ) -) +} /// Equivalent to the `println!` macro except that a newline is not printed at /// the end of the message. #[macro_export] #[stable] -macro_rules! print( +macro_rules! print { ($($arg:tt)*) => (format_args!(::std::io::stdio::print_args, $($arg)*)) -) +} /// Macro for printing to a task's stdout handle. /// @@ -300,55 +316,33 @@ macro_rules! print( /// ``` #[macro_export] #[stable] -macro_rules! println( +macro_rules! println { ($($arg:tt)*) => (format_args!(::std::io::stdio::println_args, $($arg)*)) -) - -/// Declare a task-local key with a specific type. -/// -/// # Example -/// -/// ``` -/// local_data_key!(my_integer: int) -/// -/// my_integer.replace(Some(2)); -/// println!("{}", my_integer.get().map(|a| *a)); -/// ``` -#[macro_export] -macro_rules! local_data_key( - ($name:ident: $ty:ty) => ( - #[allow(non_upper_case_globals)] - static $name: ::std::local_data::Key<$ty> = &::std::local_data::KeyValueKey; - ); - (pub $name:ident: $ty:ty) => ( - #[allow(non_upper_case_globals)] - pub static $name: ::std::local_data::Key<$ty> = &::std::local_data::KeyValueKey; - ); -) +} /// Helper macro for unwrapping `Result` values while returning early with an /// error if the value of the expression is `Err`. For more information, see /// `std::io`. #[macro_export] -macro_rules! try ( +macro_rules! try { ($expr:expr) => ({ match $expr { Ok(val) => val, Err(err) => return Err(::std::error::FromError::from_error(err)) } }) -) +} /// Create a `std::vec::Vec` containing the arguments. #[macro_export] -macro_rules! vec[ +macro_rules! vec { ($($x:expr),*) => ({ - use std::slice::BoxedSlicePrelude; + use std::slice::BoxedSliceExt; let xs: ::std::boxed::Box<[_]> = box [$($x),*]; xs.into_vec() }); ($($x:expr,)*) => (vec![$($x),*]) -] +} /// A macro to select an event from a number of receivers. /// @@ -359,13 +353,15 @@ macro_rules! vec[ /// # Example /// /// ``` +/// use std::thread::Thread; +/// /// let (tx1, rx1) = channel(); /// let (tx2, rx2) = channel(); /// # fn long_running_task() {} /// # fn calculate_the_answer() -> int { 42i } /// -/// spawn(proc() { long_running_task(); tx1.send(()) }); -/// spawn(proc() { tx2.send(calculate_the_answer()) }); +/// Thread::spawn(move|| { long_running_task(); tx1.send(()) }).detach(); +/// Thread::spawn(move|| { tx2.send(calculate_the_answer()) }).detach(); /// /// select! ( /// () = rx1.recv() => println!("the long running task finished first"), @@ -400,11 +396,11 @@ macro_rules! select { // uses. To get around this difference, we redefine the log!() macro here to be // just a dumb version of what it should be. #[cfg(test)] -macro_rules! log ( +macro_rules! log { ($lvl:expr, $($args:tt)*) => ( if log_enabled!($lvl) { println!($($args)*) } ) -) +} /// Built-in macros to the compiler itself. /// @@ -436,9 +432,9 @@ pub mod builtin { /// }, "hello {}", "world"); /// ``` #[macro_export] - macro_rules! format_args( ($closure:expr, $fmt:expr $($args:tt)*) => ({ + macro_rules! format_args { ($closure:expr, $fmt:expr $($args:tt)*) => ({ /* compiler built-in */ - }) ) + }) } /// Inspect an environment variable at compile time. /// @@ -456,7 +452,7 @@ pub mod builtin { /// println!("the $PATH variable at the time of compiling was: {}", path); /// ``` #[macro_export] - macro_rules! env( ($name:expr) => ({ /* compiler built-in */ }) ) + macro_rules! env { ($name:expr) => ({ /* compiler built-in */ }) } /// Optionally inspect an environment variable at compile time. /// @@ -475,7 +471,7 @@ pub mod builtin { /// println!("the secret key might be: {}", key); /// ``` #[macro_export] - macro_rules! option_env( ($name:expr) => ({ /* compiler built-in */ }) ) + macro_rules! option_env { ($name:expr) => ({ /* compiler built-in */ }) } /// Concatenate literals into a static byte slice. /// @@ -495,7 +491,7 @@ pub mod builtin { /// assert_eq!(rust[4], 255); /// ``` #[macro_export] - macro_rules! bytes( ($($e:expr),*) => ({ /* compiler built-in */ }) ) + macro_rules! bytes { ($($e:expr),*) => ({ /* compiler built-in */ }) } /// Concatenate identifiers into one identifier. /// @@ -519,7 +515,9 @@ pub mod builtin { /// # } /// ``` #[macro_export] - macro_rules! concat_idents( ($($e:ident),*) => ({ /* compiler built-in */ }) ) + macro_rules! concat_idents { + ($($e:ident),*) => ({ /* compiler built-in */ }) + } /// Concatenates literals into a static string slice. /// @@ -537,7 +535,7 @@ pub mod builtin { /// assert_eq!(s, "test10btrue"); /// ``` #[macro_export] - macro_rules! concat( ($($e:expr),*) => ({ /* compiler built-in */ }) ) + macro_rules! concat { ($($e:expr),*) => ({ /* compiler built-in */ }) } /// A macro which expands to the line number on which it was invoked. /// @@ -552,7 +550,7 @@ pub mod builtin { /// println!("defined on line: {}", current_line); /// ``` #[macro_export] - macro_rules! line( () => ({ /* compiler built-in */ }) ) + macro_rules! line { () => ({ /* compiler built-in */ }) } /// A macro which expands to the column number on which it was invoked. /// @@ -567,7 +565,7 @@ pub mod builtin { /// println!("defined on column: {}", current_col); /// ``` #[macro_export] - macro_rules! column( () => ({ /* compiler built-in */ }) ) + macro_rules! column { () => ({ /* compiler built-in */ }) } /// A macro which expands to the file name from which it was invoked. /// @@ -583,7 +581,7 @@ pub mod builtin { /// println!("defined in file: {}", this_file); /// ``` #[macro_export] - macro_rules! file( () => ({ /* compiler built-in */ }) ) + macro_rules! file { () => ({ /* compiler built-in */ }) } /// A macro which stringifies its argument. /// @@ -598,7 +596,7 @@ pub mod builtin { /// assert_eq!(one_plus_one, "1 + 1"); /// ``` #[macro_export] - macro_rules! stringify( ($t:tt) => ({ /* compiler built-in */ }) ) + macro_rules! stringify { ($t:tt) => ({ /* compiler built-in */ }) } /// Includes a utf8-encoded file as a string. /// @@ -612,7 +610,7 @@ pub mod builtin { /// let secret_key = include_str!("secret-key.ascii"); /// ``` #[macro_export] - macro_rules! include_str( ($file:expr) => ({ /* compiler built-in */ }) ) + macro_rules! include_str { ($file:expr) => ({ /* compiler built-in */ }) } /// Includes a file as a byte slice. /// @@ -626,7 +624,7 @@ pub mod builtin { /// let secret_key = include_bin!("secret-key.bin"); /// ``` #[macro_export] - macro_rules! include_bin( ($file:expr) => ({ /* compiler built-in */ }) ) + macro_rules! include_bin { ($file:expr) => ({ /* compiler built-in */ }) } /// Expands to a string that represents the current module path. /// @@ -646,7 +644,7 @@ pub mod builtin { /// test::foo(); /// ``` #[macro_export] - macro_rules! module_path( () => ({ /* compiler built-in */ }) ) + macro_rules! module_path { () => ({ /* compiler built-in */ }) } /// Boolean evaluation of configuration flags. /// @@ -667,5 +665,5 @@ pub mod builtin { /// }; /// ``` #[macro_export] - macro_rules! cfg( ($cfg:tt) => ({ /* compiler built-in */ }) ) + macro_rules! cfg { ($cfg:tt) => ({ /* compiler built-in */ }) } } diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 9aac857bb65..1f76382ce8a 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -21,6 +21,9 @@ use intrinsics; use libc::c_int; use num::{Float, FloatMath}; use num::strconv; +use num::strconv::ExponentFormat::{ExpNone, ExpDec}; +use num::strconv::SignificantDigits::{DigAll, DigMax, DigExact}; +use num::strconv::SignFormat::SignNeg; pub use core::f32::{RADIX, MANTISSA_DIGITS, DIGITS, EPSILON, MIN_VALUE}; pub use core::f32::{MIN_POS_VALUE, MAX_VALUE, MIN_EXP, MAX_EXP, MIN_10_EXP}; @@ -252,7 +255,7 @@ impl FloatMath for f32 { #[experimental = "may be removed or relocated"] pub fn to_string(num: f32) -> String { let (r, _) = strconv::float_to_str_common( - num, 10u, true, strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false); + num, 10u, true, SignNeg, DigAll, ExpNone, false); r } @@ -265,7 +268,7 @@ pub fn to_string(num: f32) -> String { #[experimental = "may be removed or relocated"] pub fn to_str_hex(num: f32) -> String { let (r, _) = strconv::float_to_str_common( - num, 16u, true, strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false); + num, 16u, true, SignNeg, DigAll, ExpNone, false); r } @@ -279,8 +282,7 @@ pub fn to_str_hex(num: f32) -> String { #[inline] #[experimental = "may be removed or relocated"] pub fn to_str_radix_special(num: f32, rdx: uint) -> (String, bool) { - strconv::float_to_str_common(num, rdx, true, - strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false) + strconv::float_to_str_common(num, rdx, true, SignNeg, DigAll, ExpNone, false) } /// Converts a float to a string with exactly the number of @@ -294,7 +296,7 @@ pub fn to_str_radix_special(num: f32, rdx: uint) -> (String, bool) { #[experimental = "may be removed or relocated"] pub fn to_str_exact(num: f32, dig: uint) -> String { let (r, _) = strconv::float_to_str_common( - num, 10u, true, strconv::SignNeg, strconv::DigExact(dig), strconv::ExpNone, false); + num, 10u, true, SignNeg, DigExact(dig), ExpNone, false); r } @@ -309,7 +311,7 @@ pub fn to_str_exact(num: f32, dig: uint) -> String { #[experimental = "may be removed or relocated"] pub fn to_str_digits(num: f32, dig: uint) -> String { let (r, _) = strconv::float_to_str_common( - num, 10u, true, strconv::SignNeg, strconv::DigMax(dig), strconv::ExpNone, false); + num, 10u, true, SignNeg, DigMax(dig), ExpNone, false); r } @@ -325,7 +327,7 @@ pub fn to_str_digits(num: f32, dig: uint) -> String { #[experimental = "may be removed or relocated"] pub fn to_str_exp_exact(num: f32, dig: uint, upper: bool) -> String { let (r, _) = strconv::float_to_str_common( - num, 10u, true, strconv::SignNeg, strconv::DigExact(dig), strconv::ExpDec, upper); + num, 10u, true, SignNeg, DigExact(dig), ExpDec, upper); r } @@ -341,7 +343,7 @@ pub fn to_str_exp_exact(num: f32, dig: uint, upper: bool) -> String { #[experimental = "may be removed or relocated"] pub fn to_str_exp_digits(num: f32, dig: uint, upper: bool) -> String { let (r, _) = strconv::float_to_str_common( - num, 10u, true, strconv::SignNeg, strconv::DigMax(dig), strconv::ExpDec, upper); + num, 10u, true, SignNeg, DigMax(dig), ExpDec, upper); r } @@ -349,7 +351,7 @@ pub fn to_str_exp_digits(num: f32, dig: uint, upper: bool) -> String { mod tests { use f32::*; use num::*; - use num; + use num::FpCategory as Fp; #[test] fn test_min_nan() { @@ -364,8 +366,8 @@ mod tests { } #[test] - fn test_num() { - num::test_num(10f32, 2f32); + fn test_num_f32() { + test_num(10f32, 2f32); } #[test] @@ -619,14 +621,14 @@ mod tests { let neg_inf: f32 = Float::neg_infinity(); let zero: f32 = Float::zero(); let neg_zero: f32 = Float::neg_zero(); - assert_eq!(nan.classify(), FPNaN); - assert_eq!(inf.classify(), FPInfinite); - assert_eq!(neg_inf.classify(), FPInfinite); - assert_eq!(zero.classify(), FPZero); - assert_eq!(neg_zero.classify(), FPZero); - assert_eq!(1f32.classify(), FPNormal); - assert_eq!(1e-37f32.classify(), FPNormal); - assert_eq!(1e-38f32.classify(), FPSubnormal); + assert_eq!(nan.classify(), Fp::Nan); + assert_eq!(inf.classify(), Fp::Infinite); + assert_eq!(neg_inf.classify(), Fp::Infinite); + assert_eq!(zero.classify(), Fp::Zero); + assert_eq!(neg_zero.classify(), Fp::Zero); + assert_eq!(1f32.classify(), Fp::Normal); + assert_eq!(1e-37f32.classify(), Fp::Normal); + assert_eq!(1e-38f32.classify(), Fp::Subnormal); } #[test] @@ -671,8 +673,8 @@ mod tests { let inf: f32 = Float::infinity(); let neg_inf: f32 = Float::neg_infinity(); let nan: f32 = Float::nan(); - assert_eq!(match inf.frexp() { (x, _) => x }, inf) - assert_eq!(match neg_inf.frexp() { (x, _) => x }, neg_inf) + assert_eq!(match inf.frexp() { (x, _) => x }, inf); + assert_eq!(match neg_inf.frexp() { (x, _) => x }, neg_inf); assert!(match nan.frexp() { (x, _) => x.is_nan() }) } diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index 29ccfe512b9..221ecf62c05 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -20,6 +20,9 @@ use intrinsics; use libc::c_int; use num::{Float, FloatMath}; use num::strconv; +use num::strconv::ExponentFormat::{ExpNone, ExpDec}; +use num::strconv::SignificantDigits::{DigAll, DigMax, DigExact}; +use num::strconv::SignFormat::SignNeg; pub use core::f64::{RADIX, MANTISSA_DIGITS, DIGITS, EPSILON, MIN_VALUE}; pub use core::f64::{MIN_POS_VALUE, MAX_VALUE, MIN_EXP, MAX_EXP, MIN_10_EXP}; @@ -260,7 +263,7 @@ impl FloatMath for f64 { #[experimental = "may be removed or relocated"] pub fn to_string(num: f64) -> String { let (r, _) = strconv::float_to_str_common( - num, 10u, true, strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false); + num, 10u, true, SignNeg, DigAll, ExpNone, false); r } @@ -273,7 +276,7 @@ pub fn to_string(num: f64) -> String { #[experimental = "may be removed or relocated"] pub fn to_str_hex(num: f64) -> String { let (r, _) = strconv::float_to_str_common( - num, 16u, true, strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false); + num, 16u, true, SignNeg, DigAll, ExpNone, false); r } @@ -287,8 +290,7 @@ pub fn to_str_hex(num: f64) -> String { #[inline] #[experimental = "may be removed or relocated"] pub fn to_str_radix_special(num: f64, rdx: uint) -> (String, bool) { - strconv::float_to_str_common(num, rdx, true, - strconv::SignNeg, strconv::DigAll, strconv::ExpNone, false) + strconv::float_to_str_common(num, rdx, true, SignNeg, DigAll, ExpNone, false) } /// Converts a float to a string with exactly the number of @@ -302,7 +304,7 @@ pub fn to_str_radix_special(num: f64, rdx: uint) -> (String, bool) { #[experimental = "may be removed or relocated"] pub fn to_str_exact(num: f64, dig: uint) -> String { let (r, _) = strconv::float_to_str_common( - num, 10u, true, strconv::SignNeg, strconv::DigExact(dig), strconv::ExpNone, false); + num, 10u, true, SignNeg, DigExact(dig), ExpNone, false); r } @@ -317,7 +319,7 @@ pub fn to_str_exact(num: f64, dig: uint) -> String { #[experimental = "may be removed or relocated"] pub fn to_str_digits(num: f64, dig: uint) -> String { let (r, _) = strconv::float_to_str_common( - num, 10u, true, strconv::SignNeg, strconv::DigMax(dig), strconv::ExpNone, false); + num, 10u, true, SignNeg, DigMax(dig), ExpNone, false); r } @@ -333,7 +335,7 @@ pub fn to_str_digits(num: f64, dig: uint) -> String { #[experimental = "may be removed or relocated"] pub fn to_str_exp_exact(num: f64, dig: uint, upper: bool) -> String { let (r, _) = strconv::float_to_str_common( - num, 10u, true, strconv::SignNeg, strconv::DigExact(dig), strconv::ExpDec, upper); + num, 10u, true, SignNeg, DigExact(dig), ExpDec, upper); r } @@ -349,7 +351,7 @@ pub fn to_str_exp_exact(num: f64, dig: uint, upper: bool) -> String { #[experimental = "may be removed or relocated"] pub fn to_str_exp_digits(num: f64, dig: uint, upper: bool) -> String { let (r, _) = strconv::float_to_str_common( - num, 10u, true, strconv::SignNeg, strconv::DigMax(dig), strconv::ExpDec, upper); + num, 10u, true, SignNeg, DigMax(dig), ExpDec, upper); r } @@ -357,7 +359,7 @@ pub fn to_str_exp_digits(num: f64, dig: uint, upper: bool) -> String { mod tests { use f64::*; use num::*; - use num; + use num::FpCategory as Fp; #[test] fn test_min_nan() { @@ -372,8 +374,8 @@ mod tests { } #[test] - fn test_num() { - num::test_num(10f64, 2f64); + fn test_num_f64() { + test_num(10f64, 2f64); } #[test] @@ -622,13 +624,13 @@ mod tests { let neg_inf: f64 = Float::neg_infinity(); let zero: f64 = Float::zero(); let neg_zero: f64 = Float::neg_zero(); - assert_eq!(nan.classify(), FPNaN); - assert_eq!(inf.classify(), FPInfinite); - assert_eq!(neg_inf.classify(), FPInfinite); - assert_eq!(zero.classify(), FPZero); - assert_eq!(neg_zero.classify(), FPZero); - assert_eq!(1e-307f64.classify(), FPNormal); - assert_eq!(1e-308f64.classify(), FPSubnormal); + assert_eq!(nan.classify(), Fp::Nan); + assert_eq!(inf.classify(), Fp::Infinite); + assert_eq!(neg_inf.classify(), Fp::Infinite); + assert_eq!(zero.classify(), Fp::Zero); + assert_eq!(neg_zero.classify(), Fp::Zero); + assert_eq!(1e-307f64.classify(), Fp::Normal); + assert_eq!(1e-308f64.classify(), Fp::Subnormal); } #[test] @@ -673,8 +675,8 @@ mod tests { let inf: f64 = Float::infinity(); let neg_inf: f64 = Float::neg_infinity(); let nan: f64 = Float::nan(); - assert_eq!(match inf.frexp() { (x, _) => x }, inf) - assert_eq!(match neg_inf.frexp() { (x, _) => x }, neg_inf) + assert_eq!(match inf.frexp() { (x, _) => x }, inf); + assert_eq!(match neg_inf.frexp() { (x, _) => x }, neg_inf); assert!(match nan.frexp() { (x, _) => x.is_nan() }) } diff --git a/src/libstd/num/float_macros.rs b/src/libstd/num/float_macros.rs index 4b3727ead61..fd00f15662a 100644 --- a/src/libstd/num/float_macros.rs +++ b/src/libstd/num/float_macros.rs @@ -12,11 +12,11 @@ #![macro_escape] #![doc(hidden)] -macro_rules! assert_approx_eq( +macro_rules! assert_approx_eq { ($a:expr, $b:expr) => ({ use num::Float; let (a, b) = (&$a, &$b); assert!((*a - *b).abs() < 1.0e-6, "{} is not approximately equal to {}", *a, *b); }) -) +} diff --git a/src/libstd/num/i16.rs b/src/libstd/num/i16.rs index 333d1d7df0b..367147b84be 100644 --- a/src/libstd/num/i16.rs +++ b/src/libstd/num/i16.rs @@ -15,4 +15,4 @@ pub use core::i16::{BITS, BYTES, MIN, MAX}; -int_module!(i16) +int_module! { i16 } diff --git a/src/libstd/num/i32.rs b/src/libstd/num/i32.rs index 44b5397bf74..19fb40c9644 100644 --- a/src/libstd/num/i32.rs +++ b/src/libstd/num/i32.rs @@ -15,4 +15,4 @@ pub use core::i32::{BITS, BYTES, MIN, MAX}; -int_module!(i32) +int_module! { i32 } diff --git a/src/libstd/num/i64.rs b/src/libstd/num/i64.rs index de6fa0d3ef8..2379b03c64f 100644 --- a/src/libstd/num/i64.rs +++ b/src/libstd/num/i64.rs @@ -15,4 +15,4 @@ pub use core::i64::{BITS, BYTES, MIN, MAX}; -int_module!(i64) +int_module! { i64 } diff --git a/src/libstd/num/i8.rs b/src/libstd/num/i8.rs index 3b9fbcb768b..a09ceefc6a0 100644 --- a/src/libstd/num/i8.rs +++ b/src/libstd/num/i8.rs @@ -15,4 +15,4 @@ pub use core::i8::{BITS, BYTES, MIN, MAX}; -int_module!(i8) +int_module! { i8 } diff --git a/src/libstd/num/int.rs b/src/libstd/num/int.rs index 36c021efe0a..9ccb1544fdc 100644 --- a/src/libstd/num/int.rs +++ b/src/libstd/num/int.rs @@ -10,9 +10,9 @@ //! Operations and constants for architecture-sized signed integers (`int` type) -#![unstable] +#![stable] #![doc(primitive = "int")] pub use core::int::{BITS, BYTES, MIN, MAX}; -int_module!(int) +int_module! { int } diff --git a/src/libstd/num/int_macros.rs b/src/libstd/num/int_macros.rs index 2f1162d28e5..fce150c4ad1 100644 --- a/src/libstd/num/int_macros.rs +++ b/src/libstd/num/int_macros.rs @@ -12,6 +12,6 @@ #![macro_escape] #![doc(hidden)] -macro_rules! int_module (($T:ty) => ( +macro_rules! int_module { ($T:ty) => ( -)) +) } diff --git a/src/libstd/num/mod.rs b/src/libstd/num/mod.rs index a15e71b4a2a..7c8763979bb 100644 --- a/src/libstd/num/mod.rs +++ b/src/libstd/num/mod.rs @@ -19,6 +19,7 @@ #[cfg(test)] use cmp::PartialEq; #[cfg(test)] use fmt::Show; #[cfg(test)] use ops::{Add, Sub, Mul, Div, Rem}; +#[cfg(test)] use kinds::Copy; pub use core::num::{Num, div_rem, Zero, zero, One, one}; pub use core::num::{Unsigned, pow, Bounded}; @@ -30,8 +31,7 @@ pub use core::num::{from_int, from_i8, from_i16, from_i32, from_i64}; pub use core::num::{from_uint, from_u8, from_u16, from_u32, from_u64}; pub use core::num::{from_f32, from_f64}; pub use core::num::{FromStrRadix, from_str_radix}; -pub use core::num::{FPCategory, FPNaN, FPInfinite, FPZero, FPSubnormal}; -pub use core::num::{FPNormal, Float}; +pub use core::num::{FpCategory, Float}; #[experimental = "may be removed or relocated"] pub mod strconv; @@ -130,18 +130,19 @@ pub fn test_num<T>(ten: T, two: T) where + Add<T, T> + Sub<T, T> + Mul<T, T> + Div<T, T> + Rem<T, T> + Show + + Copy { - assert_eq!(ten.add(&two), cast(12i).unwrap()); - assert_eq!(ten.sub(&two), cast(8i).unwrap()); - assert_eq!(ten.mul(&two), cast(20i).unwrap()); - assert_eq!(ten.div(&two), cast(5i).unwrap()); - assert_eq!(ten.rem(&two), cast(0i).unwrap()); - - assert_eq!(ten.add(&two), ten + two); - assert_eq!(ten.sub(&two), ten - two); - assert_eq!(ten.mul(&two), ten * two); - assert_eq!(ten.div(&two), ten / two); - assert_eq!(ten.rem(&two), ten % two); + assert_eq!(ten.add(two), cast(12i).unwrap()); + assert_eq!(ten.sub(two), cast(8i).unwrap()); + assert_eq!(ten.mul(two), cast(20i).unwrap()); + assert_eq!(ten.div(two), cast(5i).unwrap()); + assert_eq!(ten.rem(two), cast(0i).unwrap()); + + assert_eq!(ten.add(two), ten + two); + assert_eq!(ten.sub(two), ten - two); + assert_eq!(ten.mul(two), ten * two); + assert_eq!(ten.div(two), ten / two); + assert_eq!(ten.rem(two), ten % two); } #[cfg(test)] @@ -159,7 +160,7 @@ mod tests { use u64; use uint; - macro_rules! test_cast_20( + macro_rules! test_cast_20 { ($_20:expr) => ({ let _20 = $_20; @@ -202,7 +203,7 @@ mod tests { assert_eq!(_20, cast(20f32).unwrap()); assert_eq!(_20, cast(20f64).unwrap()); }) - ) + } #[test] fn test_u8_cast() { test_cast_20!(20u8) } #[test] fn test_u16_cast() { test_cast_20!(20u16) } @@ -662,11 +663,32 @@ mod tests { assert_eq!(third.checked_mul(4), None); } - macro_rules! test_next_power_of_two( + macro_rules! test_is_power_of_two { + ($test_name:ident, $T:ident) => ( + fn $test_name() { + #![test] + assert_eq!((0 as $T).is_power_of_two(), false); + assert_eq!((1 as $T).is_power_of_two(), true); + assert_eq!((2 as $T).is_power_of_two(), true); + assert_eq!((3 as $T).is_power_of_two(), false); + assert_eq!((4 as $T).is_power_of_two(), true); + assert_eq!((5 as $T).is_power_of_two(), false); + assert!(($T::MAX / 2 + 1).is_power_of_two(), true); + } + ) + } + + test_is_power_of_two!{ test_is_power_of_two_u8, u8 } + test_is_power_of_two!{ test_is_power_of_two_u16, u16 } + test_is_power_of_two!{ test_is_power_of_two_u32, u32 } + test_is_power_of_two!{ test_is_power_of_two_u64, u64 } + test_is_power_of_two!{ test_is_power_of_two_uint, uint } + + macro_rules! test_next_power_of_two { ($test_name:ident, $T:ident) => ( fn $test_name() { #![test] - assert_eq!((0 as $T).next_power_of_two(), 0); + assert_eq!((0 as $T).next_power_of_two(), 1); let mut next_power = 1; for i in range::<$T>(1, 40) { assert_eq!(i.next_power_of_two(), next_power); @@ -674,36 +696,36 @@ mod tests { } } ) - ) + } - test_next_power_of_two!(test_next_power_of_two_u8, u8) - test_next_power_of_two!(test_next_power_of_two_u16, u16) - test_next_power_of_two!(test_next_power_of_two_u32, u32) - test_next_power_of_two!(test_next_power_of_two_u64, u64) - test_next_power_of_two!(test_next_power_of_two_uint, uint) + test_next_power_of_two! { test_next_power_of_two_u8, u8 } + test_next_power_of_two! { test_next_power_of_two_u16, u16 } + test_next_power_of_two! { test_next_power_of_two_u32, u32 } + test_next_power_of_two! { test_next_power_of_two_u64, u64 } + test_next_power_of_two! { test_next_power_of_two_uint, uint } - macro_rules! test_checked_next_power_of_two( + macro_rules! test_checked_next_power_of_two { ($test_name:ident, $T:ident) => ( fn $test_name() { #![test] - assert_eq!((0 as $T).checked_next_power_of_two(), None); + assert_eq!((0 as $T).checked_next_power_of_two(), Some(1)); + assert!(($T::MAX / 2).checked_next_power_of_two().is_some()); + assert_eq!(($T::MAX - 1).checked_next_power_of_two(), None); + assert_eq!($T::MAX.checked_next_power_of_two(), None); let mut next_power = 1; for i in range::<$T>(1, 40) { assert_eq!(i.checked_next_power_of_two(), Some(next_power)); if i == next_power { next_power *= 2 } } - assert!(($T::MAX / 2).checked_next_power_of_two().is_some()); - assert_eq!(($T::MAX - 1).checked_next_power_of_two(), None); - assert_eq!($T::MAX.checked_next_power_of_two(), None); } ) - ) + } - test_checked_next_power_of_two!(test_checked_next_power_of_two_u8, u8) - test_checked_next_power_of_two!(test_checked_next_power_of_two_u16, u16) - test_checked_next_power_of_two!(test_checked_next_power_of_two_u32, u32) - test_checked_next_power_of_two!(test_checked_next_power_of_two_u64, u64) - test_checked_next_power_of_two!(test_checked_next_power_of_two_uint, uint) + test_checked_next_power_of_two! { test_checked_next_power_of_two_u8, u8 } + test_checked_next_power_of_two! { test_checked_next_power_of_two_u16, u16 } + test_checked_next_power_of_two! { test_checked_next_power_of_two_u32, u32 } + test_checked_next_power_of_two! { test_checked_next_power_of_two_u64, u64 } + test_checked_next_power_of_two! { test_checked_next_power_of_two_uint, uint } #[deriving(PartialEq, Show)] struct Value { x: int } @@ -757,13 +779,13 @@ mod tests { let one: T = Int::one(); range(0, exp).fold(one, |acc, _| acc * base) } - macro_rules! assert_pow( + macro_rules! assert_pow { (($num:expr, $exp:expr) => $expected:expr) => {{ let result = $num.pow($exp); assert_eq!(result, $expected); assert_eq!(result, naive_pow($num, $exp)); }} - ) + } assert_pow!((3i, 0 ) => 1); assert_pow!((5i, 1 ) => 5); assert_pow!((-4i, 2 ) => 16); diff --git a/src/libstd/num/strconv.rs b/src/libstd/num/strconv.rs index 649298d9c08..b1f4e5acb93 100644 --- a/src/libstd/num/strconv.rs +++ b/src/libstd/num/strconv.rs @@ -12,20 +12,21 @@ #![allow(missing_docs)] -pub use self::ExponentFormat::*; -pub use self::SignificantDigits::*; -pub use self::SignFormat::*; - -use char; -use char::Char; -use num; -use num::{Int, Float, FPNaN, FPInfinite, ToPrimitive}; -use slice::{SlicePrelude, CloneSliceAllocPrelude}; -use str::StrPrelude; +use self::ExponentFormat::*; +use self::SignificantDigits::*; +use self::SignFormat::*; + +use char::{mod, Char}; +use num::{mod, Int, Float, ToPrimitive}; +use num::FpCategory as Fp; +use ops::FnMut; +use slice::{SliceExt, CloneSliceExt}; +use str::StrExt; use string::String; use vec::Vec; /// A flag that specifies whether to use exponential (scientific) notation. +#[deriving(Copy)] pub enum ExponentFormat { /// Do not use exponential notation. ExpNone, @@ -40,6 +41,7 @@ pub enum ExponentFormat { /// The number of digits used for emitting the fractional part of a number, if /// any. +#[deriving(Copy)] pub enum SignificantDigits { /// All calculable digits will be printed. /// @@ -56,6 +58,7 @@ pub enum SignificantDigits { } /// How to emit the sign of a number. +#[deriving(Copy)] pub enum SignFormat { /// No sign will be printed. The exponent sign will also be emitted. SignNone, @@ -67,32 +70,29 @@ pub enum SignFormat { SignAll, } -/** - * Converts an integral number to its string representation as a byte vector. - * This is meant to be a common base implementation for all integral string - * conversion functions like `to_string()` or `to_str_radix()`. - * - * # Arguments - * - `num` - The number to convert. Accepts any number that - * implements the numeric traits. - * - `radix` - Base to use. Accepts only the values 2-36. - * - `sign` - How to emit the sign. Options are: - * - `SignNone`: No sign at all. Basically emits `abs(num)`. - * - `SignNeg`: Only `-` on negative values. - * - `SignAll`: Both `+` on positive, and `-` on negative numbers. - * - `f` - a callback which will be invoked for each ascii character - * which composes the string representation of this integer - * - * # Return value - * A tuple containing the byte vector, and a boolean flag indicating - * whether it represents a special value like `inf`, `-inf`, `NaN` or not. - * It returns a tuple because there can be ambiguity between a special value - * and a number representation at higher bases. - * - * # Panics - * - Panics if `radix` < 2 or `radix` > 36. - */ -fn int_to_str_bytes_common<T: Int>(num: T, radix: uint, sign: SignFormat, f: |u8|) { +/// Converts an integral number to its string representation as a byte vector. +/// This is meant to be a common base implementation for all integral string +/// conversion functions like `to_string()` or `to_str_radix()`. +/// +/// # Arguments +/// +/// - `num` - The number to convert. Accepts any number that +/// implements the numeric traits. +/// - `radix` - Base to use. Accepts only the values 2-36. +/// - `sign` - How to emit the sign. Options are: +/// - `SignNone`: No sign at all. Basically emits `abs(num)`. +/// - `SignNeg`: Only `-` on negative values. +/// - `SignAll`: Both `+` on positive, and `-` on negative numbers. +/// - `f` - a callback which will be invoked for each ascii character +/// which composes the string representation of this integer +/// +/// # Panics +/// +/// - Panics if `radix` < 2 or `radix` > 36. +fn int_to_str_bytes_common<T, F>(num: T, radix: uint, sign: SignFormat, mut f: F) where + T: Int, + F: FnMut(u8), +{ assert!(2 <= radix && radix <= 36); let _0: T = Int::zero(); @@ -146,40 +146,41 @@ fn int_to_str_bytes_common<T: Int>(num: T, radix: uint, sign: SignFormat, f: |u8 } } -/** - * Converts a number to its string representation as a byte vector. - * This is meant to be a common base implementation for all numeric string - * conversion functions like `to_string()` or `to_str_radix()`. - * - * # Arguments - * - `num` - The number to convert. Accepts any number that - * implements the numeric traits. - * - `radix` - Base to use. Accepts only the values 2-36. If the exponential notation - * is used, then this base is only used for the significand. The exponent - * itself always printed using a base of 10. - * - `negative_zero` - Whether to treat the special value `-0` as - * `-0` or as `+0`. - * - `sign` - How to emit the sign. See `SignFormat`. - * - `digits` - The amount of digits to use for emitting the fractional - * part, if any. See `SignificantDigits`. - * - `exp_format` - Whether or not to use the exponential (scientific) notation. - * See `ExponentFormat`. - * - `exp_capital` - Whether or not to use a capital letter for the exponent sign, if - * exponential notation is desired. - * - * # Return value - * A tuple containing the byte vector, and a boolean flag indicating - * whether it represents a special value like `inf`, `-inf`, `NaN` or not. - * It returns a tuple because there can be ambiguity between a special value - * and a number representation at higher bases. - * - * # Panics - * - Panics if `radix` < 2 or `radix` > 36. - * - Panics if `radix` > 14 and `exp_format` is `ExpDec` due to conflict - * between digit and exponent sign `'e'`. - * - Panics if `radix` > 25 and `exp_format` is `ExpBin` due to conflict - * between digit and exponent sign `'p'`. - */ +/// Converts a number to its string representation as a byte vector. +/// This is meant to be a common base implementation for all numeric string +/// conversion functions like `to_string()` or `to_str_radix()`. +/// +/// # Arguments +/// +/// - `num` - The number to convert. Accepts any number that +/// implements the numeric traits. +/// - `radix` - Base to use. Accepts only the values 2-36. If the exponential notation +/// is used, then this base is only used for the significand. The exponent +/// itself always printed using a base of 10. +/// - `negative_zero` - Whether to treat the special value `-0` as +/// `-0` or as `+0`. +/// - `sign` - How to emit the sign. See `SignFormat`. +/// - `digits` - The amount of digits to use for emitting the fractional +/// part, if any. See `SignificantDigits`. +/// - `exp_format` - Whether or not to use the exponential (scientific) notation. +/// See `ExponentFormat`. +/// - `exp_capital` - Whether or not to use a capital letter for the exponent sign, if +/// exponential notation is desired. +/// +/// # Return value +/// +/// A tuple containing the byte vector, and a boolean flag indicating +/// whether it represents a special value like `inf`, `-inf`, `NaN` or not. +/// It returns a tuple because there can be ambiguity between a special value +/// and a number representation at higher bases. +/// +/// # Panics +/// +/// - Panics if `radix` < 2 or `radix` > 36. +/// - Panics if `radix` > 14 and `exp_format` is `ExpDec` due to conflict +/// between digit and exponent sign `'e'`. +/// - Panics if `radix` > 25 and `exp_format` is `ExpBin` due to conflict +/// between digit and exponent sign `'p'`. pub fn float_to_str_bytes_common<T: Float>( num: T, radix: uint, negative_zero: bool, sign: SignFormat, digits: SignificantDigits, exp_format: ExponentFormat, exp_upper: bool @@ -199,14 +200,14 @@ pub fn float_to_str_bytes_common<T: Float>( let _1: T = Float::one(); match num.classify() { - FPNaN => { return (b"NaN".to_vec(), true); } - FPInfinite if num > _0 => { + Fp::Nan => { return (b"NaN".to_vec(), true); } + Fp::Infinite if num > _0 => { return match sign { SignAll => (b"+inf".to_vec(), true), _ => (b"inf".to_vec(), true) }; } - FPInfinite if num < _0 => { + Fp::Infinite if num < _0 => { return match sign { SignNone => (b"inf".to_vec(), true), _ => (b"-inf".to_vec(), true), @@ -407,10 +408,8 @@ pub fn float_to_str_bytes_common<T: Float>( (buf, false) } -/** - * Converts a number to its string representation. This is a wrapper for - * `to_str_bytes_common()`, for details see there. - */ +/// Converts a number to its string representation. This is a wrapper for +/// `to_str_bytes_common()`, for details see there. #[inline] pub fn float_to_str_common<T: Float>( num: T, radix: uint, negative_zero: bool, @@ -433,28 +432,28 @@ mod tests { #[test] fn test_int_to_str_overflow() { let mut i8_val: i8 = 127_i8; - assert_eq!(i8_val.to_string(), "127".to_string()); + assert_eq!(i8_val.to_string(), "127"); i8_val += 1 as i8; - assert_eq!(i8_val.to_string(), "-128".to_string()); + assert_eq!(i8_val.to_string(), "-128"); let mut i16_val: i16 = 32_767_i16; - assert_eq!(i16_val.to_string(), "32767".to_string()); + assert_eq!(i16_val.to_string(), "32767"); i16_val += 1 as i16; - assert_eq!(i16_val.to_string(), "-32768".to_string()); + assert_eq!(i16_val.to_string(), "-32768"); let mut i32_val: i32 = 2_147_483_647_i32; - assert_eq!(i32_val.to_string(), "2147483647".to_string()); + assert_eq!(i32_val.to_string(), "2147483647"); i32_val += 1 as i32; - assert_eq!(i32_val.to_string(), "-2147483648".to_string()); + assert_eq!(i32_val.to_string(), "-2147483648"); let mut i64_val: i64 = 9_223_372_036_854_775_807_i64; - assert_eq!(i64_val.to_string(), "9223372036854775807".to_string()); + assert_eq!(i64_val.to_string(), "9223372036854775807"); i64_val += 1 as i64; - assert_eq!(i64_val.to_string(), "-9223372036854775808".to_string()); + assert_eq!(i64_val.to_string(), "-9223372036854775808"); } } diff --git a/src/libstd/num/u16.rs b/src/libstd/num/u16.rs index a83a66c23a5..46699b78599 100644 --- a/src/libstd/num/u16.rs +++ b/src/libstd/num/u16.rs @@ -15,4 +15,6 @@ pub use core::u16::{BITS, BYTES, MIN, MAX}; -uint_module!(u16) +use ops::FnOnce; + +uint_module! { u16 } diff --git a/src/libstd/num/u32.rs b/src/libstd/num/u32.rs index 7271203b23b..45ee9251d2f 100644 --- a/src/libstd/num/u32.rs +++ b/src/libstd/num/u32.rs @@ -15,4 +15,6 @@ pub use core::u32::{BITS, BYTES, MIN, MAX}; -uint_module!(u32) +use ops::FnOnce; + +uint_module! { u32 } diff --git a/src/libstd/num/u64.rs b/src/libstd/num/u64.rs index 25de2f3b255..1d8ff77dac8 100644 --- a/src/libstd/num/u64.rs +++ b/src/libstd/num/u64.rs @@ -15,4 +15,6 @@ pub use core::u64::{BITS, BYTES, MIN, MAX}; -uint_module!(u64) +use ops::FnOnce; + +uint_module! { u64 } diff --git a/src/libstd/num/u8.rs b/src/libstd/num/u8.rs index 22dedeecf3b..0663ace2e5b 100644 --- a/src/libstd/num/u8.rs +++ b/src/libstd/num/u8.rs @@ -15,4 +15,6 @@ pub use core::u8::{BITS, BYTES, MIN, MAX}; -uint_module!(u8) +use ops::FnOnce; + +uint_module! { u8 } diff --git a/src/libstd/num/uint.rs b/src/libstd/num/uint.rs index a425aab3aa1..cd000b3098b 100644 --- a/src/libstd/num/uint.rs +++ b/src/libstd/num/uint.rs @@ -10,9 +10,11 @@ //! Operations and constants for architecture-sized unsigned integers (`uint` type) -#![unstable] +#![stable] #![doc(primitive = "uint")] pub use core::uint::{BITS, BYTES, MIN, MAX}; -uint_module!(uint) +use ops::FnOnce; + +uint_module! { uint } diff --git a/src/libstd/num/uint_macros.rs b/src/libstd/num/uint_macros.rs index 7b79e535201..c42b7eebfdd 100644 --- a/src/libstd/num/uint_macros.rs +++ b/src/libstd/num/uint_macros.rs @@ -13,7 +13,7 @@ #![doc(hidden)] #![allow(unsigned_negation)] -macro_rules! uint_module (($T:ty) => ( +macro_rules! uint_module { ($T:ty) => ( // String conversion functions and impl num -> str @@ -32,7 +32,9 @@ macro_rules! uint_module (($T:ty) => ( /// ``` #[inline] #[deprecated = "just use .to_string(), or a BufWriter with write! if you mustn't allocate"] -pub fn to_str_bytes<U>(n: $T, radix: uint, f: |v: &[u8]| -> U) -> U { +pub fn to_str_bytes<U, F>(n: $T, radix: uint, f: F) -> U where + F: FnOnce(&[u8]) -> U, +{ use io::{Writer, Seek}; // The radix can be as low as 2, so we need at least 64 characters for a // base 2 number, and then we need another for a possible '-' character. @@ -79,28 +81,28 @@ mod tests { #[test] fn test_uint_to_str_overflow() { let mut u8_val: u8 = 255_u8; - assert_eq!(u8_val.to_string(), "255".to_string()); + assert_eq!(u8_val.to_string(), "255"); u8_val += 1 as u8; - assert_eq!(u8_val.to_string(), "0".to_string()); + assert_eq!(u8_val.to_string(), "0"); let mut u16_val: u16 = 65_535_u16; - assert_eq!(u16_val.to_string(), "65535".to_string()); + assert_eq!(u16_val.to_string(), "65535"); u16_val += 1 as u16; - assert_eq!(u16_val.to_string(), "0".to_string()); + assert_eq!(u16_val.to_string(), "0"); let mut u32_val: u32 = 4_294_967_295_u32; - assert_eq!(u32_val.to_string(), "4294967295".to_string()); + assert_eq!(u32_val.to_string(), "4294967295"); u32_val += 1 as u32; - assert_eq!(u32_val.to_string(), "0".to_string()); + assert_eq!(u32_val.to_string(), "0"); let mut u64_val: u64 = 18_446_744_073_709_551_615_u64; - assert_eq!(u64_val.to_string(), "18446744073709551615".to_string()); + assert_eq!(u64_val.to_string(), "18446744073709551615"); u64_val += 1 as u64; - assert_eq!(u64_val.to_string(), "0".to_string()); + assert_eq!(u64_val.to_string(), "0"); } #[test] @@ -139,4 +141,4 @@ mod tests { } } -)) +) } diff --git a/src/libstd/os.rs b/src/libstd/os.rs index d7ba4877086..ceb9a4102f6 100644 --- a/src/libstd/os.rs +++ b/src/libstd/os.rs @@ -8,59 +8,61 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! - * Higher-level interfaces to libc::* functions and operating system services. - * - * In general these take and return rust types, use rust idioms (enums, - * closures, vectors) rather than C idioms, and do more extensive safety - * checks. - * - * This module is not meant to only contain 1:1 mappings to libc entries; any - * os-interface code that is reasonably useful and broadly applicable can go - * here. Including utility routines that merely build on other os code. - * - * We assume the general case is that users do not care, and do not want to - * be made to care, which operating system they are on. While they may want - * to special case various special cases -- and so we will not _hide_ the - * facts of which OS the user is on -- they should be given the opportunity - * to write OS-ignorant code by default. - */ +//! Higher-level interfaces to libc::* functions and operating system services. +//! +//! In general these take and return rust types, use rust idioms (enums, closures, vectors) rather +//! than C idioms, and do more extensive safety checks. +//! +//! This module is not meant to only contain 1:1 mappings to libc entries; any os-interface code +//! that is reasonably useful and broadly applicable can go here. Including utility routines that +//! merely build on other os code. +//! +//! We assume the general case is that users do not care, and do not want to be made to care, which +//! operating system they are on. While they may want to special case various special cases -- and +//! so we will not _hide_ the facts of which OS the user is on -- they should be given the +//! opportunity to write OS-ignorant code by default. #![experimental] #![allow(missing_docs)] #![allow(non_snake_case)] +#![allow(unused_imports)] -pub use self::MemoryMapKind::*; -pub use self::MapOption::*; -pub use self::MapError::*; +use self::MemoryMapKind::*; +use self::MapOption::*; +use self::MapError::*; use clone::Clone; use error::{FromError, Error}; use fmt; use io::{IoResult, IoError}; -use iter::Iterator; -use libc::{c_void, c_int}; +use iter::{Iterator, IteratorExt}; +use kinds::Copy; +use libc::{c_void, c_int, c_char}; use libc; use boxed::Box; -use ops::Drop; -use option::{Some, None, Option}; -use os; +use ops::{Drop, FnOnce}; +use option::Option; +use option::Option::{Some, None}; use path::{Path, GenericPath, BytesContainer}; use sys; -use sys::os as os_imp; use ptr::RawPtr; use ptr; -use result::{Err, Ok, Result}; -use slice::{AsSlice, SlicePrelude, PartialEqSlicePrelude}; -use slice::CloneSliceAllocPrelude; -use str::{Str, StrPrelude, StrAllocating}; +use result::Result; +use result::Result::{Err, Ok}; +use slice::{AsSlice, SliceExt}; +use slice::CloneSliceExt; +use str::{Str, StrExt}; use string::{String, ToString}; use sync::atomic::{AtomicInt, INIT_ATOMIC_INT, SeqCst}; use vec::Vec; #[cfg(unix)] use c_str::ToCStr; -#[cfg(unix)] use libc::c_char; + +#[cfg(unix)] +pub use sys::ext as unix; +#[cfg(windows)] +pub use sys::ext as windows; /// Get the number of cores available pub fn num_cpus() -> uint { @@ -74,42 +76,6 @@ pub fn num_cpus() -> uint { } pub const TMPBUF_SZ : uint = 1000u; -const BUF_BYTES : uint = 2048u; - -/// Returns the current working directory as a `Path`. -/// -/// # Errors -/// -/// Returns an `Err` if the current working directory value is invalid. -/// Possible cases: -/// -/// * Current directory does not exist. -/// * There are insufficient permissions to access the current directory. -/// * The internal buffer is not large enough to hold the path. -/// -/// # Example -/// -/// ```rust -/// use std::os; -/// -/// // We assume that we are in a valid directory like "/home". -/// let current_working_directory = os::getcwd().unwrap(); -/// println!("The current directory is {}", current_working_directory.display()); -/// // /home -/// ``` -#[cfg(unix)] -pub fn getcwd() -> IoResult<Path> { - use c_str::CString; - - let mut buf = [0 as c_char, ..BUF_BYTES]; - unsafe { - if libc::getcwd(buf.as_mut_ptr(), buf.len() as libc::size_t).is_null() { - Err(IoError::last_error()) - } else { - Ok(Path::new(CString::new(buf.as_ptr(), false))) - } - } -} /// Returns the current working directory as a `Path`. /// @@ -127,95 +93,27 @@ pub fn getcwd() -> IoResult<Path> { /// ```rust /// use std::os; /// -/// // We assume that we are in a valid directory like "C:\\Windows". +/// // We assume that we are in a valid directory. /// let current_working_directory = os::getcwd().unwrap(); /// println!("The current directory is {}", current_working_directory.display()); -/// // C:\\Windows /// ``` -#[cfg(windows)] pub fn getcwd() -> IoResult<Path> { - use libc::DWORD; - use libc::GetCurrentDirectoryW; - use io::OtherIoError; - - let mut buf = [0 as u16, ..BUF_BYTES]; - unsafe { - if libc::GetCurrentDirectoryW(buf.len() as DWORD, buf.as_mut_ptr()) == 0 as DWORD { - return Err(IoError::last_error()); - } - } - - match String::from_utf16(::str::truncate_utf16_at_nul(&buf)) { - Some(ref cwd) => Ok(Path::new(cwd)), - None => Err(IoError { - kind: OtherIoError, - desc: "GetCurrentDirectoryW returned invalid UTF-16", - detail: None, - }), - } -} - -#[cfg(windows)] -pub mod windows { - use libc::types::os::arch::extra::DWORD; - use libc; - use option::{None, Option}; - use option; - use os::TMPBUF_SZ; - use slice::{SlicePrelude}; - use string::String; - use str::StrPrelude; - use vec::Vec; - - pub fn fill_utf16_buf_and_decode(f: |*mut u16, DWORD| -> DWORD) - -> Option<String> { - - unsafe { - let mut n = TMPBUF_SZ as DWORD; - let mut res = None; - let mut done = false; - while !done { - let mut buf = Vec::from_elem(n as uint, 0u16); - let k = f(buf.as_mut_ptr(), n); - if k == (0 as DWORD) { - done = true; - } else if k == n && - libc::GetLastError() == - libc::ERROR_INSUFFICIENT_BUFFER as DWORD { - n *= 2 as DWORD; - } else if k >= n { - n = k; - } else { - done = true; - } - if k != 0 && done { - let sub = buf.slice(0, k as uint); - // We want to explicitly catch the case when the - // closure returned invalid UTF-16, rather than - // set `res` to None and continue. - let s = String::from_utf16(sub) - .expect("fill_utf16_buf_and_decode: closure created invalid UTF-16"); - res = option::Some(s) - } - } - return res; - } - } + sys::os::getcwd() } /* Accessing environment variables is not generally threadsafe. Serialize access through a global lock. */ -fn with_env_lock<T>(f: || -> T) -> T { - use rustrt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; +fn with_env_lock<T, F>(f: F) -> T where + F: FnOnce() -> T, +{ + use sync::{StaticMutex, MUTEX_INIT}; - static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; + static LOCK: StaticMutex = MUTEX_INIT; - unsafe { - let _guard = LOCK.lock(); - f() - } + let _guard = LOCK.lock(); + f() } /// Returns a vector of (variable, value) pairs, for all the environment @@ -236,8 +134,8 @@ fn with_env_lock<T>(f: || -> T) -> T { /// ``` pub fn env() -> Vec<(String,String)> { env_as_bytes().into_iter().map(|(k,v)| { - let k = String::from_utf8_lossy(k.as_slice()).into_string(); - let v = String::from_utf8_lossy(v.as_slice()).into_string(); + let k = String::from_utf8_lossy(k.as_slice()).into_owned(); + let v = String::from_utf8_lossy(v.as_slice()).into_owned(); (k,v) }).collect() } @@ -246,75 +144,10 @@ pub fn env() -> Vec<(String,String)> { /// environment variables of the current process. pub fn env_as_bytes() -> Vec<(Vec<u8>,Vec<u8>)> { unsafe { - #[cfg(windows)] - unsafe fn get_env_pairs() -> Vec<Vec<u8>> { - use slice::raw; - - use libc::funcs::extra::kernel32::{ - GetEnvironmentStringsW, - FreeEnvironmentStringsW - }; - let ch = GetEnvironmentStringsW(); - if ch as uint == 0 { - panic!("os::env() failure getting env string from OS: {}", - os::last_os_error()); - } - // Here, we lossily decode the string as UTF16. - // - // The docs suggest that the result should be in Unicode, but - // Windows doesn't guarantee it's actually UTF16 -- it doesn't - // validate the environment string passed to CreateProcess nor - // SetEnvironmentVariable. Yet, it's unlikely that returning a - // raw u16 buffer would be of practical use since the result would - // be inherently platform-dependent and introduce additional - // complexity to this code. - // - // Using the non-Unicode version of GetEnvironmentStrings is even - // worse since the result is in an OEM code page. Characters that - // can't be encoded in the code page would be turned into question - // marks. - let mut result = Vec::new(); - let mut i = 0; - while *ch.offset(i) != 0 { - let p = &*ch.offset(i); - let mut len = 0; - while *(p as *const _).offset(len) != 0 { - len += 1; - } - raw::buf_as_slice(p, len as uint, |s| { - result.push(String::from_utf16_lossy(s).into_bytes()); - }); - i += len as int + 1; - } - FreeEnvironmentStringsW(ch); - result - } - #[cfg(unix)] - unsafe fn get_env_pairs() -> Vec<Vec<u8>> { - use c_str::CString; - - extern { - fn rust_env_pairs() -> *const *const c_char; - } - let mut environ = rust_env_pairs(); - if environ as uint == 0 { - panic!("os::env() failure getting env string from OS: {}", - os::last_os_error()); - } - let mut result = Vec::new(); - while *environ != 0 as *const _ { - let env_pair = - CString::new(*environ, false).as_bytes_no_nul().to_vec(); - result.push(env_pair); - environ = environ.offset(1); - } - result - } - fn env_convert(input: Vec<Vec<u8>>) -> Vec<(Vec<u8>, Vec<u8>)> { let mut pairs = Vec::new(); for p in input.iter() { - let mut it = p.as_slice().splitn(1, |b| *b == b'='); + let mut it = p.splitn(1, |b| *b == b'='); let key = it.next().unwrap().to_vec(); let default: &[u8] = &[]; let val = it.next().unwrap_or(default).to_vec(); @@ -323,7 +156,7 @@ pub fn env_as_bytes() -> Vec<(Vec<u8>,Vec<u8>)> { pairs } with_env_lock(|| { - let unparsed_environ = get_env_pairs(); + let unparsed_environ = sys::os::get_env_pairs(); env_convert(unparsed_environ) }) } @@ -352,7 +185,7 @@ pub fn env_as_bytes() -> Vec<(Vec<u8>,Vec<u8>)> { /// } /// ``` pub fn getenv(n: &str) -> Option<String> { - getenv_as_bytes(n).map(|v| String::from_utf8_lossy(v.as_slice()).into_string()) + getenv_as_bytes(n).map(|v| String::from_utf8_lossy(v.as_slice()).into_owned()) } #[cfg(unix)] @@ -371,7 +204,7 @@ pub fn getenv_as_bytes(n: &str) -> Option<Vec<u8>> { if s.is_null() { None } else { - Some(CString::new(s as *const i8, false).as_bytes_no_nul().to_vec()) + Some(CString::new(s as *const libc::c_char, false).as_bytes_no_nul().to_vec()) } }) } @@ -383,7 +216,7 @@ pub fn getenv_as_bytes(n: &str) -> Option<Vec<u8>> { pub fn getenv(n: &str) -> Option<String> { unsafe { with_env_lock(|| { - use os::windows::{fill_utf16_buf_and_decode}; + use sys::os::fill_utf16_buf_and_decode; let mut n: Vec<u16> = n.utf16_units().collect(); n.push(0); fill_utf16_buf_and_decode(|buf, sz| { @@ -499,52 +332,7 @@ pub fn unsetenv(n: &str) { /// } /// ``` pub fn split_paths<T: BytesContainer>(unparsed: T) -> Vec<Path> { - #[cfg(unix)] - fn _split_paths<T: BytesContainer>(unparsed: T) -> Vec<Path> { - unparsed.container_as_bytes() - .split(|b| *b == b':') - .map(Path::new) - .collect() - } - - #[cfg(windows)] - fn _split_paths<T: BytesContainer>(unparsed: T) -> Vec<Path> { - // On Windows, the PATH environment variable is semicolon separated. Double - // quotes are used as a way of introducing literal semicolons (since - // c:\some;dir is a valid Windows path). Double quotes are not themselves - // permitted in path names, so there is no way to escape a double quote. - // Quoted regions can appear in arbitrary locations, so - // - // c:\foo;c:\som"e;di"r;c:\bar - // - // Should parse as [c:\foo, c:\some;dir, c:\bar]. - // - // (The above is based on testing; there is no clear reference available - // for the grammar.) - - let mut parsed = Vec::new(); - let mut in_progress = Vec::new(); - let mut in_quote = false; - - for b in unparsed.container_as_bytes().iter() { - match *b { - b';' if !in_quote => { - parsed.push(Path::new(in_progress.as_slice())); - in_progress.truncate(0) - } - b'"' => { - in_quote = !in_quote; - } - _ => { - in_progress.push(*b); - } - } - } - parsed.push(Path::new(in_progress)); - parsed - } - - _split_paths(unparsed) + sys::os::split_paths(unparsed.container_as_bytes()) } /// Joins a collection of `Path`s appropriately for the `PATH` @@ -569,45 +357,11 @@ pub fn split_paths<T: BytesContainer>(unparsed: T) -> Vec<Path> { /// os::setenv(key, os::join_paths(paths.as_slice()).unwrap()); /// ``` pub fn join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> { - #[cfg(windows)] - fn _join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> { - let mut joined = Vec::new(); - let sep = b';'; - - for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() { - if i > 0 { joined.push(sep) } - if path.contains(&b'"') { - return Err("path segment contains `\"`"); - } else if path.contains(&sep) { - joined.push(b'"'); - joined.push_all(path); - joined.push(b'"'); - } else { - joined.push_all(path); - } - } - - Ok(joined) - } - - #[cfg(unix)] - fn _join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> { - let mut joined = Vec::new(); - let sep = b':'; - - for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() { - if i > 0 { joined.push(sep) } - if path.contains(&sep) { return Err("path segment contains separator `:`") } - joined.push_all(path); - } - - Ok(joined) - } - - _join_paths(paths) + sys::os::join_paths(paths) } /// A low-level OS in-memory pipe. +#[deriving(Copy)] pub struct Pipe { /// A file descriptor representing the reading end of the pipe. Data written /// on the `out` file descriptor can be read from this file descriptor. @@ -655,69 +409,7 @@ pub fn dll_filename(base: &str) -> String { /// }; /// ``` pub fn self_exe_name() -> Option<Path> { - - #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] - fn load_self() -> Option<Vec<u8>> { - unsafe { - use libc::funcs::bsd44::*; - use libc::consts::os::extra::*; - let mut mib = vec![CTL_KERN as c_int, - KERN_PROC as c_int, - KERN_PROC_PATHNAME as c_int, - -1 as c_int]; - let mut sz: libc::size_t = 0; - let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint, - ptr::null_mut(), &mut sz, ptr::null_mut(), - 0u as libc::size_t); - if err != 0 { return None; } - if sz == 0 { return None; } - let mut v: Vec<u8> = Vec::with_capacity(sz as uint); - let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint, - v.as_mut_ptr() as *mut c_void, &mut sz, - ptr::null_mut(), 0u as libc::size_t); - if err != 0 { return None; } - if sz == 0 { return None; } - v.set_len(sz as uint - 1); // chop off trailing NUL - Some(v) - } - } - - #[cfg(any(target_os = "linux", target_os = "android"))] - fn load_self() -> Option<Vec<u8>> { - use std::io; - - match io::fs::readlink(&Path::new("/proc/self/exe")) { - Ok(path) => Some(path.into_vec()), - Err(..) => None - } - } - - #[cfg(any(target_os = "macos", target_os = "ios"))] - fn load_self() -> Option<Vec<u8>> { - unsafe { - use libc::funcs::extra::_NSGetExecutablePath; - let mut sz: u32 = 0; - _NSGetExecutablePath(ptr::null_mut(), &mut sz); - if sz == 0 { return None; } - let mut v: Vec<u8> = Vec::with_capacity(sz as uint); - let err = _NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz); - if err != 0 { return None; } - v.set_len(sz as uint - 1); // chop off trailing NUL - Some(v) - } - } - - #[cfg(windows)] - fn load_self() -> Option<Vec<u8>> { - unsafe { - use os::windows::fill_utf16_buf_and_decode; - fill_utf16_buf_and_decode(|buf, sz| { - libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz) - }).map(|s| s.into_string().into_bytes()) - } - } - - load_self().and_then(Path::new_opt) + sys::os::load_self().and_then(Path::new_opt) } /// Optionally returns the filesystem path to the current executable which is @@ -788,18 +480,16 @@ pub fn homedir() -> Option<Path> { _homedir() } -/** - * Returns the path to a temporary directory. - * - * On Unix, returns the value of the 'TMPDIR' environment variable if it is - * set, otherwise for non-Android it returns '/tmp'. If Android, since there - * is no global temporary folder (it is usually allocated per-app), we return - * '/data/local/tmp'. - * - * On Windows, returns the value of, in order, the 'TMP', 'TEMP', - * 'USERPROFILE' environment variable if any are set and not the empty - * string. Otherwise, tmpdir returns the path to the Windows directory. - */ +/// Returns the path to a temporary directory. +/// +/// On Unix, returns the value of the 'TMPDIR' environment variable if it is +/// set, otherwise for non-Android it returns '/tmp'. If Android, since there +/// is no global temporary folder (it is usually allocated per-app), we return +/// '/data/local/tmp'. +/// +/// On Windows, returns the value of, in order, the 'TMP', 'TEMP', +/// 'USERPROFILE' environment variable if any are set and not the empty +/// string. Otherwise, tmpdir returns the path to the Windows directory. pub fn tmpdir() -> Path { return lookup(); @@ -835,7 +525,6 @@ pub fn tmpdir() -> Path { } } -/// /// Convert a relative path to an absolute path /// /// If the given path is relative, return it prepended with the current working @@ -880,37 +569,12 @@ pub fn make_absolute(p: &Path) -> IoResult<Path> { /// println!("Successfully changed working directory to {}!", root.display()); /// ``` pub fn change_dir(p: &Path) -> IoResult<()> { - return chdir(p); - - #[cfg(windows)] - fn chdir(p: &Path) -> IoResult<()> { - let mut p = p.as_str().unwrap().utf16_units().collect::<Vec<u16>>(); - p.push(0); - - unsafe { - match libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL) { - true => Ok(()), - false => Err(IoError::last_error()), - } - } - } - - #[cfg(unix)] - fn chdir(p: &Path) -> IoResult<()> { - p.with_c_str(|buf| { - unsafe { - match libc::chdir(buf) == (0 as c_int) { - true => Ok(()), - false => Err(IoError::last_error()), - } - } - }) - } + return sys::os::chdir(p); } /// Returns the platform-specific value of errno pub fn errno() -> uint { - os_imp::errno() as uint + sys::os::errno() as uint } /// Return the string corresponding to an `errno()` value of `errnum`. @@ -923,7 +587,7 @@ pub fn errno() -> uint { /// println!("{}", os::error_string(os::errno() as uint)); /// ``` pub fn error_string(errnum: uint) -> String { - return os_imp::error_string(errnum as i32); + return sys::os::error_string(errnum as i32); } /// Get a string representing the platform-dependent last error @@ -933,16 +597,14 @@ pub fn last_os_error() -> String { static EXIT_STATUS: AtomicInt = INIT_ATOMIC_INT; -/** - * Sets the process exit code - * - * Sets the exit code returned by the process if all supervised tasks - * terminate successfully (without panicking). If the current root task panics - * and is supervised by the scheduler then any user-specified exit status is - * ignored and the process exits with the default panic status. - * - * Note that this is not synchronized against modifications of other threads. - */ +/// Sets the process exit code +/// +/// Sets the exit code returned by the process if all supervised tasks +/// terminate successfully (without panicking). If the current root task panics +/// and is supervised by the scheduler then any user-specified exit status is +/// ignored and the process exits with the default panic status. +/// +/// Note that this is not synchronized against modifications of other threads. pub fn set_exit_status(code: int) { EXIT_STATUS.store(code, SeqCst) } @@ -963,11 +625,9 @@ unsafe fn load_argc_and_argv(argc: int, }) } -/** - * Returns the command line arguments - * - * Returns a list of the command line arguments. - */ +/// Returns the command line arguments +/// +/// Returns a list of the command line arguments. #[cfg(target_os = "macos")] fn real_args_as_bytes() -> Vec<Vec<u8>> { unsafe { @@ -1039,19 +699,15 @@ fn real_args_as_bytes() -> Vec<Vec<u8>> { target_os = "freebsd", target_os = "dragonfly"))] fn real_args_as_bytes() -> Vec<Vec<u8>> { - use rustrt; - - match rustrt::args::clone() { - Some(args) => args, - None => panic!("process arguments not initialized") - } + use rt; + rt::args::clone().unwrap_or_else(|| vec![]) } #[cfg(not(windows))] fn real_args() -> Vec<String> { real_args_as_bytes().into_iter() .map(|v| { - String::from_utf8_lossy(v.as_slice()).into_string() + String::from_utf8_lossy(v.as_slice()).into_owned() }).collect() } @@ -1071,9 +727,9 @@ fn real_args() -> Vec<String> { while *ptr.offset(len as int) != 0 { len += 1; } // Push it onto the list. - let opt_s = slice::raw::buf_as_slice(ptr as *const _, len, |buf| { - String::from_utf16(::str::truncate_utf16_at_nul(buf)) - }); + let ptr = ptr as *const u16; + let buf = slice::from_raw_buf(&ptr, len); + let opt_s = String::from_utf16(sys::os::truncate_utf16_at_nul(buf)); opt_s.expect("CommandLineToArgvW returned invalid UTF-16") }); @@ -1110,7 +766,7 @@ extern "system" { /// /// The first element is traditionally the path to the executable, but it can be /// set to arbitrary text, and it may not even exist, so this property should not -// be relied upon for security purposes. +/// be relied upon for security purposes. /// /// The arguments are interpreted as utf-8, with invalid bytes replaced with \uFFFD. /// See `String::from_utf8_lossy` for details. @@ -1141,38 +797,9 @@ extern { pub fn _NSGetArgv() -> *mut *mut *mut c_char; } -// Round up `from` to be divisible by `to` -fn round_up(from: uint, to: uint) -> uint { - let r = if from % to == 0 { - from - } else { - from + to - (from % to) - }; - if r == 0 { - to - } else { - r - } -} - /// Returns the page size of the current architecture in bytes. -#[cfg(unix)] pub fn page_size() -> uint { - unsafe { - libc::sysconf(libc::_SC_PAGESIZE) as uint - } -} - -/// Returns the page size of the current architecture in bytes. -#[cfg(windows)] -pub fn page_size() -> uint { - use mem; - unsafe { - let mut info = mem::zeroed(); - libc::GetSystemInfo(&mut info); - - return info.dwPageSize as uint; - } + sys::os::page_size() } /// A memory mapped file or chunk of memory. This is a very system-specific @@ -1183,6 +810,7 @@ pub fn page_size() -> uint { /// /// The memory map is released (unmapped) when the destructor is run, so don't /// let it leave scope by accident if you want it to stick around. +#[allow(missing_copy_implementations)] pub struct MemoryMap { data: *mut u8, len: uint, @@ -1200,6 +828,8 @@ pub enum MemoryMapKind { MapVirtual } +impl Copy for MemoryMapKind {} + /// Options the memory map is created with pub enum MapOption { /// The memory should be readable @@ -1211,7 +841,11 @@ pub enum MapOption { /// Create a map for a specific address range. Corresponds to `MAP_FIXED` on /// POSIX. MapAddr(*const u8), + /// Create a memory mapping for a file with a given HANDLE. + #[cfg(windows)] + MapFd(libc::HANDLE), /// Create a memory mapping for a file with a given fd. + #[cfg(not(windows))] MapFd(c_int), /// When using `MapFd`, the start of the map is `uint` bytes from the start /// of the file. @@ -1223,9 +857,12 @@ pub enum MapOption { MapNonStandardFlags(c_int), } +impl Copy for MapOption {} + /// Possible errors when creating a map. +#[deriving(Copy)] pub enum MapError { - /// ## The following are POSIX-specific + /// # The following are POSIX-specific /// /// fd was not open for reading or, if using `MapWritable`, was not open for /// writing. @@ -1247,7 +884,7 @@ pub enum MapError { ErrZeroLength, /// Unrecognized error. The inner value is the unrecognized errno. ErrUnknown(int), - /// ## The following are Windows-specific + /// # The following are Windows-specific /// /// Unsupported combination of protection flags /// (`MapReadable`/`MapWritable`/`MapExecutable`). @@ -1311,6 +948,20 @@ impl FromError<MapError> for Box<Error> { } } +// Round up `from` to be divisible by `to` +fn round_up(from: uint, to: uint) -> uint { + let r = if from % to == 0 { + from + } else { + from + to - (from % to) + }; + if r == 0 { + to + } else { + r + } +} + #[cfg(unix)] impl MemoryMap { /// Create a new mapping with the given `options`, at least `min_len` bytes @@ -1405,7 +1056,7 @@ impl MemoryMap { let mut readable = false; let mut writable = false; let mut executable = false; - let mut fd: c_int = -1; + let mut handle: HANDLE = libc::INVALID_HANDLE_VALUE; let mut offset: uint = 0; let len = round_up(min_len, page_size()); @@ -1415,23 +1066,23 @@ impl MemoryMap { MapWritable => { writable = true; }, MapExecutable => { executable = true; } MapAddr(addr_) => { lpAddress = addr_ as LPVOID; }, - MapFd(fd_) => { fd = fd_; }, + MapFd(handle_) => { handle = handle_; }, MapOffset(offset_) => { offset = offset_; }, MapNonStandardFlags(..) => {} } } let flProtect = match (executable, readable, writable) { - (false, false, false) if fd == -1 => libc::PAGE_NOACCESS, + (false, false, false) if handle == libc::INVALID_HANDLE_VALUE => libc::PAGE_NOACCESS, (false, true, false) => libc::PAGE_READONLY, (false, true, true) => libc::PAGE_READWRITE, - (true, false, false) if fd == -1 => libc::PAGE_EXECUTE, + (true, false, false) if handle == libc::INVALID_HANDLE_VALUE => libc::PAGE_EXECUTE, (true, true, false) => libc::PAGE_EXECUTE_READ, (true, true, true) => libc::PAGE_EXECUTE_READWRITE, _ => return Err(ErrUnsupProt) }; - if fd == -1 { + if handle == libc::INVALID_HANDLE_VALUE { if offset != 0 { return Err(ErrUnsupOffset); } @@ -1459,7 +1110,7 @@ impl MemoryMap { // we should never get here. }; unsafe { - let hFile = libc::get_osfhandle(fd) as HANDLE; + let hFile = handle; let mapping = libc::CreateFileMappingW(hFile, ptr::null_mut(), flProtect, @@ -1774,7 +1425,6 @@ mod arch_consts { #[cfg(test)] mod tests { use prelude::*; - use c_str::ToCStr; use option; use os::{env, getcwd, getenv, make_absolute}; use os::{split_paths, join_paths, setenv, unsetenv}; @@ -1804,7 +1454,7 @@ mod tests { fn test_setenv() { let n = make_rand_name(); setenv(n.as_slice(), "VALUE"); - assert_eq!(getenv(n.as_slice()), option::Some("VALUE".to_string())); + assert_eq!(getenv(n.as_slice()), option::Option::Some("VALUE".to_string())); } #[test] @@ -1812,7 +1462,7 @@ mod tests { let n = make_rand_name(); setenv(n.as_slice(), "VALUE"); unsetenv(n.as_slice()); - assert_eq!(getenv(n.as_slice()), option::None); + assert_eq!(getenv(n.as_slice()), option::Option::None); } #[test] @@ -1821,9 +1471,9 @@ mod tests { let n = make_rand_name(); setenv(n.as_slice(), "1"); setenv(n.as_slice(), "2"); - assert_eq!(getenv(n.as_slice()), option::Some("2".to_string())); + assert_eq!(getenv(n.as_slice()), option::Option::Some("2".to_string())); setenv(n.as_slice(), ""); - assert_eq!(getenv(n.as_slice()), option::Some("".to_string())); + assert_eq!(getenv(n.as_slice()), option::Option::Some("".to_string())); } // Windows GetEnvironmentVariable requires some extra work to make sure @@ -1840,7 +1490,7 @@ mod tests { let n = make_rand_name(); setenv(n.as_slice(), s.as_slice()); debug!("{}", s.clone()); - assert_eq!(getenv(n.as_slice()), option::Some(s)); + assert_eq!(getenv(n.as_slice()), option::Option::Some(s)); } #[test] @@ -1877,7 +1527,7 @@ mod tests { // MingW seems to set some funky environment variables like // "=C:=C:\MinGW\msys\1.0\bin" and "!::=::\" that are returned // from env() but not visible from getenv(). - assert!(v2.is_none() || v2 == option::Some(v)); + assert!(v2.is_none() || v2 == option::Option::Some(v)); } } @@ -1964,11 +1614,11 @@ mod tests { #[test] fn memory_map_rw() { - use result::{Ok, Err}; + use result::Result::{Ok, Err}; let chunk = match os::MemoryMap::new(16, &[ - os::MapReadable, - os::MapWritable + os::MapOption::MapReadable, + os::MapOption::MapWritable ]) { Ok(chunk) => chunk, Err(msg) => panic!("{}", msg) @@ -1983,55 +1633,47 @@ mod tests { #[test] fn memory_map_file() { - use result::{Ok, Err}; + use libc; use os::*; - use libc::*; - use io::fs; - - #[cfg(unix)] - fn lseek_(fd: c_int, size: uint) { - unsafe { - assert!(lseek(fd, size as off_t, SEEK_SET) == size as off_t); - } + use io::fs::{File, unlink}; + use io::SeekStyle::SeekSet; + use io::FileMode::Open; + use io::FileAccess::ReadWrite; + + #[cfg(not(windows))] + fn get_fd(file: &File) -> libc::c_int { + use os::unix::AsRawFd; + file.as_raw_fd() } + #[cfg(windows)] - fn lseek_(fd: c_int, size: uint) { - unsafe { - assert!(lseek(fd, size as c_long, SEEK_SET) == size as c_long); - } + fn get_fd(file: &File) -> libc::HANDLE { + use os::windows::AsRawHandle; + file.as_raw_handle() } let mut path = tmpdir(); path.push("mmap_file.tmp"); let size = MemoryMap::granularity() * 2; - - let fd = unsafe { - let fd = path.with_c_str(|path| { - open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR) - }); - lseek_(fd, size); - "x".with_c_str(|x| assert!(write(fd, x as *const c_void, 1) == 1)); - fd - }; - let chunk = match MemoryMap::new(size / 2, &[ - MapReadable, - MapWritable, - MapFd(fd), - MapOffset(size / 2) - ]) { - Ok(chunk) => chunk, - Err(msg) => panic!("{}", msg) - }; + let mut file = File::open_mode(&path, Open, ReadWrite).unwrap(); + file.seek(size as i64, SeekSet); + file.write_u8(0); + + let chunk = MemoryMap::new(size / 2, &[ + MapOption::MapReadable, + MapOption::MapWritable, + MapOption::MapFd(get_fd(&file)), + MapOption::MapOffset(size / 2) + ]).unwrap(); assert!(chunk.len > 0); unsafe { *chunk.data = 0xbe; assert!(*chunk.data == 0xbe); - close(fd); } drop(chunk); - fs::unlink(&path).unwrap(); + unlink(&path).unwrap(); } #[test] @@ -2039,7 +1681,7 @@ mod tests { fn split_paths_windows() { fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { split_paths(unparsed) == - parsed.iter().map(|s| Path::new(*s)).collect() + parsed.iter().map(|s| Path::new(*s)).collect::<Vec<_>>() } assert!(check_parse("", &mut [""])); @@ -2059,7 +1701,7 @@ mod tests { fn split_paths_unix() { fn check_parse(unparsed: &str, parsed: &[&str]) -> bool { split_paths(unparsed) == - parsed.iter().map(|s| Path::new(*s)).collect() + parsed.iter().map(|s| Path::new(*s)).collect::<Vec<_>>() } assert!(check_parse("", &mut [""])); @@ -2073,7 +1715,7 @@ mod tests { #[cfg(unix)] fn join_paths_unix() { fn test_eq(input: &[&str], output: &str) -> bool { - join_paths(input).unwrap().as_slice() == output.as_bytes() + join_paths(input).unwrap() == output.as_bytes() } assert!(test_eq(&[], "")); @@ -2088,7 +1730,7 @@ mod tests { #[cfg(windows)] fn join_paths_windows() { fn test_eq(input: &[&str], output: &str) -> bool { - join_paths(input).unwrap().as_slice() == output.as_bytes() + join_paths(input).unwrap() == output.as_bytes() } assert!(test_eq(&[], "")); diff --git a/src/libstd/path/mod.rs b/src/libstd/path/mod.rs index a185a29a700..30f3f56bc1c 100644 --- a/src/libstd/path/mod.rs +++ b/src/libstd/path/mod.rs @@ -8,62 +8,56 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! - -Cross-platform path support - -This module implements support for two flavors of paths. `PosixPath` represents -a path on any unix-like system, whereas `WindowsPath` represents a path on -Windows. This module also exposes a typedef `Path` which is equal to the -appropriate platform-specific path variant. - -Both `PosixPath` and `WindowsPath` implement a trait `GenericPath`, which -contains the set of methods that behave the same for both paths. They each also -implement some methods that could not be expressed in `GenericPath`, yet behave -identically for both path flavors, such as `.components()`. - -The three main design goals of this module are 1) to avoid unnecessary -allocation, 2) to behave the same regardless of which flavor of path is being -used, and 3) to support paths that cannot be represented in UTF-8 (as Linux has -no restriction on paths beyond disallowing NUL). - -## Usage - -Usage of this module is fairly straightforward. Unless writing platform-specific -code, `Path` should be used to refer to the platform-native path. - -Creation of a path is typically done with either `Path::new(some_str)` or -`Path::new(some_vec)`. This path can be modified with `.push()` and -`.pop()` (and other setters). The resulting Path can either be passed to another -API that expects a path, or can be turned into a `&[u8]` with `.as_vec()` or a -`Option<&str>` with `.as_str()`. Similarly, attributes of the path can be queried -with methods such as `.filename()`. There are also methods that return a new -path instead of modifying the receiver, such as `.join()` or `.dir_path()`. - -Paths are always kept in normalized form. This means that creating the path -`Path::new("a/b/../c")` will return the path `a/c`. Similarly any attempt -to mutate the path will always leave it in normalized form. - -When rendering a path to some form of output, there is a method `.display()` -which is compatible with the `format!()` parameter `{}`. This will render the -path as a string, replacing all non-utf8 sequences with the Replacement -Character (U+FFFD). As such it is not suitable for passing to any API that -actually operates on the path; it is only intended for display. - -## Example - -```rust -use std::io::fs::PathExtensions; - -let mut path = Path::new("/tmp/path"); -println!("path: {}", path.display()); -path.set_filename("foo"); -path.push("bar"); -println!("new path: {}", path.display()); -println!("path exists: {}", path.exists()); -``` - -*/ +//! Cross-platform path support +//! +//! This module implements support for two flavors of paths. `PosixPath` represents a path on any +//! unix-like system, whereas `WindowsPath` represents a path on Windows. This module also exposes +//! a typedef `Path` which is equal to the appropriate platform-specific path variant. +//! +//! Both `PosixPath` and `WindowsPath` implement a trait `GenericPath`, which contains the set of +//! methods that behave the same for both paths. They each also implement some methods that could +//! not be expressed in `GenericPath`, yet behave identically for both path flavors, such as +//! `.components()`. +//! +//! The three main design goals of this module are 1) to avoid unnecessary allocation, 2) to behave +//! the same regardless of which flavor of path is being used, and 3) to support paths that cannot +//! be represented in UTF-8 (as Linux has no restriction on paths beyond disallowing NUL). +//! +//! ## Usage +//! +//! Usage of this module is fairly straightforward. Unless writing platform-specific code, `Path` +//! should be used to refer to the platform-native path. +//! +//! Creation of a path is typically done with either `Path::new(some_str)` or +//! `Path::new(some_vec)`. This path can be modified with `.push()` and `.pop()` (and other +//! setters). The resulting Path can either be passed to another API that expects a path, or can be +//! turned into a `&[u8]` with `.as_vec()` or a `Option<&str>` with `.as_str()`. Similarly, +//! attributes of the path can be queried with methods such as `.filename()`. There are also +//! methods that return a new path instead of modifying the receiver, such as `.join()` or +//! `.dir_path()`. +//! +//! Paths are always kept in normalized form. This means that creating the path +//! `Path::new("a/b/../c")` will return the path `a/c`. Similarly any attempt to mutate the path +//! will always leave it in normalized form. +//! +//! When rendering a path to some form of output, there is a method `.display()` which is +//! compatible with the `format!()` parameter `{}`. This will render the path as a string, +//! replacing all non-utf8 sequences with the Replacement Character (U+FFFD). As such it is not +//! suitable for passing to any API that actually operates on the path; it is only intended for +//! display. +//! +//! ## Example +//! +//! ```rust +//! use std::io::fs::PathExtensions; +//! +//! let mut path = Path::new("/tmp/path"); +//! println!("path: {}", path.display()); +//! path.set_filename("foo"); +//! path.push("bar"); +//! println!("new path: {}", path.display()); +//! println!("path exists: {}", path.exists()); +//! ``` #![experimental] @@ -71,13 +65,14 @@ use core::kinds::Sized; use c_str::CString; use clone::Clone; use fmt; -use iter::Iterator; -use option::{Option, None, Some}; +use iter::IteratorExt; +use option::Option; +use option::Option::{None, Some}; use str; -use str::{MaybeOwned, Str, StrPrelude}; +use str::{CowString, MaybeOwned, Str, StrExt}; use string::String; -use slice::{AsSlice, CloneSliceAllocPrelude}; -use slice::{PartialEqSlicePrelude, SlicePrelude}; +use slice::{AsSlice, CloneSliceExt}; +use slice::{PartialEqSliceExt, SliceExt}; use vec::Vec; /// Typedef for POSIX file paths. @@ -202,7 +197,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// ``` #[inline] fn as_str<'a>(&'a self) -> Option<&'a str> { - str::from_utf8(self.as_vec()) + str::from_utf8(self.as_vec()).ok() } /// Returns the path as a byte vector @@ -298,7 +293,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// ``` #[inline] fn dirname_str<'a>(&'a self) -> Option<&'a str> { - str::from_utf8(self.dirname()) + str::from_utf8(self.dirname()).ok() } /// Returns the file component of `self`, as a byte vector. @@ -332,7 +327,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// ``` #[inline] fn filename_str<'a>(&'a self) -> Option<&'a str> { - self.filename().and_then(str::from_utf8) + self.filename().and_then(|s| str::from_utf8(s).ok()) } /// Returns the stem of the filename of `self`, as a byte vector. @@ -378,7 +373,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// ``` #[inline] fn filestem_str<'a>(&'a self) -> Option<&'a str> { - self.filestem().and_then(str::from_utf8) + self.filestem().and_then(|s| str::from_utf8(s).ok()) } /// Returns the extension of the filename of `self`, as an optional byte vector. @@ -425,7 +420,7 @@ pub trait GenericPath: Clone + GenericPathUnsafe { /// ``` #[inline] fn extension_str<'a>(&'a self) -> Option<&'a str> { - self.extension().and_then(str::from_utf8) + self.extension().and_then(|s| str::from_utf8(s).ok()) } /// Replaces the filename portion of the path with the given byte vector or string. @@ -798,7 +793,7 @@ pub trait BytesContainer for Sized? { /// Returns the receiver interpreted as a utf-8 string, if possible #[inline] fn container_as_str<'a>(&'a self) -> Option<&'a str> { - str::from_utf8(self.container_as_bytes()) + str::from_utf8(self.container_as_bytes()).ok() } /// Returns whether .container_as_str() is guaranteed to not fail // FIXME (#8888): Remove unused arg once ::<for T> works @@ -830,7 +825,7 @@ pub struct Display<'a, P:'a> { impl<'a, P: GenericPath> fmt::Show for Display<'a, P> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.as_maybe_owned().as_slice().fmt(f) + self.as_cow().fmt(f) } } @@ -840,7 +835,7 @@ impl<'a, P: GenericPath> Display<'a, P> { /// If the path is not UTF-8, invalid sequences will be replaced with the /// Unicode replacement char. This involves allocation. #[inline] - pub fn as_maybe_owned(&self) -> MaybeOwned<'a> { + pub fn as_cow(&self) -> CowString<'a> { String::from_utf8_lossy(if self.filename { match self.path.filename() { None => { @@ -875,7 +870,7 @@ impl BytesContainer for String { } #[inline] fn container_as_str(&self) -> Option<&str> { - Some(self.as_slice()) + Some(self[]) } #[inline] fn is_str(_: Option<&String>) -> bool { true } @@ -891,7 +886,7 @@ impl BytesContainer for [u8] { impl BytesContainer for Vec<u8> { #[inline] fn container_as_bytes(&self) -> &[u8] { - self.as_slice() + self[] } } @@ -902,6 +897,7 @@ impl BytesContainer for CString { } } +#[allow(deprecated)] impl<'a> BytesContainer for str::MaybeOwned<'a> { #[inline] fn container_as_bytes<'b>(&'b self) -> &'b [u8] { @@ -936,8 +932,6 @@ fn contains_nul<T: BytesContainer>(v: &T) -> bool { #[cfg(test)] mod tests { use prelude::*; - use super::{GenericPath, PosixPath, WindowsPath}; - use c_str::ToCStr; #[test] fn test_cstring() { @@ -947,6 +941,6 @@ mod tests { let input = r"\foo\bar\baz"; let path: WindowsPath = WindowsPath::new(input.to_c_str()); - assert_eq!(path.as_str().unwrap(), input.as_slice()); + assert_eq!(path.as_str().unwrap(), input); } } diff --git a/src/libstd/path/posix.rs b/src/libstd/path/posix.rs index 2b444fdc32b..f0a00b421c3 100644 --- a/src/libstd/path/posix.rs +++ b/src/libstd/path/posix.rs @@ -15,23 +15,25 @@ use clone::Clone; use cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering}; use hash; use io::Writer; -use iter::{DoubleEndedIterator, AdditiveIterator, Extend, Iterator, Map}; +use iter::{DoubleEndedIteratorExt, AdditiveIterator, Extend}; +use iter::{Iterator, IteratorExt, Map}; +use option::Option; +use option::Option::{None, Some}; use kinds::Sized; -use option::{Option, None, Some}; use str::{FromStr, Str}; use str; -use slice::{CloneSliceAllocPrelude, Splits, AsSlice, VectorVector, - PartialEqSlicePrelude, SlicePrelude}; +use slice::{CloneSliceExt, Splits, AsSlice, VectorVector, + PartialEqSliceExt, SliceExt}; use vec::Vec; use super::{BytesContainer, GenericPath, GenericPathUnsafe}; /// Iterator that yields successive components of a Path as &[u8] -pub type Components<'a> = Splits<'a, u8>; +pub type Components<'a> = Splits<'a, u8, fn(&u8) -> bool>; /// Iterator that yields successive components of a Path as Option<&str> -pub type StrComponents<'a> = Map<'a, &'a [u8], Option<&'a str>, - Components<'a>>; +pub type StrComponents<'a> = + Map<&'a [u8], Option<&'a str>, Components<'a>, fn(&[u8]) -> Option<&str>>; /// Represents a POSIX file path #[deriving(Clone)] @@ -130,7 +132,7 @@ impl GenericPathUnsafe for Path { unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T) { let filename = filename.container_as_bytes(); match self.sepidx { - None if b".." == self.repr.as_slice() => { + None if b".." == self.repr => { let mut v = Vec::with_capacity(3 + filename.len()); v.push_all(dot_dot_static); v.push(SEP_BYTE); @@ -157,7 +159,7 @@ impl GenericPathUnsafe for Path { self.repr = Path::normalize(v.as_slice()); } } - self.sepidx = self.repr.as_slice().rposition_elem(&SEP_BYTE); + self.sepidx = self.repr.rposition_elem(&SEP_BYTE); } unsafe fn push_unchecked<T: BytesContainer>(&mut self, path: T) { @@ -173,7 +175,7 @@ impl GenericPathUnsafe for Path { // FIXME: this is slow self.repr = Path::normalize(v.as_slice()); } - self.sepidx = self.repr.as_slice().rposition_elem(&SEP_BYTE); + self.sepidx = self.repr.rposition_elem(&SEP_BYTE); } } } @@ -190,7 +192,7 @@ impl GenericPath for Path { fn dirname<'a>(&'a self) -> &'a [u8] { match self.sepidx { - None if b".." == self.repr.as_slice() => self.repr.as_slice(), + None if b".." == self.repr => self.repr.as_slice(), None => dot_static, Some(0) => self.repr[..1], Some(idx) if self.repr[idx+1..] == b".." => self.repr.as_slice(), @@ -200,8 +202,8 @@ impl GenericPath for Path { fn filename<'a>(&'a self) -> Option<&'a [u8]> { match self.sepidx { - None if b"." == self.repr.as_slice() || - b".." == self.repr.as_slice() => None, + None if b"." == self.repr || + b".." == self.repr => None, None => Some(self.repr.as_slice()), Some(idx) if self.repr[idx+1..] == b".." => None, Some(0) if self.repr[1..].is_empty() => None, @@ -211,20 +213,20 @@ impl GenericPath for Path { fn pop(&mut self) -> bool { match self.sepidx { - None if b"." == self.repr.as_slice() => false, + None if b"." == self.repr => false, None => { self.repr = vec![b'.']; self.sepidx = None; true } - Some(0) if b"/" == self.repr.as_slice() => false, + Some(0) if b"/" == self.repr => false, Some(idx) => { if idx == 0 { self.repr.truncate(idx+1); } else { self.repr.truncate(idx); } - self.sepidx = self.repr.as_slice().rposition_elem(&SEP_BYTE); + self.sepidx = self.repr.rposition_elem(&SEP_BYTE); true } } @@ -249,7 +251,7 @@ impl GenericPath for Path { } else { let mut ita = self.components(); let mut itb = other.components(); - if b"." == self.repr.as_slice() { + if b"." == self.repr { return match itb.next() { None => true, Some(b) => b != b".." @@ -304,7 +306,7 @@ impl GenericPath for Path { } } } - Some(Path::new(comps.as_slice().connect_vec(&SEP_BYTE))) + Some(Path::new(comps.connect_vec(&SEP_BYTE))) } } @@ -388,6 +390,7 @@ impl Path { let v = if self.repr[0] == SEP_BYTE { self.repr[1..] } else { self.repr.as_slice() }; + let is_sep_byte: fn(&u8) -> bool = is_sep_byte; // coerce to fn ptr let mut ret = v.split(is_sep_byte); if v.is_empty() { // consume the empty "" component @@ -399,13 +402,17 @@ impl Path { /// Returns an iterator that yields each component of the path as Option<&str>. /// See components() for details. pub fn str_components<'a>(&'a self) -> StrComponents<'a> { - self.components().map(str::from_utf8) + fn from_utf8(s: &[u8]) -> Option<&str> { + str::from_utf8(s).ok() + } + let f: fn(&[u8]) -> Option<&str> = from_utf8; // coerce to fn ptr + self.components().map(f) } } // None result means the byte vector didn't need normalizing fn normalize_helper<'a>(v: &'a [u8], is_abs: bool) -> Option<Vec<&'a [u8]>> { - if is_abs && v.as_slice().is_empty() { + if is_abs && v.is_empty() { return None; } let mut comps: Vec<&'a [u8]> = vec![]; @@ -442,11 +449,9 @@ static dot_dot_static: &'static [u8] = b".."; mod tests { use prelude::*; use super::*; - use mem; use str; - use str::StrPrelude; - macro_rules! t( + macro_rules! t { (s: $path:expr, $exp:expr) => ( { let path = $path; @@ -459,7 +464,7 @@ mod tests { assert!(path.as_vec() == $exp); } ) - ) + } #[test] fn test_paths() { @@ -495,8 +500,8 @@ mod tests { t!(s: Path::new("foo/../../.."), "../.."); t!(s: Path::new("foo/../../bar"), "../bar"); - assert_eq!(Path::new(b"foo/bar").into_vec().as_slice(), b"foo/bar"); - assert_eq!(Path::new(b"/foo/../../bar").into_vec().as_slice(), + assert_eq!(Path::new(b"foo/bar").into_vec(), b"foo/bar"); + assert_eq!(Path::new(b"/foo/../../bar").into_vec(), b"/bar"); let p = Path::new(b"foo/bar\x80"); @@ -513,62 +518,62 @@ mod tests { #[test] fn test_null_byte() { - use task; - let result = task::try(proc() { + use thread::Thread; + let result = Thread::spawn(move|| { Path::new(b"foo/bar\0") - }); + }).join(); assert!(result.is_err()); - let result = task::try(proc() { + let result = Thread::spawn(move|| { Path::new("test").set_filename(b"f\0o") - }); + }).join(); assert!(result.is_err()); - let result = task::try(proc() { + let result = Thread::spawn(move|| { Path::new("test").push(b"f\0o"); - }); + }).join(); assert!(result.is_err()); } #[test] fn test_display_str() { - macro_rules! t( + macro_rules! t { ($path:expr, $disp:ident, $exp:expr) => ( { let path = Path::new($path); - assert!(path.$disp().to_string().as_slice() == $exp); + assert!(path.$disp().to_string() == $exp); } ) - ) + } t!("foo", display, "foo"); - t!(b"foo\x80", display, "foo\uFFFD"); - t!(b"foo\xFFbar", display, "foo\uFFFDbar"); + t!(b"foo\x80", display, "foo\u{FFFD}"); + t!(b"foo\xFFbar", display, "foo\u{FFFD}bar"); t!(b"foo\xFF/bar", filename_display, "bar"); - t!(b"foo/\xFFbar", filename_display, "\uFFFDbar"); + t!(b"foo/\xFFbar", filename_display, "\u{FFFD}bar"); t!(b"/", filename_display, ""); macro_rules! t( ($path:expr, $exp:expr) => ( { let path = Path::new($path); - let mo = path.display().as_maybe_owned(); + let mo = path.display().as_cow(); assert!(mo.as_slice() == $exp); } ); ($path:expr, $exp:expr, filename) => ( { let path = Path::new($path); - let mo = path.filename_display().as_maybe_owned(); + let mo = path.filename_display().as_cow(); assert!(mo.as_slice() == $exp); } ) - ) + ); t!("foo", "foo"); - t!(b"foo\x80", "foo\uFFFD"); - t!(b"foo\xFFbar", "foo\uFFFDbar"); + t!(b"foo\x80", "foo\u{FFFD}"); + t!(b"foo\xFFbar", "foo\u{FFFD}bar"); t!(b"foo\xFF/bar", "bar", filename); - t!(b"foo/\xFFbar", "\uFFFDbar", filename); + t!(b"foo/\xFFbar", "\u{FFFD}bar", filename); t!(b"/", "", filename); } @@ -579,20 +584,20 @@ mod tests { { let path = Path::new($path); let f = format!("{}", path.display()); - assert!(f.as_slice() == $exp); + assert!(f == $exp); let f = format!("{}", path.filename_display()); - assert!(f.as_slice() == $expf); + assert!(f == $expf); } ) - ) + ); t!(b"foo", "foo", "foo"); t!(b"foo/bar", "foo/bar", "bar"); t!(b"/", "/", ""); - t!(b"foo\xFF", "foo\uFFFD", "foo\uFFFD"); - t!(b"foo\xFF/bar", "foo\uFFFD/bar", "bar"); - t!(b"foo/\xFFbar", "foo/\uFFFDbar", "\uFFFDbar"); - t!(b"\xFFfoo/bar\xFF", "\uFFFDfoo/bar\uFFFD", "bar\uFFFD"); + t!(b"foo\xFF", "foo\u{FFFD}", "foo\u{FFFD}"); + t!(b"foo\xFF/bar", "foo\u{FFFD}/bar", "bar"); + t!(b"foo/\xFFbar", "foo/\u{FFFD}bar", "\u{FFFD}bar"); + t!(b"\xFFfoo/bar\xFF", "\u{FFFD}foo/bar\u{FFFD}", "bar\u{FFFD}"); } #[test] @@ -600,10 +605,8 @@ mod tests { macro_rules! t( (s: $path:expr, $op:ident, $exp:expr) => ( { - unsafe { - let path = Path::new($path); - assert!(path.$op() == mem::transmute(($exp).as_bytes())); - } + let path = Path::new($path); + assert!(path.$op() == ($exp).as_bytes()); } ); (s: $path:expr, $op:ident, $exp:expr, opt) => ( @@ -615,14 +618,12 @@ mod tests { ); (v: $path:expr, $op:ident, $exp:expr) => ( { - unsafe { - let arg = $path; - let path = Path::new(arg); - assert!(path.$op() == mem::transmute($exp)); - } + let arg = $path; + let path = Path::new(arg); + assert!(path.$op() == $exp); } ); - ) + ); t!(v: b"a/b/c", filename, Some(b"c")); t!(v: b"a/b/c\xFF", filename, Some(b"c\xFF")); @@ -667,9 +668,8 @@ mod tests { t!(v: b"hi/there.txt", extension, Some(b"txt")); t!(v: b"hi/there\x80.txt", extension, Some(b"txt")); t!(v: b"hi/there.t\x80xt", extension, Some(b"t\x80xt")); - let no: Option<&'static [u8]> = None; - t!(v: b"hi/there", extension, no); - t!(v: b"hi/there\x80", extension, no); + t!(v: b"hi/there", extension, None); + t!(v: b"hi/there\x80", extension, None); t!(s: "hi/there.txt", extension, Some("txt"), opt); t!(s: "hi/there", extension, None, opt); t!(s: "there.txt", extension, Some("txt"), opt); @@ -697,7 +697,7 @@ mod tests { assert!(p1 == p2.join(join)); } ) - ) + ); t!(s: "a/b/c", ".."); t!(s: "/a/b/c", "d"); @@ -716,7 +716,7 @@ mod tests { assert!(p.as_str() == Some($exp)); } ) - ) + ); t!(s: "a/b/c", "d", "a/b/c/d"); t!(s: "/a/b/c", "d", "/a/b/c/d"); @@ -743,7 +743,7 @@ mod tests { assert!(p.as_vec() == $exp); } ) - ) + ); t!(s: "a/b/c", ["d", "e"], "a/b/c/d/e"); t!(s: "a/b/c", ["d", "/e"], "/e"); @@ -773,7 +773,7 @@ mod tests { assert!(result == $right); } ) - ) + ); t!(b: b"a/b/c", b"a/b", true); t!(b: b"a", b".", true); @@ -821,7 +821,7 @@ mod tests { assert!(res.as_str() == Some($exp)); } ) - ) + ); t!(s: "a/b/c", "..", "a/b"); t!(s: "/a/b/c", "d", "/a/b/c/d"); @@ -848,7 +848,7 @@ mod tests { assert!(res.as_vec() == $exp); } ) - ) + ); t!(s: "a/b/c", ["d", "e"], "a/b/c/d/e"); t!(s: "a/b/c", ["..", "d"], "a/b/d"); @@ -932,7 +932,7 @@ mod tests { assert!(p1 == p2.$with(arg)); } ) - ) + ); t!(v: b"a/b/c", set_filename, with_filename, b"d"); t!(v: b"/", set_filename, with_filename, b"foo"); @@ -958,62 +958,57 @@ mod tests { macro_rules! t( (s: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => ( { - unsafe { - let path = $path; - let filename = $filename; - assert!(path.filename_str() == filename, - "{}.filename_str(): Expected `{}`, found {}", - path.as_str().unwrap(), filename, path.filename_str()); - let dirname = $dirname; - assert!(path.dirname_str() == dirname, - "`{}`.dirname_str(): Expected `{}`, found `{}`", - path.as_str().unwrap(), dirname, path.dirname_str()); - let filestem = $filestem; - assert!(path.filestem_str() == filestem, - "`{}`.filestem_str(): Expected `{}`, found `{}`", - path.as_str().unwrap(), filestem, path.filestem_str()); - let ext = $ext; - assert!(path.extension_str() == mem::transmute(ext), - "`{}`.extension_str(): Expected `{}`, found `{}`", - path.as_str().unwrap(), ext, path.extension_str()); - } + let path = $path; + let filename = $filename; + assert!(path.filename_str() == filename, + "{}.filename_str(): Expected `{}`, found {}", + path.as_str().unwrap(), filename, path.filename_str()); + let dirname = $dirname; + assert!(path.dirname_str() == dirname, + "`{}`.dirname_str(): Expected `{}`, found `{}`", + path.as_str().unwrap(), dirname, path.dirname_str()); + let filestem = $filestem; + assert!(path.filestem_str() == filestem, + "`{}`.filestem_str(): Expected `{}`, found `{}`", + path.as_str().unwrap(), filestem, path.filestem_str()); + let ext = $ext; + assert!(path.extension_str() == ext, + "`{}`.extension_str(): Expected `{}`, found `{}`", + path.as_str().unwrap(), ext, path.extension_str()); } ); (v: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => ( { - unsafe { - let path = $path; - assert!(path.filename() == mem::transmute($filename)); - assert!(path.dirname() == mem::transmute($dirname)); - assert!(path.filestem() == mem::transmute($filestem)); - assert!(path.extension() == mem::transmute($ext)); - } + let path = $path; + assert!(path.filename() == $filename); + assert!(path.dirname() == $dirname); + assert!(path.filestem() == $filestem); + assert!(path.extension() == $ext); } ) - ) + ); - let no: Option<&'static str> = None; - t!(v: Path::new(b"a/b/c"), Some(b"c"), b"a/b", Some(b"c"), no); - t!(v: Path::new(b"a/b/\xFF"), Some(b"\xFF"), b"a/b", Some(b"\xFF"), no); + t!(v: Path::new(b"a/b/c"), Some(b"c"), b"a/b", Some(b"c"), None); + t!(v: Path::new(b"a/b/\xFF"), Some(b"\xFF"), b"a/b", Some(b"\xFF"), None); t!(v: Path::new(b"hi/there.\xFF"), Some(b"there.\xFF"), b"hi", Some(b"there"), Some(b"\xFF")); - t!(s: Path::new("a/b/c"), Some("c"), Some("a/b"), Some("c"), no); - t!(s: Path::new("."), None, Some("."), None, no); - t!(s: Path::new("/"), None, Some("/"), None, no); - t!(s: Path::new(".."), None, Some(".."), None, no); - t!(s: Path::new("../.."), None, Some("../.."), None, no); + t!(s: Path::new("a/b/c"), Some("c"), Some("a/b"), Some("c"), None); + t!(s: Path::new("."), None, Some("."), None, None); + t!(s: Path::new("/"), None, Some("/"), None, None); + t!(s: Path::new(".."), None, Some(".."), None, None); + t!(s: Path::new("../.."), None, Some("../.."), None, None); t!(s: Path::new("hi/there.txt"), Some("there.txt"), Some("hi"), Some("there"), Some("txt")); - t!(s: Path::new("hi/there"), Some("there"), Some("hi"), Some("there"), no); + t!(s: Path::new("hi/there"), Some("there"), Some("hi"), Some("there"), None); t!(s: Path::new("hi/there."), Some("there."), Some("hi"), Some("there"), Some("")); - t!(s: Path::new("hi/.there"), Some(".there"), Some("hi"), Some(".there"), no); + t!(s: Path::new("hi/.there"), Some(".there"), Some("hi"), Some(".there"), None); t!(s: Path::new("hi/..there"), Some("..there"), Some("hi"), Some("."), Some("there")); - t!(s: Path::new(b"a/b/\xFF"), None, Some("a/b"), None, no); + t!(s: Path::new(b"a/b/\xFF"), None, Some("a/b"), None, None); t!(s: Path::new(b"a/b/\xFF.txt"), None, Some("a/b"), None, Some("txt")); - t!(s: Path::new(b"a/b/c.\x80"), None, Some("a/b"), Some("c"), no); - t!(s: Path::new(b"\xFF/b"), Some("b"), None, Some("b"), no); + t!(s: Path::new(b"a/b/c.\x80"), None, Some("a/b"), Some("c"), None); + t!(s: Path::new(b"\xFF/b"), Some("b"), None, Some("b"), None); } #[test] @@ -1038,7 +1033,7 @@ mod tests { assert_eq!(path.is_relative(), $rel); } ) - ) + ); t!(s: "a/b/c", false, true); t!(s: "/a/b/c", true, false); t!(s: "a", false, true); @@ -1059,7 +1054,7 @@ mod tests { assert_eq!(path.is_ancestor_of(&dest), $exp); } ) - ) + ); t!(s: "a/b/c", "a/b/c/d", true); t!(s: "a/b/c", "a/b/c", true); @@ -1100,7 +1095,7 @@ mod tests { assert_eq!(path.ends_with_path(&child), $exp); } ) - ) + ); t!(s: "a/b/c", "c", true); t!(s: "a/b/c", "d", false); @@ -1133,7 +1128,7 @@ mod tests { assert_eq!(res.as_ref().and_then(|x| x.as_str()), $exp); } ) - ) + ); t!(s: "a/b/c", "a/b", Some("c")); t!(s: "a/b/c", "a/b/d", Some("../c")); @@ -1189,13 +1184,13 @@ mod tests { let path = Path::new($arg); let comps = path.components().collect::<Vec<&[u8]>>(); let exp: &[&[u8]] = &[$($exp),*]; - assert_eq!(comps.as_slice(), exp); + assert_eq!(comps, exp); let comps = path.components().rev().collect::<Vec<&[u8]>>(); let exp = exp.iter().rev().map(|&x|x).collect::<Vec<&[u8]>>(); assert_eq!(comps, exp) } ) - ) + ); t!(b: b"a/b/c", [b"a", b"b", b"c"]); t!(b: b"/\xFF/a/\x80", [b"\xFF", b"a", b"\x80"]); @@ -1221,13 +1216,13 @@ mod tests { let path = Path::new($arg); let comps = path.str_components().collect::<Vec<Option<&str>>>(); let exp: &[Option<&str>] = &$exp; - assert_eq!(comps.as_slice(), exp); + assert_eq!(comps, exp); let comps = path.str_components().rev().collect::<Vec<Option<&str>>>(); let exp = exp.iter().rev().map(|&x|x).collect::<Vec<Option<&str>>>(); assert_eq!(comps, exp); } ) - ) + ); t!(b: b"a/b/c", [Some("a"), Some("b"), Some("c")]); t!(b: b"/\xFF/a/\x80", [None, Some("a"), None]); diff --git a/src/libstd/path/windows.rs b/src/libstd/path/windows.rs index f31ffdab17b..7d10188c437 100644 --- a/src/libstd/path/windows.rs +++ b/src/libstd/path/windows.rs @@ -12,7 +12,7 @@ //! Windows file path handling -pub use self::PathPrefix::*; +use self::PathPrefix::*; use ascii::AsciiCast; use c_str::{CString, ToCStr}; @@ -20,12 +20,14 @@ use clone::Clone; use cmp::{PartialEq, Eq, PartialOrd, Ord, Ordering}; use hash; use io::Writer; -use iter::{AdditiveIterator, DoubleEndedIterator, Extend, Iterator, Map}; +use iter::{AdditiveIterator, DoubleEndedIteratorExt, Extend}; +use iter::{Iterator, IteratorExt, Map}; use mem; -use option::{Option, Some, None}; -use slice::{AsSlice, SlicePrelude}; -use str::{CharSplits, FromStr, Str, StrAllocating, StrVector, StrPrelude}; -use string::String; +use option::Option; +use option::Option::{Some, None}; +use slice::SliceExt; +use str::{CharSplits, FromStr, StrVector, StrExt}; +use string::{String, ToString}; use unicode::char::UnicodeChar; use vec::Vec; @@ -35,12 +37,12 @@ use super::{contains_nul, BytesContainer, GenericPath, GenericPathUnsafe}; /// /// Each component is yielded as Option<&str> for compatibility with PosixPath, but /// every component in WindowsPath is guaranteed to be Some. -pub type StrComponents<'a> = Map<'a, &'a str, Option<&'a str>, - CharSplits<'a, char>>; +pub type StrComponents<'a> = + Map<&'a str, Option<&'a str>, CharSplits<'a, char>, fn(&'a str) -> Option<&'a str>>; /// Iterator that yields successive components of a Path as &[u8] -pub type Components<'a> = Map<'a, Option<&'a str>, &'a [u8], - StrComponents<'a>>; +pub type Components<'a> = + Map<Option<&'a str>, &'a [u8], StrComponents<'a>, fn(Option<&str>) -> &[u8]>; /// Represents a Windows path // Notes for Windows path impl: @@ -180,35 +182,35 @@ impl GenericPathUnsafe for Path { unsafe fn set_filename_unchecked<T: BytesContainer>(&mut self, filename: T) { let filename = filename.container_as_str().unwrap(); match self.sepidx_or_prefix_len() { - None if ".." == self.repr.as_slice() => { + None if ".." == self.repr => { let mut s = String::with_capacity(3 + filename.len()); s.push_str(".."); s.push(SEP); s.push_str(filename); - self.update_normalized(s); + self.update_normalized(s[]); } None => { self.update_normalized(filename); } - Some((_,idxa,end)) if self.repr.as_slice().slice(idxa,end) == ".." => { + Some((_,idxa,end)) if self.repr[idxa..end] == ".." => { let mut s = String::with_capacity(end + 1 + filename.len()); - s.push_str(self.repr.as_slice().slice_to(end)); + s.push_str(self.repr[0..end]); s.push(SEP); s.push_str(filename); - self.update_normalized(s); + self.update_normalized(s[]); } Some((idxb,idxa,_)) if self.prefix == Some(DiskPrefix) && idxa == self.prefix_len() => { let mut s = String::with_capacity(idxb + filename.len()); - s.push_str(self.repr.as_slice().slice_to(idxb)); + s.push_str(self.repr[0..idxb]); s.push_str(filename); - self.update_normalized(s); + self.update_normalized(s[]); } Some((idxb,_,_)) => { let mut s = String::with_capacity(idxb + 1 + filename.len()); - s.push_str(self.repr.as_slice().slice_to(idxb)); + s.push_str(self.repr[0..idxb]); s.push(SEP); s.push_str(filename); - self.update_normalized(s); + self.update_normalized(s[]); } } } @@ -227,18 +229,18 @@ impl GenericPathUnsafe for Path { let path = path.container_as_str().unwrap(); fn is_vol_abs(path: &str, prefix: Option<PathPrefix>) -> bool { // assume prefix is Some(DiskPrefix) - let rest = path.slice_from(prefix_len(prefix)); + let rest = path[prefix_len(prefix)..]; !rest.is_empty() && rest.as_bytes()[0].is_ascii() && is_sep(rest.as_bytes()[0] as char) } fn shares_volume(me: &Path, path: &str) -> bool { // path is assumed to have a prefix of Some(DiskPrefix) - let repr = me.repr.as_slice(); + let repr = me.repr[]; match me.prefix { Some(DiskPrefix) => { - repr.as_bytes()[0] == path.as_bytes()[0].to_ascii().to_uppercase().to_byte() + repr.as_bytes()[0] == path.as_bytes()[0].to_ascii().to_uppercase().as_byte() } Some(VerbatimDiskPrefix) => { - repr.as_bytes()[4] == path.as_bytes()[0].to_ascii().to_uppercase().to_byte() + repr.as_bytes()[4] == path.as_bytes()[0].to_ascii().to_uppercase().as_byte() } _ => false } @@ -264,7 +266,7 @@ impl GenericPathUnsafe for Path { else { None }; let pathlen = path_.as_ref().map_or(path.len(), |p| p.len()); let mut s = String::with_capacity(me.repr.len() + 1 + pathlen); - s.push_str(me.repr.as_slice()); + s.push_str(me.repr[]); let plen = me.prefix_len(); // if me is "C:" we don't want to add a path separator match me.prefix { @@ -276,9 +278,9 @@ impl GenericPathUnsafe for Path { } match path_ { None => s.push_str(path), - Some(p) => s.push_str(p.as_slice()) + Some(p) => s.push_str(p[]), }; - me.update_normalized(s) + me.update_normalized(s[]) } if !path.is_empty() { @@ -286,7 +288,7 @@ impl GenericPathUnsafe for Path { match prefix { Some(DiskPrefix) if !is_vol_abs(path, prefix) && shares_volume(self, path) => { // cwd-relative path, self is on the same volume - append_path(self, path.slice_from(prefix_len(prefix))); + append_path(self, path[prefix_len(prefix)..]); } Some(_) => { // absolute path, or cwd-relative and self is not same volume @@ -332,7 +334,7 @@ impl GenericPath for Path { /// Always returns a `Some` value. #[inline] fn as_str<'a>(&'a self) -> Option<&'a str> { - Some(self.repr.as_slice()) + Some(self.repr[]) } #[inline] @@ -354,21 +356,17 @@ impl GenericPath for Path { /// Always returns a `Some` value. fn dirname_str<'a>(&'a self) -> Option<&'a str> { Some(match self.sepidx_or_prefix_len() { - None if ".." == self.repr.as_slice() => self.repr.as_slice(), + None if ".." == self.repr => self.repr[], None => ".", - Some((_,idxa,end)) if self.repr.as_slice().slice(idxa, end) == ".." => { - self.repr.as_slice() - } - Some((idxb,_,end)) if self.repr.as_slice().slice(idxb, end) == "\\" => { - self.repr.as_slice() - } - Some((0,idxa,_)) => self.repr.as_slice().slice_to(idxa), + Some((_,idxa,end)) if self.repr[idxa..end] == ".." => self.repr[], + Some((idxb,_,end)) if self.repr[idxb..end] == "\\" => self.repr[], + Some((0,idxa,_)) => self.repr[0..idxa], Some((idxb,idxa,_)) => { match self.prefix { Some(DiskPrefix) | Some(VerbatimDiskPrefix) if idxb == self.prefix_len() => { - self.repr.as_slice().slice_to(idxa) + self.repr[0..idxa] } - _ => self.repr.as_slice().slice_to(idxb) + _ => self.repr[0..idxb] } } }) @@ -382,13 +380,13 @@ impl GenericPath for Path { /// See `GenericPath::filename_str` for info. /// Always returns a `Some` value if `filename` returns a `Some` value. fn filename_str<'a>(&'a self) -> Option<&'a str> { - let repr = self.repr.as_slice(); + let repr = self.repr[]; match self.sepidx_or_prefix_len() { None if "." == repr || ".." == repr => None, None => Some(repr), - Some((_,idxa,end)) if repr.slice(idxa, end) == ".." => None, + Some((_,idxa,end)) if repr[idxa..end] == ".." => None, Some((_,idxa,end)) if idxa == end => None, - Some((_,idxa,end)) => Some(repr.slice(idxa, end)) + Some((_,idxa,end)) => Some(repr[idxa..end]) } } @@ -413,14 +411,14 @@ impl GenericPath for Path { #[inline] fn pop(&mut self) -> bool { match self.sepidx_or_prefix_len() { - None if "." == self.repr.as_slice() => false, + None if "." == self.repr => false, None => { self.repr = String::from_str("."); self.sepidx = None; true } Some((idxb,idxa,end)) if idxb == idxa && idxb == end => false, - Some((idxb,_,end)) if self.repr.as_slice().slice(idxb, end) == "\\" => false, + Some((idxb,_,end)) if self.repr[idxb..end] == "\\" => false, Some((idxb,idxa,_)) => { let trunc = match self.prefix { Some(DiskPrefix) | Some(VerbatimDiskPrefix) | None => { @@ -440,15 +438,15 @@ impl GenericPath for Path { if self.prefix.is_some() { Some(Path::new(match self.prefix { Some(DiskPrefix) if self.is_absolute() => { - self.repr.as_slice().slice_to(self.prefix_len()+1) + self.repr[0..self.prefix_len()+1] } Some(VerbatimDiskPrefix) => { - self.repr.as_slice().slice_to(self.prefix_len()+1) + self.repr[0..self.prefix_len()+1] } - _ => self.repr.as_slice().slice_to(self.prefix_len()) + _ => self.repr[0..self.prefix_len()] })) } else if is_vol_relative(self) { - Some(Path::new(self.repr.as_slice().slice_to(1))) + Some(Path::new(self.repr[0..1])) } else { None } @@ -467,7 +465,7 @@ impl GenericPath for Path { fn is_absolute(&self) -> bool { match self.prefix { Some(DiskPrefix) => { - let rest = self.repr.as_slice().slice_from(self.prefix_len()); + let rest = self.repr[self.prefix_len()..]; rest.len() > 0 && rest.as_bytes()[0] == SEP_BYTE } Some(_) => true, @@ -489,7 +487,7 @@ impl GenericPath for Path { } else { let mut ita = self.str_components().map(|x|x.unwrap()); let mut itb = other.str_components().map(|x|x.unwrap()); - if "." == self.repr.as_slice() { + if "." == self.repr { return itb.next() != Some(".."); } loop { @@ -642,18 +640,19 @@ impl Path { /// Does not distinguish between absolute and cwd-relative paths, e.g. /// C:\foo and C:foo. pub fn str_components<'a>(&'a self) -> StrComponents<'a> { - let repr = self.repr.as_slice(); + let repr = self.repr[]; let s = match self.prefix { Some(_) => { let plen = self.prefix_len(); if repr.len() > plen && repr.as_bytes()[plen] == SEP_BYTE { - repr.slice_from(plen+1) - } else { repr.slice_from(plen) } + repr[plen+1..] + } else { repr[plen..] } } - None if repr.as_bytes()[0] == SEP_BYTE => repr.slice_from(1), + None if repr.as_bytes()[0] == SEP_BYTE => repr[1..], None => repr }; - let ret = s.split_terminator(SEP).map(Some); + let some: fn(&'a str) -> Option<&'a str> = Some; // coerce to fn ptr + let ret = s.split_terminator(SEP).map(some); ret } @@ -664,47 +663,51 @@ impl Path { #![inline] x.unwrap().as_bytes() } + let convert: for<'b> fn(Option<&'b str>) -> &'b [u8] = convert; // coerce to fn ptr self.str_components().map(convert) } fn equiv_prefix(&self, other: &Path) -> bool { - let s_repr = self.repr.as_slice(); - let o_repr = other.repr.as_slice(); + let s_repr = self.repr[]; + let o_repr = other.repr[]; match (self.prefix, other.prefix) { (Some(DiskPrefix), Some(VerbatimDiskPrefix)) => { self.is_absolute() && - s_repr.as_bytes()[0].to_ascii().eq_ignore_case(o_repr.as_bytes()[4].to_ascii()) + s_repr.as_bytes()[0].to_ascii().to_lowercase() == + o_repr.as_bytes()[4].to_ascii().to_lowercase() } (Some(VerbatimDiskPrefix), Some(DiskPrefix)) => { other.is_absolute() && - s_repr.as_bytes()[4].to_ascii().eq_ignore_case(o_repr.as_bytes()[0].to_ascii()) + s_repr.as_bytes()[4].to_ascii().to_lowercase() == + o_repr.as_bytes()[0].to_ascii().to_lowercase() } (Some(VerbatimDiskPrefix), Some(VerbatimDiskPrefix)) => { - s_repr.as_bytes()[4].to_ascii().eq_ignore_case(o_repr.as_bytes()[4].to_ascii()) + s_repr.as_bytes()[4].to_ascii().to_lowercase() == + o_repr.as_bytes()[4].to_ascii().to_lowercase() } (Some(UNCPrefix(_,_)), Some(VerbatimUNCPrefix(_,_))) => { - s_repr.slice(2, self.prefix_len()) == o_repr.slice(8, other.prefix_len()) + s_repr[2..self.prefix_len()] == o_repr[8..other.prefix_len()] } (Some(VerbatimUNCPrefix(_,_)), Some(UNCPrefix(_,_))) => { - s_repr.slice(8, self.prefix_len()) == o_repr.slice(2, other.prefix_len()) + s_repr[8..self.prefix_len()] == o_repr[2..other.prefix_len()] } (None, None) => true, (a, b) if a == b => { - s_repr.slice_to(self.prefix_len()) == o_repr.slice_to(other.prefix_len()) + s_repr[0..self.prefix_len()] == o_repr[0..other.prefix_len()] } _ => false } } - fn normalize_<S: StrAllocating>(s: S) -> (Option<PathPrefix>, String) { + fn normalize_(s: &str) -> (Option<PathPrefix>, String) { // make borrowck happy let (prefix, val) = { - let prefix = parse_prefix(s.as_slice()); - let path = Path::normalize__(s.as_slice(), prefix); + let prefix = parse_prefix(s); + let path = Path::normalize__(s, prefix); (prefix, path) }; (prefix, match val { - None => s.into_string(), + None => s.to_string(), Some(val) => val }) } @@ -744,13 +747,10 @@ impl Path { match prefix.unwrap() { DiskPrefix => { let len = prefix_len(prefix) + is_abs as uint; - let mut s = String::from_str(s.slice_to(len)); + let mut s = String::from_str(s[0..len]); unsafe { let v = s.as_mut_vec(); - v[0] = (*v)[0] - .to_ascii() - .to_uppercase() - .to_byte(); + v[0] = (*v)[0].to_ascii().to_uppercase().as_byte(); } if is_abs { // normalize C:/ to C:\ @@ -762,24 +762,24 @@ impl Path { } VerbatimDiskPrefix => { let len = prefix_len(prefix) + is_abs as uint; - let mut s = String::from_str(s.slice_to(len)); + let mut s = String::from_str(s[0..len]); unsafe { let v = s.as_mut_vec(); - v[4] = (*v)[4].to_ascii().to_uppercase().to_byte(); + v[4] = (*v)[4].to_ascii().to_uppercase().as_byte(); } Some(s) } _ => { let plen = prefix_len(prefix); if s.len() > plen { - Some(String::from_str(s.slice_to(plen))) + Some(String::from_str(s[0..plen])) } else { None } } } } else if is_abs && comps.is_empty() { Some(String::from_char(1, SEP)) } else { - let prefix_ = s.slice_to(prefix_len(prefix)); + let prefix_ = s[0..prefix_len(prefix)]; let n = prefix_.len() + if is_abs { comps.len() } else { comps.len() - 1} + comps.iter().map(|v| v.len()).sum(); @@ -787,20 +787,20 @@ impl Path { match prefix { Some(DiskPrefix) => { s.push(prefix_.as_bytes()[0].to_ascii() - .to_uppercase().to_char()); + .to_uppercase().as_char()); s.push(':'); } Some(VerbatimDiskPrefix) => { - s.push_str(prefix_.slice_to(4)); + s.push_str(prefix_[0..4]); s.push(prefix_.as_bytes()[4].to_ascii() - .to_uppercase().to_char()); - s.push_str(prefix_.slice_from(5)); + .to_uppercase().as_char()); + s.push_str(prefix_[5..]); } Some(UNCPrefix(a,b)) => { s.push_str("\\\\"); - s.push_str(prefix_.slice(2, a+2)); + s.push_str(prefix_[2..a+2]); s.push(SEP); - s.push_str(prefix_.slice(3+a, 3+a+b)); + s.push_str(prefix_[3+a..3+a+b]); } Some(_) => s.push_str(prefix_), None => () @@ -825,10 +825,14 @@ impl Path { fn update_sepidx(&mut self) { let s = if self.has_nonsemantic_trailing_slash() { - self.repr.as_slice().slice_to(self.repr.len()-1) - } else { self.repr.as_slice() }; - let idx = s.rfind(if !prefix_is_verbatim(self.prefix) { is_sep } - else { is_sep_verbatim }); + self.repr[0..self.repr.len()-1] + } else { self.repr[] }; + let sep_test: fn(char) -> bool = if !prefix_is_verbatim(self.prefix) { + is_sep + } else { + is_sep_verbatim + }; + let idx = s.rfind(sep_test); let prefixlen = self.prefix_len(); self.sepidx = idx.and_then(|x| if x < prefixlen { None } else { Some(x) }); } @@ -858,8 +862,8 @@ impl Path { self.repr.as_bytes()[self.repr.len()-1] == SEP_BYTE } - fn update_normalized<S: Str>(&mut self, s: S) { - let (prefix, path) = Path::normalize_(s.as_slice()); + fn update_normalized(&mut self, s: &str) { + let (prefix, path) = Path::normalize_(s); self.repr = path; self.prefix = prefix; self.update_sepidx(); @@ -901,17 +905,17 @@ pub fn is_verbatim(path: &Path) -> bool { /// non-verbatim, the non-verbatim version is returned. /// Otherwise, None is returned. pub fn make_non_verbatim(path: &Path) -> Option<Path> { - let repr = path.repr.as_slice(); + let repr = path.repr[]; let new_path = match path.prefix { Some(VerbatimPrefix(_)) | Some(DeviceNSPrefix(_)) => return None, Some(UNCPrefix(_,_)) | Some(DiskPrefix) | None => return Some(path.clone()), Some(VerbatimDiskPrefix) => { // \\?\D:\ - Path::new(repr.slice_from(4)) + Path::new(repr[4..]) } Some(VerbatimUNCPrefix(_,_)) => { // \\?\UNC\server\share - Path::new(format!(r"\{}", repr.slice_from(7))) + Path::new(format!(r"\{}", repr[7..])) } }; if new_path.prefix.is_none() { @@ -920,8 +924,8 @@ pub fn make_non_verbatim(path: &Path) -> Option<Path> { return None; } // now ensure normalization didn't change anything - if repr.slice_from(path.prefix_len()) == - new_path.repr.as_slice().slice_from(new_path.prefix_len()) { + if repr[path.prefix_len()..] == + new_path.repr[new_path.prefix_len()..] { Some(new_path) } else { None @@ -967,7 +971,7 @@ pub fn is_sep_byte_verbatim(u: &u8) -> bool { } /// Prefix types for Path -#[deriving(PartialEq, Clone, Show)] +#[deriving(Copy, PartialEq, Clone, Show)] pub enum PathPrefix { /// Prefix `\\?\`, uint is the length of the following component VerbatimPrefix(uint), @@ -986,13 +990,13 @@ pub enum PathPrefix { fn parse_prefix<'a>(mut path: &'a str) -> Option<PathPrefix> { if path.starts_with("\\\\") { // \\ - path = path.slice_from(2); + path = path[2..]; if path.starts_with("?\\") { // \\?\ - path = path.slice_from(2); + path = path[2..]; if path.starts_with("UNC\\") { // \\?\UNC\server\share - path = path.slice_from(4); + path = path[4..]; let (idx_a, idx_b) = match parse_two_comps(path, is_sep_verbatim) { Some(x) => x, None => (path.len(), 0) @@ -1013,7 +1017,7 @@ fn parse_prefix<'a>(mut path: &'a str) -> Option<PathPrefix> { } } else if path.starts_with(".\\") { // \\.\path - path = path.slice_from(2); + path = path[2..]; let idx = path.find('\\').unwrap_or(path.len()); return Some(DeviceNSPrefix(idx)); } @@ -1033,13 +1037,12 @@ fn parse_prefix<'a>(mut path: &'a str) -> Option<PathPrefix> { } return None; - fn parse_two_comps<'a>(mut path: &'a str, f: |char| -> bool) - -> Option<(uint, uint)> { - let idx_a = match path.find(|x| f(x)) { + fn parse_two_comps(mut path: &str, f: fn(char) -> bool) -> Option<(uint, uint)> { + let idx_a = match path.find(f) { None => return None, Some(x) => x }; - path = path.slice_from(idx_a+1); + path = path[idx_a+1..]; let idx_b = path.find(f).unwrap_or(path.len()); Some((idx_a, idx_b)) } @@ -1047,10 +1050,14 @@ fn parse_prefix<'a>(mut path: &'a str) -> Option<PathPrefix> { // None result means the string didn't need normalizing fn normalize_helper<'a>(s: &'a str, prefix: Option<PathPrefix>) -> (bool, Option<Vec<&'a str>>) { - let f = if !prefix_is_verbatim(prefix) { is_sep } else { is_sep_verbatim }; + let f: fn(char) -> bool = if !prefix_is_verbatim(prefix) { + is_sep + } else { + is_sep_verbatim + }; let is_abs = s.len() > prefix_len(prefix) && f(s.char_at(prefix_len(prefix))); - let s_ = s.slice_from(prefix_len(prefix)); - let s_ = if is_abs { s_.slice_from(1) } else { s_ }; + let s_ = s[prefix_len(prefix)..]; + let s_ = if is_abs { s_[1..] } else { s_ }; if is_abs && s_.is_empty() { return (is_abs, match prefix { @@ -1114,12 +1121,12 @@ fn prefix_len(p: Option<PathPrefix>) -> uint { #[cfg(test)] mod tests { - use mem; use prelude::*; use super::*; + use super::PathPrefix::*; use super::parse_prefix; - macro_rules! t( + macro_rules! t { (s: $path:expr, $exp:expr) => ( { let path = $path; @@ -1132,7 +1139,7 @@ mod tests { assert!(path.as_vec() == $exp); } ) - ) + } #[test] fn test_parse_prefix() { @@ -1146,7 +1153,7 @@ mod tests { "parse_prefix(\"{}\"): expected {}, found {}", path, exp, res); } ) - ) + ); t!("\\\\SERVER\\share\\foo", Some(UNCPrefix(6,5))); t!("\\\\", None); @@ -1232,8 +1239,8 @@ mod tests { t!(s: Path::new("foo\\..\\..\\.."), "..\\.."); t!(s: Path::new("foo\\..\\..\\bar"), "..\\bar"); - assert_eq!(Path::new(b"foo\\bar").into_vec().as_slice(), b"foo\\bar"); - assert_eq!(Path::new(b"\\foo\\..\\..\\bar").into_vec().as_slice(), b"\\bar"); + assert_eq!(Path::new(b"foo\\bar").into_vec(), b"foo\\bar"); + assert_eq!(Path::new(b"\\foo\\..\\..\\bar").into_vec(), b"\\bar"); t!(s: Path::new("\\\\a"), "\\a"); t!(s: Path::new("\\\\a\\"), "\\a"); @@ -1295,20 +1302,20 @@ mod tests { #[test] fn test_null_byte() { - use task; - let result = task::try(proc() { + use thread::Thread; + let result = Thread::spawn(move|| { Path::new(b"foo/bar\0") - }); + }).join(); assert!(result.is_err()); - let result = task::try(proc() { + let result = Thread::spawn(move|| { Path::new("test").set_filename(b"f\0o") - }); + }).join(); assert!(result.is_err()); - let result = task::try(proc() { + let result = Thread::spawn(move || { Path::new("test").push(b"f\0o"); - }); + }).join(); assert!(result.is_err()); } @@ -1321,15 +1328,15 @@ mod tests { #[test] fn test_display_str() { let path = Path::new("foo"); - assert_eq!(path.display().to_string(), "foo".to_string()); + assert_eq!(path.display().to_string(), "foo"); let path = Path::new(b"\\"); - assert_eq!(path.filename_display().to_string(), "".to_string()); + assert_eq!(path.filename_display().to_string(), ""); let path = Path::new("foo"); - let mo = path.display().as_maybe_owned(); + let mo = path.display().as_cow(); assert_eq!(mo.as_slice(), "foo"); let path = Path::new(b"\\"); - let mo = path.filename_display().as_maybe_owned(); + let mo = path.filename_display().as_cow(); assert_eq!(mo.as_slice(), ""); } @@ -1340,12 +1347,12 @@ mod tests { { let path = Path::new($path); let f = format!("{}", path.display()); - assert_eq!(f.as_slice(), $exp); + assert_eq!(f, $exp); let f = format!("{}", path.filename_display()); - assert_eq!(f.as_slice(), $expf); + assert_eq!(f, $expf); } ) - ) + ); t!("foo", "foo", "foo"); t!("foo\\bar", "foo\\bar", "bar"); @@ -1357,11 +1364,9 @@ mod tests { macro_rules! t( (s: $path:expr, $op:ident, $exp:expr) => ( { - unsafe { - let path = $path; - let path = Path::new(path); - assert!(path.$op() == Some(mem::transmute($exp))); - } + let path = $path; + let path = Path::new(path); + assert!(path.$op() == Some($exp)); } ); (s: $path:expr, $op:ident, $exp:expr, opt) => ( @@ -1374,14 +1379,12 @@ mod tests { ); (v: $path:expr, $op:ident, $exp:expr) => ( { - unsafe { - let path = $path; - let path = Path::new(path); - assert!(path.$op() == mem::transmute($exp)); - } + let path = $path; + let path = Path::new(path); + assert!(path.$op() == $exp); } ) - ) + ); t!(v: b"a\\b\\c", filename, Some(b"c")); t!(s: "a\\b\\c", filename_str, "c"); @@ -1463,8 +1466,7 @@ mod tests { // filestem is based on filename, so we don't need the full set of prefix tests t!(v: b"hi\\there.txt", extension, Some(b"txt")); - let no: Option<&'static [u8]> = None; - t!(v: b"hi\\there", extension, no); + t!(v: b"hi\\there", extension, None); t!(s: "hi\\there.txt", extension_str, Some("txt"), opt); t!(s: "hi\\there", extension_str, None, opt); t!(s: "there.txt", extension_str, Some("txt"), opt); @@ -1493,7 +1495,7 @@ mod tests { assert!(p1 == p2.join(join)); } ) - ) + ); t!(s: "a\\b\\c", ".."); t!(s: "\\a\\b\\c", "d"); @@ -1526,7 +1528,7 @@ mod tests { assert_eq!(p.as_str(), Some($exp)); } ) - ) + ); t!(s: "a\\b\\c", "d", "a\\b\\c\\d"); t!(s: "\\a\\b\\c", "d", "\\a\\b\\c\\d"); @@ -1584,7 +1586,7 @@ mod tests { assert_eq!(p.as_vec(), $exp); } ) - ) + ); t!(s: "a\\b\\c", ["d", "e"], "a\\b\\c\\d\\e"); t!(s: "a\\b\\c", ["d", "\\e"], "\\e"); @@ -1619,7 +1621,7 @@ mod tests { assert!(result == $right); } ) - ) + ); t!(s: "a\\b\\c", "a\\b", true); t!(s: "a", ".", true); @@ -1696,7 +1698,7 @@ mod tests { assert_eq!(res.as_str(), Some($exp)); } ) - ) + ); t!(s: "a\\b\\c", "..", "a\\b"); t!(s: "\\a\\b\\c", "d", "\\a\\b\\c\\d"); @@ -1725,7 +1727,7 @@ mod tests { assert_eq!(res.as_vec(), $exp); } ) - ) + ); t!(s: "a\\b\\c", ["d", "e"], "a\\b\\c\\d\\e"); t!(s: "a\\b\\c", ["..", "d"], "a\\b\\d"); @@ -1751,7 +1753,7 @@ mod tests { pstr, stringify!($op), arg, exp, res.as_str().unwrap()); } ) - ) + ); t!(s: "a\\b\\c", with_filename, "d", "a\\b\\d"); t!(s: ".", with_filename, "foo", "foo"); @@ -1844,7 +1846,7 @@ mod tests { assert!(p1 == p2.$with(arg)); } ) - ) + ); t!(v: b"a\\b\\c", set_filename, with_filename, b"d"); t!(v: b"\\", set_filename, with_filename, b"foo"); @@ -1871,53 +1873,48 @@ mod tests { macro_rules! t( (s: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => ( { - unsafe { - let path = $path; - let filename = $filename; - assert!(path.filename_str() == filename, - "`{}`.filename_str(): Expected `{}`, found `{}`", - path.as_str().unwrap(), filename, path.filename_str()); - let dirname = $dirname; - assert!(path.dirname_str() == dirname, - "`{}`.dirname_str(): Expected `{}`, found `{}`", - path.as_str().unwrap(), dirname, path.dirname_str()); - let filestem = $filestem; - assert!(path.filestem_str() == filestem, - "`{}`.filestem_str(): Expected `{}`, found `{}`", - path.as_str().unwrap(), filestem, path.filestem_str()); - let ext = $ext; - assert!(path.extension_str() == mem::transmute(ext), - "`{}`.extension_str(): Expected `{}`, found `{}`", - path.as_str().unwrap(), ext, path.extension_str()); - } + let path = $path; + let filename = $filename; + assert!(path.filename_str() == filename, + "`{}`.filename_str(): Expected `{}`, found `{}`", + path.as_str().unwrap(), filename, path.filename_str()); + let dirname = $dirname; + assert!(path.dirname_str() == dirname, + "`{}`.dirname_str(): Expected `{}`, found `{}`", + path.as_str().unwrap(), dirname, path.dirname_str()); + let filestem = $filestem; + assert!(path.filestem_str() == filestem, + "`{}`.filestem_str(): Expected `{}`, found `{}`", + path.as_str().unwrap(), filestem, path.filestem_str()); + let ext = $ext; + assert!(path.extension_str() == ext, + "`{}`.extension_str(): Expected `{}`, found `{}`", + path.as_str().unwrap(), ext, path.extension_str()); } ); (v: $path:expr, $filename:expr, $dirname:expr, $filestem:expr, $ext:expr) => ( { - unsafe { - let path = $path; - assert!(path.filename() == mem::transmute($filename)); - assert!(path.dirname() == mem::transmute($dirname)); - assert!(path.filestem() == mem::transmute($filestem)); - assert!(path.extension() == mem::transmute($ext)); - } + let path = $path; + assert!(path.filename() == $filename); + assert!(path.dirname() == $dirname); + assert!(path.filestem() == $filestem); + assert!(path.extension() == $ext); } ) - ) + ); - let no: Option<&'static str> = None; - t!(v: Path::new(b"a\\b\\c"), Some(b"c"), b"a\\b", Some(b"c"), no); - t!(s: Path::new("a\\b\\c"), Some("c"), Some("a\\b"), Some("c"), no); - t!(s: Path::new("."), None, Some("."), None, no); - t!(s: Path::new("\\"), None, Some("\\"), None, no); - t!(s: Path::new(".."), None, Some(".."), None, no); - t!(s: Path::new("..\\.."), None, Some("..\\.."), None, no); + t!(v: Path::new(b"a\\b\\c"), Some(b"c"), b"a\\b", Some(b"c"), None); + t!(s: Path::new("a\\b\\c"), Some("c"), Some("a\\b"), Some("c"), None); + t!(s: Path::new("."), None, Some("."), None, None); + t!(s: Path::new("\\"), None, Some("\\"), None, None); + t!(s: Path::new(".."), None, Some(".."), None, None); + t!(s: Path::new("..\\.."), None, Some("..\\.."), None, None); t!(s: Path::new("hi\\there.txt"), Some("there.txt"), Some("hi"), Some("there"), Some("txt")); - t!(s: Path::new("hi\\there"), Some("there"), Some("hi"), Some("there"), no); + t!(s: Path::new("hi\\there"), Some("there"), Some("hi"), Some("there"), None); t!(s: Path::new("hi\\there."), Some("there."), Some("hi"), Some("there"), Some("")); - t!(s: Path::new("hi\\.there"), Some(".there"), Some("hi"), Some(".there"), no); + t!(s: Path::new("hi\\.there"), Some(".there"), Some("hi"), Some(".there"), None); t!(s: Path::new("hi\\..there"), Some("..there"), Some("hi"), Some("."), Some("there")); @@ -1958,7 +1955,7 @@ mod tests { path.as_str().unwrap(), rel, b); } ) - ) + ); t!("a\\b\\c", false, false, false, true); t!("\\a\\b\\c", false, true, false, false); t!("a", false, false, false, true); @@ -1991,7 +1988,7 @@ mod tests { path.as_str().unwrap(), dest.as_str().unwrap(), exp, res); } ) - ) + ); t!(s: "a\\b\\c", "a\\b\\c\\d", true); t!(s: "a\\b\\c", "a\\b\\c", true); @@ -2090,7 +2087,7 @@ mod tests { assert_eq!(path.ends_with_path(&child), $exp); } ); - ) + ); t!(s: "a\\b\\c", "c", true); t!(s: "a\\b\\c", "d", false); @@ -2127,7 +2124,7 @@ mod tests { res.as_ref().and_then(|x| x.as_str())); } ) - ) + ); t!(s: "a\\b\\c", "a\\b", Some("c")); t!(s: "a\\b\\c", "a\\b\\d", Some("..\\c")); @@ -2255,14 +2252,14 @@ mod tests { let comps = path.str_components().map(|x|x.unwrap()) .collect::<Vec<&str>>(); let exp: &[&str] = &$exp; - assert_eq!(comps.as_slice(), exp); + assert_eq!(comps, exp); let comps = path.str_components().rev().map(|x|x.unwrap()) .collect::<Vec<&str>>(); let exp = exp.iter().rev().map(|&x|x).collect::<Vec<&str>>(); assert_eq!(comps, exp); } ); - ) + ); t!(s: b"a\\b\\c", ["a", "b", "c"]); t!(s: "a\\b\\c", ["a", "b", "c"]); @@ -2312,13 +2309,13 @@ mod tests { let path = Path::new($path); let comps = path.components().collect::<Vec<&[u8]>>(); let exp: &[&[u8]] = &$exp; - assert_eq!(comps.as_slice(), exp); + assert_eq!(comps, exp); let comps = path.components().rev().collect::<Vec<&[u8]>>(); let exp = exp.iter().rev().map(|&x|x).collect::<Vec<&[u8]>>(); assert_eq!(comps, exp); } ) - ) + ); t!(s: "a\\b\\c", [b"a", b"b", b"c"]); t!(s: ".", [b"."]); @@ -2336,7 +2333,7 @@ mod tests { assert!(make_non_verbatim(&path) == exp); } ) - ) + ); t!(r"\a\b\c", Some(r"\a\b\c")); t!(r"a\b\c", Some(r"a\b\c")); diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index 65f45c3f97e..49b888d17f4 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -50,7 +50,7 @@ #[doc(no_inline)] pub use ops::{Fn, FnMut, FnOnce}; // Reexported functions -#[doc(no_inline)] pub use iter::{range, repeat}; +#[doc(no_inline)] pub use iter::range; #[doc(no_inline)] pub use mem::drop; #[doc(no_inline)] pub use str::from_str; @@ -58,16 +58,18 @@ #[doc(no_inline)] pub use ascii::{Ascii, AsciiCast, OwnedAsciiCast, AsciiStr}; #[doc(no_inline)] pub use ascii::IntoBytes; +#[doc(no_inline)] pub use borrow::IntoCow; #[doc(no_inline)] pub use c_str::ToCStr; #[doc(no_inline)] pub use char::{Char, UnicodeChar}; #[doc(no_inline)] pub use clone::Clone; #[doc(no_inline)] pub use cmp::{PartialEq, PartialOrd, Eq, Ord}; #[doc(no_inline)] pub use cmp::{Ordering, Equiv}; #[doc(no_inline)] pub use cmp::Ordering::{Less, Equal, Greater}; -#[doc(no_inline)] pub use iter::{FromIterator, Extend, ExactSize}; -#[doc(no_inline)] pub use iter::{Iterator, DoubleEndedIterator}; -#[doc(no_inline)] pub use iter::{RandomAccessIterator, CloneableIterator}; -#[doc(no_inline)] pub use iter::{OrdIterator, MutableDoubleEndedIterator}; +#[doc(no_inline)] pub use iter::{FromIterator, Extend, ExactSizeIterator}; +#[doc(no_inline)] pub use iter::{Iterator, IteratorExt, DoubleEndedIterator}; +#[doc(no_inline)] pub use iter::{DoubleEndedIteratorExt, CloneIteratorExt}; +#[doc(no_inline)] pub use iter::{RandomAccessIterator, IteratorCloneExt}; +#[doc(no_inline)] pub use iter::{IteratorOrdExt, MutableDoubleEndedIterator}; #[doc(no_inline)] pub use num::{ToPrimitive, FromPrimitive}; #[doc(no_inline)] pub use boxed::Box; #[doc(no_inline)] pub use option::Option; @@ -77,14 +79,15 @@ #[doc(no_inline)] pub use result::Result; #[doc(no_inline)] pub use result::Result::{Ok, Err}; #[doc(no_inline)] pub use io::{Buffer, Writer, Reader, Seek, BufferPrelude}; -#[doc(no_inline)] pub use str::{Str, StrVector, StrPrelude}; -#[doc(no_inline)] pub use str::{IntoMaybeOwned, StrAllocating, UnicodeStrPrelude}; -#[doc(no_inline)] pub use tuple::{Tuple1, Tuple2, Tuple3, Tuple4}; -#[doc(no_inline)] pub use tuple::{Tuple5, Tuple6, Tuple7, Tuple8}; -#[doc(no_inline)] pub use tuple::{Tuple9, Tuple10, Tuple11, Tuple12}; -#[doc(no_inline)] pub use slice::{SlicePrelude, AsSlice, CloneSlicePrelude}; -#[doc(no_inline)] pub use slice::{VectorVector, PartialEqSlicePrelude, OrdSlicePrelude}; -#[doc(no_inline)] pub use slice::{CloneSliceAllocPrelude, OrdSliceAllocPrelude, SliceAllocPrelude}; +#[doc(no_inline)] pub use core::prelude::{Tuple1, Tuple2, Tuple3, Tuple4}; +#[doc(no_inline)] pub use core::prelude::{Tuple5, Tuple6, Tuple7, Tuple8}; +#[doc(no_inline)] pub use core::prelude::{Tuple9, Tuple10, Tuple11, Tuple12}; +#[doc(no_inline)] pub use str::{Str, StrVector}; +#[doc(no_inline)] pub use str::StrExt; +#[doc(no_inline)] pub use slice::AsSlice; +#[doc(no_inline)] pub use slice::{VectorVector, PartialEqSliceExt}; +#[doc(no_inline)] pub use slice::{CloneSliceExt, OrdSliceExt, SliceExt}; +#[doc(no_inline)] pub use slice::{BoxedSliceExt}; #[doc(no_inline)] pub use string::{IntoString, String, ToString}; #[doc(no_inline)] pub use vec::Vec; diff --git a/src/libstd/rand/mod.rs b/src/libstd/rand/mod.rs index 08eb7350bcf..c590c0f575e 100644 --- a/src/libstd/rand/mod.rs +++ b/src/libstd/rand/mod.rs @@ -45,7 +45,7 @@ //! so the "quality" of `/dev/random` is not better than `/dev/urandom` in most cases. //! However, this means that `/dev/urandom` can yield somewhat predictable randomness //! if the entropy pool is very small, such as immediately after first booting. -//! Linux 3,17 added `getrandom(2)` system call which solves the issue: it blocks if entropy +//! Linux 3.17 added the `getrandom(2)` system call which solves the issue: it blocks if entropy //! pool is not initialized yet, but it does not block once initialized. //! `OsRng` tries to use `getrandom(2)` if available, and use `/dev/urandom` fallback if not. //! If an application does not have `getrandom` and likely to be run soon after first booting, @@ -80,7 +80,7 @@ //! circle, both centered at the origin. Since the area of a unit circle is π, //! we have: //! -//! ```notrust +//! ```text //! (area of unit circle) / (area of square) = π / 4 //! ``` //! @@ -126,7 +126,7 @@ //! > Is it to your advantage to switch your choice? //! //! The rather unintuitive answer is that you will have a 2/3 chance of winning if -//! you switch and a 1/3 chance of winning of you don't, so it's better to switch. +//! you switch and a 1/3 chance of winning if you don't, so it's better to switch. //! //! This program will simulate the game show and with large enough simulation steps //! it will indeed confirm that it is better to switch. @@ -224,11 +224,10 @@ use cell::RefCell; use clone::Clone; use io::IoResult; -use iter::Iterator; +use iter::{Iterator, IteratorExt}; use mem; -use option::{Some, None}; use rc::Rc; -use result::{Ok, Err}; +use result::Result::{Ok, Err}; use vec::Vec; #[cfg(not(target_word_size="64"))] @@ -246,7 +245,10 @@ pub mod reader; /// The standard RNG. This is designed to be efficient on the current /// platform. -pub struct StdRng { rng: IsaacWordRng } +#[deriving(Copy)] +pub struct StdRng { + rng: IsaacWordRng, +} impl StdRng { /// Create a randomly seeded instance of `StdRng`. @@ -337,24 +339,18 @@ pub struct TaskRng { /// explicitly select an RNG, e.g. `IsaacRng` or `Isaac64Rng`. pub fn task_rng() -> TaskRng { // used to make space in TLS for a random number generator - local_data_key!(TASK_RNG_KEY: Rc<RefCell<TaskRngInner>>) - - match TASK_RNG_KEY.get() { - None => { - let r = match StdRng::new() { - Ok(r) => r, - Err(e) => panic!("could not initialize task_rng: {}", e) - }; - let rng = reseeding::ReseedingRng::new(r, - TASK_RNG_RESEED_THRESHOLD, - TaskRngReseeder); - let rng = Rc::new(RefCell::new(rng)); - TASK_RNG_KEY.replace(Some(rng.clone())); - - TaskRng { rng: rng } - } - Some(rng) => TaskRng { rng: rng.clone() } - } + thread_local!(static TASK_RNG_KEY: Rc<RefCell<TaskRngInner>> = { + let r = match StdRng::new() { + Ok(r) => r, + Err(e) => panic!("could not initialize task_rng: {}", e) + }; + let rng = reseeding::ReseedingRng::new(r, + TASK_RNG_RESEED_THRESHOLD, + TaskRngReseeder); + Rc::new(RefCell::new(rng)) + }); + + TaskRng { rng: TASK_RNG_KEY.with(|t| t.clone()) } } impl Rng for TaskRng { @@ -536,7 +532,7 @@ mod test { let mut one = [1i]; r.shuffle(&mut one); let b: &[_] = &[1]; - assert_eq!(one.as_slice(), b); + assert_eq!(one, b); let mut two = [1i, 2]; r.shuffle(&mut two); @@ -545,7 +541,7 @@ mod test { let mut x = [1i, 1, 1]; r.shuffle(&mut x); let b: &[_] = &[1, 1, 1]; - assert_eq!(x.as_slice(), b); + assert_eq!(x, b); } #[test] @@ -555,7 +551,7 @@ mod test { let mut v = [1i, 1, 1]; r.shuffle(&mut v); let b: &[_] = &[1, 1, 1]; - assert_eq!(v.as_slice(), b); + assert_eq!(v, b); assert_eq!(r.gen_range(0u, 1u), 0u); } diff --git a/src/libstd/rand/os.rs b/src/libstd/rand/os.rs index 2a4d8347c30..68c99b12758 100644 --- a/src/libstd/rand/os.rs +++ b/src/libstd/rand/os.rs @@ -23,8 +23,8 @@ mod imp { use path::Path; use rand::Rng; use rand::reader::ReaderRng; - use result::{Ok, Err}; - use slice::SlicePrelude; + use result::Result::{Ok, Err}; + use slice::SliceExt; use mem; use os::errno; @@ -117,7 +117,8 @@ mod imp { /// `/dev/urandom`, or from `getrandom(2)` system call if available. /// - Windows: calls `CryptGenRandom`, using the default cryptographic /// service provider with the `PROV_RSA_FULL` type. - /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed + /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed. + /// /// This does not block. pub struct OsRng { inner: OsRngInner, @@ -169,13 +170,12 @@ mod imp { extern crate libc; use io::{IoResult}; - use kinds::marker; use mem; use os; use rand::Rng; - use result::{Ok}; + use result::Result::{Ok}; use self::libc::{c_int, size_t}; - use slice::{SlicePrelude}; + use slice::SliceExt; /// A random number generator that retrieves randomness straight from /// the operating system. Platform sources: @@ -184,10 +184,13 @@ mod imp { /// `/dev/urandom`, or from `getrandom(2)` system call if available. /// - Windows: calls `CryptGenRandom`, using the default cryptographic /// service provider with the `PROV_RSA_FULL` type. - /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed + /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed. + /// /// This does not block. + #[allow(missing_copy_implementations)] pub struct OsRng { - marker: marker::NoCopy + // dummy field to ensure that this struct cannot be constructed outside of this module + _dummy: (), } #[repr(C)] @@ -205,7 +208,7 @@ mod imp { impl OsRng { /// Create a new `OsRng`. pub fn new() -> IoResult<OsRng> { - Ok(OsRng {marker: marker::NoCopy} ) + Ok(OsRng { _dummy: () }) } } @@ -240,10 +243,10 @@ mod imp { use ops::Drop; use os; use rand::Rng; - use result::{Ok, Err}; + use result::Result::{Ok, Err}; use self::libc::{DWORD, BYTE, LPCSTR, BOOL}; use self::libc::types::os::arch::extra::{LONG_PTR}; - use slice::{SlicePrelude}; + use slice::SliceExt; type HCRYPTPROV = LONG_PTR; @@ -254,7 +257,8 @@ mod imp { /// `/dev/urandom`, or from `getrandom(2)` system call if available. /// - Windows: calls `CryptGenRandom`, using the default cryptographic /// service provider with the `PROV_RSA_FULL` type. - /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed + /// - iOS: calls SecRandomCopyBytes as /dev/(u)random is sandboxed. + /// /// This does not block. pub struct OsRng { hcryptprov: HCRYPTPROV @@ -335,7 +339,7 @@ mod test { use super::OsRng; use rand::Rng; - use task; + use thread::Thread; #[test] fn test_os_rng() { @@ -355,25 +359,26 @@ mod test { for _ in range(0u, 20) { let (tx, rx) = channel(); txs.push(tx); - task::spawn(proc() { + + Thread::spawn(move|| { // wait until all the tasks are ready to go. rx.recv(); // deschedule to attempt to interleave things as much // as possible (XXX: is this a good test?) let mut r = OsRng::new().unwrap(); - task::deschedule(); + Thread::yield_now(); let mut v = [0u8, .. 1000]; for _ in range(0u, 100) { r.next_u32(); - task::deschedule(); + Thread::yield_now(); r.next_u64(); - task::deschedule(); + Thread::yield_now(); r.fill_bytes(&mut v); - task::deschedule(); + Thread::yield_now(); } - }) + }).detach(); } // start all the tasks diff --git a/src/libstd/rand/reader.rs b/src/libstd/rand/reader.rs index 796bf7853f7..7298b2ef0ac 100644 --- a/src/libstd/rand/reader.rs +++ b/src/libstd/rand/reader.rs @@ -12,8 +12,8 @@ use io::Reader; use rand::Rng; -use result::{Ok, Err}; -use slice::SlicePrelude; +use result::Result::{Ok, Err}; +use slice::SliceExt; /// An RNG that reads random bytes straight from a `Reader`. This will /// work best with an infinite reader, but this is not required. diff --git a/src/libstd/rt/args.rs b/src/libstd/rt/args.rs new file mode 100644 index 00000000000..b1f268597c7 --- /dev/null +++ b/src/libstd/rt/args.rs @@ -0,0 +1,166 @@ +// Copyright 2012-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. + +//! Global storage for command line arguments +//! +//! The current incarnation of the Rust runtime expects for +//! the processes `argc` and `argv` arguments to be stored +//! in a globally-accessible location for use by the `os` module. +//! +//! Only valid to call on Linux. Mac and Windows use syscalls to +//! discover the command line arguments. +//! +//! FIXME #7756: Would be nice for this to not exist. + +use core::prelude::*; +use vec::Vec; + +/// One-time global initialization. +pub unsafe fn init(argc: int, argv: *const *const u8) { imp::init(argc, argv) } + +/// One-time global cleanup. +pub unsafe fn cleanup() { imp::cleanup() } + +/// Take the global arguments from global storage. +pub fn take() -> Option<Vec<Vec<u8>>> { imp::take() } + +/// Give the global arguments to global storage. +/// +/// It is an error if the arguments already exist. +pub fn put(args: Vec<Vec<u8>>) { imp::put(args) } + +/// Make a clone of the global arguments. +pub fn clone() -> Option<Vec<Vec<u8>>> { imp::clone() } + +#[cfg(any(target_os = "linux", + target_os = "android", + target_os = "freebsd", + target_os = "dragonfly"))] +mod imp { + use prelude::*; + + use mem; + use slice; + + use sync::{StaticMutex, MUTEX_INIT}; + + static mut GLOBAL_ARGS_PTR: uint = 0; + static LOCK: StaticMutex = MUTEX_INIT; + + pub unsafe fn init(argc: int, argv: *const *const u8) { + let args = load_argc_and_argv(argc, argv); + put(args); + } + + pub unsafe fn cleanup() { + take(); + LOCK.destroy(); + } + + pub fn take() -> Option<Vec<Vec<u8>>> { + let _guard = LOCK.lock(); + unsafe { + let ptr = get_global_ptr(); + let val = mem::replace(&mut *ptr, None); + val.as_ref().map(|s: &Box<Vec<Vec<u8>>>| (**s).clone()) + } + } + + pub fn put(args: Vec<Vec<u8>>) { + let _guard = LOCK.lock(); + unsafe { + let ptr = get_global_ptr(); + rtassert!((*ptr).is_none()); + (*ptr) = Some(box args.clone()); + } + } + + pub fn clone() -> Option<Vec<Vec<u8>>> { + let _guard = LOCK.lock(); + unsafe { + let ptr = get_global_ptr(); + (*ptr).as_ref().map(|s: &Box<Vec<Vec<u8>>>| (**s).clone()) + } + } + + fn get_global_ptr() -> *mut Option<Box<Vec<Vec<u8>>>> { + unsafe { mem::transmute(&GLOBAL_ARGS_PTR) } + } + + unsafe fn load_argc_and_argv(argc: int, argv: *const *const u8) -> Vec<Vec<u8>> { + Vec::from_fn(argc as uint, |i| { + let arg = *argv.offset(i as int); + let mut len = 0u; + while *arg.offset(len as int) != 0 { + len += 1u; + } + slice::from_raw_buf(&arg, len).to_vec() + }) + } + + #[cfg(test)] + mod tests { + use prelude::*; + use finally::Finally; + + use super::*; + + #[test] + fn smoke_test() { + // Preserve the actual global state. + let saved_value = take(); + + let expected = vec![ + b"happy".to_vec(), + b"today?".to_vec(), + ]; + + put(expected.clone()); + assert!(clone() == Some(expected.clone())); + assert!(take() == Some(expected.clone())); + assert!(take() == None); + + (|&mut:| { + }).finally(|| { + // Restore the actual global state. + match saved_value { + Some(ref args) => put(args.clone()), + None => () + } + }) + } + } +} + +#[cfg(any(target_os = "macos", + target_os = "ios", + target_os = "windows"))] +mod imp { + use core::prelude::*; + use vec::Vec; + + pub unsafe fn init(_argc: int, _argv: *const *const u8) { + } + + pub fn cleanup() { + } + + pub fn take() -> Option<Vec<Vec<u8>>> { + panic!() + } + + pub fn put(_args: Vec<Vec<u8>>) { + panic!() + } + + pub fn clone() -> Option<Vec<Vec<u8>>> { + panic!() + } +} diff --git a/src/libstd/rt/at_exit_imp.rs b/src/libstd/rt/at_exit_imp.rs new file mode 100644 index 00000000000..5823f8453d8 --- /dev/null +++ b/src/libstd/rt/at_exit_imp.rs @@ -0,0 +1,75 @@ +// 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. + +//! Implementation of running at_exit routines +//! +//! Documentation can be found on the `rt::at_exit` function. + +use core::prelude::*; + +use boxed::Box; +use vec::Vec; +use mem; +use thunk::Thunk; +use sys_common::mutex::{Mutex, MUTEX_INIT}; + +type Queue = Vec<Thunk>; + +// NB these are specifically not types from `std::sync` as they currently rely +// on poisoning and this module needs to operate at a lower level than requiring +// the thread infrastructure to be in place (useful on the borders of +// initialization/destruction). +static LOCK: Mutex = MUTEX_INIT; +static mut QUEUE: *mut Queue = 0 as *mut Queue; + +unsafe fn init() { + if QUEUE.is_null() { + let state: Box<Queue> = box Vec::new(); + QUEUE = mem::transmute(state); + } else { + // can't re-init after a cleanup + rtassert!(QUEUE as uint != 1); + } + + // FIXME: switch this to use atexit as below. Currently this + // segfaults (the queue's memory is mysteriously gone), so + // instead the cleanup is tied to the `std::rt` entry point. + // + // ::libc::atexit(cleanup); +} + +pub fn cleanup() { + unsafe { + LOCK.lock(); + let queue = QUEUE; + QUEUE = 1 as *mut _; + LOCK.unlock(); + + // make sure we're not recursively cleaning up + rtassert!(queue as uint != 1); + + // If we never called init, not need to cleanup! + if queue as uint != 0 { + let queue: Box<Queue> = mem::transmute(queue); + for to_run in queue.into_iter() { + to_run.invoke(()); + } + } + } +} + +pub fn push(f: Thunk) { + unsafe { + LOCK.lock(); + init(); + (*QUEUE).push(f); + LOCK.unlock(); + } +} diff --git a/src/libstd/rt/backtrace.rs b/src/libstd/rt/backtrace.rs index 81022994387..775e9bb526f 100644 --- a/src/libstd/rt/backtrace.rs +++ b/src/libstd/rt/backtrace.rs @@ -12,16 +12,12 @@ #![allow(non_camel_case_types)] -use io::{IoResult, Writer}; -use iter::Iterator; -use option::{Some, None}; +use prelude::*; + use os; -use result::{Ok, Err}; -use str::{StrPrelude, from_str}; use sync::atomic; -use unicode::char::UnicodeChar; -pub use self::imp::write; +pub use sys::backtrace::write; // For now logging is turned off by default, and this function checks to see // whether the magical environment variable is present to see if it's turned on. @@ -41,979 +37,15 @@ pub fn log_enabled() -> bool { val == 2 } -#[cfg(target_word_size = "64")] const HEX_WIDTH: uint = 18; -#[cfg(target_word_size = "32")] const HEX_WIDTH: uint = 10; - -// All rust symbols are in theory lists of "::"-separated identifiers. Some -// assemblers, however, can't handle these characters in symbol names. To get -// around this, we use C++-style mangling. The mangling method is: -// -// 1. Prefix the symbol with "_ZN" -// 2. For each element of the path, emit the length plus the element -// 3. End the path with "E" -// -// For example, "_ZN4testE" => "test" and "_ZN3foo3bar" => "foo::bar". -// -// We're the ones printing our backtraces, so we can't rely on anything else to -// demangle our symbols. It's *much* nicer to look at demangled symbols, so -// this function is implemented to give us nice pretty output. -// -// Note that this demangler isn't quite as fancy as it could be. We have lots -// of other information in our symbols like hashes, version, type information, -// etc. Additionally, this doesn't handle glue symbols at all. -fn demangle(writer: &mut Writer, s: &str) -> IoResult<()> { - // First validate the symbol. If it doesn't look like anything we're - // expecting, we just print it literally. Note that we must handle non-rust - // symbols because we could have any function in the backtrace. - let mut valid = true; - if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") { - let mut chars = s.slice(3, s.len() - 1).chars(); - while valid { - let mut i = 0; - for c in chars { - if c.is_numeric() { - i = i * 10 + c as uint - '0' as uint; - } else { - break - } - } - if i == 0 { - valid = chars.next().is_none(); - break - } else if chars.by_ref().take(i - 1).count() != i - 1 { - valid = false; - } - } - } else { - valid = false; - } - - // Alright, let's do this. - if !valid { - try!(writer.write_str(s)); - } else { - let mut s = s.slice_from(3); - let mut first = true; - while s.len() > 1 { - if !first { - try!(writer.write_str("::")); - } else { - first = false; - } - let mut rest = s; - while rest.char_at(0).is_numeric() { - rest = rest.slice_from(1); - } - let i: uint = from_str(s.slice_to(s.len() - rest.len())).unwrap(); - s = rest.slice_from(i); - rest = rest.slice_to(i); - while rest.len() > 0 { - if rest.starts_with("$") { - macro_rules! demangle( - ($($pat:expr => $demangled:expr),*) => ({ - $(if rest.starts_with($pat) { - try!(writer.write_str($demangled)); - rest = rest.slice_from($pat.len()); - } else)* - { - try!(writer.write_str(rest)); - break; - } - - }) - ) - // see src/librustc/back/link.rs for these mappings - demangle! ( - "$SP$" => "@", - "$UP$" => "Box", - "$RP$" => "*", - "$BP$" => "&", - "$LT$" => "<", - "$GT$" => ">", - "$LP$" => "(", - "$RP$" => ")", - "$C$" => ",", - - // in theory we can demangle any Unicode code point, but - // for simplicity we just catch the common ones. - "$x20" => " ", - "$x27" => "'", - "$x5b" => "[", - "$x5d" => "]" - ) - } else { - let idx = match rest.find('$') { - None => rest.len(), - Some(i) => i, - }; - try!(writer.write_str(rest.slice_to(idx))); - rest = rest.slice_from(idx); - } - } - } - } - - Ok(()) -} - -/// Backtrace support built on libgcc with some extra OS-specific support -/// -/// Some methods of getting a backtrace: -/// -/// * The backtrace() functions on unix. It turns out this doesn't work very -/// well for green threads on OSX, and the address to symbol portion of it -/// suffers problems that are described below. -/// -/// * Using libunwind. This is more difficult than it sounds because libunwind -/// isn't installed everywhere by default. It's also a bit of a hefty library, -/// so possibly not the best option. When testing, libunwind was excellent at -/// getting both accurate backtraces and accurate symbols across platforms. -/// This route was not chosen in favor of the next option, however. -/// -/// * We're already using libgcc_s for exceptions in rust (triggering task -/// unwinding and running destructors on the stack), and it turns out that it -/// conveniently comes with a function that also gives us a backtrace. All of -/// these functions look like _Unwind_*, but it's not quite the full -/// repertoire of the libunwind API. Due to it already being in use, this was -/// the chosen route of getting a backtrace. -/// -/// After choosing libgcc_s for backtraces, the sad part is that it will only -/// give us a stack trace of instruction pointers. Thankfully these instruction -/// pointers are accurate (they work for green and native threads), but it's -/// then up to us again to figure out how to translate these addresses to -/// symbols. As with before, we have a few options. Before, that, a little bit -/// of an interlude about symbols. This is my very limited knowledge about -/// symbol tables, and this information is likely slightly wrong, but the -/// general idea should be correct. -/// -/// When talking about symbols, it's helpful to know a few things about where -/// symbols are located. Some symbols are located in the dynamic symbol table -/// of the executable which in theory means that they're available for dynamic -/// linking and lookup. Other symbols end up only in the local symbol table of -/// the file. This loosely corresponds to pub and priv functions in Rust. -/// -/// Armed with this knowledge, we know that our solution for address to symbol -/// translation will need to consult both the local and dynamic symbol tables. -/// With that in mind, here's our options of translating an address to -/// a symbol. -/// -/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr() -/// behind the scenes to translate, and this is why backtrace() was not used. -/// Conveniently, this method works fantastically on OSX. It appears dladdr() -/// uses magic to consult the local symbol table, or we're putting everything -/// in the dynamic symbol table anyway. Regardless, for OSX, this is the -/// method used for translation. It's provided by the system and easy to do.o -/// -/// Sadly, all other systems have a dladdr() implementation that does not -/// consult the local symbol table. This means that most functions are blank -/// because they don't have symbols. This means that we need another solution. -/// -/// * Use unw_get_proc_name(). This is part of the libunwind api (not the -/// libgcc_s version of the libunwind api), but involves taking a dependency -/// to libunwind. We may pursue this route in the future if we bundle -/// libunwind, but libunwind was unwieldy enough that it was not chosen at -/// this time to provide this functionality. -/// -/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a -/// semi-reasonable solution. The stdlib already knows how to spawn processes, -/// so in theory it could invoke readelf, parse the output, and consult the -/// local/dynamic symbol tables from there. This ended up not getting chosen -/// due to the craziness of the idea plus the advent of the next option. -/// -/// * Use `libbacktrace`. It turns out that this is a small library bundled in -/// the gcc repository which provides backtrace and symbol translation -/// functionality. All we really need from it is the backtrace functionality, -/// and we only really need this on everything that's not OSX, so this is the -/// chosen route for now. -/// -/// In summary, the current situation uses libgcc_s to get a trace of stack -/// pointers, and we use dladdr() or libbacktrace to translate these addresses -/// to symbols. This is a bit of a hokey implementation as-is, but it works for -/// all unix platforms we support right now, so it at least gets the job done. -#[cfg(unix)] -mod imp { - use c_str::CString; - use io::{IoResult, Writer}; - use libc; - use mem; - use option::{Some, None, Option}; - use result::{Ok, Err}; - use rustrt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; - - /// As always - iOS on arm uses SjLj exceptions and - /// _Unwind_Backtrace is even not available there. Still, - /// backtraces could be extracted using a backtrace function, - /// which thanks god is public - /// - /// As mentioned in a huge comment block above, backtrace doesn't - /// play well with green threads, so while it is extremely nice - /// and simple to use it should be used only on iOS devices as the - /// only viable option. - #[cfg(all(target_os = "ios", target_arch = "arm"))] - #[inline(never)] - pub fn write(w: &mut Writer) -> IoResult<()> { - use iter::{Iterator, range}; - use result; - use slice::{SlicePrelude}; - - extern { - fn backtrace(buf: *mut *mut libc::c_void, - sz: libc::c_int) -> libc::c_int; - } - - // while it doesn't requires lock for work as everything is - // local, it still displays much nicer backtraces when a - // couple of tasks panic simultaneously - static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; - let _g = unsafe { LOCK.lock() }; - - try!(writeln!(w, "stack backtrace:")); - // 100 lines should be enough - const SIZE: uint = 100; - let mut buf: [*mut libc::c_void, ..SIZE] = unsafe {mem::zeroed()}; - let cnt = unsafe { backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as uint}; - - // skipping the first one as it is write itself - let iter = range(1, cnt).map(|i| { - print(w, i as int, buf[i]) - }); - result::fold(iter, (), |_, _| ()) - } - - #[cfg(not(all(target_os = "ios", target_arch = "arm")))] - #[inline(never)] // if we know this is a function call, we can skip it when - // tracing - pub fn write(w: &mut Writer) -> IoResult<()> { - use io::IoError; - - struct Context<'a> { - idx: int, - writer: &'a mut Writer+'a, - last_error: Option<IoError>, - } - - // When using libbacktrace, we use some necessary global state, so we - // need to prevent more than one thread from entering this block. This - // is semi-reasonable in terms of printing anyway, and we know that all - // I/O done here is blocking I/O, not green I/O, so we don't have to - // worry about this being a native vs green mutex. - static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; - let _g = unsafe { LOCK.lock() }; - - try!(writeln!(w, "stack backtrace:")); - - let mut cx = Context { writer: w, last_error: None, idx: 0 }; - return match unsafe { - uw::_Unwind_Backtrace(trace_fn, - &mut cx as *mut Context as *mut libc::c_void) - } { - uw::_URC_NO_REASON => { - match cx.last_error { - Some(err) => Err(err), - None => Ok(()) - } - } - _ => Ok(()), - }; - - extern fn trace_fn(ctx: *mut uw::_Unwind_Context, - arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { - let cx: &mut Context = unsafe { mem::transmute(arg) }; - let ip = unsafe { uw::_Unwind_GetIP(ctx) as *mut libc::c_void }; - // dladdr() on osx gets whiny when we use FindEnclosingFunction, and - // it appears to work fine without it, so we only use - // FindEnclosingFunction on non-osx platforms. In doing so, we get a - // slightly more accurate stack trace in the process. - // - // This is often because panic involves the last instruction of a - // function being "call std::rt::begin_unwind", with no ret - // instructions after it. This means that the return instruction - // pointer points *outside* of the calling function, and by - // unwinding it we go back to the original function. - let ip = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { - ip - } else { - unsafe { uw::_Unwind_FindEnclosingFunction(ip) } - }; - - // Don't print out the first few frames (they're not user frames) - cx.idx += 1; - if cx.idx <= 0 { return uw::_URC_NO_REASON } - // Don't print ginormous backtraces - if cx.idx > 100 { - match write!(cx.writer, " ... <frames omitted>\n") { - Ok(()) => {} - Err(e) => { cx.last_error = Some(e); } - } - return uw::_URC_FAILURE - } - - // Once we hit an error, stop trying to print more frames - if cx.last_error.is_some() { return uw::_URC_FAILURE } - - match print(cx.writer, cx.idx, ip) { - Ok(()) => {} - Err(e) => { cx.last_error = Some(e); } - } - - // keep going - return uw::_URC_NO_REASON - } - } - - #[cfg(any(target_os = "macos", target_os = "ios"))] - fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { - use intrinsics; - #[repr(C)] - struct Dl_info { - dli_fname: *const libc::c_char, - dli_fbase: *mut libc::c_void, - dli_sname: *const libc::c_char, - dli_saddr: *mut libc::c_void, - } - extern { - fn dladdr(addr: *const libc::c_void, - info: *mut Dl_info) -> libc::c_int; - } - - let mut info: Dl_info = unsafe { intrinsics::init() }; - if unsafe { dladdr(addr as *const libc::c_void, &mut info) == 0 } { - output(w, idx,addr, None) - } else { - output(w, idx, addr, Some(unsafe { - CString::new(info.dli_sname, false) - })) - } - } - - #[cfg(not(any(target_os = "macos", target_os = "ios")))] - fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { - use iter::Iterator; - use os; - use path::GenericPath; - use ptr::RawPtr; - use ptr; - use slice::{SlicePrelude}; - - //////////////////////////////////////////////////////////////////////// - // libbacktrace.h API - //////////////////////////////////////////////////////////////////////// - type backtrace_syminfo_callback = - extern "C" fn(data: *mut libc::c_void, - pc: libc::uintptr_t, - symname: *const libc::c_char, - symval: libc::uintptr_t, - symsize: libc::uintptr_t); - type backtrace_error_callback = - extern "C" fn(data: *mut libc::c_void, - msg: *const libc::c_char, - errnum: libc::c_int); - enum backtrace_state {} - #[link(name = "backtrace", kind = "static")] - #[cfg(not(test))] - extern {} - - extern { - fn backtrace_create_state(filename: *const libc::c_char, - threaded: libc::c_int, - error: backtrace_error_callback, - data: *mut libc::c_void) - -> *mut backtrace_state; - fn backtrace_syminfo(state: *mut backtrace_state, - addr: libc::uintptr_t, - cb: backtrace_syminfo_callback, - error: backtrace_error_callback, - data: *mut libc::c_void) -> libc::c_int; - } - - //////////////////////////////////////////////////////////////////////// - // helper callbacks - //////////////////////////////////////////////////////////////////////// - - extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char, - _errnum: libc::c_int) { - // do nothing for now - } - extern fn syminfo_cb(data: *mut libc::c_void, - _pc: libc::uintptr_t, - symname: *const libc::c_char, - _symval: libc::uintptr_t, - _symsize: libc::uintptr_t) { - let slot = data as *mut *const libc::c_char; - unsafe { *slot = symname; } - } - - // The libbacktrace API supports creating a state, but it does not - // support destroying a state. I personally take this to mean that a - // state is meant to be created and then live forever. - // - // I would love to register an at_exit() handler which cleans up this - // state, but libbacktrace provides no way to do so. - // - // With these constraints, this function has a statically cached state - // that is calculated the first time this is requested. Remember that - // backtracing all happens serially (one global lock). - // - // An additionally oddity in this function is that we initialize the - // filename via self_exe_name() to pass to libbacktrace. It turns out - // that on Linux libbacktrace seamlessly gets the filename of the - // current executable, but this fails on freebsd. by always providing - // it, we make sure that libbacktrace never has a reason to not look up - // the symbols. The libbacktrace API also states that the filename must - // be in "permanent memory", so we copy it to a static and then use the - // static as the pointer. - // - // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't - // tested if this is required or not. - unsafe fn init_state() -> *mut backtrace_state { - static mut STATE: *mut backtrace_state = 0 as *mut backtrace_state; - static mut LAST_FILENAME: [libc::c_char, ..256] = [0, ..256]; - if !STATE.is_null() { return STATE } - let selfname = if cfg!(target_os = "freebsd") || - cfg!(target_os = "dragonfly") { - os::self_exe_name() - } else { - None - }; - let filename = match selfname { - Some(path) => { - let bytes = path.as_vec(); - if bytes.len() < LAST_FILENAME.len() { - let i = bytes.iter(); - for (slot, val) in LAST_FILENAME.iter_mut().zip(i) { - *slot = *val as libc::c_char; - } - LAST_FILENAME.as_ptr() - } else { - ptr::null() - } - } - None => ptr::null(), - }; - STATE = backtrace_create_state(filename, 0, error_cb, - ptr::null_mut()); - return STATE - } - - //////////////////////////////////////////////////////////////////////// - // translation - //////////////////////////////////////////////////////////////////////// - - // backtrace errors are currently swept under the rug, only I/O - // errors are reported - let state = unsafe { init_state() }; - if state.is_null() { - return output(w, idx, addr, None) - } - let mut data = 0 as *const libc::c_char; - let data_addr = &mut data as *mut *const libc::c_char; - let ret = unsafe { - backtrace_syminfo(state, addr as libc::uintptr_t, - syminfo_cb, error_cb, - data_addr as *mut libc::c_void) - }; - if ret == 0 || data.is_null() { - output(w, idx, addr, None) - } else { - output(w, idx, addr, Some(unsafe { CString::new(data, false) })) - } - } - - // Finally, after all that work above, we can emit a symbol. - fn output(w: &mut Writer, idx: int, addr: *mut libc::c_void, - s: Option<CString>) -> IoResult<()> { - try!(write!(w, " {:2}: {:2$} - ", idx, addr, super::HEX_WIDTH)); - match s.as_ref().and_then(|c| c.as_str()) { - Some(string) => try!(super::demangle(w, string)), - None => try!(write!(w, "<unknown>")), - } - w.write(&['\n' as u8]) - } - - /// Unwind library interface used for backtraces - /// - /// Note that the native libraries come from librustrt, not this - /// module. - /// Note that dead code is allowed as here are just bindings - /// iOS doesn't use all of them it but adding more - /// platform-specific configs pollutes the code too much - #[allow(non_camel_case_types)] - #[allow(non_snake_case)] - #[allow(dead_code)] - mod uw { - pub use self::_Unwind_Reason_Code::*; - - use libc; - - #[repr(C)] - pub enum _Unwind_Reason_Code { - _URC_NO_REASON = 0, - _URC_FOREIGN_EXCEPTION_CAUGHT = 1, - _URC_FATAL_PHASE2_ERROR = 2, - _URC_FATAL_PHASE1_ERROR = 3, - _URC_NORMAL_STOP = 4, - _URC_END_OF_STACK = 5, - _URC_HANDLER_FOUND = 6, - _URC_INSTALL_CONTEXT = 7, - _URC_CONTINUE_UNWIND = 8, - _URC_FAILURE = 9, // used only by ARM EABI - } - - pub enum _Unwind_Context {} - - pub type _Unwind_Trace_Fn = - extern fn(ctx: *mut _Unwind_Context, - arg: *mut libc::c_void) -> _Unwind_Reason_Code; - - extern { - // No native _Unwind_Backtrace on iOS - #[cfg(not(all(target_os = "ios", target_arch = "arm")))] - pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, - trace_argument: *mut libc::c_void) - -> _Unwind_Reason_Code; - - #[cfg(all(not(target_os = "android"), - not(all(target_os = "linux", target_arch = "arm"))))] - pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t; - - #[cfg(all(not(target_os = "android"), - not(all(target_os = "linux", target_arch = "arm"))))] - pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) - -> *mut libc::c_void; - } - - // On android, the function _Unwind_GetIP is a macro, and this is the - // expansion of the macro. This is all copy/pasted directly from the - // header file with the definition of _Unwind_GetIP. - #[cfg(any(target_os = "android", - all(target_os = "linux", target_arch = "arm")))] - pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { - #[repr(C)] - enum _Unwind_VRS_Result { - _UVRSR_OK = 0, - _UVRSR_NOT_IMPLEMENTED = 1, - _UVRSR_FAILED = 2, - } - #[repr(C)] - enum _Unwind_VRS_RegClass { - _UVRSC_CORE = 0, - _UVRSC_VFP = 1, - _UVRSC_FPA = 2, - _UVRSC_WMMXD = 3, - _UVRSC_WMMXC = 4, - } - #[repr(C)] - enum _Unwind_VRS_DataRepresentation { - _UVRSD_UINT32 = 0, - _UVRSD_VFPX = 1, - _UVRSD_FPAX = 2, - _UVRSD_UINT64 = 3, - _UVRSD_FLOAT = 4, - _UVRSD_DOUBLE = 5, - } - - type _Unwind_Word = libc::c_uint; - extern { - fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context, - klass: _Unwind_VRS_RegClass, - word: _Unwind_Word, - repr: _Unwind_VRS_DataRepresentation, - data: *mut libc::c_void) - -> _Unwind_VRS_Result; - } - - let mut val: _Unwind_Word = 0; - let ptr = &mut val as *mut _Unwind_Word; - let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15, - _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, - ptr as *mut libc::c_void); - (val & !1) as libc::uintptr_t - } - - // This function also doesn't exist on Android or ARM/Linux, so make it - // a no-op - #[cfg(any(target_os = "android", - all(target_os = "linux", target_arch = "arm")))] - pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) - -> *mut libc::c_void - { - pc - } - } -} - -/// As always, windows has something very different than unix, we mainly want -/// to avoid having to depend too much on libunwind for windows. -/// -/// If you google around, you'll find a fair bit of references to built-in -/// functions to get backtraces on windows. It turns out that most of these are -/// in an external library called dbghelp. I was unable to find this library -/// via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent -/// of it. -/// -/// You'll also find that there's a function called CaptureStackBackTrace -/// mentioned frequently (which is also easy to use), but sadly I didn't have a -/// copy of that function in my mingw install (maybe it was broken?). Instead, -/// this takes the route of using StackWalk64 in order to walk the stack. -#[cfg(windows)] -#[allow(dead_code, non_snake_case)] -mod imp { - use c_str::CString; - use intrinsics; - use io::{IoResult, Writer}; - use libc; - use mem; - use ops::Drop; - use option::{Some, None}; - use path::Path; - use result::{Ok, Err}; - use rustrt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT}; - use slice::SlicePrelude; - use str::StrPrelude; - use dynamic_lib::DynamicLibrary; - - #[allow(non_snake_case)] - extern "system" { - fn GetCurrentProcess() -> libc::HANDLE; - fn GetCurrentThread() -> libc::HANDLE; - fn RtlCaptureContext(ctx: *mut arch::CONTEXT); - } - - type SymFromAddrFn = - extern "system" fn(libc::HANDLE, u64, *mut u64, - *mut SYMBOL_INFO) -> libc::BOOL; - type SymInitializeFn = - extern "system" fn(libc::HANDLE, *mut libc::c_void, - libc::BOOL) -> libc::BOOL; - type SymCleanupFn = - extern "system" fn(libc::HANDLE) -> libc::BOOL; - - type StackWalk64Fn = - extern "system" fn(libc::DWORD, libc::HANDLE, libc::HANDLE, - *mut STACKFRAME64, *mut arch::CONTEXT, - *mut libc::c_void, *mut libc::c_void, - *mut libc::c_void, *mut libc::c_void) -> libc::BOOL; - - const MAX_SYM_NAME: uint = 2000; - const IMAGE_FILE_MACHINE_I386: libc::DWORD = 0x014c; - const IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200; - const IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664; - - #[repr(C)] - struct SYMBOL_INFO { - SizeOfStruct: libc::c_ulong, - TypeIndex: libc::c_ulong, - Reserved: [u64, ..2], - Index: libc::c_ulong, - Size: libc::c_ulong, - ModBase: u64, - Flags: libc::c_ulong, - Value: u64, - Address: u64, - Register: libc::c_ulong, - Scope: libc::c_ulong, - Tag: libc::c_ulong, - NameLen: libc::c_ulong, - MaxNameLen: libc::c_ulong, - // note that windows has this as 1, but it basically just means that - // the name is inline at the end of the struct. For us, we just bump - // the struct size up to MAX_SYM_NAME. - Name: [libc::c_char, ..MAX_SYM_NAME], - } - - - #[repr(C)] - enum ADDRESS_MODE { - AddrMode1616, - AddrMode1632, - AddrModeReal, - AddrModeFlat, - } - - struct ADDRESS64 { - Offset: u64, - Segment: u16, - Mode: ADDRESS_MODE, - } - - struct STACKFRAME64 { - AddrPC: ADDRESS64, - AddrReturn: ADDRESS64, - AddrFrame: ADDRESS64, - AddrStack: ADDRESS64, - AddrBStore: ADDRESS64, - FuncTableEntry: *mut libc::c_void, - Params: [u64, ..4], - Far: libc::BOOL, - Virtual: libc::BOOL, - Reserved: [u64, ..3], - KdHelp: KDHELP64, - } - - struct KDHELP64 { - Thread: u64, - ThCallbackStack: libc::DWORD, - ThCallbackBStore: libc::DWORD, - NextCallback: libc::DWORD, - FramePointer: libc::DWORD, - KiCallUserMode: u64, - KeUserCallbackDispatcher: u64, - SystemRangeStart: u64, - KiUserExceptionDispatcher: u64, - StackBase: u64, - StackLimit: u64, - Reserved: [u64, ..5], - } - - #[cfg(target_arch = "x86")] - mod arch { - use libc; - - const MAXIMUM_SUPPORTED_EXTENSION: uint = 512; - - #[repr(C)] - pub struct CONTEXT { - ContextFlags: libc::DWORD, - Dr0: libc::DWORD, - Dr1: libc::DWORD, - Dr2: libc::DWORD, - Dr3: libc::DWORD, - Dr6: libc::DWORD, - Dr7: libc::DWORD, - FloatSave: FLOATING_SAVE_AREA, - SegGs: libc::DWORD, - SegFs: libc::DWORD, - SegEs: libc::DWORD, - SegDs: libc::DWORD, - Edi: libc::DWORD, - Esi: libc::DWORD, - Ebx: libc::DWORD, - Edx: libc::DWORD, - Ecx: libc::DWORD, - Eax: libc::DWORD, - Ebp: libc::DWORD, - Eip: libc::DWORD, - SegCs: libc::DWORD, - EFlags: libc::DWORD, - Esp: libc::DWORD, - SegSs: libc::DWORD, - ExtendedRegisters: [u8, ..MAXIMUM_SUPPORTED_EXTENSION], - } - - #[repr(C)] - pub struct FLOATING_SAVE_AREA { - ControlWord: libc::DWORD, - StatusWord: libc::DWORD, - TagWord: libc::DWORD, - ErrorOffset: libc::DWORD, - ErrorSelector: libc::DWORD, - DataOffset: libc::DWORD, - DataSelector: libc::DWORD, - RegisterArea: [u8, ..80], - Cr0NpxState: libc::DWORD, - } - - pub fn init_frame(frame: &mut super::STACKFRAME64, - ctx: &CONTEXT) -> libc::DWORD { - frame.AddrPC.Offset = ctx.Eip as u64; - frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; - frame.AddrStack.Offset = ctx.Esp as u64; - frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; - frame.AddrFrame.Offset = ctx.Ebp as u64; - frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; - super::IMAGE_FILE_MACHINE_I386 - } - } - - #[cfg(target_arch = "x86_64")] - mod arch { - use libc::{c_longlong, c_ulonglong}; - use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG}; - use simd; - - #[repr(C)] - pub struct CONTEXT { - _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte - P1Home: DWORDLONG, - P2Home: DWORDLONG, - P3Home: DWORDLONG, - P4Home: DWORDLONG, - P5Home: DWORDLONG, - P6Home: DWORDLONG, - - ContextFlags: DWORD, - MxCsr: DWORD, - - SegCs: WORD, - SegDs: WORD, - SegEs: WORD, - SegFs: WORD, - SegGs: WORD, - SegSs: WORD, - EFlags: DWORD, - - Dr0: DWORDLONG, - Dr1: DWORDLONG, - Dr2: DWORDLONG, - Dr3: DWORDLONG, - Dr6: DWORDLONG, - Dr7: DWORDLONG, - - Rax: DWORDLONG, - Rcx: DWORDLONG, - Rdx: DWORDLONG, - Rbx: DWORDLONG, - Rsp: DWORDLONG, - Rbp: DWORDLONG, - Rsi: DWORDLONG, - Rdi: DWORDLONG, - R8: DWORDLONG, - R9: DWORDLONG, - R10: DWORDLONG, - R11: DWORDLONG, - R12: DWORDLONG, - R13: DWORDLONG, - R14: DWORDLONG, - R15: DWORDLONG, - - Rip: DWORDLONG, - - FltSave: FLOATING_SAVE_AREA, - - VectorRegister: [M128A, .. 26], - VectorControl: DWORDLONG, - - DebugControl: DWORDLONG, - LastBranchToRip: DWORDLONG, - LastBranchFromRip: DWORDLONG, - LastExceptionToRip: DWORDLONG, - LastExceptionFromRip: DWORDLONG, - } - - #[repr(C)] - pub struct M128A { - _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte - Low: c_ulonglong, - High: c_longlong - } - - #[repr(C)] - pub struct FLOATING_SAVE_AREA { - _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte - _Dummy: [u8, ..512] // FIXME: Fill this out - } - - pub fn init_frame(frame: &mut super::STACKFRAME64, - ctx: &CONTEXT) -> DWORD { - frame.AddrPC.Offset = ctx.Rip as u64; - frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; - frame.AddrStack.Offset = ctx.Rsp as u64; - frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; - frame.AddrFrame.Offset = ctx.Rbp as u64; - frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; - super::IMAGE_FILE_MACHINE_AMD64 - } - } - - #[repr(C)] - struct Cleanup { - handle: libc::HANDLE, - SymCleanup: SymCleanupFn, - } - - impl Drop for Cleanup { - fn drop(&mut self) { (self.SymCleanup)(self.handle); } - } - - pub fn write(w: &mut Writer) -> IoResult<()> { - // According to windows documentation, all dbghelp functions are - // single-threaded. - static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT; - let _g = unsafe { LOCK.lock() }; - - // Open up dbghelp.dll, we don't link to it explicitly because it can't - // always be found. Additionally, it's nice having fewer dependencies. - let path = Path::new("dbghelp.dll"); - let lib = match DynamicLibrary::open(Some(&path)) { - Ok(lib) => lib, - Err(..) => return Ok(()), - }; - - macro_rules! sym( ($e:expr, $t:ident) => (unsafe { - match lib.symbol($e) { - Ok(f) => mem::transmute::<*mut u8, $t>(f), - Err(..) => return Ok(()) - } - }) ) - - // Fetch the symbols necessary from dbghelp.dll - let SymFromAddr = sym!("SymFromAddr", SymFromAddrFn); - let SymInitialize = sym!("SymInitialize", SymInitializeFn); - let SymCleanup = sym!("SymCleanup", SymCleanupFn); - let StackWalk64 = sym!("StackWalk64", StackWalk64Fn); - - // Allocate necessary structures for doing the stack walk - let process = unsafe { GetCurrentProcess() }; - let thread = unsafe { GetCurrentThread() }; - let mut context: arch::CONTEXT = unsafe { intrinsics::init() }; - unsafe { RtlCaptureContext(&mut context); } - let mut frame: STACKFRAME64 = unsafe { intrinsics::init() }; - let image = arch::init_frame(&mut frame, &context); - - // Initialize this process's symbols - let ret = SymInitialize(process, 0 as *mut libc::c_void, libc::TRUE); - if ret != libc::TRUE { return Ok(()) } - let _c = Cleanup { handle: process, SymCleanup: SymCleanup }; - - // And now that we're done with all the setup, do the stack walking! - let mut i = 0i; - try!(write!(w, "stack backtrace:\n")); - while StackWalk64(image, process, thread, &mut frame, &mut context, - 0 as *mut libc::c_void, - 0 as *mut libc::c_void, - 0 as *mut libc::c_void, - 0 as *mut libc::c_void) == libc::TRUE{ - let addr = frame.AddrPC.Offset; - if addr == frame.AddrReturn.Offset || addr == 0 || - frame.AddrReturn.Offset == 0 { break } - - i += 1; - try!(write!(w, " {:2}: {:#2$x}", i, addr, super::HEX_WIDTH)); - let mut info: SYMBOL_INFO = unsafe { intrinsics::init() }; - info.MaxNameLen = MAX_SYM_NAME as libc::c_ulong; - // the struct size in C. the value is different to - // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81) - // due to struct alignment. - info.SizeOfStruct = 88; - - let mut displacement = 0u64; - let ret = SymFromAddr(process, addr as u64, &mut displacement, - &mut info); - - if ret == libc::TRUE { - try!(write!(w, " - ")); - let cstr = unsafe { CString::new(info.Name.as_ptr(), false) }; - let bytes = cstr.as_bytes(); - match cstr.as_str() { - Some(s) => try!(super::demangle(w, s)), - None => try!(w.write(bytes[..bytes.len()-1])), - } - } - try!(w.write(&['\n' as u8])); - } - - Ok(()) - } -} - #[cfg(test)] mod test { use prelude::*; - macro_rules! t( ($a:expr, $b:expr) => ({ + use sys_common; + macro_rules! t { ($a:expr, $b:expr) => ({ let mut m = Vec::new(); - super::demangle(&mut m, $a).unwrap(); - assert_eq!(String::from_utf8(m).unwrap(), $b.to_string()); - }) ) + sys_common::backtrace::demangle(&mut m, $a).unwrap(); + assert_eq!(String::from_utf8(m).unwrap(), $b); + }) } #[test] fn demangle() { @@ -1036,4 +68,11 @@ mod test { t!("_ZN12test$x20test4foobE", "test test::foob"); t!("_ZN12test$UP$test4foobE", "testBoxtest::foob"); } + + #[test] + fn demangle_windows() { + t!("ZN4testE", "test"); + t!("ZN12test$x20test4foobE", "test test::foob"); + t!("ZN12test$UP$test4foobE", "testBoxtest::foob"); + } } diff --git a/src/libstd/rt/exclusive.rs b/src/libstd/rt/exclusive.rs new file mode 100644 index 00000000000..1d3082d1b4c --- /dev/null +++ b/src/libstd/rt/exclusive.rs @@ -0,0 +1,115 @@ +// 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 core::prelude::*; + +use cell::UnsafeCell; +use rt::mutex; + +/// An OS mutex over some data. +/// +/// This is not a safe primitive to use, it is unaware of the libgreen +/// scheduler, as well as being easily susceptible to misuse due to the usage of +/// the inner NativeMutex. +/// +/// > **Note**: This type is not recommended for general use. The mutex provided +/// > as part of `libsync` should almost always be favored. +pub struct Exclusive<T> { + lock: mutex::NativeMutex, + data: UnsafeCell<T>, +} + +/// An RAII guard returned via `lock` +pub struct ExclusiveGuard<'a, T:'a> { + // FIXME #12808: strange name to try to avoid interfering with + // field accesses of the contained type via Deref + _data: &'a mut T, + _guard: mutex::LockGuard<'a>, +} + +impl<T: Send> Exclusive<T> { + /// Creates a new `Exclusive` which will protect the data provided. + pub fn new(user_data: T) -> Exclusive<T> { + Exclusive { + lock: unsafe { mutex::NativeMutex::new() }, + data: UnsafeCell::new(user_data), + } + } + + /// Acquires this lock, returning a guard which the data is accessed through + /// and from which that lock will be unlocked. + /// + /// This method is unsafe due to many of the same reasons that the + /// NativeMutex itself is unsafe. + pub unsafe fn lock<'a>(&'a self) -> ExclusiveGuard<'a, T> { + let guard = self.lock.lock(); + let data = &mut *self.data.get(); + + ExclusiveGuard { + _data: data, + _guard: guard, + } + } +} + +impl<'a, T: Send> ExclusiveGuard<'a, T> { + // The unsafety here should be ok because our loan guarantees that the lock + // itself is not moving + pub fn signal(&self) { + unsafe { self._guard.signal() } + } + pub fn wait(&self) { + unsafe { self._guard.wait() } + } +} + +impl<'a, T: Send> Deref<T> for ExclusiveGuard<'a, T> { + fn deref(&self) -> &T { &*self._data } +} +impl<'a, T: Send> DerefMut<T> for ExclusiveGuard<'a, T> { + fn deref_mut(&mut self) -> &mut T { &mut *self._data } +} + +#[cfg(test)] +mod tests { + use prelude::*; + use sync::Arc; + use super::Exclusive; + use task; + + #[test] + fn exclusive_new_arc() { + unsafe { + let mut futures = Vec::new(); + + let num_tasks = 10; + let count = 10; + + let total = Arc::new(Exclusive::new(box 0)); + + for _ in range(0u, num_tasks) { + let total = total.clone(); + let (tx, rx) = channel(); + futures.push(rx); + + task::spawn(move || { + for _ in range(0u, count) { + **total.lock() += 1; + } + tx.send(()); + }); + }; + + for f in futures.iter_mut() { f.recv() } + + assert_eq!(**total.lock(), num_tasks * count); + } + } +} diff --git a/src/libstd/rt/libunwind.rs b/src/libstd/rt/libunwind.rs new file mode 100644 index 00000000000..2feea7fa0a4 --- /dev/null +++ b/src/libstd/rt/libunwind.rs @@ -0,0 +1,128 @@ +// Copyright 2014 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. + +//! Unwind library interface + +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(dead_code)] // these are just bindings + +#[cfg(any(not(target_arch = "arm"), target_os = "ios"))] +pub use self::_Unwind_Action::*; +#[cfg(target_arch = "arm")] +pub use self::_Unwind_State::*; +pub use self::_Unwind_Reason_Code::*; + +use libc; + +#[cfg(any(not(target_arch = "arm"), target_os = "ios"))] +#[repr(C)] +#[deriving(Copy)] +pub enum _Unwind_Action { + _UA_SEARCH_PHASE = 1, + _UA_CLEANUP_PHASE = 2, + _UA_HANDLER_FRAME = 4, + _UA_FORCE_UNWIND = 8, + _UA_END_OF_STACK = 16, +} + +#[cfg(target_arch = "arm")] +#[repr(C)] +pub enum _Unwind_State { + _US_VIRTUAL_UNWIND_FRAME = 0, + _US_UNWIND_FRAME_STARTING = 1, + _US_UNWIND_FRAME_RESUME = 2, + _US_ACTION_MASK = 3, + _US_FORCE_UNWIND = 8, + _US_END_OF_STACK = 16 +} + +#[repr(C)] +pub enum _Unwind_Reason_Code { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8, + _URC_FAILURE = 9, // used only by ARM EABI +} + +pub type _Unwind_Exception_Class = u64; + +pub type _Unwind_Word = libc::uintptr_t; + +#[cfg(target_arch = "x86")] +pub const unwinder_private_data_size: uint = 5; + +#[cfg(target_arch = "x86_64")] +pub const unwinder_private_data_size: uint = 6; + +#[cfg(all(target_arch = "arm", not(target_os = "ios")))] +pub const unwinder_private_data_size: uint = 20; + +#[cfg(all(target_arch = "arm", target_os = "ios"))] +pub const unwinder_private_data_size: uint = 5; + +#[cfg(any(target_arch = "mips", target_arch = "mipsel"))] +pub const unwinder_private_data_size: uint = 2; + +#[repr(C)] +pub struct _Unwind_Exception { + pub exception_class: _Unwind_Exception_Class, + pub exception_cleanup: _Unwind_Exception_Cleanup_Fn, + pub private: [_Unwind_Word, ..unwinder_private_data_size], +} + +pub enum _Unwind_Context {} + +pub type _Unwind_Exception_Cleanup_Fn = + extern "C" fn(unwind_code: _Unwind_Reason_Code, + exception: *mut _Unwind_Exception); + +#[cfg(any(target_os = "linux", target_os = "freebsd"))] +#[link(name = "gcc_s")] +extern {} + +#[cfg(target_os = "android")] +#[link(name = "gcc")] +extern {} + +#[cfg(target_os = "dragonfly")] +#[link(name = "gcc_pic")] +extern {} + +extern "C" { + // iOS on armv7 uses SjLj exceptions and requires to link + // against corresponding routine (..._SjLj_...) + #[cfg(not(all(target_os = "ios", target_arch = "arm")))] + pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) + -> _Unwind_Reason_Code; + + #[cfg(all(target_os = "ios", target_arch = "arm"))] + fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) + -> _Unwind_Reason_Code; + + pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception); +} + +// ... and now we just providing access to SjLj counterspart +// through a standard name to hide those details from others +// (see also comment above regarding _Unwind_RaiseException) +#[cfg(all(target_os = "ios", target_arch = "arm"))] +#[inline(always)] +pub unsafe fn _Unwind_RaiseException(exc: *mut _Unwind_Exception) + -> _Unwind_Reason_Code { + _Unwind_SjLj_RaiseException(exc) +} diff --git a/src/libstd/rt/macros.rs b/src/libstd/rt/macros.rs new file mode 100644 index 00000000000..bee8b5b82f4 --- /dev/null +++ b/src/libstd/rt/macros.rs @@ -0,0 +1,45 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Macros used by the runtime. +//! +//! These macros call functions which are only accessible in the `rt` module, so +//! they aren't defined anywhere outside of the `rt` module. + +#![macro_escape] + +macro_rules! rterrln { + ($fmt:expr $($arg:tt)*) => ( { + format_args!(::rt::util::dumb_print, concat!($fmt, "\n") $($arg)*) + } ) +} + +// Some basic logging. Enabled by passing `--cfg rtdebug` to the libstd build. +macro_rules! rtdebug { + ($($arg:tt)*) => ( { + if cfg!(rtdebug) { + rterrln!($($arg)*) + } + }) +} + +macro_rules! rtassert { + ( $arg:expr ) => ( { + if ::rt::util::ENFORCE_SANITY { + if !$arg { + rtabort!(" assertion failed: {}", stringify!($arg)); + } + } + } ) +} + +macro_rules! rtabort { + ($($arg:tt)*) => (format_args!(::rt::util::abort, $($arg)*)) +} diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 21b4edb6375..d64336569c6 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -8,46 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! Runtime services, including the task scheduler and I/O dispatcher - -The `rt` module provides the private runtime infrastructure necessary -to support core language features like the exchange and local heap, -logging, local data and unwinding. It also implements the default task -scheduler and task model. Initialization routines are provided for setting -up runtime resources in common configurations, including that used by -`rustc` when generating executables. - -It is intended that the features provided by `rt` can be factored in a -way such that the core library can be built with different 'profiles' -for different use cases, e.g. excluding the task scheduler. A number -of runtime features though are critical to the functioning of the -language and an implementation must be provided regardless of the -execution environment. - -Of foremost importance is the global exchange heap, in the module -`heap`. Very little practical Rust code can be written without -access to the global heap. Unlike most of `rt` the global heap is -truly a global resource and generally operates independently of the -rest of the runtime. - -All other runtime features are task-local, including the local heap, -local storage, logging and the stack unwinder. - -The relationship between `rt` and the rest of the core library is -not entirely clear yet and some modules will be moving into or -out of `rt` as development proceeds. - -Several modules in `core` are clients of `rt`: - -* `std::task` - The user-facing interface to the Rust task model. -* `std::local_data` - The interface to local data. -* `std::unstable::lang` - Miscellaneous lang items, some of which rely on `std::rt`. -* `std::cleanup` - Local heap destruction. -* `std::io` - In the future `std::io` will use an `rt` implementation. -* `std::logging` -* `std::comm` - -*/ +//! Runtime services +//! +//! The `rt` module provides a narrow set of runtime services, +//! including the global heap (exported in `heap`) and unwinding and +//! backtrace support. The APIs in this module are highly unstable, +//! and should be considered as private implementation details for the +//! time being. #![experimental] @@ -56,64 +23,51 @@ Several modules in `core` are clients of `rt`: #![allow(dead_code)] -use failure; -use rustrt; use os; +use thunk::Thunk; +use kinds::Send; +use thread::Thread; +use ops::FnOnce; +use sys; +use sys_common; +use sys_common::thread_info::{mod, NewThread}; // Reexport some of our utilities which are expected by other crates. pub use self::util::{default_sched_threads, min_stack, running_on_valgrind}; +pub use self::unwind::{begin_unwind, begin_unwind_fmt}; -// Reexport functionality from librustrt and other crates underneath the -// standard library which work together to create the entire runtime. +// Reexport some functionality from liballoc. pub use alloc::heap; -pub use rustrt::{begin_unwind, begin_unwind_fmt, at_exit}; // Simple backtrace functionality (to print on panic) pub mod backtrace; -// Just stuff -mod util; +// Internals +mod macros; -/// One-time runtime initialization. -/// -/// Initializes global state, including frobbing -/// the crate's logging flags, registering GC -/// metadata, and storing the process arguments. -#[allow(experimental)] -pub fn init(argc: int, argv: *const *const u8) { - rustrt::init(argc, argv); - unsafe { rustrt::unwind::register(failure::on_fail); } -} +// These should be refactored/moved/made private over time +pub mod util; +pub mod unwind; +pub mod args; + +mod at_exit_imp; +mod libunwind; + +/// The default error code of the rust runtime if the main task panics instead +/// of exiting cleanly. +pub const DEFAULT_ERROR_CODE: int = 101; #[cfg(any(windows, android))] -static OS_DEFAULT_STACK_ESTIMATE: uint = 1 << 20; +const OS_DEFAULT_STACK_ESTIMATE: uint = 1 << 20; #[cfg(all(unix, not(android)))] -static OS_DEFAULT_STACK_ESTIMATE: uint = 2 * (1 << 20); +const OS_DEFAULT_STACK_ESTIMATE: uint = 2 * (1 << 20); #[cfg(not(test))] #[lang = "start"] fn lang_start(main: *const u8, argc: int, argv: *const *const u8) -> int { use mem; - start(argc, argv, proc() { - let main: extern "Rust" fn() = unsafe { mem::transmute(main) }; - main(); - }) -} - -/// Executes the given procedure after initializing the runtime with the given -/// argc/argv. -/// -/// This procedure is guaranteed to run on the thread calling this function, but -/// the stack bounds for this rust task will *not* be set. Care must be taken -/// for this function to not overflow its stack. -/// -/// This function will only return once *all* native threads in the system have -/// exited. -pub fn start(argc: int, argv: *const *const u8, main: proc()) -> int { use prelude::*; use rt; - use rustrt::task::Task; - use str; let something_around_the_top_of_the_stack = 1; let addr = &something_around_the_top_of_the_stack as *const int; @@ -124,40 +78,76 @@ pub fn start(argc: int, argv: *const *const u8, main: proc()) -> int { // frames above our current position. let my_stack_bottom = my_stack_top + 20000 - OS_DEFAULT_STACK_ESTIMATE; - // When using libgreen, one of the first things that we do is to turn off - // the SIGPIPE signal (set it to ignore). By default, some platforms will - // send a *signal* when a EPIPE error would otherwise be delivered. This - // runtime doesn't install a SIGPIPE handler, causing it to kill the - // program, which isn't exactly what we want! - // - // Hence, we set SIGPIPE to ignore when the program starts up in order to - // prevent this problem. - #[cfg(windows)] fn ignore_sigpipe() {} - #[cfg(unix)] fn ignore_sigpipe() { - use libc; - use libc::funcs::posix01::signal::signal; - unsafe { - assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != -1); + let failed = unsafe { + // First, make sure we don't trigger any __morestack overflow checks, + // and next set up our stack to have a guard page and run through our + // own fault handlers if we hit it. + sys_common::stack::record_os_managed_stack_bounds(my_stack_bottom, + my_stack_top); + sys::thread::guard::init(); + sys::stack_overflow::init(); + + // Next, set up the current Thread with the guard information we just + // created. Note that this isn't necessary in general for new threads, + // but we just do this to name the main thread and to give it correct + // info about the stack bounds. + let thread: Thread = NewThread::new(Some("<main>".to_string())); + thread_info::set((my_stack_bottom, my_stack_top), + sys::thread::guard::main(), + thread); + + // By default, some platforms will send a *signal* when a EPIPE error + // would otherwise be delivered. This runtime doesn't install a SIGPIPE + // handler, causing it to kill the program, which isn't exactly what we + // want! + // + // Hence, we set SIGPIPE to ignore when the program starts up in order + // to prevent this problem. + #[cfg(windows)] fn ignore_sigpipe() {} + #[cfg(unix)] fn ignore_sigpipe() { + use libc; + use libc::funcs::posix01::signal::signal; + unsafe { + assert!(signal(libc::SIGPIPE, libc::SIG_IGN) != -1); + } } + ignore_sigpipe(); + + // Store our args if necessary in a squirreled away location + args::init(argc, argv); + + // And finally, let's run some code! + let res = unwind::try(|| { + let main: fn() = mem::transmute(main); + main(); + }); + cleanup(); + res.is_err() + }; + + // If the exit code wasn't set, then the try block must have panicked. + if failed { + rt::DEFAULT_ERROR_CODE + } else { + os::get_exit_status() } - ignore_sigpipe(); - - init(argc, argv); - let mut exit_code = None; - let mut main = Some(main); - let mut task = box Task::new(Some((my_stack_bottom, my_stack_top)), - Some(rustrt::thread::main_guard_page())); - task.name = Some(str::Slice("<main>")); - drop(task.run(|| { - unsafe { - rustrt::stack::record_os_managed_stack_bounds(my_stack_bottom, my_stack_top); - } - (main.take().unwrap())(); - exit_code = Some(os::get_exit_status()); - }).destroy()); - unsafe { rt::cleanup(); } - // If the exit code wasn't set, then the task block must have panicked. - return exit_code.unwrap_or(rustrt::DEFAULT_ERROR_CODE); +} + +/// Enqueues a procedure to run when the runtime is cleaned up +/// +/// The procedure passed to this function will be executed as part of the +/// runtime cleanup phase. For normal rust programs, this means that it will run +/// after all other tasks have exited. +/// +/// The procedure is *not* executed with a local `Task` available to it, so +/// primitives like logging, I/O, channels, spawning, etc, are *not* available. +/// This is meant for "bare bones" usage to clean up runtime details, this is +/// not meant as a general-purpose "let's clean everything up" function. +/// +/// It is forbidden for procedures to register more `at_exit` handlers when they +/// are running, and doing so will lead to a process abort. +pub fn at_exit<F:FnOnce()+Send>(f: F) { + at_exit_imp::push(Thunk::new(f)); } /// One-time runtime cleanup. @@ -170,5 +160,10 @@ pub fn start(argc: int, argv: *const *const u8, main: proc()) -> int { /// Invoking cleanup while portions of the runtime are still in use may cause /// undefined behavior. pub unsafe fn cleanup() { - rustrt::cleanup(); + args::cleanup(); + sys::stack_overflow::cleanup(); + // FIXME: (#20012): the resources being cleaned up by at_exit + // currently are not prepared for cleanup to happen asynchronously + // with detached threads using the resources; for now, we leak. + // at_exit_imp::cleanup(); } diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs new file mode 100644 index 00000000000..98940a2b381 --- /dev/null +++ b/src/libstd/rt/task.rs @@ -0,0 +1,554 @@ +// Copyright 2013-2014 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. + +//! Language-level runtime services that should reasonably expected +//! to be available 'everywhere'. Unwinding, local storage, and logging. +//! Even a 'freestanding' Rust would likely want to implement this. + +pub use self::BlockedTask::*; +use self::TaskState::*; + +use any::Any; +use boxed::Box; +use sync::Arc; +use sync::atomic::{AtomicUint, SeqCst}; +use iter::{IteratorExt, Take}; +use kinds::marker; +use mem; +use ops::FnMut; +use core::prelude::{Clone, Drop, Err, Iterator, None, Ok, Option, Send, Some}; +use core::prelude::{drop}; +use str::SendStr; +use thunk::Thunk; + +use rt; +use rt::mutex::NativeMutex; +use rt::local::Local; +use rt::thread::{mod, Thread}; +use sys_common::stack; +use rt::unwind; +use rt::unwind::Unwinder; + +/// State associated with Rust tasks. +/// +/// This structure is currently undergoing major changes, and is +/// likely to be move/be merged with a `Thread` structure. +pub struct Task { + pub unwinder: Unwinder, + pub death: Death, + pub name: Option<SendStr>, + + state: TaskState, + lock: NativeMutex, // native synchronization + awoken: bool, // used to prevent spurious wakeups + + // This field holds the known bounds of the stack in (lo, hi) form. Not all + // native tasks necessarily know their precise bounds, hence this is + // optional. + stack_bounds: (uint, uint), + + stack_guard: uint +} + +// Once a task has entered the `Armed` state it must be destroyed via `drop`, +// and no other method. This state is used to track this transition. +#[deriving(PartialEq)] +enum TaskState { + New, + Armed, + Destroyed, +} + +pub struct TaskOpts { + /// Invoke this procedure with the result of the task when it finishes. + pub on_exit: Option<Thunk<Result>>, + /// A name for the task-to-be, for identification in panic messages + pub name: Option<SendStr>, + /// The size of the stack for the spawned task + pub stack_size: Option<uint>, +} + +/// Indicates the manner in which a task exited. +/// +/// A task that completes without panicking is considered to exit successfully. +/// +/// If you wish for this result's delivery to block until all +/// children tasks complete, recommend using a result future. +pub type Result = ::core::result::Result<(), Box<Any + Send>>; + +/// A handle to a blocked task. Usually this means having the Box<Task> +/// pointer by ownership, but if the task is killable, a killer can steal it +/// at any time. +pub enum BlockedTask { + Owned(Box<Task>), + Shared(Arc<AtomicUint>), +} + +/// Per-task state related to task death, killing, panic, etc. +pub struct Death { + pub on_exit: Option<Thunk<Result>>, +} + +pub struct BlockedTasks { + inner: Arc<AtomicUint>, +} + +impl Task { + /// Creates a new uninitialized task. + pub fn new(stack_bounds: Option<(uint, uint)>, stack_guard: Option<uint>) -> Task { + Task { + unwinder: Unwinder::new(), + death: Death::new(), + state: New, + name: None, + lock: unsafe { NativeMutex::new() }, + awoken: false, + // these *should* get overwritten + stack_bounds: stack_bounds.unwrap_or((0, 0)), + stack_guard: stack_guard.unwrap_or(0) + } + } + + pub fn spawn<F>(opts: TaskOpts, f: F) + where F : FnOnce(), F : Send + { + Task::spawn_thunk(opts, Thunk::new(f)) + } + + fn spawn_thunk(opts: TaskOpts, f: Thunk) { + let TaskOpts { name, stack_size, on_exit } = opts; + + let mut task = box Task::new(None, None); + task.name = name; + task.death.on_exit = on_exit; + + let stack = stack_size.unwrap_or(rt::min_stack()); + + // Spawning a new OS thread guarantees that __morestack will never get + // triggered, but we must manually set up the actual stack bounds once + // this function starts executing. This raises the lower limit by a bit + // because by the time that this function is executing we've already + // consumed at least a little bit of stack (we don't know the exact byte + // address at which our stack started). + Thread::spawn_stack(stack, move|| { + let something_around_the_top_of_the_stack = 1; + let addr = &something_around_the_top_of_the_stack as *const int; + let my_stack = addr as uint; + unsafe { + stack::record_os_managed_stack_bounds(my_stack - stack + 1024, + my_stack); + } + task.stack_guard = thread::current_guard_page(); + task.stack_bounds = (my_stack - stack + 1024, my_stack); + + let mut f = Some(f); + drop(task.run(|| { f.take().unwrap().invoke(()) }).destroy()); + }) + } + + /// Consumes ownership of a task, runs some code, and returns the task back. + /// + /// This function can be used as an emulated "try/catch" to interoperate + /// with the rust runtime at the outermost boundary. It is not possible to + /// use this function in a nested fashion (a try/catch inside of another + /// try/catch). Invoking this function is quite cheap. + /// + /// If the closure `f` succeeds, then the returned task can be used again + /// for another invocation of `run`. If the closure `f` panics then `self` + /// will be internally destroyed along with all of the other associated + /// resources of this task. The `on_exit` callback is invoked with the + /// cause of panic (not returned here). This can be discovered by querying + /// `is_destroyed()`. + /// + /// Note that it is possible to view partial execution of the closure `f` + /// because it is not guaranteed to run to completion, but this function is + /// guaranteed to return if it panicks. Care should be taken to ensure that + /// stack references made by `f` are handled appropriately. + /// + /// It is invalid to call this function with a task that has been previously + /// destroyed via a failed call to `run`. + pub fn run(mut self: Box<Task>, f: ||) -> Box<Task> { + assert!(!self.is_destroyed(), "cannot re-use a destroyed task"); + + // First, make sure that no one else is in TLS. This does not allow + // recursive invocations of run(). If there's no one else, then + // relinquish ownership of ourselves back into TLS. + if Local::exists(None::<Task>) { + panic!("cannot run a task recursively inside another"); + } + self.state = Armed; + Local::put(self); + + // There are two primary reasons that general try/catch is unsafe. The + // first is that we do not support nested try/catch. The above check for + // an existing task in TLS is sufficient for this invariant to be + // upheld. The second is that unwinding while unwinding is not defined. + // We take care of that by having an 'unwinding' flag in the task + // itself. For these reasons, this unsafety should be ok. + let result = unsafe { unwind::try(f) }; + + // After running the closure given return the task back out if it ran + // successfully, or clean up the task if it panicked. + let task: Box<Task> = Local::take(); + match result { + Ok(()) => task, + Err(cause) => { task.cleanup(Err(cause)) } + } + } + + /// Destroy all associated resources of this task. + /// + /// This function will perform any necessary clean up to prepare the task + /// for destruction. It is required that this is called before a `Task` + /// falls out of scope. + /// + /// The returned task cannot be used for running any more code, but it may + /// be used to extract the runtime as necessary. + pub fn destroy(self: Box<Task>) -> Box<Task> { + if self.is_destroyed() { + self + } else { + self.cleanup(Ok(())) + } + } + + /// Cleans up a task, processing the result of the task as appropriate. + /// + /// This function consumes ownership of the task, deallocating it once it's + /// done being processed. It is assumed that TLD and the local heap have + /// already been destroyed and/or annihilated. + fn cleanup(mut self: Box<Task>, result: Result) -> Box<Task> { + // After taking care of the data above, we need to transmit the result + // of this task. + let what_to_do = self.death.on_exit.take(); + Local::put(self); + + // FIXME: this is running in a seriously constrained context. If this + // allocates TLD then it will likely abort the runtime. Similarly, + // if this panics, this will also likely abort the runtime. + // + // This closure is currently limited to a channel send via the + // standard library's task interface, but this needs + // reconsideration to whether it's a reasonable thing to let a + // task to do or not. + match what_to_do { + Some(f) => { f.invoke(result) } + None => { drop(result) } + } + + // Now that we're done, we remove the task from TLS and flag it for + // destruction. + let mut task: Box<Task> = Local::take(); + task.state = Destroyed; + return task; + } + + /// Queries whether this can be destroyed or not. + pub fn is_destroyed(&self) -> bool { self.state == Destroyed } + + /// Deschedules the current task, invoking `f` `amt` times. It is not + /// recommended to use this function directly, but rather communication + /// primitives in `std::comm` should be used. + // + // This function gets a little interesting. There are a few safety and + // ownership violations going on here, but this is all done in the name of + // shared state. Additionally, all of the violations are protected with a + // mutex, so in theory there are no races. + // + // The first thing we need to do is to get a pointer to the task's internal + // mutex. This address will not be changing (because the task is allocated + // on the heap). We must have this handle separately because the task will + // have its ownership transferred to the given closure. We're guaranteed, + // however, that this memory will remain valid because *this* is the current + // task's execution thread. + // + // The next weird part is where ownership of the task actually goes. We + // relinquish it to the `f` blocking function, but upon returning this + // function needs to replace the task back in TLS. There is no communication + // from the wakeup thread back to this thread about the task pointer, and + // there's really no need to. In order to get around this, we cast the task + // to a `uint` which is then used at the end of this function to cast back + // to a `Box<Task>` object. Naturally, this looks like it violates + // ownership semantics in that there may be two `Box<Task>` objects. + // + // The fun part is that the wakeup half of this implementation knows to + // "forget" the task on the other end. This means that the awakening half of + // things silently relinquishes ownership back to this thread, but not in a + // way that the compiler can understand. The task's memory is always valid + // for both tasks because these operations are all done inside of a mutex. + // + // You'll also find that if blocking fails (the `f` function hands the + // BlockedTask back to us), we will `mem::forget` the handles. The + // reasoning for this is the same logic as above in that the task silently + // transfers ownership via the `uint`, not through normal compiler + // semantics. + // + // On a mildly unrelated note, it should also be pointed out that OS + // condition variables are susceptible to spurious wakeups, which we need to + // be ready for. In order to accommodate for this fact, we have an extra + // `awoken` field which indicates whether we were actually woken up via some + // invocation of `reawaken`. This flag is only ever accessed inside the + // lock, so there's no need to make it atomic. + pub fn deschedule<F>(mut self: Box<Task>, times: uint, mut f: F) where + F: FnMut(BlockedTask) -> ::core::result::Result<(), BlockedTask>, + { + unsafe { + let me = &mut *self as *mut Task; + let task = BlockedTask::block(self); + + if times == 1 { + let guard = (*me).lock.lock(); + (*me).awoken = false; + match f(task) { + Ok(()) => { + while !(*me).awoken { + guard.wait(); + } + } + Err(task) => { mem::forget(task.wake()); } + } + } else { + let iter = task.make_selectable(times); + let guard = (*me).lock.lock(); + (*me).awoken = false; + + // Apply the given closure to all of the "selectable tasks", + // bailing on the first one that produces an error. Note that + // care must be taken such that when an error is occurred, we + // may not own the task, so we may still have to wait for the + // task to become available. In other words, if task.wake() + // returns `None`, then someone else has ownership and we must + // wait for their signal. + match iter.map(f).filter_map(|a| a.err()).next() { + None => {} + Some(task) => { + match task.wake() { + Some(task) => { + mem::forget(task); + (*me).awoken = true; + } + None => {} + } + } + } + while !(*me).awoken { + guard.wait(); + } + } + // put the task back in TLS, and everything is as it once was. + Local::put(mem::transmute(me)); + } + } + + /// Wakes up a previously blocked task. This function can only be + /// called on tasks that were previously blocked in `deschedule`. + // + // See the comments on `deschedule` for why the task is forgotten here, and + // why it's valid to do so. + pub fn reawaken(mut self: Box<Task>) { + unsafe { + let me = &mut *self as *mut Task; + mem::forget(self); + let guard = (*me).lock.lock(); + (*me).awoken = true; + guard.signal(); + } + } + + /// Yields control of this task to another task. This function will + /// eventually return, but possibly not immediately. This is used as an + /// opportunity to allow other tasks a chance to run. + pub fn yield_now() { + Thread::yield_now(); + } + + /// Returns the stack bounds for this task in (lo, hi) format. The stack + /// bounds may not be known for all tasks, so the return value may be + /// `None`. + pub fn stack_bounds(&self) -> (uint, uint) { + self.stack_bounds + } + + /// Returns the stack guard for this task, if known. + pub fn stack_guard(&self) -> Option<uint> { + if self.stack_guard != 0 { + Some(self.stack_guard) + } else { + None + } + } + + /// Consume this task, flagging it as a candidate for destruction. + /// + /// This function is required to be invoked to destroy a task. A task + /// destroyed through a normal drop will abort. + pub fn drop(mut self) { + self.state = Destroyed; + } +} + +impl Drop for Task { + fn drop(&mut self) { + rtdebug!("called drop for a task: {}", self as *mut Task as uint); + rtassert!(self.state != Armed); + } +} + +impl TaskOpts { + pub fn new() -> TaskOpts { + TaskOpts { on_exit: None, name: None, stack_size: None } + } +} + +impl Iterator<BlockedTask> for BlockedTasks { + fn next(&mut self) -> Option<BlockedTask> { + Some(Shared(self.inner.clone())) + } +} + +impl BlockedTask { + /// Returns Some if the task was successfully woken; None if already killed. + pub fn wake(self) -> Option<Box<Task>> { + match self { + Owned(task) => Some(task), + Shared(arc) => { + match arc.swap(0, SeqCst) { + 0 => None, + n => Some(unsafe { mem::transmute(n) }), + } + } + } + } + + /// Reawakens this task if ownership is acquired. If finer-grained control + /// is desired, use `wake` instead. + pub fn reawaken(self) { + self.wake().map(|t| t.reawaken()); + } + + // This assertion has two flavours because the wake involves an atomic op. + // In the faster version, destructors will panic dramatically instead. + #[cfg(not(test))] pub fn trash(self) { } + #[cfg(test)] pub fn trash(self) { assert!(self.wake().is_none()); } + + /// Create a blocked task, unless the task was already killed. + pub fn block(task: Box<Task>) -> BlockedTask { + Owned(task) + } + + /// Converts one blocked task handle to a list of many handles to the same. + pub fn make_selectable(self, num_handles: uint) -> Take<BlockedTasks> { + let arc = match self { + Owned(task) => { + let flag = unsafe { AtomicUint::new(mem::transmute(task)) }; + Arc::new(flag) + } + Shared(arc) => arc.clone(), + }; + BlockedTasks{ inner: arc }.take(num_handles) + } + + /// Convert to an unsafe uint value. Useful for storing in a pipe's state + /// flag. + #[inline] + pub unsafe fn cast_to_uint(self) -> uint { + match self { + Owned(task) => { + let blocked_task_ptr: uint = mem::transmute(task); + rtassert!(blocked_task_ptr & 0x1 == 0); + blocked_task_ptr + } + Shared(arc) => { + let blocked_task_ptr: uint = mem::transmute(box arc); + rtassert!(blocked_task_ptr & 0x1 == 0); + blocked_task_ptr | 0x1 + } + } + } + + /// Convert from an unsafe uint value. Useful for retrieving a pipe's state + /// flag. + #[inline] + pub unsafe fn cast_from_uint(blocked_task_ptr: uint) -> BlockedTask { + if blocked_task_ptr & 0x1 == 0 { + Owned(mem::transmute(blocked_task_ptr)) + } else { + let ptr: Box<Arc<AtomicUint>> = + mem::transmute(blocked_task_ptr & !1); + Shared(*ptr) + } + } +} + +impl Death { + pub fn new() -> Death { + Death { on_exit: None } + } +} + +#[cfg(test)] +mod test { + use super::*; + use prelude::*; + use task; + use rt::unwind; + + #[test] + fn unwind() { + let result = task::try(move|| ()); + rtdebug!("trying first assert"); + assert!(result.is_ok()); + let result = task::try(move|| -> () panic!()); + rtdebug!("trying second assert"); + assert!(result.is_err()); + } + + #[test] + fn rng() { + use rand::{StdRng, Rng}; + let mut r = StdRng::new().ok().unwrap(); + let _ = r.next_u32(); + } + + #[test] + fn comm_stream() { + let (tx, rx) = channel(); + tx.send(10i); + assert!(rx.recv() == 10); + } + + #[test] + fn comm_shared_chan() { + let (tx, rx) = channel(); + tx.send(10i); + assert!(rx.recv() == 10); + } + + #[test] + #[should_fail] + fn test_begin_unwind() { + use rt::unwind::begin_unwind; + begin_unwind("cause", &(file!(), line!())) + } + + #[test] + fn drop_new_task_ok() { + drop(Task::new(None, None)); + } + + // Task blocking tests + + #[test] + fn block_and_wake() { + let task = box Task::new(None, None); + let task = BlockedTask::block(task).wake().unwrap(); + task.drop(); + } +} diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs new file mode 100644 index 00000000000..eb15a7ba378 --- /dev/null +++ b/src/libstd/rt/unwind.rs @@ -0,0 +1,613 @@ +// 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. + +//! Implementation of Rust stack unwinding +//! +//! For background on exception handling and stack unwinding please see +//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and +//! documents linked from it. +//! These are also good reads: +//! http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/ +//! http://monoinfinito.wordpress.com/series/exception-handling-in-c/ +//! http://www.airs.com/blog/index.php?s=exception+frames +//! +//! ## A brief summary +//! +//! Exception handling happens in two phases: a search phase and a cleanup phase. +//! +//! In both phases the unwinder walks stack frames from top to bottom using +//! information from the stack frame unwind sections of the current process's +//! modules ("module" here refers to an OS module, i.e. an executable or a +//! dynamic library). +//! +//! For each stack frame, it invokes the associated "personality routine", whose +//! address is also stored in the unwind info section. +//! +//! In the search phase, the job of a personality routine is to examine exception +//! object being thrown, and to decide whether it should be caught at that stack +//! frame. Once the handler frame has been identified, cleanup phase begins. +//! +//! In the cleanup phase, personality routines invoke cleanup code associated +//! with their stack frames (i.e. destructors). Once stack has been unwound down +//! to the handler frame level, unwinding stops and the last personality routine +//! transfers control to its catch block. +//! +//! ## Frame unwind info registration +//! +//! Each module has its own frame unwind info section (usually ".eh_frame"), and +//! unwinder needs to know about all of them in order for unwinding to be able to +//! cross module boundaries. +//! +//! On some platforms, like Linux, this is achieved by dynamically enumerating +//! currently loaded modules via the dl_iterate_phdr() API and finding all +//! .eh_frame sections. +//! +//! Others, like Windows, require modules to actively register their unwind info +//! sections by calling __register_frame_info() API at startup. In the latter +//! case it is essential that there is only one copy of the unwinder runtime in +//! the process. This is usually achieved by linking to the dynamic version of +//! the unwind runtime. +//! +//! Currently Rust uses unwind runtime provided by libgcc. + +use prelude::*; + +use any::Any; +use cell::Cell; +use cmp; +use failure; +use fmt; +use intrinsics; +use libc::c_void; +use mem; +use sync::atomic; +use sync::{Once, ONCE_INIT}; + +use rt::libunwind as uw; + +struct Exception { + uwe: uw::_Unwind_Exception, + cause: Option<Box<Any + Send>>, +} + +pub type Callback = fn(msg: &(Any + Send), file: &'static str, line: uint); + +// Variables used for invoking callbacks when a task starts to unwind. +// +// For more information, see below. +const MAX_CALLBACKS: uint = 16; +static CALLBACKS: [atomic::AtomicUint, ..MAX_CALLBACKS] = + [atomic::INIT_ATOMIC_UINT, atomic::INIT_ATOMIC_UINT, + atomic::INIT_ATOMIC_UINT, atomic::INIT_ATOMIC_UINT, + atomic::INIT_ATOMIC_UINT, atomic::INIT_ATOMIC_UINT, + atomic::INIT_ATOMIC_UINT, atomic::INIT_ATOMIC_UINT, + atomic::INIT_ATOMIC_UINT, atomic::INIT_ATOMIC_UINT, + atomic::INIT_ATOMIC_UINT, atomic::INIT_ATOMIC_UINT, + atomic::INIT_ATOMIC_UINT, atomic::INIT_ATOMIC_UINT, + atomic::INIT_ATOMIC_UINT, atomic::INIT_ATOMIC_UINT]; +static CALLBACK_CNT: atomic::AtomicUint = atomic::INIT_ATOMIC_UINT; + +thread_local! { static PANICKING: Cell<bool> = Cell::new(false) } + +/// Invoke a closure, capturing the cause of panic if one occurs. +/// +/// This function will return `None` if the closure did not panic, and will +/// return `Some(cause)` if the closure panics. The `cause` returned is the +/// object with which panic was originally invoked. +/// +/// This function also is unsafe for a variety of reasons: +/// +/// * This is not safe to call in a nested fashion. The unwinding +/// interface for Rust is designed to have at most one try/catch block per +/// task, not multiple. No runtime checking is currently performed to uphold +/// this invariant, so this function is not safe. A nested try/catch block +/// may result in corruption of the outer try/catch block's state, especially +/// if this is used within a task itself. +/// +/// * It is not sound to trigger unwinding while already unwinding. Rust tasks +/// have runtime checks in place to ensure this invariant, but it is not +/// guaranteed that a rust task is in place when invoking this function. +/// Unwinding twice can lead to resource leaks where some destructors are not +/// run. +pub unsafe fn try<F: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> { + let mut f = Some(f); + + let prev = PANICKING.with(|s| s.get()); + PANICKING.with(|s| s.set(false)); + let ep = rust_try(try_fn::<F>, &mut f as *mut _ as *mut c_void); + PANICKING.with(|s| s.set(prev)); + return if ep.is_null() { + Ok(()) + } else { + let my_ep = ep as *mut Exception; + rtdebug!("caught {}", (*my_ep).uwe.exception_class); + let cause = (*my_ep).cause.take(); + uw::_Unwind_DeleteException(ep); + Err(cause.unwrap()) + }; + + extern fn try_fn<F: FnOnce()>(opt_closure: *mut c_void) { + let opt_closure = opt_closure as *mut Option<F>; + unsafe { (*opt_closure).take().unwrap()(); } + } + + #[link(name = "rustrt_native", kind = "static")] + #[cfg(not(test))] + extern {} + + extern { + // Rust's try-catch + // When f(...) returns normally, the return value is null. + // When f(...) throws, the return value is a pointer to the caught + // exception object. + fn rust_try(f: extern fn(*mut c_void), + data: *mut c_void) -> *mut uw::_Unwind_Exception; + } +} + +/// Test if the current thread is currently panicking. +pub fn panicking() -> bool { + PANICKING.with(|s| s.get()) +} + +// An uninlined, unmangled function upon which to slap yer breakpoints +#[inline(never)] +#[no_mangle] +fn rust_panic(cause: Box<Any + Send>) -> ! { + rtdebug!("begin_unwind()"); + + unsafe { + let exception = box Exception { + uwe: uw::_Unwind_Exception { + exception_class: rust_exception_class(), + exception_cleanup: exception_cleanup, + private: [0, ..uw::unwinder_private_data_size], + }, + cause: Some(cause), + }; + let error = uw::_Unwind_RaiseException(mem::transmute(exception)); + rtabort!("Could not unwind stack, error = {}", error as int) + } + + extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code, + exception: *mut uw::_Unwind_Exception) { + rtdebug!("exception_cleanup()"); + unsafe { + let _: Box<Exception> = mem::transmute(exception); + } + } +} + +// Rust's exception class identifier. This is used by personality routines to +// determine whether the exception was thrown by their own runtime. +fn rust_exception_class() -> uw::_Unwind_Exception_Class { + // M O Z \0 R U S T -- vendor, language + 0x4d4f5a_00_52555354 +} + +// We could implement our personality routine in pure Rust, however exception +// info decoding is tedious. More importantly, personality routines have to +// handle various platform quirks, which are not fun to maintain. For this +// reason, we attempt to reuse personality routine of the C language: +// __gcc_personality_v0. +// +// Since C does not support exception catching, __gcc_personality_v0 simply +// always returns _URC_CONTINUE_UNWIND in search phase, and always returns +// _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase. +// +// This is pretty close to Rust's exception handling approach, except that Rust +// does have a single "catch-all" handler at the bottom of each task's stack. +// So we have two versions of the personality routine: +// - rust_eh_personality, used by all cleanup landing pads, which never catches, +// so the behavior of __gcc_personality_v0 is perfectly adequate there, and +// - rust_eh_personality_catch, used only by rust_try(), which always catches. +// +// Note, however, that for implementation simplicity, rust_eh_personality_catch +// lacks code to install a landing pad, so in order to obtain exception object +// pointer (which it needs to return upstream), rust_try() employs another trick: +// it calls into the nested rust_try_inner(), whose landing pad does not resume +// unwinds. Instead, it extracts the exception pointer and performs a "normal" +// return. +// +// See also: rt/rust_try.ll + +#[cfg(all(not(target_arch = "arm"), + not(all(windows, target_arch = "x86_64")), + not(test)))] +#[doc(hidden)] +pub mod eabi { + use rt::libunwind as uw; + use libc::c_int; + + extern "C" { + fn __gcc_personality_v0(version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; + } + + #[lang="eh_personality"] + #[no_mangle] // referenced from rust_try.ll + extern fn rust_eh_personality( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + unsafe { + __gcc_personality_v0(version, actions, exception_class, ue_header, + context) + } + } + + #[no_mangle] // referenced from rust_try.ll + pub extern "C" fn rust_eh_personality_catch( + _version: c_int, + actions: uw::_Unwind_Action, + _exception_class: uw::_Unwind_Exception_Class, + _ue_header: *mut uw::_Unwind_Exception, + _context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + + if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase + uw::_URC_HANDLER_FOUND // catch! + } + else { // cleanup phase + uw::_URC_INSTALL_CONTEXT + } + } +} + +// iOS on armv7 is using SjLj exceptions and therefore requires to use +// a specialized personality routine: __gcc_personality_sj0 + +#[cfg(all(target_os = "ios", target_arch = "arm", not(test)))] +#[doc(hidden)] +pub mod eabi { + use rt::libunwind as uw; + use libc::c_int; + + extern "C" { + fn __gcc_personality_sj0(version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; + } + + #[lang="eh_personality"] + #[no_mangle] // referenced from rust_try.ll + pub extern "C" fn rust_eh_personality( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + unsafe { + __gcc_personality_sj0(version, actions, exception_class, ue_header, + context) + } + } + + #[no_mangle] // referenced from rust_try.ll + pub extern "C" fn rust_eh_personality_catch( + _version: c_int, + actions: uw::_Unwind_Action, + _exception_class: uw::_Unwind_Exception_Class, + _ue_header: *mut uw::_Unwind_Exception, + _context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase + uw::_URC_HANDLER_FOUND // catch! + } + else { // cleanup phase + unsafe { + __gcc_personality_sj0(_version, actions, _exception_class, _ue_header, + _context) + } + } + } +} + + +// ARM EHABI uses a slightly different personality routine signature, +// but otherwise works the same. +#[cfg(all(target_arch = "arm", not(target_os = "ios"), not(test)))] +#[doc(hidden)] +pub mod eabi { + use rt::libunwind as uw; + use libc::c_int; + + extern "C" { + fn __gcc_personality_v0(state: uw::_Unwind_State, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; + } + + #[lang="eh_personality"] + #[no_mangle] // referenced from rust_try.ll + extern "C" fn rust_eh_personality( + state: uw::_Unwind_State, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + unsafe { + __gcc_personality_v0(state, ue_header, context) + } + } + + #[no_mangle] // referenced from rust_try.ll + pub extern "C" fn rust_eh_personality_catch( + state: uw::_Unwind_State, + _ue_header: *mut uw::_Unwind_Exception, + _context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + if (state as c_int & uw::_US_ACTION_MASK as c_int) + == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase + uw::_URC_HANDLER_FOUND // catch! + } + else { // cleanup phase + uw::_URC_INSTALL_CONTEXT + } + } +} + +// Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx) +// +// This looks a bit convoluted because rather than implementing a native SEH handler, +// GCC reuses the same personality routine as for the other architectures by wrapping it +// with an "API translator" layer (_GCC_specific_handler). + +#[cfg(all(windows, target_arch = "x86_64", not(test)))] +#[doc(hidden)] +#[allow(non_camel_case_types, non_snake_case)] +pub mod eabi { + pub use self::EXCEPTION_DISPOSITION::*; + use rt::libunwind as uw; + use libc::{c_void, c_int}; + + #[repr(C)] + #[allow(missing_copy_implementations)] + pub struct EXCEPTION_RECORD; + #[repr(C)] + #[allow(missing_copy_implementations)] + pub struct CONTEXT; + #[repr(C)] + #[allow(missing_copy_implementations)] + pub struct DISPATCHER_CONTEXT; + + #[repr(C)] + #[deriving(Copy)] + pub enum EXCEPTION_DISPOSITION { + ExceptionContinueExecution, + ExceptionContinueSearch, + ExceptionNestedException, + ExceptionCollidedUnwind + } + + type _Unwind_Personality_Fn = + extern "C" fn( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code; + + extern "C" { + fn __gcc_personality_seh0( + exceptionRecord: *mut EXCEPTION_RECORD, + establisherFrame: *mut c_void, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT + ) -> EXCEPTION_DISPOSITION; + + fn _GCC_specific_handler( + exceptionRecord: *mut EXCEPTION_RECORD, + establisherFrame: *mut c_void, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT, + personality: _Unwind_Personality_Fn + ) -> EXCEPTION_DISPOSITION; + } + + #[lang="eh_personality"] + #[no_mangle] // referenced from rust_try.ll + extern "C" fn rust_eh_personality( + exceptionRecord: *mut EXCEPTION_RECORD, + establisherFrame: *mut c_void, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT + ) -> EXCEPTION_DISPOSITION + { + unsafe { + __gcc_personality_seh0(exceptionRecord, establisherFrame, + contextRecord, dispatcherContext) + } + } + + #[no_mangle] // referenced from rust_try.ll + pub extern "C" fn rust_eh_personality_catch( + exceptionRecord: *mut EXCEPTION_RECORD, + establisherFrame: *mut c_void, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT + ) -> EXCEPTION_DISPOSITION + { + extern "C" fn inner( + _version: c_int, + actions: uw::_Unwind_Action, + _exception_class: uw::_Unwind_Exception_Class, + _ue_header: *mut uw::_Unwind_Exception, + _context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase + uw::_URC_HANDLER_FOUND // catch! + } + else { // cleanup phase + uw::_URC_INSTALL_CONTEXT + } + } + + unsafe { + _GCC_specific_handler(exceptionRecord, establisherFrame, + contextRecord, dispatcherContext, + inner) + } + } +} + +// Entry point of panic from the libcore crate +#[cfg(not(test))] +#[lang = "panic_fmt"] +pub extern fn rust_begin_unwind(msg: &fmt::Arguments, + file: &'static str, line: uint) -> ! { + begin_unwind_fmt(msg, &(file, line)) +} + +/// The entry point for unwinding with a formatted message. +/// +/// This is designed to reduce the amount of code required at the call +/// site as much as possible (so that `panic!()` has as low an impact +/// on (e.g.) the inlining of other functions as possible), by moving +/// the actual formatting into this shared place. +#[inline(never)] #[cold] +pub fn begin_unwind_fmt(msg: &fmt::Arguments, file_line: &(&'static str, uint)) -> ! { + use fmt::FormatWriter; + + // We do two allocations here, unfortunately. But (a) they're + // required with the current scheme, and (b) we don't handle + // panic + OOM properly anyway (see comment in begin_unwind + // below). + + struct VecWriter<'a> { v: &'a mut Vec<u8> } + + impl<'a> fmt::FormatWriter for VecWriter<'a> { + fn write(&mut self, buf: &[u8]) -> fmt::Result { + self.v.push_all(buf); + Ok(()) + } + } + + let mut v = Vec::new(); + let _ = write!(&mut VecWriter { v: &mut v }, "{}", msg); + + let msg = box String::from_utf8_lossy(v.as_slice()).into_owned(); + begin_unwind_inner(msg, file_line) +} + +/// This is the entry point of unwinding for panic!() and assert!(). +#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible +pub fn begin_unwind<M: Any + Send>(msg: M, file_line: &(&'static str, uint)) -> ! { + // Note that this should be the only allocation performed in this code path. + // Currently this means that panic!() on OOM will invoke this code path, + // but then again we're not really ready for panic on OOM anyway. If + // we do start doing this, then we should propagate this allocation to + // be performed in the parent of this task instead of the task that's + // panicking. + + // see below for why we do the `Any` coercion here. + begin_unwind_inner(box msg, file_line) +} + +/// The core of the unwinding. +/// +/// This is non-generic to avoid instantiation bloat in other crates +/// (which makes compilation of small crates noticeably slower). (Note: +/// we need the `Any` object anyway, we're not just creating it to +/// avoid being generic.) +/// +/// Doing this split took the LLVM IR line counts of `fn main() { panic!() +/// }` from ~1900/3700 (-O/no opts) to 180/590. +#[inline(never)] #[cold] // this is the slow path, please never inline this +fn begin_unwind_inner(msg: Box<Any + Send>, file_line: &(&'static str, uint)) -> ! { + // Make sure the default failure handler is registered before we look at the + // callbacks. + static INIT: Once = ONCE_INIT; + INIT.doit(|| unsafe { register(failure::on_fail); }); + + // First, invoke call the user-defined callbacks triggered on task panic. + // + // By the time that we see a callback has been registered (by reading + // MAX_CALLBACKS), the actual callback itself may have not been stored yet, + // so we just chalk it up to a race condition and move on to the next + // callback. Additionally, CALLBACK_CNT may briefly be higher than + // MAX_CALLBACKS, so we're sure to clamp it as necessary. + let callbacks = { + let amt = CALLBACK_CNT.load(atomic::SeqCst); + CALLBACKS[..cmp::min(amt, MAX_CALLBACKS)] + }; + for cb in callbacks.iter() { + match cb.load(atomic::SeqCst) { + 0 => {} + n => { + let f: Callback = unsafe { mem::transmute(n) }; + let (file, line) = *file_line; + f(&*msg, file, line); + } + } + }; + + // Now that we've run all the necessary unwind callbacks, we actually + // perform the unwinding. + if panicking() { + // If a thread panics while it's already unwinding then we + // have limited options. Currently our preference is to + // just abort. In the future we may consider resuming + // unwinding or otherwise exiting the task cleanly. + rterrln!("thread panicked while panicking. aborting."); + unsafe { intrinsics::abort() } + } + PANICKING.with(|s| s.set(true)); + rust_panic(msg); +} + +/// Register a callback to be invoked when a task unwinds. +/// +/// This is an unsafe and experimental API which allows for an arbitrary +/// callback to be invoked when a task panics. This callback is invoked on both +/// the initial unwinding and a double unwinding if one occurs. Additionally, +/// the local `Task` will be in place for the duration of the callback, and +/// the callback must ensure that it remains in place once the callback returns. +/// +/// Only a limited number of callbacks can be registered, and this function +/// returns whether the callback was successfully registered or not. It is not +/// currently possible to unregister a callback once it has been registered. +#[experimental] +pub unsafe fn register(f: Callback) -> bool { + match CALLBACK_CNT.fetch_add(1, atomic::SeqCst) { + // The invocation code has knowledge of this window where the count has + // been incremented, but the callback has not been stored. We're + // guaranteed that the slot we're storing into is 0. + n if n < MAX_CALLBACKS => { + let prev = CALLBACKS[n].swap(mem::transmute(f), atomic::SeqCst); + rtassert!(prev == 0); + true + } + // If we accidentally bumped the count too high, pull it back. + _ => { + CALLBACK_CNT.store(MAX_CALLBACKS, atomic::SeqCst); + false + } + } +} diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs index 92657d1b59b..d8cd8455deb 100644 --- a/src/libstd/rt/util.rs +++ b/src/libstd/rt/util.rs @@ -7,11 +7,19 @@ // <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. +// +// ignore-lexer-test FIXME #15677 + +use prelude::*; +use cmp; +use fmt; +use intrinsics; use libc::uintptr_t; -use option::{Some, None, Option}; +use libc; use os; -use str::{FromStr, from_str, Str}; +use slice; +use str; use sync::atomic; /// Dynamically inquire about whether we're running under V. @@ -44,7 +52,7 @@ pub fn min_stack() -> uint { 0 => {} n => return n - 1, } - let amt = os::getenv("RUST_MIN_STACK").and_then(|s| from_str(s.as_slice())); + let amt = os::getenv("RUST_MIN_STACK").and_then(|s| s.parse()); let amt = amt.unwrap_or(2 * 1024 * 1024); // 0 is our sentinel value, so ensure that we'll never see 0 after // initialization has run @@ -57,7 +65,7 @@ pub fn min_stack() -> uint { pub fn default_sched_threads() -> uint { match os::getenv("RUST_THREADS") { Some(nstr) => { - let opt_n: Option<uint> = FromStr::from_str(nstr.as_slice()); + let opt_n: Option<uint> = nstr.parse(); match opt_n { Some(n) if n > 0 => n, _ => panic!("`RUST_THREADS` is `{}`, should be a positive integer", nstr) @@ -72,3 +80,130 @@ pub fn default_sched_threads() -> uint { } } } + +// Indicates whether we should perform expensive sanity checks, including rtassert! +// +// FIXME: Once the runtime matures remove the `true` below to turn off rtassert, +// etc. +pub const ENFORCE_SANITY: bool = true || !cfg!(rtopt) || cfg!(rtdebug) || + cfg!(rtassert); + +#[allow(missing_copy_implementations)] +pub struct Stdio(libc::c_int); + +#[allow(non_upper_case_globals)] +pub const Stdout: Stdio = Stdio(libc::STDOUT_FILENO); +#[allow(non_upper_case_globals)] +pub const Stderr: Stdio = Stdio(libc::STDERR_FILENO); + +impl fmt::FormatWriter for Stdio { + fn write(&mut self, data: &[u8]) -> fmt::Result { + #[cfg(unix)] + type WriteLen = libc::size_t; + #[cfg(windows)] + type WriteLen = libc::c_uint; + unsafe { + let Stdio(fd) = *self; + libc::write(fd, + data.as_ptr() as *const libc::c_void, + data.len() as WriteLen); + } + Ok(()) // yes, we're lying + } +} + +pub fn dumb_print(args: &fmt::Arguments) { + let mut w = Stderr; + let _ = write!(&mut w, "{}", args); +} + +pub fn abort(args: &fmt::Arguments) -> ! { + use fmt::FormatWriter; + + struct BufWriter<'a> { + buf: &'a mut [u8], + pos: uint, + } + impl<'a> FormatWriter for BufWriter<'a> { + fn write(&mut self, bytes: &[u8]) -> fmt::Result { + let left = self.buf[mut self.pos..]; + let to_write = bytes[..cmp::min(bytes.len(), left.len())]; + slice::bytes::copy_memory(left, to_write); + self.pos += to_write.len(); + Ok(()) + } + } + + // Convert the arguments into a stack-allocated string + let mut msg = [0u8, ..512]; + let mut w = BufWriter { buf: &mut msg, pos: 0 }; + let _ = write!(&mut w, "{}", args); + let msg = str::from_utf8(w.buf[mut ..w.pos]).unwrap_or("aborted"); + let msg = if msg.is_empty() {"aborted"} else {msg}; + + // Give some context to the message + let hash = msg.bytes().fold(0, |accum, val| accum + (val as uint) ); + let quote = match hash % 10 { + 0 => " +It was from the artists and poets that the pertinent answers came, and I +know that panic would have broken loose had they been able to compare notes. +As it was, lacking their original letters, I half suspected the compiler of +having asked leading questions, or of having edited the correspondence in +corroboration of what he had latently resolved to see.", + 1 => " +There are not many persons who know what wonders are opened to them in the +stories and visions of their youth; for when as children we listen and dream, +we think but half-formed thoughts, and when as men we try to remember, we are +dulled and prosaic with the poison of life. But some of us awake in the night +with strange phantasms of enchanted hills and gardens, of fountains that sing +in the sun, of golden cliffs overhanging murmuring seas, of plains that stretch +down to sleeping cities of bronze and stone, and of shadowy companies of heroes +that ride caparisoned white horses along the edges of thick forests; and then +we know that we have looked back through the ivory gates into that world of +wonder which was ours before we were wise and unhappy.", + 2 => " +Instead of the poems I had hoped for, there came only a shuddering blackness +and ineffable loneliness; and I saw at last a fearful truth which no one had +ever dared to breathe before — the unwhisperable secret of secrets — The fact +that this city of stone and stridor is not a sentient perpetuation of Old New +York as London is of Old London and Paris of Old Paris, but that it is in fact +quite dead, its sprawling body imperfectly embalmed and infested with queer +animate things which have nothing to do with it as it was in life.", + 3 => " +The ocean ate the last of the land and poured into the smoking gulf, thereby +giving up all it had ever conquered. From the new-flooded lands it flowed +again, uncovering death and decay; and from its ancient and immemorial bed it +trickled loathsomely, uncovering nighted secrets of the years when Time was +young and the gods unborn. Above the waves rose weedy remembered spires. The +moon laid pale lilies of light on dead London, and Paris stood up from its damp +grave to be sanctified with star-dust. Then rose spires and monoliths that were +weedy but not remembered; terrible spires and monoliths of lands that men never +knew were lands...", + 4 => " +There was a night when winds from unknown spaces whirled us irresistibly into +limitless vacuum beyond all thought and entity. Perceptions of the most +maddeningly untransmissible sort thronged upon us; perceptions of infinity +which at the time convulsed us with joy, yet which are now partly lost to my +memory and partly incapable of presentation to others.", + _ => "You've met with a terrible fate, haven't you?" + }; + rterrln!("{}", ""); + rterrln!("{}", quote); + rterrln!("{}", ""); + rterrln!("fatal runtime error: {}", msg); + unsafe { intrinsics::abort(); } +} + +pub unsafe fn report_overflow() { + use thread::Thread; + + // See the message below for why this is not emitted to the + // ^ Where did the message below go? + // task's logger. This has the additional conundrum of the + // logger may not be initialized just yet, meaning that an FFI + // call would happen to initialized it (calling out to libuv), + // and the FFI call needs 2MB of stack when we just ran out. + + rterrln!("\nthread '{}' has overflowed its stack", + Thread::current().name().unwrap_or("<unknown>")); +} diff --git a/src/libstd/rtdeps.rs b/src/libstd/rtdeps.rs index 35a87137115..862808a9e3d 100644 --- a/src/libstd/rtdeps.rs +++ b/src/libstd/rtdeps.rs @@ -22,7 +22,7 @@ extern {} // LLVM implements the `frem` instruction as a call to `fmod`, which lives in // libm. Hence, we must explicitly link to it. // -// On Linux, librt and libdl are indirect dependencies via rustrt, +// On Linux, librt and libdl are indirect dependencies via std, // and binutils 2.22+ won't add them automatically #[cfg(target_os = "linux")] #[link(name = "dl")] diff --git a/src/libstd/sync/atomic.rs b/src/libstd/sync/atomic.rs new file mode 100644 index 00000000000..26778ef70b3 --- /dev/null +++ b/src/libstd/sync/atomic.rs @@ -0,0 +1,225 @@ +// Copyright 2012-2014 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. + +//! Atomic types +//! +//! Atomic types provide primitive shared-memory communication between +//! threads, and are the building blocks of other concurrent +//! types. +//! +//! This module defines atomic versions of a select number of primitive +//! types, including `AtomicBool`, `AtomicInt`, `AtomicUint`, and `AtomicOption`. +//! Atomic types present operations that, when used correctly, synchronize +//! updates between threads. +//! +//! Each method takes an `Ordering` which represents the strength of +//! the memory barrier for that operation. These orderings are the +//! same as [C++11 atomic orderings][1]. +//! +//! [1]: http://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync +//! +//! Atomic variables are safe to share between threads (they implement `Sync`) +//! but they do not themselves provide the mechanism for sharing. The most +//! common way to share an atomic variable is to put it into an `Arc` (an +//! atomically-reference-counted shared pointer). +//! +//! Most atomic types may be stored in static variables, initialized using +//! the provided static initializers like `INIT_ATOMIC_BOOL`. Atomic statics +//! are often used for lazy global initialization. +//! +//! +//! # Examples +//! +//! A simple spinlock: +//! +//! ``` +//! use std::sync::Arc; +//! use std::sync::atomic::{AtomicUint, SeqCst}; +//! use std::thread::Thread; +//! +//! fn main() { +//! let spinlock = Arc::new(AtomicUint::new(1)); +//! +//! let spinlock_clone = spinlock.clone(); +//! Thread::spawn(move|| { +//! spinlock_clone.store(0, SeqCst); +//! }).detach(); +//! +//! // Wait for the other task to release the lock +//! while spinlock.load(SeqCst) != 0 {} +//! } +//! ``` +//! +//! Transferring a heap object with `AtomicOption`: +//! +//! ``` +//! use std::sync::Arc; +//! use std::sync::atomic::{AtomicOption, SeqCst}; +//! use std::thread::Thread; +//! +//! fn main() { +//! struct BigObject; +//! +//! let shared_big_object = Arc::new(AtomicOption::empty()); +//! +//! let shared_big_object_clone = shared_big_object.clone(); +//! Thread::spawn(move|| { +//! let unwrapped_big_object = shared_big_object_clone.take(SeqCst); +//! if unwrapped_big_object.is_some() { +//! println!("got a big object from another task"); +//! } else { +//! println!("other task hasn't sent big object yet"); +//! } +//! }).detach(); +//! +//! shared_big_object.swap(box BigObject, SeqCst); +//! } +//! ``` +//! +//! Keep a global count of live tasks: +//! +//! ``` +//! use std::sync::atomic::{AtomicUint, SeqCst, INIT_ATOMIC_UINT}; +//! +//! static GLOBAL_TASK_COUNT: AtomicUint = INIT_ATOMIC_UINT; +//! +//! let old_task_count = GLOBAL_TASK_COUNT.fetch_add(1, SeqCst); +//! println!("live tasks: {}", old_task_count + 1); +//! ``` + +#![allow(deprecated)] + +use alloc::boxed::Box; +use core::mem; +use core::prelude::{Send, Drop, None, Option, Some}; + +pub use core::atomic::{AtomicBool, AtomicInt, AtomicUint, AtomicPtr}; +pub use core::atomic::{Ordering, Relaxed, Release, Acquire, AcqRel, SeqCst}; +pub use core::atomic::{INIT_ATOMIC_BOOL, INIT_ATOMIC_INT, INIT_ATOMIC_UINT}; +pub use core::atomic::fence; + +/// An atomic, nullable unique pointer +/// +/// This can be used as the concurrency primitive for operations that transfer +/// owned heap objects across tasks. +#[unsafe_no_drop_flag] +#[deprecated = "no longer used; will eventually be replaced by a higher-level\ + concept like MVar"] +pub struct AtomicOption<T> { + p: AtomicUint, +} + +impl<T: Send> AtomicOption<T> { + /// Create a new `AtomicOption` + pub fn new(p: Box<T>) -> AtomicOption<T> { + unsafe { AtomicOption { p: AtomicUint::new(mem::transmute(p)) } } + } + + /// Create a new `AtomicOption` that doesn't contain a value + pub fn empty() -> AtomicOption<T> { AtomicOption { p: AtomicUint::new(0) } } + + /// Store a value, returning the old value + #[inline] + pub fn swap(&self, val: Box<T>, order: Ordering) -> Option<Box<T>> { + let val = unsafe { mem::transmute(val) }; + + match self.p.swap(val, order) { + 0 => None, + n => Some(unsafe { mem::transmute(n) }), + } + } + + /// Remove the value, leaving the `AtomicOption` empty. + #[inline] + pub fn take(&self, order: Ordering) -> Option<Box<T>> { + unsafe { self.swap(mem::transmute(0u), order) } + } + + /// Replace an empty value with a non-empty value. + /// + /// Succeeds if the option is `None` and returns `None` if so. If + /// the option was already `Some`, returns `Some` of the rejected + /// value. + #[inline] + pub fn fill(&self, val: Box<T>, order: Ordering) -> Option<Box<T>> { + unsafe { + let val = mem::transmute(val); + let expected = mem::transmute(0u); + let oldval = self.p.compare_and_swap(expected, val, order); + if oldval == expected { + None + } else { + Some(mem::transmute(val)) + } + } + } + + /// Returns `true` if the `AtomicOption` is empty. + /// + /// Be careful: The caller must have some external method of ensuring the + /// result does not get invalidated by another task after this returns. + #[inline] + pub fn is_empty(&self, order: Ordering) -> bool { + self.p.load(order) as uint == 0 + } +} + +#[unsafe_destructor] +impl<T: Send> Drop for AtomicOption<T> { + fn drop(&mut self) { + let _ = self.take(SeqCst); + } +} + +#[cfg(test)] +mod test { + use prelude::*; + use super::*; + + #[test] + fn option_empty() { + let option: AtomicOption<()> = AtomicOption::empty(); + assert!(option.is_empty(SeqCst)); + } + + #[test] + fn option_swap() { + let p = AtomicOption::new(box 1i); + let a = box 2i; + + let b = p.swap(a, SeqCst); + + assert!(b == Some(box 1)); + assert!(p.take(SeqCst) == Some(box 2)); + } + + #[test] + fn option_take() { + let p = AtomicOption::new(box 1i); + + assert!(p.take(SeqCst) == Some(box 1)); + assert!(p.take(SeqCst) == None); + + let p2 = box 2i; + p.swap(p2, SeqCst); + + assert!(p.take(SeqCst) == Some(box 2)); + } + + #[test] + fn option_fill() { + let p = AtomicOption::new(box 1i); + assert!(p.fill(box 2i, SeqCst).is_some()); // should fail; shouldn't leak! + assert!(p.take(SeqCst) == Some(box 1)); + + assert!(p.fill(box 2i, SeqCst).is_none()); // shouldn't fail + assert!(p.take(SeqCst) == Some(box 2)); + } +} diff --git a/src/libstd/sync/barrier.rs b/src/libstd/sync/barrier.rs new file mode 100644 index 00000000000..6573d9273ce --- /dev/null +++ b/src/libstd/sync/barrier.rs @@ -0,0 +1,117 @@ +// Copyright 2014 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 sync::{Mutex, Condvar}; + +/// A barrier enables multiple tasks to synchronize the beginning +/// of some computation. +/// +/// ```rust +/// use std::sync::{Arc, Barrier}; +/// use std::thread::Thread; +/// +/// let barrier = Arc::new(Barrier::new(10)); +/// for _ in range(0u, 10) { +/// let c = barrier.clone(); +/// // The same messages will be printed together. +/// // You will NOT see any interleaving. +/// Thread::spawn(move|| { +/// println!("before wait"); +/// c.wait(); +/// println!("after wait"); +/// }).detach(); +/// } +/// ``` +pub struct Barrier { + lock: Mutex<BarrierState>, + cvar: Condvar, + num_threads: uint, +} + +// The inner state of a double barrier +struct BarrierState { + count: uint, + generation_id: uint, +} + +impl Barrier { + /// Create a new barrier that can block a given number of threads. + /// + /// A barrier will block `n`-1 threads which call `wait` and then wake up + /// all threads at once when the `n`th thread calls `wait`. + pub fn new(n: uint) -> Barrier { + Barrier { + lock: Mutex::new(BarrierState { + count: 0, + generation_id: 0, + }), + cvar: Condvar::new(), + num_threads: n, + } + } + + /// Block the current thread until all threads has rendezvoused here. + /// + /// Barriers are re-usable after all threads have rendezvoused once, and can + /// be used continuously. + pub fn wait(&self) { + let mut lock = self.lock.lock(); + let local_gen = lock.generation_id; + lock.count += 1; + if lock.count < self.num_threads { + // We need a while loop to guard against spurious wakeups. + // http://en.wikipedia.org/wiki/Spurious_wakeup + while local_gen == lock.generation_id && + lock.count < self.num_threads { + self.cvar.wait(&lock); + } + } else { + lock.count = 0; + lock.generation_id += 1; + self.cvar.notify_all(); + } + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + + use sync::{Arc, Barrier}; + use comm::Empty; + + #[test] + fn test_barrier() { + let barrier = Arc::new(Barrier::new(10)); + let (tx, rx) = channel(); + + for _ in range(0u, 9) { + let c = barrier.clone(); + let tx = tx.clone(); + spawn(move|| { + c.wait(); + tx.send(true); + }); + } + + // At this point, all spawned tasks should be blocked, + // so we shouldn't get anything from the port + assert!(match rx.try_recv() { + Err(Empty) => true, + _ => false, + }); + + barrier.wait(); + // Now, the barrier is cleared and we should get data. + for _ in range(0u, 9) { + rx.recv(); + } + } +} diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs new file mode 100644 index 00000000000..be27c06b83c --- /dev/null +++ b/src/libstd/sync/condvar.rs @@ -0,0 +1,365 @@ +// Copyright 2014 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 sync::atomic::{mod, AtomicUint}; +use sync::{mutex, StaticMutexGuard}; +use sys_common::condvar as sys; +use sys_common::mutex as sys_mutex; +use time::Duration; + +/// A Condition Variable +/// +/// Condition variables represent the ability to block a thread such that it +/// consumes no CPU time while waiting for an event to occur. Condition +/// variables are typically associated with a boolean predicate (a condition) +/// and a mutex. The predicate is always verified inside of the mutex before +/// determining that thread must block. +/// +/// Functions in this module will block the current **thread** of execution and +/// are bindings to system-provided condition variables where possible. Note +/// that this module places one additional restriction over the system condition +/// variables: each condvar can be used with precisely one mutex at runtime. Any +/// attempt to use multiple mutexes on the same condition variable will result +/// in a runtime panic. If this is not desired, then the unsafe primitives in +/// `sys` do not have this restriction but may result in undefined behavior. +/// +/// # Example +/// +/// ``` +/// use std::sync::{Arc, Mutex, Condvar}; +/// use std::thread::Thread; +/// +/// let pair = Arc::new((Mutex::new(false), Condvar::new())); +/// let pair2 = pair.clone(); +/// +/// // Inside of our lock, spawn a new thread, and then wait for it to start +/// Thread::spawn(move|| { +/// let &(ref lock, ref cvar) = &*pair2; +/// let mut started = lock.lock(); +/// *started = true; +/// cvar.notify_one(); +/// }).detach(); +/// +/// // wait for the thread to start up +/// let &(ref lock, ref cvar) = &*pair; +/// let started = lock.lock(); +/// while !*started { +/// cvar.wait(&started); +/// } +/// ``` +pub struct Condvar { inner: Box<StaticCondvar> } + +/// Statically allocated condition variables. +/// +/// This structure is identical to `Condvar` except that it is suitable for use +/// in static initializers for other structures. +/// +/// # Example +/// +/// ``` +/// use std::sync::{StaticCondvar, CONDVAR_INIT}; +/// +/// static CVAR: StaticCondvar = CONDVAR_INIT; +/// ``` +pub struct StaticCondvar { + inner: sys::Condvar, + mutex: AtomicUint, +} + +/// Constant initializer for a statically allocated condition variable. +pub const CONDVAR_INIT: StaticCondvar = StaticCondvar { + inner: sys::CONDVAR_INIT, + mutex: atomic::INIT_ATOMIC_UINT, +}; + +/// A trait for vaules which can be passed to the waiting methods of condition +/// variables. This is implemented by the mutex guards in this module. +/// +/// Note that this trait should likely not be implemented manually unless you +/// really know what you're doing. +pub trait AsMutexGuard { + #[allow(missing_docs)] + unsafe fn as_mutex_guard(&self) -> &StaticMutexGuard; +} + +impl Condvar { + /// Creates a new condition variable which is ready to be waited on and + /// notified. + pub fn new() -> Condvar { + Condvar { + inner: box StaticCondvar { + inner: unsafe { sys::Condvar::new() }, + mutex: AtomicUint::new(0), + } + } + } + + /// Block the current thread until this condition variable receives a + /// notification. + /// + /// This function will atomically unlock the mutex specified (represented by + /// `guard`) and block the current thread. This means that any calls to + /// `notify_*()` which happen logically after the mutex is unlocked are + /// candidates to wake this thread up. When this function call returns, the + /// lock specified will have been re-acquired. + /// + /// Note that this function is susceptible to spurious wakeups. Condition + /// variables normally have a boolean predicate associated with them, and + /// the predicate must always be checked each time this function returns to + /// protect against spurious wakeups. + /// + /// # Panics + /// + /// This function will `panic!()` if it is used with more than one mutex + /// over time. Each condition variable is dynamically bound to exactly one + /// mutex to ensure defined behavior across platforms. If this functionality + /// is not desired, then unsafe primitives in `sys` are provided. + pub fn wait<T: AsMutexGuard>(&self, mutex_guard: &T) { + unsafe { + let me: &'static Condvar = &*(self as *const _); + me.inner.wait(mutex_guard) + } + } + + /// Wait on this condition variable for a notification, timing out after a + /// specified duration. + /// + /// The semantics of this function are equivalent to `wait()` except that + /// the thread will be blocked for roughly no longer than `dur`. This method + /// should not be used for precise timing due to anomalies such as + /// preemption or platform differences that may not cause the maximum amount + /// of time waited to be precisely `dur`. + /// + /// If the wait timed out, then `false` will be returned. Otherwise if a + /// notification was received then `true` will be returned. + /// + /// Like `wait`, the lock specified will be re-acquired when this function + /// returns, regardless of whether the timeout elapsed or not. + // Note that this method is *not* public, and this is quite intentional + // because we're not quite sure about the semantics of relative vs absolute + // durations or how the timing guarantees play into what the system APIs + // provide. There are also additional concerns about the unix-specific + // implementation which may need to be addressed. + #[allow(dead_code)] + fn wait_timeout<T: AsMutexGuard>(&self, mutex_guard: &T, + dur: Duration) -> bool { + unsafe { + let me: &'static Condvar = &*(self as *const _); + me.inner.wait_timeout(mutex_guard, dur) + } + } + + /// Wake up one blocked thread on this condvar. + /// + /// If there is a blocked thread on this condition variable, then it will + /// be woken up from its call to `wait` or `wait_timeout`. Calls to + /// `notify_one` are not buffered in any way. + /// + /// To wake up all threads, see `notify_one()`. + pub fn notify_one(&self) { unsafe { self.inner.inner.notify_one() } } + + /// Wake up all blocked threads on this condvar. + /// + /// This method will ensure that any current waiters on the condition + /// variable are awoken. Calls to `notify_all()` are not buffered in any + /// way. + /// + /// To wake up only one thread, see `notify_one()`. + pub fn notify_all(&self) { unsafe { self.inner.inner.notify_all() } } +} + +impl Drop for Condvar { + fn drop(&mut self) { + unsafe { self.inner.inner.destroy() } + } +} + +impl StaticCondvar { + /// Block the current thread until this condition variable receives a + /// notification. + /// + /// See `Condvar::wait`. + pub fn wait<T: AsMutexGuard>(&'static self, mutex_guard: &T) { + unsafe { + let lock = mutex_guard.as_mutex_guard(); + let sys = mutex::guard_lock(lock); + self.verify(sys); + self.inner.wait(sys); + (*mutex::guard_poison(lock)).check("mutex"); + } + } + + /// Wait on this condition variable for a notification, timing out after a + /// specified duration. + /// + /// See `Condvar::wait_timeout`. + #[allow(dead_code)] // may want to stabilize this later, see wait_timeout above + fn wait_timeout<T: AsMutexGuard>(&'static self, mutex_guard: &T, + dur: Duration) -> bool { + unsafe { + let lock = mutex_guard.as_mutex_guard(); + let sys = mutex::guard_lock(lock); + self.verify(sys); + let ret = self.inner.wait_timeout(sys, dur); + (*mutex::guard_poison(lock)).check("mutex"); + return ret; + } + } + + /// Wake up one blocked thread on this condvar. + /// + /// See `Condvar::notify_one`. + pub fn notify_one(&'static self) { unsafe { self.inner.notify_one() } } + + /// Wake up all blocked threads on this condvar. + /// + /// See `Condvar::notify_all`. + pub fn notify_all(&'static self) { unsafe { self.inner.notify_all() } } + + /// Deallocate all resources associated with this static condvar. + /// + /// This method is unsafe to call as there is no guarantee that there are no + /// active users of the condvar, and this also doesn't prevent any future + /// users of the condvar. This method is required to be called to not leak + /// memory on all platforms. + pub unsafe fn destroy(&'static self) { + self.inner.destroy() + } + + fn verify(&self, mutex: &sys_mutex::Mutex) { + let addr = mutex as *const _ as uint; + match self.mutex.compare_and_swap(0, addr, atomic::SeqCst) { + // If we got out 0, then we have successfully bound the mutex to + // this cvar. + 0 => {} + + // If we get out a value that's the same as `addr`, then someone + // already beat us to the punch. + n if n == addr => {} + + // Anything else and we're using more than one mutex on this cvar, + // which is currently disallowed. + _ => panic!("attempted to use a condition variable with two \ + mutexes"), + } + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + + use time::Duration; + use super::{StaticCondvar, CONDVAR_INIT}; + use sync::{StaticMutex, MUTEX_INIT, Condvar, Mutex, Arc}; + + #[test] + fn smoke() { + let c = Condvar::new(); + c.notify_one(); + c.notify_all(); + } + + #[test] + fn static_smoke() { + static C: StaticCondvar = CONDVAR_INIT; + C.notify_one(); + C.notify_all(); + unsafe { C.destroy(); } + } + + #[test] + fn notify_one() { + static C: StaticCondvar = CONDVAR_INIT; + static M: StaticMutex = MUTEX_INIT; + + let g = M.lock(); + spawn(move|| { + let _g = M.lock(); + C.notify_one(); + }); + C.wait(&g); + drop(g); + unsafe { C.destroy(); M.destroy(); } + } + + #[test] + fn notify_all() { + const N: uint = 10; + + let data = Arc::new((Mutex::new(0), Condvar::new())); + let (tx, rx) = channel(); + for _ in range(0, N) { + let data = data.clone(); + let tx = tx.clone(); + spawn(move|| { + let &(ref lock, ref cond) = &*data; + let mut cnt = lock.lock(); + *cnt += 1; + if *cnt == N { + tx.send(()); + } + while *cnt != 0 { + cond.wait(&cnt); + } + tx.send(()); + }); + } + drop(tx); + + let &(ref lock, ref cond) = &*data; + rx.recv(); + let mut cnt = lock.lock(); + *cnt = 0; + cond.notify_all(); + drop(cnt); + + for _ in range(0, N) { + rx.recv(); + } + } + + #[test] + fn wait_timeout() { + static C: StaticCondvar = CONDVAR_INIT; + static M: StaticMutex = MUTEX_INIT; + + let g = M.lock(); + assert!(!C.wait_timeout(&g, Duration::nanoseconds(1000))); + spawn(move|| { + let _g = M.lock(); + C.notify_one(); + }); + assert!(C.wait_timeout(&g, Duration::days(1))); + drop(g); + unsafe { C.destroy(); M.destroy(); } + } + + #[test] + #[should_fail] + fn two_mutexes() { + static M1: StaticMutex = MUTEX_INIT; + static M2: StaticMutex = MUTEX_INIT; + static C: StaticCondvar = CONDVAR_INIT; + + let g = M1.lock(); + spawn(move|| { + let _g = M1.lock(); + C.notify_one(); + }); + C.wait(&g); + drop(g); + + C.wait(&M2.lock()); + + } +} diff --git a/src/libstd/sync/future.rs b/src/libstd/sync/future.rs index e37d1f83877..51899a87a32 100644 --- a/src/libstd/sync/future.rs +++ b/src/libstd/sync/future.rs @@ -8,21 +8,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -/*! - * A type representing values that may be computed concurrently and - * operations for working with them. - * - * # Example - * - * ```rust - * use std::sync::Future; - * # fn fib(n: uint) -> uint {42}; - * # fn make_a_sandwich() {}; - * let mut delayed_fib = Future::spawn(proc() { fib(5000) }); - * make_a_sandwich(); - * println!("fib(5000) = {}", delayed_fib.get()) - * ``` - */ +//! A type representing values that may be computed concurrently and operations for working with +//! them. +//! +//! # Example +//! +//! ```rust +//! use std::sync::Future; +//! # fn fib(n: uint) -> uint {42}; +//! # fn make_a_sandwich() {}; +//! let mut delayed_fib = Future::spawn(move|| { fib(5000) }); +//! make_a_sandwich(); +//! println!("fib(5000) = {}", delayed_fib.get()) +//! ``` #![allow(missing_docs)] @@ -31,7 +29,8 @@ use core::mem::replace; use self::FutureState::*; use comm::{Receiver, channel}; -use task::spawn; +use thunk::{Thunk}; +use thread::Thread; /// A type encapsulating the result of a computation which may not be complete pub struct Future<A> { @@ -39,7 +38,7 @@ pub struct Future<A> { } enum FutureState<A> { - Pending(proc():Send -> A), + Pending(Thunk<(),A>), Evaluating, Forced(A) } @@ -54,7 +53,7 @@ impl<A:Clone> Future<A> { impl<A> Future<A> { /// Gets the value from this future, forcing evaluation. - pub fn unwrap(mut self) -> A { + pub fn into_inner(mut self) -> A { self.get_ref(); let state = replace(&mut self.state, Evaluating); match state { @@ -63,6 +62,10 @@ impl<A> Future<A> { } } + /// Deprecated, use into_inner() instead + #[deprecated = "renamed to into_inner()"] + pub fn unwrap(self) -> A { self.into_inner() } + pub fn get_ref<'a>(&'a mut self) -> &'a A { /*! * Executes the future's closure and then returns a reference @@ -76,7 +79,7 @@ impl<A> Future<A> { match replace(&mut self.state, Evaluating) { Forced(_) | Evaluating => panic!("Logic error."), Pending(f) => { - self.state = Forced(f()); + self.state = Forced(f.invoke(())); self.get_ref() } } @@ -95,7 +98,9 @@ impl<A> Future<A> { Future {state: Forced(val)} } - pub fn from_fn(f: proc():Send -> A) -> Future<A> { + pub fn from_fn<F>(f: F) -> Future<A> + where F : FnOnce() -> A, F : Send + { /*! * Create a future from a function. * @@ -104,7 +109,7 @@ impl<A> Future<A> { * function. It is not spawned into another task. */ - Future {state: Pending(f)} + Future {state: Pending(Thunk::new(f))} } } @@ -117,12 +122,14 @@ impl<A:Send> Future<A> { * waiting for the result to be received on the port. */ - Future::from_fn(proc() { + Future::from_fn(move|:| { rx.recv() }) } - pub fn spawn(blk: proc():Send -> A) -> Future<A> { + pub fn spawn<F>(blk: F) -> Future<A> + where F : FnOnce() -> A, F : Send + { /*! * Create a future from a unique closure. * @@ -132,10 +139,10 @@ impl<A:Send> Future<A> { let (tx, rx) = channel(); - spawn(proc() { + Thread::spawn(move |:| { // Don't panic if the other end has hung up let _ = tx.send_opt(blk()); - }); + }).detach(); Future::from_receiver(rx) } @@ -146,12 +153,11 @@ mod test { use prelude::*; use sync::Future; use task; - use comm::{channel, Sender}; #[test] fn test_from_value() { let mut f = Future::from_value("snail".to_string()); - assert_eq!(f.get(), "snail".to_string()); + assert_eq!(f.get(), "snail"); } #[test] @@ -159,25 +165,25 @@ mod test { let (tx, rx) = channel(); tx.send("whale".to_string()); let mut f = Future::from_receiver(rx); - assert_eq!(f.get(), "whale".to_string()); + assert_eq!(f.get(), "whale"); } #[test] fn test_from_fn() { - let mut f = Future::from_fn(proc() "brail".to_string()); - assert_eq!(f.get(), "brail".to_string()); + let mut f = Future::from_fn(move|| "brail".to_string()); + assert_eq!(f.get(), "brail"); } #[test] fn test_interface_get() { let mut f = Future::from_value("fail".to_string()); - assert_eq!(f.get(), "fail".to_string()); + assert_eq!(f.get(), "fail"); } #[test] fn test_interface_unwrap() { let f = Future::from_value("fail".to_string()); - assert_eq!(f.unwrap(), "fail".to_string()); + assert_eq!(f.unwrap(), "fail"); } #[test] @@ -188,14 +194,14 @@ mod test { #[test] fn test_spawn() { - let mut f = Future::spawn(proc() "bale".to_string()); - assert_eq!(f.get(), "bale".to_string()); + let mut f = Future::spawn(move|| "bale".to_string()); + assert_eq!(f.get(), "bale"); } #[test] #[should_fail] fn test_future_panic() { - let mut f = Future::spawn(proc() panic!()); + let mut f = Future::spawn(move|| panic!()); let _x: String = f.get(); } @@ -203,35 +209,11 @@ mod test { fn test_sendable_future() { let expected = "schlorf"; let (tx, rx) = channel(); - let f = Future::spawn(proc() { expected }); - task::spawn(proc() { + let f = Future::spawn(move|| { expected }); + task::spawn(move|| { let mut f = f; tx.send(f.get()); }); assert_eq!(rx.recv(), expected); } - - #[test] - fn test_dropped_future_doesnt_panic() { - struct Bomb(Sender<bool>); - - local_data_key!(LOCAL: Bomb) - - impl Drop for Bomb { - fn drop(&mut self) { - let Bomb(ref tx) = *self; - tx.send(task::failing()); - } - } - - // Spawn a future, but drop it immediately. When we receive the result - // later on, we should never view the task as having panicked. - let (tx, rx) = channel(); - drop(Future::spawn(proc() { - LOCAL.replace(Some(Bomb(tx))); - })); - - // Make sure the future didn't panic the task. - assert!(!rx.recv()); - } } diff --git a/src/libstd/sync/mod.rs b/src/libstd/sync/mod.rs index 38e1e952f77..7605a6a96a0 100644 --- a/src/libstd/sync/mod.rs +++ b/src/libstd/sync/mod.rs @@ -17,17 +17,27 @@ #![experimental] -#[stable] -pub use core_sync::atomic; +pub use alloc::arc::{Arc, Weak}; -pub use core_sync::{deque, mpmc_bounded_queue, mpsc_queue, spsc_queue}; -pub use core_sync::{Arc, Weak, Mutex, MutexGuard, Condvar, Barrier}; -pub use core_sync::{RWLock, RWLockReadGuard, RWLockWriteGuard}; -pub use core_sync::{Semaphore, SemaphoreGuard}; -pub use core_sync::one::{Once, ONCE_INIT}; +pub use self::mutex::{Mutex, MutexGuard, StaticMutex, StaticMutexGuard, MUTEX_INIT}; +pub use self::rwlock::{RWLock, StaticRWLock, RWLOCK_INIT}; +pub use self::rwlock::{RWLockReadGuard, RWLockWriteGuard}; +pub use self::rwlock::{StaticRWLockReadGuard, StaticRWLockWriteGuard}; +pub use self::condvar::{Condvar, StaticCondvar, CONDVAR_INIT, AsMutexGuard}; +pub use self::once::{Once, ONCE_INIT}; +pub use self::semaphore::{Semaphore, SemaphoreGuard}; +pub use self::barrier::Barrier; pub use self::future::Future; pub use self::task_pool::TaskPool; +pub mod atomic; +mod barrier; +mod condvar; mod future; +mod mutex; +mod once; +mod poison; +mod rwlock; +mod semaphore; mod task_pool; diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs new file mode 100644 index 00000000000..4829be569cc --- /dev/null +++ b/src/libstd/sync/mutex.rs @@ -0,0 +1,433 @@ +// Copyright 2014 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 cell::UnsafeCell; +use kinds::marker; +use sync::{poison, AsMutexGuard}; +use sys_common::mutex as sys; + +/// A mutual exclusion primitive useful for protecting shared data +/// +/// This mutex will block threads waiting for the lock to become available. The +/// mutex can also be statically initialized or created via a `new` +/// constructor. Each mutex has a type parameter which represents the data that +/// it is protecting. The data can only be accessed through the RAII guards +/// returned from `lock` and `try_lock`, which guarantees that the data is only +/// ever accessed when the mutex is locked. +/// +/// # Poisoning +/// +/// In order to prevent access to otherwise invalid data, each mutex will +/// propagate any panics which occur while the lock is held. Once a thread has +/// panicked while holding the lock, then all other threads will immediately +/// panic as well once they hold the lock. +/// +/// # Example +/// +/// ```rust +/// use std::sync::{Arc, Mutex}; +/// use std::thread::Thread; +/// const N: uint = 10; +/// +/// // Spawn a few threads to increment a shared variable (non-atomically), and +/// // let the main thread know once all increments are done. +/// // +/// // Here we're using an Arc to share memory among tasks, and the data inside +/// // the Arc is protected with a mutex. +/// let data = Arc::new(Mutex::new(0)); +/// +/// let (tx, rx) = channel(); +/// for _ in range(0u, 10) { +/// let (data, tx) = (data.clone(), tx.clone()); +/// Thread::spawn(move|| { +/// // The shared static can only be accessed once the lock is held. +/// // Our non-atomic increment is safe because we're the only thread +/// // which can access the shared state when the lock is held. +/// let mut data = data.lock(); +/// *data += 1; +/// if *data == N { +/// tx.send(()); +/// } +/// // the lock is unlocked here when `data` goes out of scope. +/// }).detach(); +/// } +/// +/// rx.recv(); +/// ``` +pub struct Mutex<T> { + // Note that this static mutex is in a *box*, not inlined into the struct + // itself. Once a native mutex has been used once, its address can never + // change (it can't be moved). This mutex type can be safely moved at any + // time, so to ensure that the native mutex is used correctly we box the + // inner lock to give it a constant address. + inner: Box<StaticMutex>, + data: UnsafeCell<T>, +} + +/// The static mutex type is provided to allow for static allocation of mutexes. +/// +/// Note that this is a separate type because using a Mutex correctly means that +/// it needs to have a destructor run. In Rust, statics are not allowed to have +/// destructors. As a result, a `StaticMutex` has one extra method when compared +/// to a `Mutex`, a `destroy` method. This method is unsafe to call, and +/// documentation can be found directly on the method. +/// +/// # Example +/// +/// ```rust +/// use std::sync::{StaticMutex, MUTEX_INIT}; +/// +/// static LOCK: StaticMutex = MUTEX_INIT; +/// +/// { +/// let _g = LOCK.lock(); +/// // do some productive work +/// } +/// // lock is unlocked here. +/// ``` +pub struct StaticMutex { + lock: sys::Mutex, + poison: UnsafeCell<poison::Flag>, +} + +/// An RAII implementation of a "scoped lock" of a mutex. When this structure is +/// dropped (falls out of scope), the lock will be unlocked. +/// +/// The data protected by the mutex can be access through this guard via its +/// Deref and DerefMut implementations +#[must_use] +pub struct MutexGuard<'a, T: 'a> { + // funny underscores due to how Deref/DerefMut currently work (they + // disregard field privacy). + __lock: &'a Mutex<T>, + __guard: StaticMutexGuard, +} + +/// An RAII implementation of a "scoped lock" of a static mutex. When this +/// structure is dropped (falls out of scope), the lock will be unlocked. +#[must_use] +pub struct StaticMutexGuard { + lock: &'static sys::Mutex, + marker: marker::NoSend, + poison: poison::Guard<'static>, +} + +/// Static initialization of a mutex. This constant can be used to initialize +/// other mutex constants. +pub const MUTEX_INIT: StaticMutex = StaticMutex { + lock: sys::MUTEX_INIT, + poison: UnsafeCell { value: poison::Flag { failed: false } }, +}; + +impl<T: Send> Mutex<T> { + /// Creates a new mutex in an unlocked state ready for use. + pub fn new(t: T) -> Mutex<T> { + Mutex { + inner: box MUTEX_INIT, + data: UnsafeCell::new(t), + } + } + + /// Acquires a mutex, blocking the current task until it is able to do so. + /// + /// This function will block the local task until it is available to acquire + /// the mutex. Upon returning, the task is the only task with the mutex + /// held. An RAII guard is returned to allow scoped unlock of the lock. When + /// the guard goes out of scope, the mutex will be unlocked. + /// + /// # Panics + /// + /// If another user of this mutex panicked while holding the mutex, then + /// this call will immediately panic once the mutex is acquired. + pub fn lock(&self) -> MutexGuard<T> { + unsafe { + let lock: &'static StaticMutex = &*(&*self.inner as *const _); + MutexGuard::new(self, lock.lock()) + } + } + + /// Attempts to acquire this lock. + /// + /// If the lock could not be acquired at this time, then `None` is returned. + /// Otherwise, an RAII guard is returned. The lock will be unlocked when the + /// guard is dropped. + /// + /// This function does not block. + /// + /// # Panics + /// + /// If another user of this mutex panicked while holding the mutex, then + /// this call will immediately panic if the mutex would otherwise be + /// acquired. + pub fn try_lock(&self) -> Option<MutexGuard<T>> { + unsafe { + let lock: &'static StaticMutex = &*(&*self.inner as *const _); + lock.try_lock().map(|guard| { + MutexGuard::new(self, guard) + }) + } + } +} + +#[unsafe_destructor] +impl<T: Send> Drop for Mutex<T> { + fn drop(&mut self) { + // This is actually safe b/c we know that there is no further usage of + // this mutex (it's up to the user to arrange for a mutex to get + // dropped, that's not our job) + unsafe { self.inner.lock.destroy() } + } +} + +impl StaticMutex { + /// Acquires this lock, see `Mutex::lock` + pub fn lock(&'static self) -> StaticMutexGuard { + unsafe { self.lock.lock() } + StaticMutexGuard::new(self) + } + + /// Attempts to grab this lock, see `Mutex::try_lock` + pub fn try_lock(&'static self) -> Option<StaticMutexGuard> { + if unsafe { self.lock.try_lock() } { + Some(StaticMutexGuard::new(self)) + } else { + None + } + } + + /// Deallocates resources associated with this static mutex. + /// + /// This method is unsafe because it provides no guarantees that there are + /// no active users of this mutex, and safety is not guaranteed if there are + /// active users of this mutex. + /// + /// This method is required to ensure that there are no memory leaks on + /// *all* platforms. It may be the case that some platforms do not leak + /// memory if this method is not called, but this is not guaranteed to be + /// true on all platforms. + pub unsafe fn destroy(&'static self) { + self.lock.destroy() + } +} + +impl<'mutex, T> MutexGuard<'mutex, T> { + fn new(lock: &Mutex<T>, guard: StaticMutexGuard) -> MutexGuard<T> { + MutexGuard { __lock: lock, __guard: guard } + } +} + +impl<'mutex, T> AsMutexGuard for MutexGuard<'mutex, T> { + unsafe fn as_mutex_guard(&self) -> &StaticMutexGuard { &self.__guard } +} + +impl<'mutex, T> Deref<T> for MutexGuard<'mutex, T> { + fn deref<'a>(&'a self) -> &'a T { unsafe { &*self.__lock.data.get() } } +} +impl<'mutex, T> DerefMut<T> for MutexGuard<'mutex, T> { + fn deref_mut<'a>(&'a mut self) -> &'a mut T { + unsafe { &mut *self.__lock.data.get() } + } +} + +impl StaticMutexGuard { + fn new(lock: &'static StaticMutex) -> StaticMutexGuard { + unsafe { + let guard = StaticMutexGuard { + lock: &lock.lock, + marker: marker::NoSend, + poison: (*lock.poison.get()).borrow(), + }; + guard.poison.check("mutex"); + return guard; + } + } +} + +pub fn guard_lock(guard: &StaticMutexGuard) -> &sys::Mutex { guard.lock } +pub fn guard_poison(guard: &StaticMutexGuard) -> &poison::Guard { + &guard.poison +} + +impl AsMutexGuard for StaticMutexGuard { + unsafe fn as_mutex_guard(&self) -> &StaticMutexGuard { self } +} + +#[unsafe_destructor] +impl Drop for StaticMutexGuard { + fn drop(&mut self) { + unsafe { + self.poison.done(); + self.lock.unlock(); + } + } +} + +#[cfg(test)] +mod test { + use prelude::*; + + use thread::Thread; + use sync::{Arc, Mutex, StaticMutex, MUTEX_INIT, Condvar}; + + #[test] + fn smoke() { + let m = Mutex::new(()); + drop(m.lock()); + drop(m.lock()); + } + + #[test] + fn smoke_static() { + static M: StaticMutex = MUTEX_INIT; + unsafe { + drop(M.lock()); + drop(M.lock()); + M.destroy(); + } + } + + #[test] + fn lots_and_lots() { + static M: StaticMutex = MUTEX_INIT; + static mut CNT: uint = 0; + static J: uint = 1000; + static K: uint = 3; + + fn inc() { + for _ in range(0, J) { + unsafe { + let _g = M.lock(); + CNT += 1; + } + } + } + + let (tx, rx) = channel(); + for _ in range(0, K) { + let tx2 = tx.clone(); + spawn(move|| { inc(); tx2.send(()); }); + let tx2 = tx.clone(); + spawn(move|| { inc(); tx2.send(()); }); + } + + drop(tx); + for _ in range(0, 2 * K) { + rx.recv(); + } + assert_eq!(unsafe {CNT}, J * K * 2); + unsafe { + M.destroy(); + } + } + + #[test] + fn try_lock() { + let m = Mutex::new(()); + assert!(m.try_lock().is_some()); + } + + #[test] + fn test_mutex_arc_condvar() { + let arc = Arc::new((Mutex::new(false), Condvar::new())); + let arc2 = arc.clone(); + let (tx, rx) = channel(); + spawn(move|| { + // wait until parent gets in + rx.recv(); + let &(ref lock, ref cvar) = &*arc2; + let mut lock = lock.lock(); + *lock = true; + cvar.notify_one(); + }); + + let &(ref lock, ref cvar) = &*arc; + let lock = lock.lock(); + tx.send(()); + assert!(!*lock); + while !*lock { + cvar.wait(&lock); + } + } + + #[test] + #[should_fail] + fn test_arc_condvar_poison() { + let arc = Arc::new((Mutex::new(1i), Condvar::new())); + let arc2 = arc.clone(); + let (tx, rx) = channel(); + + spawn(move|| { + rx.recv(); + let &(ref lock, ref cvar) = &*arc2; + let _g = lock.lock(); + cvar.notify_one(); + // Parent should fail when it wakes up. + panic!(); + }); + + let &(ref lock, ref cvar) = &*arc; + let lock = lock.lock(); + tx.send(()); + while *lock == 1 { + cvar.wait(&lock); + } + } + + #[test] + #[should_fail] + fn test_mutex_arc_poison() { + let arc = Arc::new(Mutex::new(1i)); + let arc2 = arc.clone(); + let _ = Thread::spawn(move|| { + let lock = arc2.lock(); + assert_eq!(*lock, 2); + }).join(); + let lock = arc.lock(); + assert_eq!(*lock, 1); + } + + #[test] + fn test_mutex_arc_nested() { + // Tests nested mutexes and access + // to underlying data. + let arc = Arc::new(Mutex::new(1i)); + let arc2 = Arc::new(Mutex::new(arc)); + let (tx, rx) = channel(); + spawn(move|| { + let lock = arc2.lock(); + let lock2 = lock.deref().lock(); + assert_eq!(*lock2, 1); + tx.send(()); + }); + rx.recv(); + } + + #[test] + fn test_mutex_arc_access_in_unwind() { + let arc = Arc::new(Mutex::new(1i)); + let arc2 = arc.clone(); + let _ = Thread::spawn(move|| -> () { + struct Unwinder { + i: Arc<Mutex<int>>, + } + impl Drop for Unwinder { + fn drop(&mut self) { + *self.i.lock() += 1; + } + } + let _u = Unwinder { i: arc2 }; + panic!(); + }).join(); + let lock = arc.lock(); + assert_eq!(*lock, 2); + } +} diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs new file mode 100644 index 00000000000..a43f822e351 --- /dev/null +++ b/src/libstd/sync/once.rs @@ -0,0 +1,170 @@ +// Copyright 2014 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. + +//! A "once initialization" primitive +//! +//! This primitive is meant to be used to run one-time initialization. An +//! example use case would be for initializing an FFI library. + +use int; +use mem::drop; +use ops::FnOnce; +use sync::atomic; +use sync::{StaticMutex, MUTEX_INIT}; + +/// A synchronization primitive which can be used to run a one-time global +/// initialization. Useful for one-time initialization for FFI or related +/// functionality. This type can only be constructed with the `ONCE_INIT` +/// value. +/// +/// # Example +/// +/// ```rust +/// use std::sync::{Once, ONCE_INIT}; +/// +/// static START: Once = ONCE_INIT; +/// +/// START.doit(|| { +/// // run initialization here +/// }); +/// ``` +pub struct Once { + mutex: StaticMutex, + cnt: atomic::AtomicInt, + lock_cnt: atomic::AtomicInt, +} + +/// Initialization value for static `Once` values. +pub const ONCE_INIT: Once = Once { + mutex: MUTEX_INIT, + cnt: atomic::INIT_ATOMIC_INT, + lock_cnt: atomic::INIT_ATOMIC_INT, +}; + +impl Once { + /// Perform an initialization routine once and only once. The given closure + /// will be executed if this is the first time `doit` has been called, and + /// otherwise the routine will *not* be invoked. + /// + /// This method will block the calling task if another initialization + /// routine is currently running. + /// + /// When this function returns, it is guaranteed that some initialization + /// has run and completed (it may not be the closure specified). + pub fn doit<F>(&'static self, f: F) where F: FnOnce() { + // Optimize common path: load is much cheaper than fetch_add. + if self.cnt.load(atomic::SeqCst) < 0 { + return + } + + // Implementation-wise, this would seem like a fairly trivial primitive. + // The stickler part is where our mutexes currently require an + // allocation, and usage of a `Once` shouldn't leak this allocation. + // + // This means that there must be a deterministic destroyer of the mutex + // contained within (because it's not needed after the initialization + // has run). + // + // The general scheme here is to gate all future threads once + // initialization has completed with a "very negative" count, and to + // allow through threads to lock the mutex if they see a non negative + // count. For all threads grabbing the mutex, exactly one of them should + // be responsible for unlocking the mutex, and this should only be done + // once everyone else is done with the mutex. + // + // This atomicity is achieved by swapping a very negative value into the + // shared count when the initialization routine has completed. This will + // read the number of threads which will at some point attempt to + // acquire the mutex. This count is then squirreled away in a separate + // variable, and the last person on the way out of the mutex is then + // responsible for destroying the mutex. + // + // It is crucial that the negative value is swapped in *after* the + // initialization routine has completed because otherwise new threads + // calling `doit` will return immediately before the initialization has + // completed. + + let prev = self.cnt.fetch_add(1, atomic::SeqCst); + if prev < 0 { + // Make sure we never overflow, we'll never have int::MIN + // simultaneous calls to `doit` to make this value go back to 0 + self.cnt.store(int::MIN, atomic::SeqCst); + return + } + + // If the count is negative, then someone else finished the job, + // otherwise we run the job and record how many people will try to grab + // this lock + let guard = self.mutex.lock(); + if self.cnt.load(atomic::SeqCst) > 0 { + f(); + let prev = self.cnt.swap(int::MIN, atomic::SeqCst); + self.lock_cnt.store(prev, atomic::SeqCst); + } + drop(guard); + + // Last one out cleans up after everyone else, no leaks! + if self.lock_cnt.fetch_add(-1, atomic::SeqCst) == 1 { + unsafe { self.mutex.destroy() } + } + } +} + +#[cfg(test)] +mod test { + use prelude::*; + + use thread::Thread; + use super::{ONCE_INIT, Once}; + + #[test] + fn smoke_once() { + static O: Once = ONCE_INIT; + let mut a = 0i; + O.doit(|| a += 1); + assert_eq!(a, 1); + O.doit(|| a += 1); + assert_eq!(a, 1); + } + + #[test] + fn stampede_once() { + static O: Once = ONCE_INIT; + static mut run: bool = false; + + let (tx, rx) = channel(); + for _ in range(0u, 10) { + let tx = tx.clone(); + spawn(move|| { + for _ in range(0u, 4) { Thread::yield_now() } + unsafe { + O.doit(|| { + assert!(!run); + run = true; + }); + assert!(run); + } + tx.send(()); + }); + } + + unsafe { + O.doit(|| { + assert!(!run); + run = true; + }); + assert!(run); + } + + for _ in range(0u, 10) { + rx.recv(); + } + } +} diff --git a/src/libstd/sync/poison.rs b/src/libstd/sync/poison.rs new file mode 100644 index 00000000000..ad08e9873fa --- /dev/null +++ b/src/libstd/sync/poison.rs @@ -0,0 +1,38 @@ +// Copyright 2014 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 thread::Thread; + +pub struct Flag { pub failed: bool } + +impl Flag { + pub fn borrow(&mut self) -> Guard { + Guard { flag: &mut self.failed, panicking: Thread::panicking() } + } +} + +pub struct Guard<'a> { + flag: &'a mut bool, + panicking: bool, +} + +impl<'a> Guard<'a> { + pub fn check(&self, name: &str) { + if *self.flag { + panic!("poisoned {} - another task failed inside", name); + } + } + + pub fn done(&mut self) { + if !self.panicking && Thread::panicking() { + *self.flag = true; + } + } +} diff --git a/src/libstd/sync/rwlock.rs b/src/libstd/sync/rwlock.rs new file mode 100644 index 00000000000..3f177a42f44 --- /dev/null +++ b/src/libstd/sync/rwlock.rs @@ -0,0 +1,514 @@ +// Copyright 2014 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 kinds::marker; +use cell::UnsafeCell; +use sys_common::rwlock as sys; +use sync::poison; + +/// A reader-writer lock +/// +/// This type of lock allows a number of readers or at most one writer at any +/// point in time. The write portion of this lock typically allows modification +/// of the underlying data (exclusive access) and the read portion of this lock +/// typically allows for read-only access (shared access). +/// +/// The type parameter `T` represents the data that this lock protects. It is +/// required that `T` satisfies `Send` to be shared across tasks and `Sync` to +/// allow concurrent access through readers. The RAII guards returned from the +/// locking methods implement `Deref` (and `DerefMut` for the `write` methods) +/// to allow access to the contained of the lock. +/// +/// RWLocks, like Mutexes, will become poisoned on panics. Note, however, that +/// an RWLock may only be poisoned if a panic occurs while it is locked +/// exclusively (write mode). If a panic occurs in any reader, then the lock +/// will not be poisoned. +/// +/// # Example +/// +/// ``` +/// use std::sync::RWLock; +/// +/// let lock = RWLock::new(5i); +/// +/// // many reader locks can be held at once +/// { +/// let r1 = lock.read(); +/// let r2 = lock.read(); +/// assert_eq!(*r1, 5); +/// assert_eq!(*r2, 5); +/// } // read locks are dropped at this point +/// +/// // only one write lock may be held, however +/// { +/// let mut w = lock.write(); +/// *w += 1; +/// assert_eq!(*w, 6); +/// } // write lock is dropped here +/// ``` +pub struct RWLock<T> { + inner: Box<StaticRWLock>, + data: UnsafeCell<T>, +} + +/// Structure representing a statically allocated RWLock. +/// +/// This structure is intended to be used inside of a `static` and will provide +/// automatic global access as well as lazy initialization. The internal +/// resources of this RWLock, however, must be manually deallocated. +/// +/// # Example +/// +/// ``` +/// use std::sync::{StaticRWLock, RWLOCK_INIT}; +/// +/// static LOCK: StaticRWLock = RWLOCK_INIT; +/// +/// { +/// let _g = LOCK.read(); +/// // ... shared read access +/// } +/// { +/// let _g = LOCK.write(); +/// // ... exclusive write access +/// } +/// unsafe { LOCK.destroy() } // free all resources +/// ``` +pub struct StaticRWLock { + inner: sys::RWLock, + poison: UnsafeCell<poison::Flag>, +} + +/// Constant initialization for a statically-initialized rwlock. +pub const RWLOCK_INIT: StaticRWLock = StaticRWLock { + inner: sys::RWLOCK_INIT, + poison: UnsafeCell { value: poison::Flag { failed: false } }, +}; + +/// RAII structure used to release the shared read access of a lock when +/// dropped. +#[must_use] +pub struct RWLockReadGuard<'a, T: 'a> { + __lock: &'a RWLock<T>, + __guard: StaticRWLockReadGuard, +} + +/// RAII structure used to release the exclusive write access of a lock when +/// dropped. +#[must_use] +pub struct RWLockWriteGuard<'a, T: 'a> { + __lock: &'a RWLock<T>, + __guard: StaticRWLockWriteGuard, +} + +/// RAII structure used to release the shared read access of a lock when +/// dropped. +#[must_use] +pub struct StaticRWLockReadGuard { + lock: &'static sys::RWLock, + marker: marker::NoSend, +} + +/// RAII structure used to release the exclusive write access of a lock when +/// dropped. +#[must_use] +pub struct StaticRWLockWriteGuard { + lock: &'static sys::RWLock, + marker: marker::NoSend, + poison: poison::Guard<'static>, +} + +impl<T: Send + Sync> RWLock<T> { + /// Creates a new instance of an RWLock which is unlocked and read to go. + pub fn new(t: T) -> RWLock<T> { + RWLock { inner: box RWLOCK_INIT, data: UnsafeCell::new(t) } + } + + /// Locks this rwlock with shared read access, blocking the current thread + /// until it can be acquired. + /// + /// The calling thread will be blocked until there are no more writers which + /// hold the lock. There may be other readers currently inside the lock when + /// this method returns. This method does not provide any guarantees with + /// respect to the ordering of whether contentious readers or writers will + /// acquire the lock first. + /// + /// Returns an RAII guard which will release this thread's shared access + /// once it is dropped. + /// + /// # Panics + /// + /// This function will panic if the RWLock is poisoned. An RWLock is + /// poisoned whenever a writer panics while holding an exclusive lock. The + /// panic will occur immediately after the lock has been acquired. + #[inline] + pub fn read(&self) -> RWLockReadGuard<T> { + unsafe { + let lock: &'static StaticRWLock = &*(&*self.inner as *const _); + RWLockReadGuard::new(self, lock.read()) + } + } + + /// Attempt to acquire this lock with shared read access. + /// + /// This function will never block and will return immediately if `read` + /// would otherwise succeed. Returns `Some` of an RAII guard which will + /// release the shared access of this thread when dropped, or `None` if the + /// access could not be granted. This method does not provide any + /// guarantees with respect to the ordering of whether contentious readers + /// or writers will acquire the lock first. + /// + /// # Panics + /// + /// This function will panic if the RWLock is poisoned. An RWLock is + /// poisoned whenever a writer panics while holding an exclusive lock. A + /// panic will only occur if the lock is acquired. + #[inline] + pub fn try_read(&self) -> Option<RWLockReadGuard<T>> { + unsafe { + let lock: &'static StaticRWLock = &*(&*self.inner as *const _); + lock.try_read().map(|guard| { + RWLockReadGuard::new(self, guard) + }) + } + } + + /// Lock this rwlock with exclusive write access, blocking the current + /// thread until it can be acquired. + /// + /// This function will not return while other writers or other readers + /// currently have access to the lock. + /// + /// Returns an RAII guard which will drop the write access of this rwlock + /// when dropped. + /// + /// # Panics + /// + /// This function will panic if the RWLock is poisoned. An RWLock is + /// poisoned whenever a writer panics while holding an exclusive lock. The + /// panic will occur when the lock is acquired. + #[inline] + pub fn write(&self) -> RWLockWriteGuard<T> { + unsafe { + let lock: &'static StaticRWLock = &*(&*self.inner as *const _); + RWLockWriteGuard::new(self, lock.write()) + } + } + + /// Attempt to lock this rwlock with exclusive write access. + /// + /// This function does not ever block, and it will return `None` if a call + /// to `write` would otherwise block. If successful, an RAII guard is + /// returned. + /// + /// # Panics + /// + /// This function will panic if the RWLock is poisoned. An RWLock is + /// poisoned whenever a writer panics while holding an exclusive lock. A + /// panic will only occur if the lock is acquired. + #[inline] + pub fn try_write(&self) -> Option<RWLockWriteGuard<T>> { + unsafe { + let lock: &'static StaticRWLock = &*(&*self.inner as *const _); + lock.try_write().map(|guard| { + RWLockWriteGuard::new(self, guard) + }) + } + } +} + +#[unsafe_destructor] +impl<T> Drop for RWLock<T> { + fn drop(&mut self) { + unsafe { self.inner.inner.destroy() } + } +} + +impl StaticRWLock { + /// Locks this rwlock with shared read access, blocking the current thread + /// until it can be acquired. + /// + /// See `RWLock::read`. + #[inline] + pub fn read(&'static self) -> StaticRWLockReadGuard { + unsafe { self.inner.read() } + StaticRWLockReadGuard::new(self) + } + + /// Attempt to acquire this lock with shared read access. + /// + /// See `RWLock::try_read`. + #[inline] + pub fn try_read(&'static self) -> Option<StaticRWLockReadGuard> { + if unsafe { self.inner.try_read() } { + Some(StaticRWLockReadGuard::new(self)) + } else { + None + } + } + + /// Lock this rwlock with exclusive write access, blocking the current + /// thread until it can be acquired. + /// + /// See `RWLock::write`. + #[inline] + pub fn write(&'static self) -> StaticRWLockWriteGuard { + unsafe { self.inner.write() } + StaticRWLockWriteGuard::new(self) + } + + /// Attempt to lock this rwlock with exclusive write access. + /// + /// See `RWLock::try_write`. + #[inline] + pub fn try_write(&'static self) -> Option<StaticRWLockWriteGuard> { + if unsafe { self.inner.try_write() } { + Some(StaticRWLockWriteGuard::new(self)) + } else { + None + } + } + + /// Deallocate all resources associated with this static lock. + /// + /// This method is unsafe to call as there is no guarantee that there are no + /// active users of the lock, and this also doesn't prevent any future users + /// of this lock. This method is required to be called to not leak memory on + /// all platforms. + pub unsafe fn destroy(&'static self) { + self.inner.destroy() + } +} + +impl<'rwlock, T> RWLockReadGuard<'rwlock, T> { + fn new(lock: &RWLock<T>, guard: StaticRWLockReadGuard) + -> RWLockReadGuard<T> { + RWLockReadGuard { __lock: lock, __guard: guard } + } +} +impl<'rwlock, T> RWLockWriteGuard<'rwlock, T> { + fn new(lock: &RWLock<T>, guard: StaticRWLockWriteGuard) + -> RWLockWriteGuard<T> { + RWLockWriteGuard { __lock: lock, __guard: guard } + } +} + +impl<'rwlock, T> Deref<T> for RWLockReadGuard<'rwlock, T> { + fn deref(&self) -> &T { unsafe { &*self.__lock.data.get() } } +} +impl<'rwlock, T> Deref<T> for RWLockWriteGuard<'rwlock, T> { + fn deref(&self) -> &T { unsafe { &*self.__lock.data.get() } } +} +impl<'rwlock, T> DerefMut<T> for RWLockWriteGuard<'rwlock, T> { + fn deref_mut(&mut self) -> &mut T { unsafe { &mut *self.__lock.data.get() } } +} + +impl StaticRWLockReadGuard { + fn new(lock: &'static StaticRWLock) -> StaticRWLockReadGuard { + let guard = StaticRWLockReadGuard { + lock: &lock.inner, + marker: marker::NoSend, + }; + unsafe { (*lock.poison.get()).borrow().check("rwlock"); } + return guard; + } +} +impl StaticRWLockWriteGuard { + fn new(lock: &'static StaticRWLock) -> StaticRWLockWriteGuard { + unsafe { + let guard = StaticRWLockWriteGuard { + lock: &lock.inner, + marker: marker::NoSend, + poison: (*lock.poison.get()).borrow(), + }; + guard.poison.check("rwlock"); + return guard; + } + } +} + +#[unsafe_destructor] +impl Drop for StaticRWLockReadGuard { + fn drop(&mut self) { + unsafe { self.lock.read_unlock(); } + } +} + +#[unsafe_destructor] +impl Drop for StaticRWLockWriteGuard { + fn drop(&mut self) { + self.poison.done(); + unsafe { self.lock.write_unlock(); } + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + + use rand::{mod, Rng}; + use thread::Thread; + use sync::{Arc, RWLock, StaticRWLock, RWLOCK_INIT}; + + #[test] + fn smoke() { + let l = RWLock::new(()); + drop(l.read()); + drop(l.write()); + drop((l.read(), l.read())); + drop(l.write()); + } + + #[test] + fn static_smoke() { + static R: StaticRWLock = RWLOCK_INIT; + drop(R.read()); + drop(R.write()); + drop((R.read(), R.read())); + drop(R.write()); + unsafe { R.destroy(); } + } + + #[test] + fn frob() { + static R: StaticRWLock = RWLOCK_INIT; + static N: uint = 10; + static M: uint = 1000; + + let (tx, rx) = channel::<()>(); + for _ in range(0, N) { + let tx = tx.clone(); + spawn(move|| { + let mut rng = rand::task_rng(); + for _ in range(0, M) { + if rng.gen_weighted_bool(N) { + drop(R.write()); + } else { + drop(R.read()); + } + } + drop(tx); + }); + } + drop(tx); + let _ = rx.recv_opt(); + unsafe { R.destroy(); } + } + + #[test] + #[should_fail] + fn test_rw_arc_poison_wr() { + let arc = Arc::new(RWLock::new(1i)); + let arc2 = arc.clone(); + let _ = Thread::spawn(move|| { + let lock = arc2.write(); + assert_eq!(*lock, 2); + }).join(); + let lock = arc.read(); + assert_eq!(*lock, 1); + } + + #[test] + #[should_fail] + fn test_rw_arc_poison_ww() { + let arc = Arc::new(RWLock::new(1i)); + let arc2 = arc.clone(); + let _ = Thread::spawn(move|| { + let lock = arc2.write(); + assert_eq!(*lock, 2); + }).join(); + let lock = arc.write(); + assert_eq!(*lock, 1); + } + + #[test] + fn test_rw_arc_no_poison_rr() { + let arc = Arc::new(RWLock::new(1i)); + let arc2 = arc.clone(); + let _ = Thread::spawn(move|| { + let lock = arc2.read(); + assert_eq!(*lock, 2); + }).join(); + let lock = arc.read(); + assert_eq!(*lock, 1); + } + #[test] + fn test_rw_arc_no_poison_rw() { + let arc = Arc::new(RWLock::new(1i)); + let arc2 = arc.clone(); + let _ = Thread::spawn(move|| { + let lock = arc2.read(); + assert_eq!(*lock, 2); + }).join(); + let lock = arc.write(); + assert_eq!(*lock, 1); + } + + #[test] + fn test_rw_arc() { + let arc = Arc::new(RWLock::new(0i)); + let arc2 = arc.clone(); + let (tx, rx) = channel(); + + Thread::spawn(move|| { + let mut lock = arc2.write(); + for _ in range(0u, 10) { + let tmp = *lock; + *lock = -1; + Thread::yield_now(); + *lock = tmp + 1; + } + tx.send(()); + }).detach(); + + // Readers try to catch the writer in the act + let mut children = Vec::new(); + for _ in range(0u, 5) { + let arc3 = arc.clone(); + children.push(Thread::spawn(move|| { + let lock = arc3.read(); + assert!(*lock >= 0); + })); + } + + // Wait for children to pass their asserts + for r in children.into_iter() { + assert!(r.join().is_ok()); + } + + // Wait for writer to finish + rx.recv(); + let lock = arc.read(); + assert_eq!(*lock, 10); + } + + #[test] + fn test_rw_arc_access_in_unwind() { + let arc = Arc::new(RWLock::new(1i)); + let arc2 = arc.clone(); + let _ = Thread::spawn(move|| -> () { + struct Unwinder { + i: Arc<RWLock<int>>, + } + impl Drop for Unwinder { + fn drop(&mut self) { + let mut lock = self.i.write(); + *lock += 1; + } + } + let _u = Unwinder { i: arc2 }; + panic!(); + }).join(); + let lock = arc.read(); + assert_eq!(*lock, 2); + } +} diff --git a/src/libstd/sync/semaphore.rs b/src/libstd/sync/semaphore.rs new file mode 100644 index 00000000000..574b0f22bee --- /dev/null +++ b/src/libstd/sync/semaphore.rs @@ -0,0 +1,195 @@ +// Copyright 2014 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 ops::Drop; +use sync::{Mutex, Condvar}; + +/// A counting, blocking, semaphore. +/// +/// Semaphores are a form of atomic counter where access is only granted if the +/// counter is a positive value. Each acquisition will block the calling thread +/// until the counter is positive, and each release will increment the counter +/// and unblock any threads if necessary. +/// +/// # Example +/// +/// ``` +/// use std::sync::Semaphore; +/// +/// // Create a semaphore that represents 5 resources +/// let sem = Semaphore::new(5); +/// +/// // Acquire one of the resources +/// sem.acquire(); +/// +/// // Acquire one of the resources for a limited period of time +/// { +/// let _guard = sem.access(); +/// // ... +/// } // resources is released here +/// +/// // Release our initially acquired resource +/// sem.release(); +/// ``` +pub struct Semaphore { + lock: Mutex<int>, + cvar: Condvar, +} + +/// An RAII guard which will release a resource acquired from a semaphore when +/// dropped. +pub struct SemaphoreGuard<'a> { + sem: &'a Semaphore, +} + +impl Semaphore { + /// Creates a new semaphore with the initial count specified. + /// + /// The count specified can be thought of as a number of resources, and a + /// call to `acquire` or `access` will block until at least one resource is + /// available. It is valid to initialize a semaphore with a negative count. + pub fn new(count: int) -> Semaphore { + Semaphore { + lock: Mutex::new(count), + cvar: Condvar::new(), + } + } + + /// Acquires a resource of this semaphore, blocking the current thread until + /// it can do so. + /// + /// This method will block until the internal count of the semaphore is at + /// least 1. + pub fn acquire(&self) { + let mut count = self.lock.lock(); + while *count <= 0 { + self.cvar.wait(&count); + } + *count -= 1; + } + + /// Release a resource from this semaphore. + /// + /// This will increment the number of resources in this semaphore by 1 and + /// will notify any pending waiters in `acquire` or `access` if necessary. + pub fn release(&self) { + *self.lock.lock() += 1; + self.cvar.notify_one(); + } + + /// Acquires a resource of this semaphore, returning an RAII guard to + /// release the semaphore when dropped. + /// + /// This function is semantically equivalent to an `acquire` followed by a + /// `release` when the guard returned is dropped. + pub fn access(&self) -> SemaphoreGuard { + self.acquire(); + SemaphoreGuard { sem: self } + } +} + +#[unsafe_destructor] +impl<'a> Drop for SemaphoreGuard<'a> { + fn drop(&mut self) { + self.sem.release(); + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + + use sync::Arc; + use super::Semaphore; + + #[test] + fn test_sem_acquire_release() { + let s = Semaphore::new(1); + s.acquire(); + s.release(); + s.acquire(); + } + + #[test] + fn test_sem_basic() { + let s = Semaphore::new(1); + let _g = s.access(); + } + + #[test] + fn test_sem_as_mutex() { + let s = Arc::new(Semaphore::new(1)); + let s2 = s.clone(); + spawn(move|| { + let _g = s2.access(); + }); + let _g = s.access(); + } + + #[test] + fn test_sem_as_cvar() { + /* Child waits and parent signals */ + let (tx, rx) = channel(); + let s = Arc::new(Semaphore::new(0)); + let s2 = s.clone(); + spawn(move|| { + s2.acquire(); + tx.send(()); + }); + s.release(); + let _ = rx.recv(); + + /* Parent waits and child signals */ + let (tx, rx) = channel(); + let s = Arc::new(Semaphore::new(0)); + let s2 = s.clone(); + spawn(move|| { + s2.release(); + let _ = rx.recv(); + }); + s.acquire(); + tx.send(()); + } + + #[test] + fn test_sem_multi_resource() { + // Parent and child both get in the critical section at the same + // time, and shake hands. + let s = Arc::new(Semaphore::new(2)); + let s2 = s.clone(); + let (tx1, rx1) = channel(); + let (tx2, rx2) = channel(); + spawn(move|| { + let _g = s2.access(); + let _ = rx2.recv(); + tx1.send(()); + }); + let _g = s.access(); + tx2.send(()); + let _ = rx1.recv(); + } + + #[test] + fn test_sem_runtime_friendly_blocking() { + let s = Arc::new(Semaphore::new(1)); + let s2 = s.clone(); + let (tx, rx) = channel(); + { + let _g = s.access(); + spawn(move|| { + tx.send(()); + drop(s2.access()); + tx.send(()); + }); + rx.recv(); // wait for child to come alive + } + rx.recv(); // wait for child to be done + } +} diff --git a/src/libstd/sync/task_pool.rs b/src/libstd/sync/task_pool.rs index 4ae5cd054f6..366e4b7d35b 100644 --- a/src/libstd/sync/task_pool.rs +++ b/src/libstd/sync/task_pool.rs @@ -12,17 +12,18 @@ use core::prelude::*; -use task::spawn; +use thread::Thread; use comm::{channel, Sender, Receiver}; use sync::{Arc, Mutex}; +use thunk::Thunk; struct Sentinel<'a> { - jobs: &'a Arc<Mutex<Receiver<proc(): Send>>>, + jobs: &'a Arc<Mutex<Receiver<Thunk>>>, active: bool } impl<'a> Sentinel<'a> { - fn new(jobs: &Arc<Mutex<Receiver<proc(): Send>>>) -> Sentinel { + fn new(jobs: &Arc<Mutex<Receiver<Thunk>>>) -> Sentinel { Sentinel { jobs: jobs, active: true @@ -60,7 +61,7 @@ impl<'a> Drop for Sentinel<'a> { /// let (tx, rx) = channel(); /// for _ in range(0, 8u) { /// let tx = tx.clone(); -/// pool.execute(proc() { +/// pool.execute(move|| { /// tx.send(1u); /// }); /// } @@ -72,7 +73,7 @@ pub struct TaskPool { // // This is the only such Sender, so when it is dropped all subtasks will // quit. - jobs: Sender<proc(): Send> + jobs: Sender<Thunk> } impl TaskPool { @@ -84,7 +85,7 @@ impl TaskPool { pub fn new(tasks: uint) -> TaskPool { assert!(tasks >= 1); - let (tx, rx) = channel::<proc(): Send>(); + let (tx, rx) = channel::<Thunk>(); let rx = Arc::new(Mutex::new(rx)); // Taskpool tasks. @@ -96,13 +97,15 @@ impl TaskPool { } /// Executes the function `job` on a task in the pool. - pub fn execute(&self, job: proc():Send) { - self.jobs.send(job); + pub fn execute<F>(&self, job: F) + where F : FnOnce(), F : Send + { + self.jobs.send(Thunk::new(job)); } } -fn spawn_in_pool(jobs: Arc<Mutex<Receiver<proc(): Send>>>) { - spawn(proc() { +fn spawn_in_pool(jobs: Arc<Mutex<Receiver<Thunk>>>) { + Thread::spawn(move |:| { // Will spawn a new task on panic unless it is cancelled. let sentinel = Sentinel::new(&jobs); @@ -115,7 +118,7 @@ fn spawn_in_pool(jobs: Arc<Mutex<Receiver<proc(): Send>>>) { }; match message { - Ok(job) => job(), + Ok(job) => job.invoke(()), // The Taskpool was dropped. Err(..) => break @@ -123,15 +126,13 @@ fn spawn_in_pool(jobs: Arc<Mutex<Receiver<proc(): Send>>>) { } sentinel.cancel(); - }) + }).detach(); } #[cfg(test)] mod test { - use core::prelude::*; + use prelude::*; use super::*; - use comm::channel; - use iter::range; const TEST_TASKS: uint = 4u; @@ -144,7 +145,7 @@ mod test { let (tx, rx) = channel(); for _ in range(0, TEST_TASKS) { let tx = tx.clone(); - pool.execute(proc() { + pool.execute(move|| { tx.send(1u); }); } @@ -166,14 +167,14 @@ mod test { // Panic all the existing tasks. for _ in range(0, TEST_TASKS) { - pool.execute(proc() { panic!() }); + pool.execute(move|| -> () { panic!() }); } // Ensure new tasks were spawned to compensate. let (tx, rx) = channel(); for _ in range(0, TEST_TASKS) { let tx = tx.clone(); - pool.execute(proc() { + pool.execute(move|| { tx.send(1u); }); } @@ -191,7 +192,7 @@ mod test { // Panic all the existing tasks in a bit. for _ in range(0, TEST_TASKS) { let waiter = waiter.clone(); - pool.execute(proc() { + pool.execute(move|| { waiter.wait(); panic!(); }); @@ -203,4 +204,3 @@ mod test { waiter.wait(); } } - diff --git a/src/libstd/sys/common/backtrace.rs b/src/libstd/sys/common/backtrace.rs new file mode 100644 index 00000000000..1d646eb06b1 --- /dev/null +++ b/src/libstd/sys/common/backtrace.rs @@ -0,0 +1,136 @@ +// Copyright 2014 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 io::IoResult; + +#[cfg(target_word_size = "64")] pub const HEX_WIDTH: uint = 18; +#[cfg(target_word_size = "32")] pub const HEX_WIDTH: uint = 10; + +// All rust symbols are in theory lists of "::"-separated identifiers. Some +// assemblers, however, can't handle these characters in symbol names. To get +// around this, we use C++-style mangling. The mangling method is: +// +// 1. Prefix the symbol with "_ZN" +// 2. For each element of the path, emit the length plus the element +// 3. End the path with "E" +// +// For example, "_ZN4testE" => "test" and "_ZN3foo3bar" => "foo::bar". +// +// We're the ones printing our backtraces, so we can't rely on anything else to +// demangle our symbols. It's *much* nicer to look at demangled symbols, so +// this function is implemented to give us nice pretty output. +// +// Note that this demangler isn't quite as fancy as it could be. We have lots +// of other information in our symbols like hashes, version, type information, +// etc. Additionally, this doesn't handle glue symbols at all. +pub fn demangle(writer: &mut Writer, s: &str) -> IoResult<()> { + // First validate the symbol. If it doesn't look like anything we're + // expecting, we just print it literally. Note that we must handle non-rust + // symbols because we could have any function in the backtrace. + let mut valid = true; + let mut inner = s; + if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") { + inner = s.slice(3, s.len() - 1); + // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" form too. + } else if s.len() > 3 && s.starts_with("ZN") && s.ends_with("E") { + inner = s.slice(2, s.len() - 1); + } else { + valid = false; + } + + if valid { + let mut chars = inner.chars(); + while valid { + let mut i = 0; + for c in chars { + if c.is_numeric() { + i = i * 10 + c as uint - '0' as uint; + } else { + break + } + } + if i == 0 { + valid = chars.next().is_none(); + break + } else if chars.by_ref().take(i - 1).count() != i - 1 { + valid = false; + } + } + } + + // Alright, let's do this. + if !valid { + try!(writer.write_str(s)); + } else { + let mut first = true; + while inner.len() > 0 { + if !first { + try!(writer.write_str("::")); + } else { + first = false; + } + let mut rest = inner; + while rest.char_at(0).is_numeric() { + rest = rest.slice_from(1); + } + let i: uint = inner.slice_to(inner.len() - rest.len()).parse().unwrap(); + inner = rest.slice_from(i); + rest = rest.slice_to(i); + while rest.len() > 0 { + if rest.starts_with("$") { + macro_rules! demangle { + ($($pat:expr => $demangled:expr),*) => ({ + $(if rest.starts_with($pat) { + try!(writer.write_str($demangled)); + rest = rest.slice_from($pat.len()); + } else)* + { + try!(writer.write_str(rest)); + break; + } + + }) + } + + // see src/librustc/back/link.rs for these mappings + demangle! ( + "$SP$" => "@", + "$UP$" => "Box", + "$RP$" => "*", + "$BP$" => "&", + "$LT$" => "<", + "$GT$" => ">", + "$LP$" => "(", + "$RP$" => ")", + "$C$" => ",", + + // in theory we can demangle any Unicode code point, but + // for simplicity we just catch the common ones. + "$x20" => " ", + "$x27" => "'", + "$x5b" => "[", + "$x5d" => "]" + ) + } else { + let idx = match rest.find('$') { + None => rest.len(), + Some(i) => i, + }; + try!(writer.write_str(rest.slice_to(idx))); + rest = rest.slice_from(idx); + } + } + } + } + + Ok(()) +} diff --git a/src/libstd/sys/common/condvar.rs b/src/libstd/sys/common/condvar.rs new file mode 100644 index 00000000000..e09d9704029 --- /dev/null +++ b/src/libstd/sys/common/condvar.rs @@ -0,0 +1,67 @@ +// Copyright 2014 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 time::Duration; +use sys_common::mutex::{mod, Mutex}; +use sys::condvar as imp; + +/// An OS-based condition variable. +/// +/// This structure is the lowest layer possible on top of the OS-provided +/// condition variables. It is consequently entirely unsafe to use. It is +/// recommended to use the safer types at the top level of this crate instead of +/// this type. +pub struct Condvar(imp::Condvar); + +/// Static initializer for condition variables. +pub const CONDVAR_INIT: Condvar = Condvar(imp::CONDVAR_INIT); + +impl Condvar { + /// Creates a new condition variable for use. + /// + /// Behavior is undefined if the condition variable is moved after it is + /// first used with any of the functions below. + #[inline] + pub unsafe fn new() -> Condvar { Condvar(imp::Condvar::new()) } + + /// Signal one waiter on this condition variable to wake up. + #[inline] + pub unsafe fn notify_one(&self) { self.0.notify_one() } + + /// Awaken all current waiters on this condition variable. + #[inline] + pub unsafe fn notify_all(&self) { self.0.notify_all() } + + /// Wait for a signal on the specified mutex. + /// + /// Behavior is undefined if the mutex is not locked by the current thread. + /// Behavior is also undefined if more than one mutex is used concurrently + /// on this condition variable. + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { self.0.wait(mutex::raw(mutex)) } + + /// Wait for a signal on the specified mutex with a timeout duration + /// specified by `dur` (a relative time into the future). + /// + /// Behavior is undefined if the mutex is not locked by the current thread. + /// Behavior is also undefined if more than one mutex is used concurrently + /// on this condition variable. + #[inline] + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + self.0.wait_timeout(mutex::raw(mutex), dur) + } + + /// Deallocate all resources associated with this condition variable. + /// + /// Behavior is undefined if there are current or will be future users of + /// this condition variable. + #[inline] + pub unsafe fn destroy(&self) { self.0.destroy() } +} diff --git a/src/libstd/sys/common/helper_thread.rs b/src/libstd/sys/common/helper_thread.rs index 9508d8d9232..421778e2012 100644 --- a/src/libstd/sys/common/helper_thread.rs +++ b/src/libstd/sys/common/helper_thread.rs @@ -20,15 +20,15 @@ //! can be created in the future and there must be no active timers at that //! time. -use mem; -use rustrt::bookkeeping; -use rustrt::mutex::StaticNativeMutex; -use rustrt; +use prelude::*; + use cell::UnsafeCell; +use mem; +use sync::{StaticMutex, StaticCondvar}; +use rt; use sys::helper_signal; -use prelude::*; -use task; +use thread::Thread; /// A structure for management of a helper thread. /// @@ -39,7 +39,8 @@ use task; /// is for static initialization. pub struct Helper<M> { /// Internal lock which protects the remaining fields - pub lock: StaticNativeMutex, + pub lock: StaticMutex, + pub cond: StaticCondvar, // You'll notice that the remaining fields are UnsafeCell<T>, and this is // because all helper thread operations are done through &self, but we need @@ -53,6 +54,9 @@ pub struct Helper<M> { /// Flag if this helper thread has booted and been initialized yet. pub initialized: UnsafeCell<bool>, + + /// Flag if this helper thread has shut down + pub shutdown: UnsafeCell<bool>, } impl<M: Send> Helper<M> { @@ -65,9 +69,10 @@ impl<M: Send> Helper<M> { /// passed to the helper thread in a separate task. /// /// This function is safe to be called many times. - pub fn boot<T: Send>(&'static self, - f: || -> T, - helper: fn(helper_signal::signal, Receiver<M>, T)) { + pub fn boot<T, F>(&'static self, f: F, helper: fn(helper_signal::signal, Receiver<M>, T)) where + T: Send, + F: FnOnce() -> T, + { unsafe { let _guard = self.lock.lock(); if !*self.initialized.get() { @@ -77,13 +82,14 @@ impl<M: Send> Helper<M> { *self.signal.get() = send as uint; let t = f(); - task::spawn(proc() { - bookkeeping::decrement(); + Thread::spawn(move |:| { helper(receive, rx, t); - self.lock.lock().signal() - }); + let _g = self.lock.lock(); + *self.shutdown.get() = true; + self.cond.notify_one() + }).detach(); - rustrt::at_exit(proc() { self.shutdown() }); + rt::at_exit(move|:| { self.shutdown() }); *self.initialized.get() = true; } } @@ -119,7 +125,9 @@ impl<M: Send> Helper<M> { helper_signal::signal(*self.signal.get() as helper_signal::signal); // Wait for the child to exit - guard.wait(); + while !*self.shutdown.get() { + self.cond.wait(&guard); + } drop(guard); // Clean up after ourselves diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index cacb128faa5..dc0ad08cdbe 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -13,14 +13,22 @@ use io::{mod, IoError, IoResult}; use prelude::*; -use sys::{last_error, retry, fs}; +use sys::{last_error, retry}; use c_str::CString; use num::Int; use path::BytesContainer; use collections; -pub mod net; +pub mod backtrace; +pub mod condvar; pub mod helper_thread; +pub mod mutex; +pub mod net; +pub mod rwlock; +pub mod stack; +pub mod thread; +pub mod thread_info; +pub mod thread_local; // common error constructors @@ -65,7 +73,9 @@ pub fn mkerr_libc<T: Int>(ret: T) -> IoResult<()> { } } -pub fn keep_going(data: &[u8], f: |*const u8, uint| -> i64) -> i64 { +pub fn keep_going<F>(data: &[u8], mut f: F) -> i64 where + F: FnMut(*const u8, uint) -> i64, +{ let origamt = data.len(); let mut data = data.as_ptr(); let mut amt = origamt; @@ -83,10 +93,9 @@ pub fn keep_going(data: &[u8], f: |*const u8, uint| -> i64) -> i64 { return (origamt - amt) as i64; } -// traits for extracting representations from - -pub trait AsFileDesc { - fn as_fd(&self) -> &fs::FileDesc; +// A trait for extracting representations from std::io types +pub trait AsInner<Inner> { + fn as_inner(&self) -> &Inner; } pub trait ProcessConfig<K: BytesContainer, V: BytesContainer> { diff --git a/src/libstd/sys/common/mutex.rs b/src/libstd/sys/common/mutex.rs new file mode 100644 index 00000000000..1a8a92a105a --- /dev/null +++ b/src/libstd/sys/common/mutex.rs @@ -0,0 +1,62 @@ +// Copyright 2014 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 sys::mutex as imp; + +/// An OS-based mutual exclusion lock. +/// +/// This is the thinnest cross-platform wrapper around OS mutexes. All usage of +/// this mutex is unsafe and it is recommended to instead use the safe wrapper +/// at the top level of the crate instead of this type. +pub struct Mutex(imp::Mutex); + +/// Constant initializer for statically allocated mutexes. +pub const MUTEX_INIT: Mutex = Mutex(imp::MUTEX_INIT); + +impl Mutex { + /// Creates a newly initialized mutex. + /// + /// Behavior is undefined if the mutex is moved after the first method is + /// called on the mutex. + #[inline] + pub unsafe fn new() -> Mutex { Mutex(imp::Mutex::new()) } + + /// Lock the mutex blocking the current thread until it is available. + /// + /// Behavior is undefined if the mutex has been moved between this and any + /// previous function call. + #[inline] + pub unsafe fn lock(&self) { self.0.lock() } + + /// Attempt to lock the mutex without blocking, returning whether it was + /// successfully acquired or not. + /// + /// Behavior is undefined if the mutex has been moved between this and any + /// previous function call. + #[inline] + pub unsafe fn try_lock(&self) -> bool { self.0.try_lock() } + + /// Unlock the mutex. + /// + /// Behavior is undefined if the current thread does not actually hold the + /// mutex. + #[inline] + pub unsafe fn unlock(&self) { self.0.unlock() } + + /// Deallocate all resources associated with this mutex. + /// + /// Behavior is undefined if there are current or will be future users of + /// this mutex. + #[inline] + pub unsafe fn destroy(&self) { self.0.destroy() } +} + +// not meant to be exported to the outside world, just the containing module +pub fn raw(mutex: &Mutex) -> &imp::Mutex { &mutex.0 } diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs index 029fc852742..382f6875b28 100644 --- a/src/libstd/sys/common/net.rs +++ b/src/libstd/sys/common/net.rs @@ -8,21 +8,21 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use self::SocketStatus::*; -pub use self::InAddr::*; +use self::SocketStatus::*; +use self::InAddr::*; use alloc::arc::Arc; use libc::{mod, c_char, c_int}; use mem; use num::Int; use ptr::{mod, null, null_mut}; -use rustrt::mutex; use io::net::ip::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr}; use io::net::addrinfo; use io::{IoResult, IoError}; use sys::{mod, retry, c, sock_t, last_error, last_net_error, last_gai_error, close_sock, wrlen, msglen_t, os, wouldblock, set_nonblocking, timer, ms_to_timeval, decode_error_detailed}; +use sync::{Mutex, MutexGuard}; use sys_common::{mod, keep_going, short_write, timeout}; use prelude::*; use cmp; @@ -344,10 +344,10 @@ pub fn get_host_addresses(host: Option<&str>, servname: Option<&str>, // [1] http://twistedmatrix.com/pipermail/twisted-commits/2012-April/034692.html // [2] http://stackoverflow.com/questions/19819198/does-send-msg-dontwait -pub fn read<T>(fd: sock_t, - deadline: u64, - lock: || -> T, - read: |bool| -> libc::c_int) -> IoResult<uint> { +pub fn read<T, L, R>(fd: sock_t, deadline: u64, mut lock: L, mut read: R) -> IoResult<uint> where + L: FnMut() -> T, + R: FnMut(bool) -> libc::c_int, +{ let mut ret = -1; if deadline == 0 { ret = retry(|| read(false)); @@ -386,12 +386,15 @@ pub fn read<T>(fd: sock_t, } } -pub fn write<T>(fd: sock_t, - deadline: u64, - buf: &[u8], - write_everything: bool, - lock: || -> T, - write: |bool, *const u8, uint| -> i64) -> IoResult<uint> { +pub fn write<T, L, W>(fd: sock_t, + deadline: u64, + buf: &[u8], + write_everything: bool, + mut lock: L, + mut write: W) -> IoResult<uint> where + L: FnMut() -> T, + W: FnMut(bool, *const u8, uint) -> i64, +{ let mut ret = -1; let mut written = 0; if deadline == 0 { @@ -557,12 +560,12 @@ struct Inner { // Unused on Linux, where this lock is not necessary. #[allow(dead_code)] - lock: mutex::NativeMutex + lock: Mutex<()>, } impl Inner { fn new(fd: sock_t) -> Inner { - Inner { fd: fd, lock: unsafe { mutex::NativeMutex::new() } } + Inner { fd: fd, lock: Mutex::new(()) } } } @@ -572,7 +575,7 @@ impl Drop for Inner { pub struct Guard<'a> { pub fd: sock_t, - pub guard: mutex::LockGuard<'a>, + pub guard: MutexGuard<'a, ()>, } #[unsafe_destructor] @@ -666,7 +669,7 @@ impl TcpStream { fn lock_nonblocking<'a>(&'a self) -> Guard<'a> { let ret = Guard { fd: self.fd(), - guard: unsafe { self.inner.lock.lock() }, + guard: self.inner.lock.lock(), }; assert!(set_nonblocking(self.fd(), true).is_ok()); ret @@ -674,8 +677,8 @@ impl TcpStream { pub fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { let fd = self.fd(); - let dolock = || self.lock_nonblocking(); - let doread = |nb| unsafe { + let dolock = |&:| self.lock_nonblocking(); + let doread = |&mut: nb| unsafe { let flags = if nb {c::MSG_DONTWAIT} else {0}; libc::recv(fd, buf.as_mut_ptr() as *mut libc::c_void, @@ -687,8 +690,8 @@ impl TcpStream { pub fn write(&mut self, buf: &[u8]) -> IoResult<()> { let fd = self.fd(); - let dolock = || self.lock_nonblocking(); - let dowrite = |nb: bool, buf: *const u8, len: uint| unsafe { + let dolock = |&:| self.lock_nonblocking(); + let dowrite = |&: nb: bool, buf: *const u8, len: uint| unsafe { let flags = if nb {c::MSG_DONTWAIT} else {0}; libc::send(fd, buf as *const _, @@ -805,7 +808,7 @@ impl UdpSocket { fn lock_nonblocking<'a>(&'a self) -> Guard<'a> { let ret = Guard { fd: self.fd(), - guard: unsafe { self.inner.lock.lock() }, + guard: self.inner.lock.lock(), }; assert!(set_nonblocking(self.fd(), true).is_ok()); ret @@ -822,7 +825,7 @@ impl UdpSocket { let mut addrlen: libc::socklen_t = mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t; - let dolock = || self.lock_nonblocking(); + let dolock = |&:| self.lock_nonblocking(); let n = try!(read(fd, self.read_deadline, dolock, |nb| unsafe { let flags = if nb {c::MSG_DONTWAIT} else {0}; libc::recvfrom(fd, @@ -843,8 +846,8 @@ impl UdpSocket { let dstp = &storage as *const _ as *const libc::sockaddr; let fd = self.fd(); - let dolock = || self.lock_nonblocking(); - let dowrite = |nb, buf: *const u8, len: uint| unsafe { + let dolock = |&: | self.lock_nonblocking(); + let dowrite = |&mut: nb, buf: *const u8, len: uint| unsafe { let flags = if nb {c::MSG_DONTWAIT} else {0}; libc::sendto(fd, buf as *const libc::c_void, diff --git a/src/libstd/sys/common/rwlock.rs b/src/libstd/sys/common/rwlock.rs new file mode 100644 index 00000000000..df016b9e293 --- /dev/null +++ b/src/libstd/sys/common/rwlock.rs @@ -0,0 +1,86 @@ +// Copyright 2014 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 sys::rwlock as imp; + +/// An OS-based reader-writer lock. +/// +/// This structure is entirely unsafe and serves as the lowest layer of a +/// cross-platform binding of system rwlocks. It is recommended to use the +/// safer types at the top level of this crate instead of this type. +pub struct RWLock(imp::RWLock); + +/// Constant initializer for static RWLocks. +pub const RWLOCK_INIT: RWLock = RWLock(imp::RWLOCK_INIT); + +impl RWLock { + /// Creates a new instance of an RWLock. + /// + /// Usage of an RWLock is undefined if it is moved after its first use (any + /// function calls below). + #[inline] + pub unsafe fn new() -> RWLock { RWLock(imp::RWLock::new()) } + + /// Acquire shared access to the underlying lock, blocking the current + /// thread to do so. + /// + /// Behavior is undefined if the rwlock has been moved between this and any + /// previous methodo call. + #[inline] + pub unsafe fn read(&self) { self.0.read() } + + /// Attempt to acquire shared access to this lock, returning whether it + /// succeeded or not. + /// + /// This function does not block the current thread. + /// + /// Behavior is undefined if the rwlock has been moved between this and any + /// previous methodo call. + #[inline] + pub unsafe fn try_read(&self) -> bool { self.0.try_read() } + + /// Acquire write access to the underlying lock, blocking the current thread + /// to do so. + /// + /// Behavior is undefined if the rwlock has been moved between this and any + /// previous methodo call. + #[inline] + pub unsafe fn write(&self) { self.0.write() } + + /// Attempt to acquire exclusive access to this lock, returning whether it + /// succeeded or not. + /// + /// This function does not block the current thread. + /// + /// Behavior is undefined if the rwlock has been moved between this and any + /// previous methodo call. + #[inline] + pub unsafe fn try_write(&self) -> bool { self.0.try_write() } + + /// Unlock previously acquired shared access to this lock. + /// + /// Behavior is undefined if the current thread does not have shared access. + #[inline] + pub unsafe fn read_unlock(&self) { self.0.read_unlock() } + + /// Unlock previously acquired exclusive access to this lock. + /// + /// Behavior is undefined if the current thread does not currently have + /// exclusive access. + #[inline] + pub unsafe fn write_unlock(&self) { self.0.write_unlock() } + + /// Destroy OS-related resources with this RWLock. + /// + /// Behavior is undefined if there are any currently active users of this + /// lock. + #[inline] + pub unsafe fn destroy(&self) { self.0.destroy() } +} diff --git a/src/libstd/sys/common/stack.rs b/src/libstd/sys/common/stack.rs new file mode 100644 index 00000000000..2a88e20c8fa --- /dev/null +++ b/src/libstd/sys/common/stack.rs @@ -0,0 +1,325 @@ +// 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. + +//! Rust stack-limit management +//! +//! Currently Rust uses a segmented-stack-like scheme in order to detect stack +//! overflow for rust tasks. In this scheme, the prologue of all functions are +//! preceded with a check to see whether the current stack limits are being +//! exceeded. +//! +//! This module provides the functionality necessary in order to manage these +//! stack limits (which are stored in platform-specific locations). The +//! functions here are used at the borders of the task lifetime in order to +//! manage these limits. +//! +//! This function is an unstable module because this scheme for stack overflow +//! detection is not guaranteed to continue in the future. Usage of this module +//! is discouraged unless absolutely necessary. + +// iOS related notes +// +// It is possible to implement it using idea from +// http://www.opensource.apple.com/source/Libc/Libc-825.40.1/pthreads/pthread_machdep.h +// +// In short: _pthread_{get,set}_specific_direct allows extremely fast +// access, exactly what is required for segmented stack +// There is a pool of reserved slots for Apple internal use (0..119) +// First dynamic allocated pthread key starts with 257 (on iOS7) +// So using slot 149 should be pretty safe ASSUMING space is reserved +// for every key < first dynamic key +// +// There is also an opportunity to steal keys reserved for Garbage Collection +// ranges 80..89 and 110..119, especially considering the fact Garbage Collection +// never supposed to work on iOS. But as everybody knows it - there is a chance +// that those slots will be re-used, like it happened with key 95 (moved from +// JavaScriptCore to CoreText) +// +// Unfortunately Apple rejected patch to LLVM which generated +// corresponding prolog, decision was taken to disable segmented +// stack support on iOS. + +pub const RED_ZONE: uint = 20 * 1024; + +/// This function is invoked from rust's current __morestack function. Segmented +/// stacks are currently not enabled as segmented stacks, but rather one giant +/// stack segment. This means that whenever we run out of stack, we want to +/// truly consider it to be stack overflow rather than allocating a new stack. +#[cfg(not(test))] // in testing, use the original libstd's version +#[lang = "stack_exhausted"] +extern fn stack_exhausted() { + use intrinsics; + + unsafe { + // We're calling this function because the stack just ran out. We need + // to call some other rust functions, but if we invoke the functions + // right now it'll just trigger this handler being called again. In + // order to alleviate this, we move the stack limit to be inside of the + // red zone that was allocated for exactly this reason. + let limit = get_sp_limit(); + record_sp_limit(limit - RED_ZONE / 2); + + // This probably isn't the best course of action. Ideally one would want + // to unwind the stack here instead of just aborting the entire process. + // This is a tricky problem, however. There's a few things which need to + // be considered: + // + // 1. We're here because of a stack overflow, yet unwinding will run + // destructors and hence arbitrary code. What if that code overflows + // the stack? One possibility is to use the above allocation of an + // extra 10k to hope that we don't hit the limit, and if we do then + // abort the whole program. Not the best, but kind of hard to deal + // with unless we want to switch stacks. + // + // 2. LLVM will optimize functions based on whether they can unwind or + // not. It will flag functions with 'nounwind' if it believes that + // the function cannot trigger unwinding, but if we do unwind on + // stack overflow then it means that we could unwind in any function + // anywhere. We would have to make sure that LLVM only places the + // nounwind flag on functions which don't call any other functions. + // + // 3. The function that overflowed may have owned arguments. These + // arguments need to have their destructors run, but we haven't even + // begun executing the function yet, so unwinding will not run the + // any landing pads for these functions. If this is ignored, then + // the arguments will just be leaked. + // + // Exactly what to do here is a very delicate topic, and is possibly + // still up in the air for what exactly to do. Some relevant issues: + // + // #3555 - out-of-stack failure leaks arguments + // #3695 - should there be a stack limit? + // #9855 - possible strategies which could be taken + // #9854 - unwinding on windows through __morestack has never worked + // #2361 - possible implementation of not using landing pads + + ::rt::util::report_overflow(); + + intrinsics::abort(); + } +} + +// Windows maintains a record of upper and lower stack bounds in the Thread Information +// Block (TIB), and some syscalls do check that addresses which are supposed to be in +// the stack, indeed lie between these two values. +// (See https://github.com/rust-lang/rust/issues/3445#issuecomment-26114839) +// +// When using Rust-managed stacks (libgreen), we must maintain these values accordingly. +// For OS-managed stacks (libnative), we let the OS manage them for us. +// +// On all other platforms both variants behave identically. + +#[inline(always)] +pub unsafe fn record_os_managed_stack_bounds(stack_lo: uint, _stack_hi: uint) { + record_sp_limit(stack_lo + RED_ZONE); +} + +#[inline(always)] +pub unsafe fn record_rust_managed_stack_bounds(stack_lo: uint, stack_hi: uint) { + // When the old runtime had segmented stacks, it used a calculation that was + // "limit + RED_ZONE + FUDGE". The red zone was for things like dynamic + // symbol resolution, llvm function calls, etc. In theory this red zone + // value is 0, but it matters far less when we have gigantic stacks because + // we don't need to be so exact about our stack budget. The "fudge factor" + // was because LLVM doesn't emit a stack check for functions < 256 bytes in + // size. Again though, we have giant stacks, so we round all these + // calculations up to the nice round number of 20k. + record_sp_limit(stack_lo + RED_ZONE); + + return target_record_stack_bounds(stack_lo, stack_hi); + + #[cfg(not(windows))] #[inline(always)] + unsafe fn target_record_stack_bounds(_stack_lo: uint, _stack_hi: uint) {} + + #[cfg(all(windows, target_arch = "x86"))] #[inline(always)] + unsafe fn target_record_stack_bounds(stack_lo: uint, stack_hi: uint) { + // stack range is at TIB: %fs:0x04 (top) and %fs:0x08 (bottom) + asm!("mov $0, %fs:0x04" :: "r"(stack_hi) :: "volatile"); + asm!("mov $0, %fs:0x08" :: "r"(stack_lo) :: "volatile"); + } + #[cfg(all(windows, target_arch = "x86_64"))] #[inline(always)] + unsafe fn target_record_stack_bounds(stack_lo: uint, stack_hi: uint) { + // stack range is at TIB: %gs:0x08 (top) and %gs:0x10 (bottom) + asm!("mov $0, %gs:0x08" :: "r"(stack_hi) :: "volatile"); + asm!("mov $0, %gs:0x10" :: "r"(stack_lo) :: "volatile"); + } +} + +/// Records the current limit of the stack as specified by `end`. +/// +/// This is stored in an OS-dependent location, likely inside of the thread +/// local storage. The location that the limit is stored is a pre-ordained +/// location because it's where LLVM has emitted code to check. +/// +/// Note that this cannot be called under normal circumstances. This function is +/// changing the stack limit, so upon returning any further function calls will +/// possibly be triggering the morestack logic if you're not careful. +/// +/// Also note that this and all of the inside functions are all flagged as +/// "inline(always)" because they're messing around with the stack limits. This +/// would be unfortunate for the functions themselves to trigger a morestack +/// invocation (if they were an actual function call). +#[inline(always)] +pub unsafe fn record_sp_limit(limit: uint) { + return target_record_sp_limit(limit); + + // x86-64 + #[cfg(all(target_arch = "x86_64", + any(target_os = "macos", target_os = "ios")))] + #[inline(always)] + unsafe fn target_record_sp_limit(limit: uint) { + asm!("movq $$0x60+90*8, %rsi + movq $0, %gs:(%rsi)" :: "r"(limit) : "rsi" : "volatile") + } + #[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)] + unsafe fn target_record_sp_limit(limit: uint) { + asm!("movq $0, %fs:112" :: "r"(limit) :: "volatile") + } + #[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)] + unsafe fn target_record_sp_limit(_: uint) { + } + #[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)] + unsafe fn target_record_sp_limit(limit: uint) { + asm!("movq $0, %fs:24" :: "r"(limit) :: "volatile") + } + #[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))] #[inline(always)] + unsafe fn target_record_sp_limit(limit: uint) { + asm!("movq $0, %fs:32" :: "r"(limit) :: "volatile") + } + + // x86 + #[cfg(all(target_arch = "x86", + any(target_os = "macos", target_os = "ios")))] + #[inline(always)] + unsafe fn target_record_sp_limit(limit: uint) { + asm!("movl $$0x48+90*4, %eax + movl $0, %gs:(%eax)" :: "r"(limit) : "eax" : "volatile") + } + #[cfg(all(target_arch = "x86", + any(target_os = "linux", target_os = "freebsd")))] + #[inline(always)] + unsafe fn target_record_sp_limit(limit: uint) { + asm!("movl $0, %gs:48" :: "r"(limit) :: "volatile") + } + #[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)] + unsafe fn target_record_sp_limit(_: uint) { + } + + // mips, arm - Some brave soul can port these to inline asm, but it's over + // my head personally + #[cfg(any(target_arch = "mips", + target_arch = "mipsel", + all(target_arch = "arm", not(target_os = "ios"))))] + #[inline(always)] + unsafe fn target_record_sp_limit(limit: uint) { + use libc::c_void; + return record_sp_limit(limit as *const c_void); + extern { + fn record_sp_limit(limit: *const c_void); + } + } + + // iOS segmented stack is disabled for now, see related notes + #[cfg(all(target_arch = "arm", target_os = "ios"))] #[inline(always)] + unsafe fn target_record_sp_limit(_: uint) { + } +} + +/// The counterpart of the function above, this function will fetch the current +/// stack limit stored in TLS. +/// +/// Note that all of these functions are meant to be exact counterparts of their +/// brethren above, except that the operands are reversed. +/// +/// As with the setter, this function does not have a __morestack header and can +/// therefore be called in a "we're out of stack" situation. +#[inline(always)] +pub unsafe fn get_sp_limit() -> uint { + return target_get_sp_limit(); + + // x86-64 + #[cfg(all(target_arch = "x86_64", + any(target_os = "macos", target_os = "ios")))] + #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + let limit; + asm!("movq $$0x60+90*8, %rsi + movq %gs:(%rsi), $0" : "=r"(limit) :: "rsi" : "volatile"); + return limit; + } + #[cfg(all(target_arch = "x86_64", target_os = "linux"))] #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + let limit; + asm!("movq %fs:112, $0" : "=r"(limit) ::: "volatile"); + return limit; + } + #[cfg(all(target_arch = "x86_64", target_os = "windows"))] #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + return 1024; + } + #[cfg(all(target_arch = "x86_64", target_os = "freebsd"))] #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + let limit; + asm!("movq %fs:24, $0" : "=r"(limit) ::: "volatile"); + return limit; + } + #[cfg(all(target_arch = "x86_64", target_os = "dragonfly"))] #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + let limit; + asm!("movq %fs:32, $0" : "=r"(limit) ::: "volatile"); + return limit; + } + + + // x86 + #[cfg(all(target_arch = "x86", + any(target_os = "macos", target_os = "ios")))] + #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + let limit; + asm!("movl $$0x48+90*4, %eax + movl %gs:(%eax), $0" : "=r"(limit) :: "eax" : "volatile"); + return limit; + } + #[cfg(all(target_arch = "x86", + any(target_os = "linux", target_os = "freebsd")))] + #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + let limit; + asm!("movl %gs:48, $0" : "=r"(limit) ::: "volatile"); + return limit; + } + #[cfg(all(target_arch = "x86", target_os = "windows"))] #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + return 1024; + } + + // mips, arm - Some brave soul can port these to inline asm, but it's over + // my head personally + #[cfg(any(target_arch = "mips", + target_arch = "mipsel", + all(target_arch = "arm", not(target_os = "ios"))))] + #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + use libc::c_void; + return get_sp_limit() as uint; + extern { + fn get_sp_limit() -> *const c_void; + } + } + + // iOS doesn't support segmented stacks yet. This function might + // be called by runtime though so it is unsafe to mark it as + // unreachable, let's return a fixed constant. + #[cfg(all(target_arch = "arm", target_os = "ios"))] #[inline(always)] + unsafe fn target_get_sp_limit() -> uint { + 1024 + } +} diff --git a/src/libstd/sys/common/thread.rs b/src/libstd/sys/common/thread.rs new file mode 100644 index 00000000000..048e33399a3 --- /dev/null +++ b/src/libstd/sys/common/thread.rs @@ -0,0 +1,35 @@ +// Copyright 2014 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 core::prelude::*; + +use boxed::Box; +use mem; +use uint; +use libc; +use thunk::Thunk; +use sys_common::stack; +use sys::{thread, stack_overflow}; + +// This is the starting point of rust os threads. The first thing we do +// is make sure that we don't trigger __morestack (also why this has a +// no_stack_check annotation), and then we extract the main function +// and invoke it. +#[no_stack_check] +pub fn start_thread(main: *mut libc::c_void) -> thread::rust_thread_return { + unsafe { + stack::record_os_managed_stack_bounds(0, uint::MAX); + let handler = stack_overflow::Handler::new(); + let f: Box<Thunk> = mem::transmute(main); + f.invoke(()); + drop(handler); + mem::transmute(0 as thread::rust_thread_return) + } +} diff --git a/src/libstd/sys/common/thread_info.rs b/src/libstd/sys/common/thread_info.rs new file mode 100644 index 00000000000..dc21feb17a8 --- /dev/null +++ b/src/libstd/sys/common/thread_info.rs @@ -0,0 +1,68 @@ +// Copyright 2014 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 core::prelude::*; + +use thread::Thread; +use cell::RefCell; +use string::String; + +struct ThreadInfo { + // This field holds the known bounds of the stack in (lo, hi) + // form. Not all threads necessarily know their precise bounds, + // hence this is optional. + stack_bounds: (uint, uint), + stack_guard: uint, + thread: Thread, +} + +thread_local! { static THREAD_INFO: RefCell<Option<ThreadInfo>> = RefCell::new(None) } + +impl ThreadInfo { + fn with<R>(f: |&mut ThreadInfo| -> R) -> R { + if THREAD_INFO.destroyed() { + panic!("Use of std::thread::Thread::current() is not possible after \ + the thread's local data has been destroyed"); + } + + THREAD_INFO.with(|c| { + if c.borrow().is_none() { + *c.borrow_mut() = Some(ThreadInfo { + stack_bounds: (0, 0), + stack_guard: 0, + thread: NewThread::new(None), + }) + } + f(c.borrow_mut().as_mut().unwrap()) + }) + } +} + +pub fn current_thread() -> Thread { + ThreadInfo::with(|info| info.thread.clone()) +} + +pub fn stack_guard() -> uint { + ThreadInfo::with(|info| info.stack_guard) +} + +pub fn set(stack_bounds: (uint, uint), stack_guard: uint, thread: Thread) { + THREAD_INFO.with(|c| assert!(c.borrow().is_none())); + THREAD_INFO.with(move |c| *c.borrow_mut() = Some(ThreadInfo{ + stack_bounds: stack_bounds, + stack_guard: stack_guard, + thread: thread, + })); +} + +// a hack to get around privacy restrictions; implemented by `std::thread::Thread` +pub trait NewThread { + fn new(name: Option<String>) -> Self; +} diff --git a/src/libstd/sys/common/thread_local.rs b/src/libstd/sys/common/thread_local.rs new file mode 100644 index 00000000000..fe7a7d8d037 --- /dev/null +++ b/src/libstd/sys/common/thread_local.rs @@ -0,0 +1,284 @@ +// Copyright 2014 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. + +//! OS-based thread local storage +//! +//! This module provides an implementation of OS-based thread local storage, +//! using the native OS-provided facilities (think `TlsAlloc` or +//! `pthread_setspecific`). The interface of this differs from the other types +//! of thread-local-storage provided in this crate in that OS-based TLS can only +//! get/set pointers, +//! +//! This module also provides two flavors of TLS. One is intended for static +//! initialization, and does not contain a `Drop` implementation to deallocate +//! the OS-TLS key. The other is a type which does implement `Drop` and hence +//! has a safe interface. +//! +//! # Usage +//! +//! This module should likely not be used directly unless other primitives are +//! being built on. types such as `thread_local::scoped::Key` are likely much +//! more useful in practice than this OS-based version which likely requires +//! unsafe code to interoperate with. +//! +//! # Example +//! +//! Using a dynamically allocated TLS key. Note that this key can be shared +//! among many threads via an `Arc`. +//! +//! ```rust,ignore +//! let key = Key::new(None); +//! assert!(key.get().is_null()); +//! key.set(1 as *mut u8); +//! assert!(!key.get().is_null()); +//! +//! drop(key); // deallocate this TLS slot. +//! ``` +//! +//! Sometimes a statically allocated key is either required or easier to work +//! with, however. +//! +//! ```rust,ignore +//! static KEY: StaticKey = INIT; +//! +//! unsafe { +//! assert!(KEY.get().is_null()); +//! KEY.set(1 as *mut u8); +//! } +//! ``` + +#![allow(non_camel_case_types)] + +use prelude::*; + +use sync::atomic::{mod, AtomicUint}; +use sync::{Mutex, Once, ONCE_INIT}; + +use sys::thread_local as imp; + +/// A type for TLS keys that are statically allocated. +/// +/// This type is entirely `unsafe` to use as it does not protect against +/// use-after-deallocation or use-during-deallocation. +/// +/// The actual OS-TLS key is lazily allocated when this is used for the first +/// time. The key is also deallocated when the Rust runtime exits or `destroy` +/// is called, whichever comes first. +/// +/// # Example +/// +/// ```ignore +/// use tls::os::{StaticKey, INIT}; +/// +/// static KEY: StaticKey = INIT; +/// +/// unsafe { +/// assert!(KEY.get().is_null()); +/// KEY.set(1 as *mut u8); +/// } +/// ``` +pub struct StaticKey { + /// Inner static TLS key (internals), created with by `INIT_INNER` in this + /// module. + pub inner: StaticKeyInner, + /// Destructor for the TLS value. + /// + /// See `Key::new` for information about when the destructor runs and how + /// it runs. + pub dtor: Option<unsafe extern fn(*mut u8)>, +} + +/// Inner contents of `StaticKey`, created by the `INIT_INNER` constant. +pub struct StaticKeyInner { + key: AtomicUint, +} + +/// A type for a safely managed OS-based TLS slot. +/// +/// This type allocates an OS TLS key when it is initialized and will deallocate +/// the key when it falls out of scope. When compared with `StaticKey`, this +/// type is entirely safe to use. +/// +/// Implementations will likely, however, contain unsafe code as this type only +/// operates on `*mut u8`, an unsafe pointer. +/// +/// # Example +/// +/// ```rust,ignore +/// use tls::os::Key; +/// +/// let key = Key::new(None); +/// assert!(key.get().is_null()); +/// key.set(1 as *mut u8); +/// assert!(!key.get().is_null()); +/// +/// drop(key); // deallocate this TLS slot. +/// ``` +pub struct Key { + key: imp::Key, +} + +/// Constant initialization value for static TLS keys. +/// +/// This value specifies no destructor by default. +pub const INIT: StaticKey = StaticKey { + inner: INIT_INNER, + dtor: None, +}; + +/// Constant initialization value for the inner part of static TLS keys. +/// +/// This value allows specific configuration of the destructor for a TLS key. +pub const INIT_INNER: StaticKeyInner = StaticKeyInner { + key: atomic::INIT_ATOMIC_UINT, +}; + +static INIT_KEYS: Once = ONCE_INIT; +static mut KEYS: *mut Mutex<Vec<imp::Key>> = 0 as *mut _; + +impl StaticKey { + /// Gets the value associated with this TLS key + /// + /// This will lazily allocate a TLS key from the OS if one has not already + /// been allocated. + #[inline] + pub unsafe fn get(&self) -> *mut u8 { imp::get(self.key()) } + + /// Sets this TLS key to a new value. + /// + /// This will lazily allocate a TLS key from the OS if one has not already + /// been allocated. + #[inline] + pub unsafe fn set(&self, val: *mut u8) { imp::set(self.key(), val) } + + /// Deallocates this OS TLS key. + /// + /// This function is unsafe as there is no guarantee that the key is not + /// currently in use by other threads or will not ever be used again. + /// + /// Note that this does *not* run the user-provided destructor if one was + /// specified at definition time. Doing so must be done manually. + pub unsafe fn destroy(&self) { + match self.inner.key.swap(0, atomic::SeqCst) { + 0 => {} + n => { imp::destroy(n as imp::Key) } + } + } + + #[inline] + unsafe fn key(&self) -> imp::Key { + match self.inner.key.load(atomic::Relaxed) { + 0 => self.lazy_init() as imp::Key, + n => n as imp::Key + } + } + + unsafe fn lazy_init(&self) -> uint { + // POSIX allows the key created here to be 0, but the compare_and_swap + // below relies on using 0 as a sentinel value to check who won the + // race to set the shared TLS key. As far as I know, there is no + // guaranteed value that cannot be returned as a posix_key_create key, + // so there is no value we can initialize the inner key with to + // prove that it has not yet been set. As such, we'll continue using a + // value of 0, but with some gyrations to make sure we have a non-0 + // value returned from the creation routine. + // FIXME: this is clearly a hack, and should be cleaned up. + let key1 = imp::create(self.dtor); + let key = if key1 != 0 { + key1 + } else { + let key2 = imp::create(self.dtor); + imp::destroy(key1); + key2 + }; + assert!(key != 0); + match self.inner.key.compare_and_swap(0, key as uint, atomic::SeqCst) { + // The CAS succeeded, so we've created the actual key + 0 => key as uint, + // If someone beat us to the punch, use their key instead + n => { imp::destroy(key); n } + } + } +} + +impl Key { + /// Create a new managed OS TLS key. + /// + /// This key will be deallocated when the key falls out of scope. + /// + /// The argument provided is an optionally-specified destructor for the + /// value of this TLS key. When a thread exits and the value for this key + /// is non-null the destructor will be invoked. The TLS value will be reset + /// to null before the destructor is invoked. + /// + /// Note that the destructor will not be run when the `Key` goes out of + /// scope. + #[inline] + pub fn new(dtor: Option<unsafe extern fn(*mut u8)>) -> Key { + Key { key: unsafe { imp::create(dtor) } } + } + + /// See StaticKey::get + #[inline] + pub fn get(&self) -> *mut u8 { + unsafe { imp::get(self.key) } + } + + /// See StaticKey::set + #[inline] + pub fn set(&self, val: *mut u8) { + unsafe { imp::set(self.key, val) } + } +} + +impl Drop for Key { + fn drop(&mut self) { + unsafe { imp::destroy(self.key) } + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + use super::{Key, StaticKey, INIT_INNER}; + + fn assert_sync<T: Sync>() {} + fn assert_send<T: Send>() {} + + #[test] + fn smoke() { + assert_sync::<Key>(); + assert_send::<Key>(); + + let k1 = Key::new(None); + let k2 = Key::new(None); + assert!(k1.get().is_null()); + assert!(k2.get().is_null()); + k1.set(1 as *mut _); + k2.set(2 as *mut _); + assert_eq!(k1.get() as uint, 1); + assert_eq!(k2.get() as uint, 2); + } + + #[test] + fn statik() { + static K1: StaticKey = StaticKey { inner: INIT_INNER, dtor: None }; + static K2: StaticKey = StaticKey { inner: INIT_INNER, dtor: None }; + + unsafe { + assert!(K1.get().is_null()); + assert!(K2.get().is_null()); + K1.set(1 as *mut _); + K2.set(2 as *mut _); + assert_eq!(K1.get() as uint, 1); + assert_eq!(K2.get() as uint, 2); + } + } +} diff --git a/src/libstd/sys/unix/backtrace.rs b/src/libstd/sys/unix/backtrace.rs new file mode 100644 index 00000000000..983d0e5fa14 --- /dev/null +++ b/src/libstd/sys/unix/backtrace.rs @@ -0,0 +1,493 @@ +// Copyright 2014 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. + +/// Backtrace support built on libgcc with some extra OS-specific support +/// +/// Some methods of getting a backtrace: +/// +/// * The backtrace() functions on unix. It turns out this doesn't work very +/// well for green threads on OSX, and the address to symbol portion of it +/// suffers problems that are described below. +/// +/// * Using libunwind. This is more difficult than it sounds because libunwind +/// isn't installed everywhere by default. It's also a bit of a hefty library, +/// so possibly not the best option. When testing, libunwind was excellent at +/// getting both accurate backtraces and accurate symbols across platforms. +/// This route was not chosen in favor of the next option, however. +/// +/// * We're already using libgcc_s for exceptions in rust (triggering task +/// unwinding and running destructors on the stack), and it turns out that it +/// conveniently comes with a function that also gives us a backtrace. All of +/// these functions look like _Unwind_*, but it's not quite the full +/// repertoire of the libunwind API. Due to it already being in use, this was +/// the chosen route of getting a backtrace. +/// +/// After choosing libgcc_s for backtraces, the sad part is that it will only +/// give us a stack trace of instruction pointers. Thankfully these instruction +/// pointers are accurate (they work for green and native threads), but it's +/// then up to us again to figure out how to translate these addresses to +/// symbols. As with before, we have a few options. Before, that, a little bit +/// of an interlude about symbols. This is my very limited knowledge about +/// symbol tables, and this information is likely slightly wrong, but the +/// general idea should be correct. +/// +/// When talking about symbols, it's helpful to know a few things about where +/// symbols are located. Some symbols are located in the dynamic symbol table +/// of the executable which in theory means that they're available for dynamic +/// linking and lookup. Other symbols end up only in the local symbol table of +/// the file. This loosely corresponds to pub and priv functions in Rust. +/// +/// Armed with this knowledge, we know that our solution for address to symbol +/// translation will need to consult both the local and dynamic symbol tables. +/// With that in mind, here's our options of translating an address to +/// a symbol. +/// +/// * Use dladdr(). The original backtrace()-based idea actually uses dladdr() +/// behind the scenes to translate, and this is why backtrace() was not used. +/// Conveniently, this method works fantastically on OSX. It appears dladdr() +/// uses magic to consult the local symbol table, or we're putting everything +/// in the dynamic symbol table anyway. Regardless, for OSX, this is the +/// method used for translation. It's provided by the system and easy to do.o +/// +/// Sadly, all other systems have a dladdr() implementation that does not +/// consult the local symbol table. This means that most functions are blank +/// because they don't have symbols. This means that we need another solution. +/// +/// * Use unw_get_proc_name(). This is part of the libunwind api (not the +/// libgcc_s version of the libunwind api), but involves taking a dependency +/// to libunwind. We may pursue this route in the future if we bundle +/// libunwind, but libunwind was unwieldy enough that it was not chosen at +/// this time to provide this functionality. +/// +/// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a +/// semi-reasonable solution. The stdlib already knows how to spawn processes, +/// so in theory it could invoke readelf, parse the output, and consult the +/// local/dynamic symbol tables from there. This ended up not getting chosen +/// due to the craziness of the idea plus the advent of the next option. +/// +/// * Use `libbacktrace`. It turns out that this is a small library bundled in +/// the gcc repository which provides backtrace and symbol translation +/// functionality. All we really need from it is the backtrace functionality, +/// and we only really need this on everything that's not OSX, so this is the +/// chosen route for now. +/// +/// In summary, the current situation uses libgcc_s to get a trace of stack +/// pointers, and we use dladdr() or libbacktrace to translate these addresses +/// to symbols. This is a bit of a hokey implementation as-is, but it works for +/// all unix platforms we support right now, so it at least gets the job done. + +use c_str::CString; +use io::{IoResult, Writer}; +use libc; +use mem; +use option::Option::{mod, Some, None}; +use result::Result::{Ok, Err}; +use sync::{StaticMutex, MUTEX_INIT}; + +use sys_common::backtrace::*; + +/// As always - iOS on arm uses SjLj exceptions and +/// _Unwind_Backtrace is even not available there. Still, +/// backtraces could be extracted using a backtrace function, +/// which thanks god is public +/// +/// As mentioned in a huge comment block above, backtrace doesn't +/// play well with green threads, so while it is extremely nice +/// and simple to use it should be used only on iOS devices as the +/// only viable option. +#[cfg(all(target_os = "ios", target_arch = "arm"))] +#[inline(never)] +pub fn write(w: &mut Writer) -> IoResult<()> { + use iter::{IteratorExt, range}; + use result; + use slice::SliceExt; + + extern { + fn backtrace(buf: *mut *mut libc::c_void, + sz: libc::c_int) -> libc::c_int; + } + + // while it doesn't requires lock for work as everything is + // local, it still displays much nicer backtraces when a + // couple of tasks panic simultaneously + static LOCK: StaticMutex = MUTEX_INIT; + let _g = unsafe { LOCK.lock() }; + + try!(writeln!(w, "stack backtrace:")); + // 100 lines should be enough + const SIZE: uint = 100; + let mut buf: [*mut libc::c_void, ..SIZE] = unsafe {mem::zeroed()}; + let cnt = unsafe { backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as uint}; + + // skipping the first one as it is write itself + let iter = range(1, cnt).map(|i| { + print(w, i as int, buf[i]) + }); + result::fold(iter, (), |_, _| ()) +} + +#[cfg(not(all(target_os = "ios", target_arch = "arm")))] +#[inline(never)] // if we know this is a function call, we can skip it when + // tracing +pub fn write(w: &mut Writer) -> IoResult<()> { + use io::IoError; + + struct Context<'a> { + idx: int, + writer: &'a mut (Writer+'a), + last_error: Option<IoError>, + } + + // When using libbacktrace, we use some necessary global state, so we + // need to prevent more than one thread from entering this block. This + // is semi-reasonable in terms of printing anyway, and we know that all + // I/O done here is blocking I/O, not green I/O, so we don't have to + // worry about this being a native vs green mutex. + static LOCK: StaticMutex = MUTEX_INIT; + let _g = unsafe { LOCK.lock() }; + + try!(writeln!(w, "stack backtrace:")); + + let mut cx = Context { writer: w, last_error: None, idx: 0 }; + return match unsafe { + uw::_Unwind_Backtrace(trace_fn, + &mut cx as *mut Context as *mut libc::c_void) + } { + uw::_URC_NO_REASON => { + match cx.last_error { + Some(err) => Err(err), + None => Ok(()) + } + } + _ => Ok(()), + }; + + extern fn trace_fn(ctx: *mut uw::_Unwind_Context, + arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { + let cx: &mut Context = unsafe { mem::transmute(arg) }; + let ip = unsafe { uw::_Unwind_GetIP(ctx) as *mut libc::c_void }; + // dladdr() on osx gets whiny when we use FindEnclosingFunction, and + // it appears to work fine without it, so we only use + // FindEnclosingFunction on non-osx platforms. In doing so, we get a + // slightly more accurate stack trace in the process. + // + // This is often because panic involves the last instruction of a + // function being "call std::rt::begin_unwind", with no ret + // instructions after it. This means that the return instruction + // pointer points *outside* of the calling function, and by + // unwinding it we go back to the original function. + let ip = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { + ip + } else { + unsafe { uw::_Unwind_FindEnclosingFunction(ip) } + }; + + // Don't print out the first few frames (they're not user frames) + cx.idx += 1; + if cx.idx <= 0 { return uw::_URC_NO_REASON } + // Don't print ginormous backtraces + if cx.idx > 100 { + match write!(cx.writer, " ... <frames omitted>\n") { + Ok(()) => {} + Err(e) => { cx.last_error = Some(e); } + } + return uw::_URC_FAILURE + } + + // Once we hit an error, stop trying to print more frames + if cx.last_error.is_some() { return uw::_URC_FAILURE } + + match print(cx.writer, cx.idx, ip) { + Ok(()) => {} + Err(e) => { cx.last_error = Some(e); } + } + + // keep going + return uw::_URC_NO_REASON + } +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { + use intrinsics; + #[repr(C)] + struct Dl_info { + dli_fname: *const libc::c_char, + dli_fbase: *mut libc::c_void, + dli_sname: *const libc::c_char, + dli_saddr: *mut libc::c_void, + } + extern { + fn dladdr(addr: *const libc::c_void, + info: *mut Dl_info) -> libc::c_int; + } + + let mut info: Dl_info = unsafe { intrinsics::init() }; + if unsafe { dladdr(addr as *const libc::c_void, &mut info) == 0 } { + output(w, idx,addr, None) + } else { + output(w, idx, addr, Some(unsafe { + CString::new(info.dli_sname, false) + })) + } +} + +#[cfg(not(any(target_os = "macos", target_os = "ios")))] +fn print(w: &mut Writer, idx: int, addr: *mut libc::c_void) -> IoResult<()> { + use iter::{Iterator, IteratorExt}; + use os; + use path::GenericPath; + use ptr::RawPtr; + use ptr; + use slice::SliceExt; + + //////////////////////////////////////////////////////////////////////// + // libbacktrace.h API + //////////////////////////////////////////////////////////////////////// + type backtrace_syminfo_callback = + extern "C" fn(data: *mut libc::c_void, + pc: libc::uintptr_t, + symname: *const libc::c_char, + symval: libc::uintptr_t, + symsize: libc::uintptr_t); + type backtrace_error_callback = + extern "C" fn(data: *mut libc::c_void, + msg: *const libc::c_char, + errnum: libc::c_int); + enum backtrace_state {} + #[link(name = "backtrace", kind = "static")] + #[cfg(not(test))] + extern {} + + extern { + fn backtrace_create_state(filename: *const libc::c_char, + threaded: libc::c_int, + error: backtrace_error_callback, + data: *mut libc::c_void) + -> *mut backtrace_state; + fn backtrace_syminfo(state: *mut backtrace_state, + addr: libc::uintptr_t, + cb: backtrace_syminfo_callback, + error: backtrace_error_callback, + data: *mut libc::c_void) -> libc::c_int; + } + + //////////////////////////////////////////////////////////////////////// + // helper callbacks + //////////////////////////////////////////////////////////////////////// + + extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char, + _errnum: libc::c_int) { + // do nothing for now + } + extern fn syminfo_cb(data: *mut libc::c_void, + _pc: libc::uintptr_t, + symname: *const libc::c_char, + _symval: libc::uintptr_t, + _symsize: libc::uintptr_t) { + let slot = data as *mut *const libc::c_char; + unsafe { *slot = symname; } + } + + // The libbacktrace API supports creating a state, but it does not + // support destroying a state. I personally take this to mean that a + // state is meant to be created and then live forever. + // + // I would love to register an at_exit() handler which cleans up this + // state, but libbacktrace provides no way to do so. + // + // With these constraints, this function has a statically cached state + // that is calculated the first time this is requested. Remember that + // backtracing all happens serially (one global lock). + // + // An additionally oddity in this function is that we initialize the + // filename via self_exe_name() to pass to libbacktrace. It turns out + // that on Linux libbacktrace seamlessly gets the filename of the + // current executable, but this fails on freebsd. by always providing + // it, we make sure that libbacktrace never has a reason to not look up + // the symbols. The libbacktrace API also states that the filename must + // be in "permanent memory", so we copy it to a static and then use the + // static as the pointer. + // + // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't + // tested if this is required or not. + unsafe fn init_state() -> *mut backtrace_state { + static mut STATE: *mut backtrace_state = 0 as *mut backtrace_state; + static mut LAST_FILENAME: [libc::c_char, ..256] = [0, ..256]; + if !STATE.is_null() { return STATE } + let selfname = if cfg!(target_os = "freebsd") || + cfg!(target_os = "dragonfly") { + os::self_exe_name() + } else { + None + }; + let filename = match selfname { + Some(path) => { + let bytes = path.as_vec(); + if bytes.len() < LAST_FILENAME.len() { + let i = bytes.iter(); + for (slot, val) in LAST_FILENAME.iter_mut().zip(i) { + *slot = *val as libc::c_char; + } + LAST_FILENAME.as_ptr() + } else { + ptr::null() + } + } + None => ptr::null(), + }; + STATE = backtrace_create_state(filename, 0, error_cb, + ptr::null_mut()); + return STATE + } + + //////////////////////////////////////////////////////////////////////// + // translation + //////////////////////////////////////////////////////////////////////// + + // backtrace errors are currently swept under the rug, only I/O + // errors are reported + let state = unsafe { init_state() }; + if state.is_null() { + return output(w, idx, addr, None) + } + let mut data = 0 as *const libc::c_char; + let data_addr = &mut data as *mut *const libc::c_char; + let ret = unsafe { + backtrace_syminfo(state, addr as libc::uintptr_t, + syminfo_cb, error_cb, + data_addr as *mut libc::c_void) + }; + if ret == 0 || data.is_null() { + output(w, idx, addr, None) + } else { + output(w, idx, addr, Some(unsafe { CString::new(data, false) })) + } +} + +// Finally, after all that work above, we can emit a symbol. +fn output(w: &mut Writer, idx: int, addr: *mut libc::c_void, + s: Option<CString>) -> IoResult<()> { + try!(write!(w, " {:2}: {:2$} - ", idx, addr, HEX_WIDTH)); + match s.as_ref().and_then(|c| c.as_str()) { + Some(string) => try!(demangle(w, string)), + None => try!(write!(w, "<unknown>")), + } + w.write(&['\n' as u8]) +} + +/// Unwind library interface used for backtraces +/// +/// Note that dead code is allowed as here are just bindings +/// iOS doesn't use all of them it but adding more +/// platform-specific configs pollutes the code too much +#[allow(non_camel_case_types)] +#[allow(non_snake_case)] +#[allow(dead_code)] +mod uw { + pub use self::_Unwind_Reason_Code::*; + + use libc; + + #[repr(C)] + pub enum _Unwind_Reason_Code { + _URC_NO_REASON = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8, + _URC_FAILURE = 9, // used only by ARM EABI + } + + pub enum _Unwind_Context {} + + pub type _Unwind_Trace_Fn = + extern fn(ctx: *mut _Unwind_Context, + arg: *mut libc::c_void) -> _Unwind_Reason_Code; + + extern { + // No native _Unwind_Backtrace on iOS + #[cfg(not(all(target_os = "ios", target_arch = "arm")))] + pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, + trace_argument: *mut libc::c_void) + -> _Unwind_Reason_Code; + + #[cfg(all(not(target_os = "android"), + not(all(target_os = "linux", target_arch = "arm"))))] + pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t; + + #[cfg(all(not(target_os = "android"), + not(all(target_os = "linux", target_arch = "arm"))))] + pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) + -> *mut libc::c_void; + } + + // On android, the function _Unwind_GetIP is a macro, and this is the + // expansion of the macro. This is all copy/pasted directly from the + // header file with the definition of _Unwind_GetIP. + #[cfg(any(target_os = "android", + all(target_os = "linux", target_arch = "arm")))] + pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { + #[repr(C)] + enum _Unwind_VRS_Result { + _UVRSR_OK = 0, + _UVRSR_NOT_IMPLEMENTED = 1, + _UVRSR_FAILED = 2, + } + #[repr(C)] + enum _Unwind_VRS_RegClass { + _UVRSC_CORE = 0, + _UVRSC_VFP = 1, + _UVRSC_FPA = 2, + _UVRSC_WMMXD = 3, + _UVRSC_WMMXC = 4, + } + #[repr(C)] + enum _Unwind_VRS_DataRepresentation { + _UVRSD_UINT32 = 0, + _UVRSD_VFPX = 1, + _UVRSD_FPAX = 2, + _UVRSD_UINT64 = 3, + _UVRSD_FLOAT = 4, + _UVRSD_DOUBLE = 5, + } + + type _Unwind_Word = libc::c_uint; + extern { + fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context, + klass: _Unwind_VRS_RegClass, + word: _Unwind_Word, + repr: _Unwind_VRS_DataRepresentation, + data: *mut libc::c_void) + -> _Unwind_VRS_Result; + } + + let mut val: _Unwind_Word = 0; + let ptr = &mut val as *mut _Unwind_Word; + let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15, + _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, + ptr as *mut libc::c_void); + (val & !1) as libc::uintptr_t + } + + // This function also doesn't exist on Android or ARM/Linux, so make it + // a no-op + #[cfg(any(target_os = "android", + all(target_os = "linux", target_arch = "arm")))] + pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) + -> *mut libc::c_void + { + pc + } +} diff --git a/src/libstd/sys/unix/condvar.rs b/src/libstd/sys/unix/condvar.rs new file mode 100644 index 00000000000..f64718539ef --- /dev/null +++ b/src/libstd/sys/unix/condvar.rs @@ -0,0 +1,83 @@ +// Copyright 2014 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 cell::UnsafeCell; +use libc; +use sys::mutex::{mod, Mutex}; +use sys::sync as ffi; +use time::Duration; + +pub struct Condvar { inner: UnsafeCell<ffi::pthread_cond_t> } + +pub const CONDVAR_INIT: Condvar = Condvar { + inner: UnsafeCell { value: ffi::PTHREAD_COND_INITIALIZER }, +}; + +impl Condvar { + #[inline] + pub unsafe fn new() -> Condvar { + // Might be moved and address is changing it is better to avoid + // initialization of potentially opaque OS data before it landed + Condvar { inner: UnsafeCell::new(ffi::PTHREAD_COND_INITIALIZER) } + } + + #[inline] + pub unsafe fn notify_one(&self) { + let r = ffi::pthread_cond_signal(self.inner.get()); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn notify_all(&self) { + let r = ffi::pthread_cond_broadcast(self.inner.get()); + debug_assert_eq!(r, 0); + } + + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { + let r = ffi::pthread_cond_wait(self.inner.get(), mutex::raw(mutex)); + debug_assert_eq!(r, 0); + } + + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + assert!(dur >= Duration::nanoseconds(0)); + + // First, figure out what time it currently is + let mut tv = libc::timeval { tv_sec: 0, tv_usec: 0 }; + let r = ffi::gettimeofday(&mut tv, 0 as *mut _); + debug_assert_eq!(r, 0); + + // Offset that time with the specified duration + let abs = Duration::seconds(tv.tv_sec as i64) + + Duration::microseconds(tv.tv_usec as i64) + + dur; + let ns = abs.num_nanoseconds().unwrap() as u64; + let timeout = libc::timespec { + tv_sec: (ns / 1000000000) as libc::time_t, + tv_nsec: (ns % 1000000000) as libc::c_long, + }; + + // And wait! + let r = ffi::pthread_cond_timedwait(self.inner.get(), mutex::raw(mutex), + &timeout); + if r != 0 { + debug_assert_eq!(r as int, libc::ETIMEDOUT as int); + false + } else { + true + } + } + + #[inline] + pub unsafe fn destroy(&self) { + let r = ffi::pthread_cond_destroy(self.inner.get()); + debug_assert_eq!(r, 0); + } +} diff --git a/src/libstd/sys/unix/ext.rs b/src/libstd/sys/unix/ext.rs new file mode 100644 index 00000000000..ae3c939bf78 --- /dev/null +++ b/src/libstd/sys/unix/ext.rs @@ -0,0 +1,107 @@ +// Copyright 2014 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. + +//! Experimental extensions to `std` for Unix platforms. +//! +//! For now, this module is limited to extracting file descriptors, +//! but its functionality will grow over time. +//! +//! # Example +//! +//! ```rust,ignore +//! #![feature(globs)] +//! +//! use std::io::fs::File; +//! use std::os::unix::prelude::*; +//! +//! fn main() { +//! let f = File::create(&Path::new("foo.txt")).unwrap(); +//! let fd = f.as_raw_fd(); +//! +//! // use fd with native unix bindings +//! } +//! ``` + +#![experimental] + +use sys_common::AsInner; +use libc; + +use io; + +/// Raw file descriptors. +pub type Fd = libc::c_int; + +/// Extract raw file descriptor +pub trait AsRawFd { + /// Extract the raw file descriptor, without taking any ownership. + fn as_raw_fd(&self) -> Fd; +} + +impl AsRawFd for io::fs::File { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +impl AsRawFd for io::pipe::PipeStream { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +impl AsRawFd for io::net::pipe::UnixStream { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +impl AsRawFd for io::net::pipe::UnixListener { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +impl AsRawFd for io::net::pipe::UnixAcceptor { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +impl AsRawFd for io::net::tcp::TcpStream { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +impl AsRawFd for io::net::tcp::TcpListener { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +impl AsRawFd for io::net::tcp::TcpAcceptor { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +impl AsRawFd for io::net::udp::UdpSocket { + fn as_raw_fd(&self) -> Fd { + self.as_inner().fd() + } +} + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +pub mod prelude { + pub use super::{Fd, AsRawFd}; +} diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index 816876b5e4a..98d860f9646 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -18,14 +18,11 @@ use io; use prelude::*; use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode}; -use io::{IoResult, FileStat, SeekStyle, Reader}; +use io::{IoResult, FileStat, SeekStyle}; use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append}; -use result::{Ok, Err}; use sys::retry; use sys_common::{keep_going, eof, mkerr_libc}; -pub use path::PosixPath as Path; - pub type fd_t = libc::c_int; pub struct FileDesc { @@ -201,7 +198,7 @@ pub fn readdir(p: &Path) -> IoResult<Vec<Path>> { let size = unsafe { rust_dirent_t_size() }; let mut buf = Vec::<u8>::with_capacity(size as uint); - let ptr = buf.as_mut_slice().as_mut_ptr() as *mut dirent_t; + let ptr = buf.as_mut_ptr() as *mut dirent_t; let p = p.to_c_str(); let dir_ptr = unsafe {opendir(p.as_ptr())}; @@ -305,12 +302,12 @@ fn mkstat(stat: &libc::stat) -> FileStat { FileStat { size: stat.st_size as u64, kind: match (stat.st_mode as libc::mode_t) & libc::S_IFMT { - libc::S_IFREG => io::TypeFile, - libc::S_IFDIR => io::TypeDirectory, - libc::S_IFIFO => io::TypeNamedPipe, - libc::S_IFBLK => io::TypeBlockSpecial, - libc::S_IFLNK => io::TypeSymlink, - _ => io::TypeUnknown, + libc::S_IFREG => io::FileType::RegularFile, + libc::S_IFDIR => io::FileType::Directory, + libc::S_IFIFO => io::FileType::NamedPipe, + libc::S_IFBLK => io::FileType::BlockSpecial, + libc::S_IFLNK => io::FileType::Symlink, + _ => io::FileType::Unknown, }, perm: FilePermission::from_bits_truncate(stat.st_mode as u32), created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64), diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 664a6a1e70c..f3babca3287 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -23,25 +23,36 @@ use prelude::*; use io::{mod, IoResult, IoError}; use sys_common::mkerr_libc; -macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => ( +macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => ( static $name: Helper<$m> = Helper { - lock: ::rustrt::mutex::NATIVE_MUTEX_INIT, + lock: ::sync::MUTEX_INIT, + cond: ::sync::CONDVAR_INIT, chan: ::cell::UnsafeCell { value: 0 as *mut Sender<$m> }, signal: ::cell::UnsafeCell { value: 0 }, initialized: ::cell::UnsafeCell { value: false }, + shutdown: ::cell::UnsafeCell { value: false }, }; -) ) +) } +pub mod backtrace; pub mod c; +pub mod ext; +pub mod condvar; pub mod fs; +pub mod helper_signal; +pub mod mutex; pub mod os; -pub mod tcp; -pub mod udp; pub mod pipe; -pub mod helper_signal; pub mod process; +pub mod rwlock; +pub mod stack_overflow; +pub mod sync; +pub mod tcp; +pub mod thread; +pub mod thread_local; pub mod timer; pub mod tty; +pub mod udp; pub mod addrinfo { pub use sys_common::net::get_host_addresses; @@ -117,7 +128,10 @@ pub fn decode_error_detailed(errno: i32) -> IoError { } #[inline] -pub fn retry<T: SignedInt> (f: || -> T) -> T { +pub fn retry<T, F> (mut f: F) -> T where + T: SignedInt, + F: FnMut() -> T, +{ let one: T = Int::one(); loop { let n = f(); diff --git a/src/libstd/sys/unix/mutex.rs b/src/libstd/sys/unix/mutex.rs new file mode 100644 index 00000000000..2f01c53cb2c --- /dev/null +++ b/src/libstd/sys/unix/mutex.rs @@ -0,0 +1,52 @@ +// Copyright 2014 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 cell::UnsafeCell; +use sys::sync as ffi; +use sys_common::mutex; + +pub struct Mutex { inner: UnsafeCell<ffi::pthread_mutex_t> } + +#[inline] +pub unsafe fn raw(m: &Mutex) -> *mut ffi::pthread_mutex_t { + m.inner.get() +} + +pub const MUTEX_INIT: Mutex = Mutex { + inner: UnsafeCell { value: ffi::PTHREAD_MUTEX_INITIALIZER }, +}; + +impl Mutex { + #[inline] + pub unsafe fn new() -> Mutex { + // Might be moved and address is changing it is better to avoid + // initialization of potentially opaque OS data before it landed + MUTEX_INIT + } + #[inline] + pub unsafe fn lock(&self) { + let r = ffi::pthread_mutex_lock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn unlock(&self) { + let r = ffi::pthread_mutex_unlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn try_lock(&self) -> bool { + ffi::pthread_mutex_trylock(self.inner.get()) == 0 + } + #[inline] + pub unsafe fn destroy(&self) { + let r = ffi::pthread_mutex_destroy(self.inner.get()); + debug_assert_eq!(r, 0); + } +} diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index 4e495f043bc..316d97064ee 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -8,14 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use libc; -use libc::{c_int, c_char}; +//! Implementation of `std::os` functionality for unix systems + use prelude::*; -use io::IoResult; + +use error::{FromError, Error}; +use fmt; +use io::{IoError, IoResult}; +use libc::{mod, c_int, c_char, c_void}; +use path::BytesContainer; +use ptr; +use sync::atomic::{AtomicInt, INIT_ATOMIC_INT, SeqCst}; use sys::fs::FileDesc; +use os; use os::TMPBUF_SZ; +const BUF_BYTES : uint = 2048u; + /// Returns the platform-specific value of errno pub fn errno() -> int { #[cfg(any(target_os = "macos", @@ -98,7 +108,7 @@ pub fn error_string(errno: i32) -> String { panic!("strerror_r failure"); } - ::string::raw::from_buf(p as *const u8) + String::from_raw_buf(p as *const u8) } } @@ -110,3 +120,122 @@ pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> { Err(super::last_error()) } } + +pub fn getcwd() -> IoResult<Path> { + use c_str::CString; + + let mut buf = [0 as c_char, ..BUF_BYTES]; + unsafe { + if libc::getcwd(buf.as_mut_ptr(), buf.len() as libc::size_t).is_null() { + Err(IoError::last_error()) + } else { + Ok(Path::new(CString::new(buf.as_ptr(), false))) + } + } +} + +pub unsafe fn get_env_pairs() -> Vec<Vec<u8>> { + use c_str::CString; + + extern { + fn rust_env_pairs() -> *const *const c_char; + } + let mut environ = rust_env_pairs(); + if environ as uint == 0 { + panic!("os::env() failure getting env string from OS: {}", + os::last_os_error()); + } + let mut result = Vec::new(); + while *environ != 0 as *const _ { + let env_pair = + CString::new(*environ, false).as_bytes_no_nul().to_vec(); + result.push(env_pair); + environ = environ.offset(1); + } + result +} + +pub fn split_paths(unparsed: &[u8]) -> Vec<Path> { + unparsed.split(|b| *b == b':').map(Path::new).collect() +} + +pub fn join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> { + let mut joined = Vec::new(); + let sep = b':'; + + for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() { + if i > 0 { joined.push(sep) } + if path.contains(&sep) { return Err("path segment contains separator `:`") } + joined.push_all(path); + } + + Ok(joined) +} + +#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] +pub fn load_self() -> Option<Vec<u8>> { + unsafe { + use libc::funcs::bsd44::*; + use libc::consts::os::extra::*; + let mut mib = vec![CTL_KERN as c_int, + KERN_PROC as c_int, + KERN_PROC_PATHNAME as c_int, + -1 as c_int]; + let mut sz: libc::size_t = 0; + let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint, + ptr::null_mut(), &mut sz, ptr::null_mut(), + 0u as libc::size_t); + if err != 0 { return None; } + if sz == 0 { return None; } + let mut v: Vec<u8> = Vec::with_capacity(sz as uint); + let err = sysctl(mib.as_mut_ptr(), mib.len() as ::libc::c_uint, + v.as_mut_ptr() as *mut libc::c_void, &mut sz, + ptr::null_mut(), 0u as libc::size_t); + if err != 0 { return None; } + if sz == 0 { return None; } + v.set_len(sz as uint - 1); // chop off trailing NUL + Some(v) + } +} + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn load_self() -> Option<Vec<u8>> { + use std::io; + + match io::fs::readlink(&Path::new("/proc/self/exe")) { + Ok(path) => Some(path.into_vec()), + Err(..) => None + } +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub fn load_self() -> Option<Vec<u8>> { + unsafe { + use libc::funcs::extra::_NSGetExecutablePath; + let mut sz: u32 = 0; + _NSGetExecutablePath(ptr::null_mut(), &mut sz); + if sz == 0 { return None; } + let mut v: Vec<u8> = Vec::with_capacity(sz as uint); + let err = _NSGetExecutablePath(v.as_mut_ptr() as *mut i8, &mut sz); + if err != 0 { return None; } + v.set_len(sz as uint - 1); // chop off trailing NUL + Some(v) + } +} + +pub fn chdir(p: &Path) -> IoResult<()> { + p.with_c_str(|buf| { + unsafe { + match libc::chdir(buf) == (0 as c_int) { + true => Ok(()), + false => Err(IoError::last_error()), + } + } + }) +} + +pub fn page_size() -> uint { + unsafe { + libc::sysconf(libc::_SC_PAGESIZE) as uint + } +} diff --git a/src/libstd/sys/unix/pipe.rs b/src/libstd/sys/unix/pipe.rs index 4d3469a9c24..348b7cfad33 100644 --- a/src/libstd/sys/unix/pipe.rs +++ b/src/libstd/sys/unix/pipe.rs @@ -12,14 +12,14 @@ use alloc::arc::Arc; use libc; use c_str::CString; use mem; -use rustrt::mutex; -use sync::atomic; +use sync::{atomic, Mutex}; use io::{mod, IoResult, IoError}; use prelude::*; use sys::{mod, timer, retry, c, set_nonblocking, wouldblock}; use sys::fs::{fd_t, FileDesc}; use sys_common::net::*; +use sys_common::net::SocketStatus::*; use sys_common::{eof, mkerr_libc}; fn unix_socket(ty: libc::c_int) -> IoResult<fd_t> { @@ -60,12 +60,12 @@ struct Inner { // Unused on Linux, where this lock is not necessary. #[allow(dead_code)] - lock: mutex::NativeMutex + lock: Mutex<()>, } impl Inner { fn new(fd: fd_t) -> Inner { - Inner { fd: fd, lock: unsafe { mutex::NativeMutex::new() } } + Inner { fd: fd, lock: Mutex::new(()) } } } @@ -133,7 +133,7 @@ impl UnixStream { } } - fn fd(&self) -> fd_t { self.inner.fd } + pub fn fd(&self) -> fd_t { self.inner.fd } #[cfg(target_os = "linux")] fn lock_nonblocking(&self) {} @@ -150,8 +150,8 @@ impl UnixStream { pub fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> { let fd = self.fd(); - let dolock = || self.lock_nonblocking(); - let doread = |nb| unsafe { + let dolock = |&:| self.lock_nonblocking(); + let doread = |&mut: nb| unsafe { let flags = if nb {c::MSG_DONTWAIT} else {0}; libc::recv(fd, buf.as_mut_ptr() as *mut libc::c_void, @@ -163,8 +163,8 @@ impl UnixStream { pub fn write(&mut self, buf: &[u8]) -> IoResult<()> { let fd = self.fd(); - let dolock = || self.lock_nonblocking(); - let dowrite = |nb: bool, buf: *const u8, len: uint| unsafe { + let dolock = |&: | self.lock_nonblocking(); + let dowrite = |&: nb: bool, buf: *const u8, len: uint| unsafe { let flags = if nb {c::MSG_DONTWAIT} else {0}; libc::send(fd, buf as *const _, @@ -222,7 +222,7 @@ impl UnixListener { }) } - fn fd(&self) -> fd_t { self.inner.fd } + pub fn fd(&self) -> fd_t { self.inner.fd } pub fn listen(self) -> IoResult<UnixAcceptor> { match unsafe { libc::listen(self.fd(), 128) } { @@ -260,7 +260,7 @@ struct AcceptorInner { } impl UnixAcceptor { - fn fd(&self) -> fd_t { self.inner.listener.fd() } + pub fn fd(&self) -> fd_t { self.inner.listener.fd() } pub fn accept(&mut self) -> IoResult<UnixStream> { let deadline = if self.deadline == 0 {None} else {Some(self.deadline)}; diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index 81bc138ca91..835f4279d9b 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -11,7 +11,7 @@ use self::Req::*; use libc::{mod, pid_t, c_void, c_int}; use c_str::CString; -use io::{mod, IoResult, IoError}; +use io::{mod, IoResult, IoError, EndOfFile}; use mem; use os; use ptr; @@ -24,11 +24,11 @@ use hash::Hash; use sys::{mod, retry, c, wouldblock, set_nonblocking, ms_to_timeval}; use sys::fs::FileDesc; use sys_common::helper_thread::Helper; -use sys_common::{AsFileDesc, mkerr_libc, timeout}; +use sys_common::{AsInner, mkerr_libc, timeout}; pub use sys_common::ProcessConfig; -helper_init!(static HELPER: Helper<Req>) +helper_init! { static HELPER: Helper<Req> } /// The unique id of the process (this should never be negative). pub struct Process { @@ -39,6 +39,8 @@ enum Req { NewChild(libc::pid_t, Sender<ProcessExit>, u64), } +const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; + impl Process { pub fn id(&self) -> pid_t { self.pid @@ -56,7 +58,7 @@ impl Process { pub fn spawn<K, V, C, P>(cfg: &C, in_fd: Option<P>, out_fd: Option<P>, err_fd: Option<P>) -> IoResult<Process> - where C: ProcessConfig<K, V>, P: AsFileDesc, + where C: ProcessConfig<K, V>, P: AsInner<FileDesc>, K: BytesContainer + Eq + Hash, V: BytesContainer { use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp}; @@ -92,8 +94,8 @@ impl Process { mem::transmute::<&ProcessConfig<K,V>,&'static ProcessConfig<K,V>>(cfg) }; - with_envp(cfg.env(), proc(envp) { - with_argv(cfg.program(), cfg.args(), proc(argv) unsafe { + with_envp(cfg.env(), move|: envp: *const c_void| { + with_argv(cfg.program(), cfg.args(), move|: argv: *const *const libc::c_char| unsafe { let (input, mut output) = try!(sys::os::pipe()); // We may use this in the child, so perform allocations before the @@ -106,18 +108,36 @@ impl Process { if pid < 0 { return Err(super::last_error()) } else if pid > 0 { + #[inline] + fn combine(arr: &[u8]) -> i32 { + let a = arr[0] as u32; + let b = arr[1] as u32; + let c = arr[2] as u32; + let d = arr[3] as u32; + + ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32 + } + + let p = Process{ pid: pid }; drop(output); - let mut bytes = [0, ..4]; + let mut bytes = [0, ..8]; return match input.read(&mut bytes) { - Ok(4) => { - let errno = (bytes[0] as i32 << 24) | - (bytes[1] as i32 << 16) | - (bytes[2] as i32 << 8) | - (bytes[3] as i32 << 0); + Ok(8) => { + assert!(combine(CLOEXEC_MSG_FOOTER) == combine(bytes.slice(4, 8)), + "Validation on the CLOEXEC pipe failed: {}", bytes); + let errno = combine(bytes.slice(0, 4)); + assert!(p.wait(0).is_ok(), "wait(0) should either return Ok or panic"); Err(super::decode_error(errno)) } - Err(..) => Ok(Process { pid: pid }), - Ok(..) => panic!("short read on the cloexec pipe"), + Err(ref e) if e.kind == EndOfFile => Ok(p), + Err(e) => { + assert!(p.wait(0).is_ok(), "wait(0) should either return Ok or panic"); + panic!("the CLOEXEC pipe failed: {}", e) + }, + Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic + assert!(p.wait(0).is_ok(), "wait(0) should either return Ok or panic"); + panic!("short read on the CLOEXEC pipe") + } }; } @@ -154,13 +174,16 @@ impl Process { let _ = libc::close(input.fd()); fn fail(output: &mut FileDesc) -> ! { - let errno = sys::os::errno(); + let errno = sys::os::errno() as u32; let bytes = [ (errno >> 24) as u8, (errno >> 16) as u8, (errno >> 8) as u8, (errno >> 0) as u8, + CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1], + CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3] ]; + // pipe I/O up to PIPE_BUF bytes should be atomic assert!(output.write(&bytes).is_ok()); unsafe { libc::_exit(1) } } @@ -183,7 +206,7 @@ impl Process { libc::open(devnull.as_ptr(), flags, 0) } Some(obj) => { - let fd = obj.as_fd().fd(); + let fd = obj.as_inner().fd(); // Leak the memory and the file descriptor. We're in the // child now an all our resources are going to be // cleaned up very soon @@ -356,8 +379,8 @@ impl Process { // wait indefinitely for a message to arrive. // // FIXME: sure would be nice to not have to scan the entire array - let min = active.iter().map(|a| *a.ref2()).enumerate().min_by(|p| { - p.val1() + let min = active.iter().map(|a| a.2).enumerate().min_by(|p| { + p.1 }); let (p, idx) = match min { Some((idx, deadline)) => { @@ -508,8 +531,11 @@ impl Process { } } -fn with_argv<T>(prog: &CString, args: &[CString], - cb: proc(*const *const libc::c_char) -> T) -> T { +fn with_argv<T,F>(prog: &CString, args: &[CString], + cb: F) + -> T + where F : FnOnce(*const *const libc::c_char) -> T +{ let mut ptrs: Vec<*const libc::c_char> = Vec::with_capacity(args.len()+1); // Convert the CStrings into an array of pointers. Note: the @@ -526,9 +552,12 @@ fn with_argv<T>(prog: &CString, args: &[CString], cb(ptrs.as_ptr()) } -fn with_envp<K, V, T>(env: Option<&collections::HashMap<K, V>>, - cb: proc(*const c_void) -> T) -> T - where K: BytesContainer + Eq + Hash, V: BytesContainer +fn with_envp<K,V,T,F>(env: Option<&collections::HashMap<K, V>>, + cb: F) + -> T + where F : FnOnce(*const c_void) -> T, + K : BytesContainer + Eq + Hash, + V : BytesContainer { // On posixy systems we can pass a char** for envp, which is a // null-terminated array of "k=v\0" strings. Since we must create @@ -541,9 +570,9 @@ fn with_envp<K, V, T>(env: Option<&collections::HashMap<K, V>>, for pair in env.iter() { let mut kv = Vec::new(); - kv.push_all(pair.ref0().container_as_bytes()); + kv.push_all(pair.0.container_as_bytes()); kv.push('=' as u8); - kv.push_all(pair.ref1().container_as_bytes()); + kv.push_all(pair.1.container_as_bytes()); kv.push(0); // terminating null tmps.push(kv); } diff --git a/src/libstd/sys/unix/rwlock.rs b/src/libstd/sys/unix/rwlock.rs new file mode 100644 index 00000000000..0d63ff14ff2 --- /dev/null +++ b/src/libstd/sys/unix/rwlock.rs @@ -0,0 +1,57 @@ +// Copyright 2014 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 cell::UnsafeCell; +use sys::sync as ffi; + +pub struct RWLock { inner: UnsafeCell<ffi::pthread_rwlock_t> } + +pub const RWLOCK_INIT: RWLock = RWLock { + inner: UnsafeCell { value: ffi::PTHREAD_RWLOCK_INITIALIZER }, +}; + +impl RWLock { + #[inline] + pub unsafe fn new() -> RWLock { + // Might be moved and address is changing it is better to avoid + // initialization of potentially opaque OS data before it landed + RWLOCK_INIT + } + #[inline] + pub unsafe fn read(&self) { + let r = ffi::pthread_rwlock_rdlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn try_read(&self) -> bool { + ffi::pthread_rwlock_tryrdlock(self.inner.get()) == 0 + } + #[inline] + pub unsafe fn write(&self) { + let r = ffi::pthread_rwlock_wrlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn try_write(&self) -> bool { + ffi::pthread_rwlock_trywrlock(self.inner.get()) == 0 + } + #[inline] + pub unsafe fn read_unlock(&self) { + let r = ffi::pthread_rwlock_unlock(self.inner.get()); + debug_assert_eq!(r, 0); + } + #[inline] + pub unsafe fn write_unlock(&self) { self.read_unlock() } + #[inline] + pub unsafe fn destroy(&self) { + let r = ffi::pthread_rwlock_destroy(self.inner.get()); + debug_assert_eq!(r, 0); + } +} diff --git a/src/libstd/sys/unix/stack_overflow.rs b/src/libstd/sys/unix/stack_overflow.rs new file mode 100644 index 00000000000..340f9514241 --- /dev/null +++ b/src/libstd/sys/unix/stack_overflow.rs @@ -0,0 +1,277 @@ +// Copyright 2014 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 libc; +use core::prelude::*; +use self::imp::{make_handler, drop_handler}; + +pub use self::imp::{init, cleanup}; + +pub struct Handler { + _data: *mut libc::c_void +} + +impl Handler { + pub unsafe fn new() -> Handler { + make_handler() + } +} + +impl Drop for Handler { + fn drop(&mut self) { + unsafe { + drop_handler(self); + } + } +} + +#[cfg(any(target_os = "linux", target_os = "macos"))] +mod imp { + use core::prelude::*; + use sys_common::stack; + + use super::Handler; + use rt::util::report_overflow; + use mem; + use ptr; + use intrinsics; + use self::signal::{siginfo, sigaction, SIGBUS, SIG_DFL, + SA_SIGINFO, SA_ONSTACK, sigaltstack, + SIGSTKSZ}; + use libc; + use libc::funcs::posix88::mman::{mmap, munmap}; + use libc::consts::os::posix88::{SIGSEGV, + PROT_READ, + PROT_WRITE, + MAP_PRIVATE, + MAP_ANON, + MAP_FAILED}; + + use sys_common::thread_info; + + + // This is initialized in init() and only read from after + static mut PAGE_SIZE: uint = 0; + + #[no_stack_check] + unsafe extern fn signal_handler(signum: libc::c_int, + info: *mut siginfo, + _data: *mut libc::c_void) { + + // We can not return from a SIGSEGV or SIGBUS signal. + // See: https://www.gnu.org/software/libc/manual/html_node/Handler-Returns.html + + unsafe fn term(signum: libc::c_int) -> ! { + use core::mem::transmute; + + signal(signum, transmute(SIG_DFL)); + raise(signum); + intrinsics::abort(); + } + + // We're calling into functions with stack checks + stack::record_sp_limit(0); + + let guard = thread_info::stack_guard(); + let addr = (*info).si_addr as uint; + + if guard == 0 || addr < guard - PAGE_SIZE || addr >= guard { + term(signum); + } + + report_overflow(); + + intrinsics::abort() + } + + static mut MAIN_ALTSTACK: *mut libc::c_void = 0 as *mut libc::c_void; + + pub unsafe fn init() { + let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE); + if psize == -1 { + panic!("failed to get page size"); + } + + PAGE_SIZE = psize as uint; + + let mut action: sigaction = mem::zeroed(); + action.sa_flags = SA_SIGINFO | SA_ONSTACK; + action.sa_sigaction = signal_handler as sighandler_t; + sigaction(SIGSEGV, &action, ptr::null_mut()); + sigaction(SIGBUS, &action, ptr::null_mut()); + + let handler = make_handler(); + MAIN_ALTSTACK = handler._data; + mem::forget(handler); + } + + pub unsafe fn cleanup() { + Handler { _data: MAIN_ALTSTACK }; + } + + pub unsafe fn make_handler() -> Handler { + let alt_stack = mmap(ptr::null_mut(), + signal::SIGSTKSZ, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, + -1, + 0); + if alt_stack == MAP_FAILED { + panic!("failed to allocate an alternative stack"); + } + + let mut stack: sigaltstack = mem::zeroed(); + + stack.ss_sp = alt_stack; + stack.ss_flags = 0; + stack.ss_size = SIGSTKSZ; + + sigaltstack(&stack, ptr::null_mut()); + + Handler { _data: alt_stack } + } + + pub unsafe fn drop_handler(handler: &mut Handler) { + munmap(handler._data, SIGSTKSZ); + } + + type sighandler_t = *mut libc::c_void; + + #[cfg(any(all(target_os = "linux", target_arch = "x86"), // may not match + all(target_os = "linux", target_arch = "x86_64"), + all(target_os = "linux", target_arch = "arm"), // may not match + all(target_os = "linux", target_arch = "mips"), // may not match + all(target_os = "linux", target_arch = "mipsel"), // may not match + target_os = "android"))] // may not match + mod signal { + use libc; + use super::sighandler_t; + + pub static SA_ONSTACK: libc::c_int = 0x08000000; + pub static SA_SIGINFO: libc::c_int = 0x00000004; + pub static SIGBUS: libc::c_int = 7; + + pub static SIGSTKSZ: libc::size_t = 8192; + + pub static SIG_DFL: sighandler_t = 0i as sighandler_t; + + // This definition is not as accurate as it could be, {si_addr} is + // actually a giant union. Currently we're only interested in that field, + // however. + #[repr(C)] + pub struct siginfo { + si_signo: libc::c_int, + si_errno: libc::c_int, + si_code: libc::c_int, + pub si_addr: *mut libc::c_void + } + + #[repr(C)] + pub struct sigaction { + pub sa_sigaction: sighandler_t, + pub sa_mask: sigset_t, + pub sa_flags: libc::c_int, + sa_restorer: *mut libc::c_void, + } + + #[cfg(target_word_size = "32")] + #[repr(C)] + pub struct sigset_t { + __val: [libc::c_ulong, ..32], + } + #[cfg(target_word_size = "64")] + #[repr(C)] + pub struct sigset_t { + __val: [libc::c_ulong, ..16], + } + + #[repr(C)] + pub struct sigaltstack { + pub ss_sp: *mut libc::c_void, + pub ss_flags: libc::c_int, + pub ss_size: libc::size_t + } + + } + + #[cfg(target_os = "macos")] + mod signal { + use libc; + use super::sighandler_t; + + pub const SA_ONSTACK: libc::c_int = 0x0001; + pub const SA_SIGINFO: libc::c_int = 0x0040; + pub const SIGBUS: libc::c_int = 10; + + pub const SIGSTKSZ: libc::size_t = 131072; + + pub const SIG_DFL: sighandler_t = 0i as sighandler_t; + + pub type sigset_t = u32; + + // This structure has more fields, but we're not all that interested in + // them. + #[repr(C)] + pub struct siginfo { + pub si_signo: libc::c_int, + pub si_errno: libc::c_int, + pub si_code: libc::c_int, + pub pid: libc::pid_t, + pub uid: libc::uid_t, + pub status: libc::c_int, + pub si_addr: *mut libc::c_void + } + + #[repr(C)] + pub struct sigaltstack { + pub ss_sp: *mut libc::c_void, + pub ss_size: libc::size_t, + pub ss_flags: libc::c_int + } + + #[repr(C)] + pub struct sigaction { + pub sa_sigaction: sighandler_t, + pub sa_mask: sigset_t, + pub sa_flags: libc::c_int, + } + } + + extern { + pub fn signal(signum: libc::c_int, handler: sighandler_t) -> sighandler_t; + pub fn raise(signum: libc::c_int) -> libc::c_int; + + pub fn sigaction(signum: libc::c_int, + act: *const sigaction, + oldact: *mut sigaction) -> libc::c_int; + + pub fn sigaltstack(ss: *const sigaltstack, + oss: *mut sigaltstack) -> libc::c_int; + } +} + +#[cfg(not(any(target_os = "linux", + target_os = "macos")))] +mod imp { + use libc; + + pub unsafe fn init() { + } + + pub unsafe fn cleanup() { + } + + pub unsafe fn make_handler() -> super::Handler { + super::Handler { _data: 0i as *mut libc::c_void } + } + + pub unsafe fn drop_handler(_handler: &mut super::Handler) { + } +} diff --git a/src/libstd/sys/unix/sync.rs b/src/libstd/sys/unix/sync.rs new file mode 100644 index 00000000000..007826b4b9d --- /dev/null +++ b/src/libstd/sys/unix/sync.rs @@ -0,0 +1,208 @@ +// Copyright 2014 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. + +#![allow(bad_style)] + +use libc; + +pub use self::os::{PTHREAD_MUTEX_INITIALIZER, pthread_mutex_t}; +pub use self::os::{PTHREAD_COND_INITIALIZER, pthread_cond_t}; +pub use self::os::{PTHREAD_RWLOCK_INITIALIZER, pthread_rwlock_t}; + +extern { + // mutexes + pub fn pthread_mutex_destroy(lock: *mut pthread_mutex_t) -> libc::c_int; + pub fn pthread_mutex_lock(lock: *mut pthread_mutex_t) -> libc::c_int; + pub fn pthread_mutex_trylock(lock: *mut pthread_mutex_t) -> libc::c_int; + pub fn pthread_mutex_unlock(lock: *mut pthread_mutex_t) -> libc::c_int; + + // cvars + pub fn pthread_cond_wait(cond: *mut pthread_cond_t, + lock: *mut pthread_mutex_t) -> libc::c_int; + pub fn pthread_cond_timedwait(cond: *mut pthread_cond_t, + lock: *mut pthread_mutex_t, + abstime: *const libc::timespec) -> libc::c_int; + pub fn pthread_cond_signal(cond: *mut pthread_cond_t) -> libc::c_int; + pub fn pthread_cond_broadcast(cond: *mut pthread_cond_t) -> libc::c_int; + pub fn pthread_cond_destroy(cond: *mut pthread_cond_t) -> libc::c_int; + pub fn gettimeofday(tp: *mut libc::timeval, + tz: *mut libc::c_void) -> libc::c_int; + + // rwlocks + pub fn pthread_rwlock_destroy(lock: *mut pthread_rwlock_t) -> libc::c_int; + pub fn pthread_rwlock_rdlock(lock: *mut pthread_rwlock_t) -> libc::c_int; + pub fn pthread_rwlock_tryrdlock(lock: *mut pthread_rwlock_t) -> libc::c_int; + pub fn pthread_rwlock_wrlock(lock: *mut pthread_rwlock_t) -> libc::c_int; + pub fn pthread_rwlock_trywrlock(lock: *mut pthread_rwlock_t) -> libc::c_int; + pub fn pthread_rwlock_unlock(lock: *mut pthread_rwlock_t) -> libc::c_int; +} + +#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] +mod os { + use libc; + + pub type pthread_mutex_t = *mut libc::c_void; + pub type pthread_cond_t = *mut libc::c_void; + pub type pthread_rwlock_t = *mut libc::c_void; + + pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = 0 as *mut _; + pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = 0 as *mut _; + pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = 0 as *mut _; +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +mod os { + use libc; + + #[cfg(target_arch = "x86_64")] + const __PTHREAD_MUTEX_SIZE__: uint = 56; + #[cfg(any(target_arch = "x86", + target_arch = "arm"))] + const __PTHREAD_MUTEX_SIZE__: uint = 40; + + #[cfg(target_arch = "x86_64")] + const __PTHREAD_COND_SIZE__: uint = 40; + #[cfg(any(target_arch = "x86", + target_arch = "arm"))] + const __PTHREAD_COND_SIZE__: uint = 24; + + #[cfg(target_arch = "x86_64")] + const __PTHREAD_RWLOCK_SIZE__: uint = 192; + #[cfg(any(target_arch = "x86", + target_arch = "arm"))] + const __PTHREAD_RWLOCK_SIZE__: uint = 124; + + const _PTHREAD_MUTEX_SIG_INIT: libc::c_long = 0x32AAABA7; + const _PTHREAD_COND_SIG_INIT: libc::c_long = 0x3CB0B1BB; + const _PTHREAD_RWLOCK_SIG_INIT: libc::c_long = 0x2DA8B3B4; + + #[repr(C)] + pub struct pthread_mutex_t { + __sig: libc::c_long, + __opaque: [u8, ..__PTHREAD_MUTEX_SIZE__], + } + #[repr(C)] + pub struct pthread_cond_t { + __sig: libc::c_long, + __opaque: [u8, ..__PTHREAD_COND_SIZE__], + } + #[repr(C)] + pub struct pthread_rwlock_t { + __sig: libc::c_long, + __opaque: [u8, ..__PTHREAD_RWLOCK_SIZE__], + } + + pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { + __sig: _PTHREAD_MUTEX_SIG_INIT, + __opaque: [0, ..__PTHREAD_MUTEX_SIZE__], + }; + pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { + __sig: _PTHREAD_COND_SIG_INIT, + __opaque: [0, ..__PTHREAD_COND_SIZE__], + }; + pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t { + __sig: _PTHREAD_RWLOCK_SIG_INIT, + __opaque: [0, ..__PTHREAD_RWLOCK_SIZE__], + }; +} + +#[cfg(target_os = "linux")] +mod os { + use libc; + + // minus 8 because we have an 'align' field + #[cfg(target_arch = "x86_64")] + const __SIZEOF_PTHREAD_MUTEX_T: uint = 40 - 8; + #[cfg(any(target_arch = "x86", + target_arch = "arm", + target_arch = "mips", + target_arch = "mipsel"))] + const __SIZEOF_PTHREAD_MUTEX_T: uint = 24 - 8; + + #[cfg(any(target_arch = "x86_64", + target_arch = "x86", + target_arch = "arm", + target_arch = "mips", + target_arch = "mipsel"))] + const __SIZEOF_PTHREAD_COND_T: uint = 48 - 8; + + #[cfg(target_arch = "x86_64")] + const __SIZEOF_PTHREAD_RWLOCK_T: uint = 56 - 8; + + #[cfg(any(target_arch = "x86", + target_arch = "arm", + target_arch = "mips", + target_arch = "mipsel"))] + const __SIZEOF_PTHREAD_RWLOCK_T: uint = 32 - 8; + + #[repr(C)] + pub struct pthread_mutex_t { + __align: libc::c_longlong, + size: [u8, ..__SIZEOF_PTHREAD_MUTEX_T], + } + #[repr(C)] + pub struct pthread_cond_t { + __align: libc::c_longlong, + size: [u8, ..__SIZEOF_PTHREAD_COND_T], + } + #[repr(C)] + pub struct pthread_rwlock_t { + __align: libc::c_longlong, + size: [u8, ..__SIZEOF_PTHREAD_RWLOCK_T], + } + + pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { + __align: 0, + size: [0, ..__SIZEOF_PTHREAD_MUTEX_T], + }; + pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { + __align: 0, + size: [0, ..__SIZEOF_PTHREAD_COND_T], + }; + pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t { + __align: 0, + size: [0, ..__SIZEOF_PTHREAD_RWLOCK_T], + }; +} +#[cfg(target_os = "android")] +mod os { + use libc; + + #[repr(C)] + pub struct pthread_mutex_t { value: libc::c_int } + #[repr(C)] + pub struct pthread_cond_t { value: libc::c_int } + #[repr(C)] + pub struct pthread_rwlock_t { + lock: pthread_mutex_t, + cond: pthread_cond_t, + numLocks: libc::c_int, + writerThreadId: libc::c_int, + pendingReaders: libc::c_int, + pendingWriters: libc::c_int, + reserved: [*mut libc::c_void, ..4], + } + + pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { + value: 0, + }; + pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { + value: 0, + }; + pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t { + lock: PTHREAD_MUTEX_INITIALIZER, + cond: PTHREAD_COND_INITIALIZER, + numLocks: 0, + writerThreadId: 0, + pendingReaders: 0, + pendingWriters: 0, + reserved: [0 as *mut _, ..4], + }; +} diff --git a/src/libstd/sys/unix/tcp.rs b/src/libstd/sys/unix/tcp.rs index 00643ac0a79..5c99ad1e0ce 100644 --- a/src/libstd/sys/unix/tcp.rs +++ b/src/libstd/sys/unix/tcp.rs @@ -20,7 +20,8 @@ use sys::fs::FileDesc; use sys::{set_nonblocking, wouldblock}; use sys; use sys_common; -use sys_common::net::*; +use sys_common::net; +use sys_common::net::SocketStatus::Readable; pub use sys_common::net::TcpStream; @@ -34,17 +35,19 @@ pub struct TcpListener { impl TcpListener { pub fn bind(addr: ip::SocketAddr) -> IoResult<TcpListener> { - let fd = try!(socket(addr, libc::SOCK_STREAM)); + let fd = try!(net::socket(addr, libc::SOCK_STREAM)); let ret = TcpListener { inner: FileDesc::new(fd, true) }; let mut storage = unsafe { mem::zeroed() }; - let len = addr_to_sockaddr(addr, &mut storage); + let len = net::addr_to_sockaddr(addr, &mut storage); let addrp = &storage as *const _ as *const libc::sockaddr; // On platforms with Berkeley-derived sockets, this allows // to quickly rebind a socket, without needing to wait for // the OS to clean up the previous one. - try!(setsockopt(fd, libc::SOL_SOCKET, libc::SO_REUSEADDR, 1 as libc::c_int)); + try!(net::setsockopt(fd, libc::SOL_SOCKET, + libc::SO_REUSEADDR, + 1 as libc::c_int)); match unsafe { libc::bind(fd, addrp, len) } { @@ -77,7 +80,7 @@ impl TcpListener { } pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> { - sockname(self.fd(), libc::getsockname) + net::sockname(self.fd(), libc::getsockname) } } @@ -121,7 +124,7 @@ impl TcpAcceptor { -1 => return Err(last_net_error()), fd => return Ok(TcpStream::new(fd as sock_t)), } - try!(await(&[self.fd(), self.inner.reader.fd()], + try!(net::await(&[self.fd(), self.inner.reader.fd()], deadline, Readable)); } @@ -129,7 +132,7 @@ impl TcpAcceptor { } pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> { - sockname(self.fd(), libc::getsockname) + net::sockname(self.fd(), libc::getsockname) } pub fn set_timeout(&mut self, timeout: Option<u64>) { diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs new file mode 100644 index 00000000000..2416b64f98f --- /dev/null +++ b/src/libstd/sys/unix/thread.rs @@ -0,0 +1,271 @@ +// Copyright 2014 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 core::prelude::*; + +use boxed::Box; +use cmp; +use mem; +use ptr; +use libc::consts::os::posix01::{PTHREAD_CREATE_JOINABLE, PTHREAD_STACK_MIN}; +use libc; +use thunk::Thunk; + +use sys_common::stack::RED_ZONE; +use sys_common::thread::*; + +pub type rust_thread = libc::pthread_t; +pub type rust_thread_return = *mut u8; +pub type StartFn = extern "C" fn(*mut libc::c_void) -> rust_thread_return; + +#[no_stack_check] +pub extern fn thread_start(main: *mut libc::c_void) -> rust_thread_return { + return start_thread(main); +} + +#[cfg(all(not(target_os = "linux"), not(target_os = "macos")))] +pub mod guard { + pub unsafe fn current() -> uint { + 0 + } + + pub unsafe fn main() -> uint { + 0 + } + + pub unsafe fn init() { + } +} + +#[cfg(any(target_os = "linux", target_os = "macos"))] +pub mod guard { + use super::*; + #[cfg(any(target_os = "linux", target_os = "android"))] + use mem; + #[cfg(any(target_os = "linux", target_os = "android"))] + use ptr; + use libc; + use libc::funcs::posix88::mman::{mmap}; + use libc::consts::os::posix88::{PROT_NONE, + MAP_PRIVATE, + MAP_ANON, + MAP_FAILED, + MAP_FIXED}; + + // These are initialized in init() and only read from after + static mut PAGE_SIZE: uint = 0; + static mut GUARD_PAGE: uint = 0; + + #[cfg(target_os = "macos")] + unsafe fn get_stack_start() -> *mut libc::c_void { + current() as *mut libc::c_void + } + + #[cfg(any(target_os = "linux", target_os = "android"))] + unsafe fn get_stack_start() -> *mut libc::c_void { + let mut attr: libc::pthread_attr_t = mem::zeroed(); + if pthread_getattr_np(pthread_self(), &mut attr) != 0 { + panic!("failed to get thread attributes"); + } + let mut stackaddr = ptr::null_mut(); + let mut stacksize = 0; + if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 { + panic!("failed to get stack information"); + } + if pthread_attr_destroy(&mut attr) != 0 { + panic!("failed to destroy thread attributes"); + } + stackaddr + } + + pub unsafe fn init() { + let psize = libc::sysconf(libc::consts::os::sysconf::_SC_PAGESIZE); + if psize == -1 { + panic!("failed to get page size"); + } + + PAGE_SIZE = psize as uint; + + let stackaddr = get_stack_start(); + + // Rellocate the last page of the stack. + // This ensures SIGBUS will be raised on + // stack overflow. + let result = mmap(stackaddr, + PAGE_SIZE as libc::size_t, + PROT_NONE, + MAP_PRIVATE | MAP_ANON | MAP_FIXED, + -1, + 0); + + if result != stackaddr || result == MAP_FAILED { + panic!("failed to allocate a guard page"); + } + + let offset = if cfg!(target_os = "linux") { + 2 + } else { + 1 + }; + + GUARD_PAGE = stackaddr as uint + offset * PAGE_SIZE; + } + + pub unsafe fn main() -> uint { + GUARD_PAGE + } + + #[cfg(target_os = "macos")] + pub unsafe fn current() -> uint { + (pthread_get_stackaddr_np(pthread_self()) as libc::size_t - + pthread_get_stacksize_np(pthread_self())) as uint + } + + #[cfg(any(target_os = "linux", target_os = "android"))] + pub unsafe fn current() -> uint { + let mut attr: libc::pthread_attr_t = mem::zeroed(); + if pthread_getattr_np(pthread_self(), &mut attr) != 0 { + panic!("failed to get thread attributes"); + } + let mut guardsize = 0; + if pthread_attr_getguardsize(&attr, &mut guardsize) != 0 { + panic!("failed to get stack guard page"); + } + if guardsize == 0 { + panic!("there is no guard page"); + } + let mut stackaddr = ptr::null_mut(); + let mut stacksize = 0; + if pthread_attr_getstack(&attr, &mut stackaddr, &mut stacksize) != 0 { + panic!("failed to get stack information"); + } + if pthread_attr_destroy(&mut attr) != 0 { + panic!("failed to destroy thread attributes"); + } + + stackaddr as uint + guardsize as uint + } +} + +pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread { + let mut native: libc::pthread_t = mem::zeroed(); + let mut attr: libc::pthread_attr_t = mem::zeroed(); + assert_eq!(pthread_attr_init(&mut attr), 0); + assert_eq!(pthread_attr_setdetachstate(&mut attr, + PTHREAD_CREATE_JOINABLE), 0); + + // Reserve room for the red zone, the runtime's stack of last resort. + let stack_size = cmp::max(stack, RED_ZONE + min_stack_size(&attr) as uint); + match pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t) { + 0 => { + }, + libc::EINVAL => { + // EINVAL means |stack_size| is either too small or not a + // multiple of the system page size. Because it's definitely + // >= PTHREAD_STACK_MIN, it must be an alignment issue. + // Round up to the nearest page and try again. + let page_size = libc::sysconf(libc::_SC_PAGESIZE) as uint; + let stack_size = (stack_size + page_size - 1) & + (-(page_size as int - 1) as uint - 1); + assert_eq!(pthread_attr_setstacksize(&mut attr, stack_size as libc::size_t), 0); + }, + errno => { + // This cannot really happen. + panic!("pthread_attr_setstacksize() error: {}", errno); + }, + }; + + let arg: *mut libc::c_void = mem::transmute(box p); // must box since sizeof(p)=2*uint + let ret = pthread_create(&mut native, &attr, thread_start, arg); + assert_eq!(pthread_attr_destroy(&mut attr), 0); + + if ret != 0 { + // be sure to not leak the closure + let _p: Box<Box<FnOnce()+Send>> = mem::transmute(arg); + panic!("failed to spawn native thread: {}", ret); + } + native +} + +pub unsafe fn join(native: rust_thread) { + assert_eq!(pthread_join(native, ptr::null_mut()), 0); +} + +pub unsafe fn detach(native: rust_thread) { + assert_eq!(pthread_detach(native), 0); +} + +pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); } +// glibc >= 2.15 has a __pthread_get_minstack() function that returns +// PTHREAD_STACK_MIN plus however many bytes are needed for thread-local +// storage. We need that information to avoid blowing up when a small stack +// is created in an application with big thread-local storage requirements. +// See #6233 for rationale and details. +// +// Link weakly to the symbol for compatibility with older versions of glibc. +// Assumes that we've been dynamically linked to libpthread but that is +// currently always the case. Note that you need to check that the symbol +// is non-null before calling it! +#[cfg(target_os = "linux")] +fn min_stack_size(attr: *const libc::pthread_attr_t) -> libc::size_t { + type F = unsafe extern "C" fn(*const libc::pthread_attr_t) -> libc::size_t; + extern { + #[linkage = "extern_weak"] + static __pthread_get_minstack: *const (); + } + if __pthread_get_minstack.is_null() { + PTHREAD_STACK_MIN + } else { + unsafe { mem::transmute::<*const (), F>(__pthread_get_minstack)(attr) } + } +} + +// __pthread_get_minstack() is marked as weak but extern_weak linkage is +// not supported on OS X, hence this kludge... +#[cfg(not(target_os = "linux"))] +fn min_stack_size(_: *const libc::pthread_attr_t) -> libc::size_t { + PTHREAD_STACK_MIN +} + +#[cfg(any(target_os = "linux"))] +extern { + pub fn pthread_self() -> libc::pthread_t; + pub fn pthread_getattr_np(native: libc::pthread_t, + attr: *mut libc::pthread_attr_t) -> libc::c_int; + pub fn pthread_attr_getguardsize(attr: *const libc::pthread_attr_t, + guardsize: *mut libc::size_t) -> libc::c_int; + pub fn pthread_attr_getstack(attr: *const libc::pthread_attr_t, + stackaddr: *mut *mut libc::c_void, + stacksize: *mut libc::size_t) -> libc::c_int; +} + +#[cfg(target_os = "macos")] +extern { + pub fn pthread_self() -> libc::pthread_t; + pub fn pthread_get_stackaddr_np(thread: libc::pthread_t) -> *mut libc::c_void; + pub fn pthread_get_stacksize_np(thread: libc::pthread_t) -> libc::size_t; +} + +extern { + fn pthread_create(native: *mut libc::pthread_t, + attr: *const libc::pthread_attr_t, + f: StartFn, + value: *mut libc::c_void) -> libc::c_int; + fn pthread_join(native: libc::pthread_t, + value: *mut *mut libc::c_void) -> libc::c_int; + fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int; + pub fn pthread_attr_destroy(attr: *mut libc::pthread_attr_t) -> libc::c_int; + fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t, + stack_size: libc::size_t) -> libc::c_int; + fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t, + state: libc::c_int) -> libc::c_int; + fn pthread_detach(thread: libc::pthread_t) -> libc::c_int; + fn sched_yield() -> libc::c_int; +} diff --git a/src/libstd/sys/unix/thread_local.rs b/src/libstd/sys/unix/thread_local.rs new file mode 100644 index 00000000000..b300e93eeb6 --- /dev/null +++ b/src/libstd/sys/unix/thread_local.rs @@ -0,0 +1,52 @@ +// Copyright 2014 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 libc::c_int; + +pub type Key = pthread_key_t; + +#[inline] +pub unsafe fn create(dtor: Option<unsafe extern fn(*mut u8)>) -> Key { + let mut key = 0; + assert_eq!(pthread_key_create(&mut key, dtor), 0); + return key; +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + let r = pthread_setspecific(key, value); + debug_assert_eq!(r, 0); +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + pthread_getspecific(key) +} + +#[inline] +pub unsafe fn destroy(key: Key) { + let r = pthread_key_delete(key); + debug_assert_eq!(r, 0); +} + +#[cfg(target_os = "macos")] +type pthread_key_t = ::libc::c_ulong; + +#[cfg(not(target_os = "macos"))] +type pthread_key_t = ::libc::c_uint; + +extern { + fn pthread_key_create(key: *mut pthread_key_t, + dtor: Option<unsafe extern fn(*mut u8)>) -> c_int; + fn pthread_key_delete(key: pthread_key_t) -> c_int; + fn pthread_getspecific(key: pthread_key_t) -> *mut u8; + fn pthread_setspecific(key: pthread_key_t, value: *mut u8) -> c_int; +} diff --git a/src/libstd/sys/unix/timer.rs b/src/libstd/sys/unix/timer.rs index 6ebbedb8e90..fe393b81e3d 100644 --- a/src/libstd/sys/unix/timer.rs +++ b/src/libstd/sys/unix/timer.rs @@ -46,7 +46,7 @@ //! //! Note that all time units in this file are in *milliseconds*. -pub use self::Req::*; +use self::Req::*; use libc; use mem; @@ -60,7 +60,7 @@ use sys_common::helper_thread::Helper; use prelude::*; use io::IoResult; -helper_init!(static HELPER: Helper<Req>) +helper_init! { static HELPER: Helper<Req> } pub trait Callback { fn call(&mut self); diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs new file mode 100644 index 00000000000..42c8f7705e1 --- /dev/null +++ b/src/libstd/sys/windows/backtrace.rs @@ -0,0 +1,371 @@ +// Copyright 2014 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. +/// As always, windows has something very different than unix, we mainly want +/// to avoid having to depend too much on libunwind for windows. +/// +/// If you google around, you'll find a fair bit of references to built-in +/// functions to get backtraces on windows. It turns out that most of these are +/// in an external library called dbghelp. I was unable to find this library +/// via `-ldbghelp`, but it is apparently normal to do the `dlopen` equivalent +/// of it. +/// +/// You'll also find that there's a function called CaptureStackBackTrace +/// mentioned frequently (which is also easy to use), but sadly I didn't have a +/// copy of that function in my mingw install (maybe it was broken?). Instead, +/// this takes the route of using StackWalk64 in order to walk the stack. + +use c_str::CString; +use intrinsics; +use io::{IoResult, Writer}; +use libc; +use mem; +use ops::Drop; +use option::Option::{Some, None}; +use path::Path; +use result::Result::{Ok, Err}; +use sync::{StaticMutex, MUTEX_INIT}; +use slice::SliceExt; +use str::StrExt; +use dynamic_lib::DynamicLibrary; + +use sys_common::backtrace::*; + +#[allow(non_snake_case)] +extern "system" { + fn GetCurrentProcess() -> libc::HANDLE; + fn GetCurrentThread() -> libc::HANDLE; + fn RtlCaptureContext(ctx: *mut arch::CONTEXT); +} + +type SymFromAddrFn = + extern "system" fn(libc::HANDLE, u64, *mut u64, + *mut SYMBOL_INFO) -> libc::BOOL; +type SymInitializeFn = + extern "system" fn(libc::HANDLE, *mut libc::c_void, + libc::BOOL) -> libc::BOOL; +type SymCleanupFn = + extern "system" fn(libc::HANDLE) -> libc::BOOL; + +type StackWalk64Fn = + extern "system" fn(libc::DWORD, libc::HANDLE, libc::HANDLE, + *mut STACKFRAME64, *mut arch::CONTEXT, + *mut libc::c_void, *mut libc::c_void, + *mut libc::c_void, *mut libc::c_void) -> libc::BOOL; + +const MAX_SYM_NAME: uint = 2000; +const IMAGE_FILE_MACHINE_I386: libc::DWORD = 0x014c; +const IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200; +const IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664; + +#[repr(C)] +struct SYMBOL_INFO { + SizeOfStruct: libc::c_ulong, + TypeIndex: libc::c_ulong, + Reserved: [u64, ..2], + Index: libc::c_ulong, + Size: libc::c_ulong, + ModBase: u64, + Flags: libc::c_ulong, + Value: u64, + Address: u64, + Register: libc::c_ulong, + Scope: libc::c_ulong, + Tag: libc::c_ulong, + NameLen: libc::c_ulong, + MaxNameLen: libc::c_ulong, + // note that windows has this as 1, but it basically just means that + // the name is inline at the end of the struct. For us, we just bump + // the struct size up to MAX_SYM_NAME. + Name: [libc::c_char, ..MAX_SYM_NAME], +} + + +#[repr(C)] +enum ADDRESS_MODE { + AddrMode1616, + AddrMode1632, + AddrModeReal, + AddrModeFlat, +} + +struct ADDRESS64 { + Offset: u64, + Segment: u16, + Mode: ADDRESS_MODE, +} + +struct STACKFRAME64 { + AddrPC: ADDRESS64, + AddrReturn: ADDRESS64, + AddrFrame: ADDRESS64, + AddrStack: ADDRESS64, + AddrBStore: ADDRESS64, + FuncTableEntry: *mut libc::c_void, + Params: [u64, ..4], + Far: libc::BOOL, + Virtual: libc::BOOL, + Reserved: [u64, ..3], + KdHelp: KDHELP64, +} + +struct KDHELP64 { + Thread: u64, + ThCallbackStack: libc::DWORD, + ThCallbackBStore: libc::DWORD, + NextCallback: libc::DWORD, + FramePointer: libc::DWORD, + KiCallUserMode: u64, + KeUserCallbackDispatcher: u64, + SystemRangeStart: u64, + KiUserExceptionDispatcher: u64, + StackBase: u64, + StackLimit: u64, + Reserved: [u64, ..5], +} + +#[cfg(target_arch = "x86")] +mod arch { + use libc; + + const MAXIMUM_SUPPORTED_EXTENSION: uint = 512; + + #[repr(C)] + pub struct CONTEXT { + ContextFlags: libc::DWORD, + Dr0: libc::DWORD, + Dr1: libc::DWORD, + Dr2: libc::DWORD, + Dr3: libc::DWORD, + Dr6: libc::DWORD, + Dr7: libc::DWORD, + FloatSave: FLOATING_SAVE_AREA, + SegGs: libc::DWORD, + SegFs: libc::DWORD, + SegEs: libc::DWORD, + SegDs: libc::DWORD, + Edi: libc::DWORD, + Esi: libc::DWORD, + Ebx: libc::DWORD, + Edx: libc::DWORD, + Ecx: libc::DWORD, + Eax: libc::DWORD, + Ebp: libc::DWORD, + Eip: libc::DWORD, + SegCs: libc::DWORD, + EFlags: libc::DWORD, + Esp: libc::DWORD, + SegSs: libc::DWORD, + ExtendedRegisters: [u8, ..MAXIMUM_SUPPORTED_EXTENSION], + } + + #[repr(C)] + pub struct FLOATING_SAVE_AREA { + ControlWord: libc::DWORD, + StatusWord: libc::DWORD, + TagWord: libc::DWORD, + ErrorOffset: libc::DWORD, + ErrorSelector: libc::DWORD, + DataOffset: libc::DWORD, + DataSelector: libc::DWORD, + RegisterArea: [u8, ..80], + Cr0NpxState: libc::DWORD, + } + + pub fn init_frame(frame: &mut super::STACKFRAME64, + ctx: &CONTEXT) -> libc::DWORD { + frame.AddrPC.Offset = ctx.Eip as u64; + frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrStack.Offset = ctx.Esp as u64; + frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrFrame.Offset = ctx.Ebp as u64; + frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; + super::IMAGE_FILE_MACHINE_I386 + } +} + +#[cfg(target_arch = "x86_64")] +mod arch { + use libc::{c_longlong, c_ulonglong}; + use libc::types::os::arch::extra::{WORD, DWORD, DWORDLONG}; + use simd; + + #[repr(C)] + pub struct CONTEXT { + _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte + P1Home: DWORDLONG, + P2Home: DWORDLONG, + P3Home: DWORDLONG, + P4Home: DWORDLONG, + P5Home: DWORDLONG, + P6Home: DWORDLONG, + + ContextFlags: DWORD, + MxCsr: DWORD, + + SegCs: WORD, + SegDs: WORD, + SegEs: WORD, + SegFs: WORD, + SegGs: WORD, + SegSs: WORD, + EFlags: DWORD, + + Dr0: DWORDLONG, + Dr1: DWORDLONG, + Dr2: DWORDLONG, + Dr3: DWORDLONG, + Dr6: DWORDLONG, + Dr7: DWORDLONG, + + Rax: DWORDLONG, + Rcx: DWORDLONG, + Rdx: DWORDLONG, + Rbx: DWORDLONG, + Rsp: DWORDLONG, + Rbp: DWORDLONG, + Rsi: DWORDLONG, + Rdi: DWORDLONG, + R8: DWORDLONG, + R9: DWORDLONG, + R10: DWORDLONG, + R11: DWORDLONG, + R12: DWORDLONG, + R13: DWORDLONG, + R14: DWORDLONG, + R15: DWORDLONG, + + Rip: DWORDLONG, + + FltSave: FLOATING_SAVE_AREA, + + VectorRegister: [M128A, .. 26], + VectorControl: DWORDLONG, + + DebugControl: DWORDLONG, + LastBranchToRip: DWORDLONG, + LastBranchFromRip: DWORDLONG, + LastExceptionToRip: DWORDLONG, + LastExceptionFromRip: DWORDLONG, + } + + #[repr(C)] + pub struct M128A { + _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte + Low: c_ulonglong, + High: c_longlong + } + + #[repr(C)] + pub struct FLOATING_SAVE_AREA { + _align_hack: [simd::u64x2, ..0], // FIXME align on 16-byte + _Dummy: [u8, ..512] // FIXME: Fill this out + } + + pub fn init_frame(frame: &mut super::STACKFRAME64, + ctx: &CONTEXT) -> DWORD { + frame.AddrPC.Offset = ctx.Rip as u64; + frame.AddrPC.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrStack.Offset = ctx.Rsp as u64; + frame.AddrStack.Mode = super::ADDRESS_MODE::AddrModeFlat; + frame.AddrFrame.Offset = ctx.Rbp as u64; + frame.AddrFrame.Mode = super::ADDRESS_MODE::AddrModeFlat; + super::IMAGE_FILE_MACHINE_AMD64 + } +} + +#[repr(C)] +struct Cleanup { + handle: libc::HANDLE, + SymCleanup: SymCleanupFn, +} + +impl Drop for Cleanup { + fn drop(&mut self) { (self.SymCleanup)(self.handle); } +} + +pub fn write(w: &mut Writer) -> IoResult<()> { + // According to windows documentation, all dbghelp functions are + // single-threaded. + static LOCK: StaticMutex = MUTEX_INIT; + let _g = unsafe { LOCK.lock() }; + + // Open up dbghelp.dll, we don't link to it explicitly because it can't + // always be found. Additionally, it's nice having fewer dependencies. + let path = Path::new("dbghelp.dll"); + let lib = match DynamicLibrary::open(Some(&path)) { + Ok(lib) => lib, + Err(..) => return Ok(()), + }; + + macro_rules! sym{ ($e:expr, $t:ident) => (unsafe { + match lib.symbol($e) { + Ok(f) => mem::transmute::<*mut u8, $t>(f), + Err(..) => return Ok(()) + } + }) } + + // Fetch the symbols necessary from dbghelp.dll + let SymFromAddr = sym!("SymFromAddr", SymFromAddrFn); + let SymInitialize = sym!("SymInitialize", SymInitializeFn); + let SymCleanup = sym!("SymCleanup", SymCleanupFn); + let StackWalk64 = sym!("StackWalk64", StackWalk64Fn); + + // Allocate necessary structures for doing the stack walk + let process = unsafe { GetCurrentProcess() }; + let thread = unsafe { GetCurrentThread() }; + let mut context: arch::CONTEXT = unsafe { intrinsics::init() }; + unsafe { RtlCaptureContext(&mut context); } + let mut frame: STACKFRAME64 = unsafe { intrinsics::init() }; + let image = arch::init_frame(&mut frame, &context); + + // Initialize this process's symbols + let ret = SymInitialize(process, 0 as *mut libc::c_void, libc::TRUE); + if ret != libc::TRUE { return Ok(()) } + let _c = Cleanup { handle: process, SymCleanup: SymCleanup }; + + // And now that we're done with all the setup, do the stack walking! + let mut i = 0i; + try!(write!(w, "stack backtrace:\n")); + while StackWalk64(image, process, thread, &mut frame, &mut context, + 0 as *mut libc::c_void, + 0 as *mut libc::c_void, + 0 as *mut libc::c_void, + 0 as *mut libc::c_void) == libc::TRUE{ + let addr = frame.AddrPC.Offset; + if addr == frame.AddrReturn.Offset || addr == 0 || + frame.AddrReturn.Offset == 0 { break } + + i += 1; + try!(write!(w, " {:2}: {:#2$x}", i, addr, HEX_WIDTH)); + let mut info: SYMBOL_INFO = unsafe { intrinsics::init() }; + info.MaxNameLen = MAX_SYM_NAME as libc::c_ulong; + // the struct size in C. the value is different to + // `size_of::<SYMBOL_INFO>() - MAX_SYM_NAME + 1` (== 81) + // due to struct alignment. + info.SizeOfStruct = 88; + + let mut displacement = 0u64; + let ret = SymFromAddr(process, addr as u64, &mut displacement, + &mut info); + + if ret == libc::TRUE { + try!(write!(w, " - ")); + let cstr = unsafe { CString::new(info.Name.as_ptr(), false) }; + let bytes = cstr.as_bytes(); + match cstr.as_str() { + Some(s) => try!(demangle(w, s)), + None => try!(w.write(bytes[..bytes.len()-1])), + } + } + try!(w.write(&['\n' as u8])); + } + + Ok(()) +} diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index b8e9b1dca3a..d1cb91bcdb3 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -131,7 +131,7 @@ extern "system" { pub mod compat { use intrinsics::{atomic_store_relaxed, transmute}; - use iter::Iterator; + use iter::IteratorExt; use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID}; use prelude::*; @@ -169,7 +169,7 @@ pub mod compat { /// /// Note that arguments unused by the fallback implementation should not be called `_` as /// they are used to be passed to the real function if available. - macro_rules! compat_fn( + macro_rules! compat_fn { ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) -> $rettype:ty $fallback:block) => ( #[inline(always)] @@ -195,7 +195,7 @@ pub mod compat { ($module:ident::$symbol:ident($($argname:ident: $argtype:ty),*) $fallback:block) => ( compat_fn!($module::$symbol($($argname: $argtype),*) -> () $fallback) ) - ) + } /// Compatibility layer for functions in `kernel32.dll` /// @@ -211,20 +211,20 @@ pub mod compat { fn SetLastError(dwErrCode: DWORD); } - compat_fn!(kernel32::CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR, + compat_fn! { kernel32::CreateSymbolicLinkW(_lpSymlinkFileName: LPCWSTR, _lpTargetFileName: LPCWSTR, _dwFlags: DWORD) -> BOOLEAN { unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); } 0 - }) + } } - compat_fn!(kernel32::GetFinalPathNameByHandleW(_hFile: HANDLE, + compat_fn! { kernel32::GetFinalPathNameByHandleW(_hFile: HANDLE, _lpszFilePath: LPCWSTR, _cchFilePath: DWORD, _dwFlags: DWORD) -> DWORD { unsafe { SetLastError(ERROR_CALL_NOT_IMPLEMENTED as DWORD); } 0 - }) + } } } } diff --git a/src/libstd/sys/windows/condvar.rs b/src/libstd/sys/windows/condvar.rs new file mode 100644 index 00000000000..7f9d669c447 --- /dev/null +++ b/src/libstd/sys/windows/condvar.rs @@ -0,0 +1,62 @@ +// Copyright 2014 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 cell::UnsafeCell; +use libc::{mod, DWORD}; +use os; +use sys::mutex::{mod, Mutex}; +use sys::sync as ffi; +use time::Duration; + +pub struct Condvar { inner: UnsafeCell<ffi::CONDITION_VARIABLE> } + +pub const CONDVAR_INIT: Condvar = Condvar { + inner: UnsafeCell { value: ffi::CONDITION_VARIABLE_INIT } +}; + +impl Condvar { + #[inline] + pub unsafe fn new() -> Condvar { CONDVAR_INIT } + + #[inline] + pub unsafe fn wait(&self, mutex: &Mutex) { + let r = ffi::SleepConditionVariableCS(self.inner.get(), + mutex::raw(mutex), + libc::INFINITE); + debug_assert!(r != 0); + } + + pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { + let r = ffi::SleepConditionVariableCS(self.inner.get(), + mutex::raw(mutex), + dur.num_milliseconds() as DWORD); + if r == 0 { + const ERROR_TIMEOUT: DWORD = 0x5B4; + debug_assert_eq!(os::errno() as uint, ERROR_TIMEOUT as uint); + false + } else { + true + } + } + + #[inline] + pub unsafe fn notify_one(&self) { + ffi::WakeConditionVariable(self.inner.get()) + } + + #[inline] + pub unsafe fn notify_all(&self) { + ffi::WakeAllConditionVariable(self.inner.get()) + } + + pub unsafe fn destroy(&self) { + // ... + } +} diff --git a/src/libstd/sys/windows/ext.rs b/src/libstd/sys/windows/ext.rs new file mode 100644 index 00000000000..049aca3f590 --- /dev/null +++ b/src/libstd/sys/windows/ext.rs @@ -0,0 +1,100 @@ +// Copyright 2014 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. + +//! Experimental extensions to `std` for Windows. +//! +//! For now, this module is limited to extracting handles, file +//! descriptors, and sockets, but its functionality will grow over +//! time. + +#![experimental] + +use sys_common::AsInner; +use libc; + +use io; + +/// Raw HANDLEs. +pub type Handle = libc::HANDLE; + +/// Raw SOCKETs. +pub type Socket = libc::SOCKET; + +/// Extract raw handles. +pub trait AsRawHandle { + /// Extract the raw handle, without taking any ownership. + fn as_raw_handle(&self) -> Handle; +} + +impl AsRawHandle for io::fs::File { + fn as_raw_handle(&self) -> Handle { + self.as_inner().handle() + } +} + +impl AsRawHandle for io::pipe::PipeStream { + fn as_raw_handle(&self) -> Handle { + self.as_inner().handle() + } +} + +impl AsRawHandle for io::net::pipe::UnixStream { + fn as_raw_handle(&self) -> Handle { + self.as_inner().handle() + } +} + +impl AsRawHandle for io::net::pipe::UnixListener { + fn as_raw_handle(&self) -> Handle { + self.as_inner().handle() + } +} + +impl AsRawHandle for io::net::pipe::UnixAcceptor { + fn as_raw_handle(&self) -> Handle { + self.as_inner().handle() + } +} + +/// Extract raw sockets. +pub trait AsRawSocket { + fn as_raw_socket(&self) -> Socket; +} + +impl AsRawSocket for io::net::tcp::TcpStream { + fn as_raw_socket(&self) -> Socket { + self.as_inner().fd() + } +} + +impl AsRawSocket for io::net::tcp::TcpListener { + fn as_raw_socket(&self) -> Socket { + self.as_inner().socket() + } +} + +impl AsRawSocket for io::net::tcp::TcpAcceptor { + fn as_raw_socket(&self) -> Socket { + self.as_inner().socket() + } +} + +impl AsRawSocket for io::net::udp::UdpSocket { + fn as_raw_socket(&self) -> Socket { + self.as_inner().fd() + } +} + +/// A prelude for conveniently writing platform-specific code. +/// +/// Includes all extension traits, and some important type definitions. +pub mod prelude { + pub use super::{Socket, Handle, AsRawSocket, AsRawHandle}; +} diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index b881eb2d495..15eddd569be 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -15,7 +15,7 @@ use libc::{mod, c_int}; use c_str::CString; use mem; -use os::windows::fill_utf16_buf_and_decode; +use sys::os::fill_utf16_buf_and_decode; use path; use ptr; use str; @@ -23,13 +23,13 @@ use io; use prelude::*; use sys; +use sys::os; use sys_common::{keep_going, eof, mkerr_libc}; use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode}; -use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader}; +use io::{IoResult, IoError, FileStat, SeekStyle}; use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append}; -pub use path::WindowsPath as Path; pub type fd_t = libc::c_int; pub struct FileDesc { @@ -131,7 +131,7 @@ impl FileDesc { return ret; } - pub fn fstat(&mut self) -> IoResult<io::FileStat> { + pub fn fstat(&self) -> IoResult<io::FileStat> { let mut stat: libc::stat = unsafe { mem::zeroed() }; match unsafe { libc::fstat(self.fd(), &mut stat) } { 0 => Ok(mkstat(&stat)), @@ -263,7 +263,7 @@ pub fn readdir(p: &Path) -> IoResult<Vec<Path>> { let mut more_files = 1 as libc::BOOL; while more_files != 0 { { - let filename = str::truncate_utf16_at_nul(&wfd.cFileName); + let filename = os::truncate_utf16_at_nul(&wfd.cFileName); match String::from_utf16(filename) { Some(filename) => paths.push(Path::new(filename)), None => { @@ -376,8 +376,8 @@ pub fn readlink(p: &Path) -> IoResult<Path> { libc::VOLUME_NAME_DOS) }); let ret = match ret { - Some(ref s) if s.as_slice().starts_with(r"\\?\") => { // " - Ok(Path::new(s.as_slice().slice_from(4))) + Some(ref s) if s.starts_with(r"\\?\") => { // " + Ok(Path::new(s.slice_from(4))) } Some(s) => Ok(Path::new(s)), None => Err(super::last_error()), @@ -407,12 +407,12 @@ fn mkstat(stat: &libc::stat) -> FileStat { FileStat { size: stat.st_size as u64, kind: match (stat.st_mode as libc::c_int) & libc::S_IFMT { - libc::S_IFREG => io::TypeFile, - libc::S_IFDIR => io::TypeDirectory, - libc::S_IFIFO => io::TypeNamedPipe, - libc::S_IFBLK => io::TypeBlockSpecial, - libc::S_IFLNK => io::TypeSymlink, - _ => io::TypeUnknown, + libc::S_IFREG => io::FileType::RegularFile, + libc::S_IFDIR => io::FileType::Directory, + libc::S_IFIFO => io::FileType::NamedPipe, + libc::S_IFBLK => io::FileType::BlockSpecial, + libc::S_IFLNK => io::FileType::Symlink, + _ => io::FileType::Unknown, }, perm: FilePermission::from_bits_truncate(stat.st_mode as u32), created: stat.st_ctime as u64, diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 815ace21f87..6924687d8c4 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -24,25 +24,36 @@ use prelude::*; use io::{mod, IoResult, IoError}; use sync::{Once, ONCE_INIT}; -macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => ( +macro_rules! helper_init { (static $name:ident: Helper<$m:ty>) => ( static $name: Helper<$m> = Helper { - lock: ::rustrt::mutex::NATIVE_MUTEX_INIT, + lock: ::sync::MUTEX_INIT, + cond: ::sync::CONDVAR_INIT, chan: ::cell::UnsafeCell { value: 0 as *mut Sender<$m> }, signal: ::cell::UnsafeCell { value: 0 }, initialized: ::cell::UnsafeCell { value: false }, + shutdown: ::cell::UnsafeCell { value: false }, }; -) ) +) } +pub mod backtrace; pub mod c; +pub mod ext; +pub mod condvar; pub mod fs; +pub mod helper_signal; +pub mod mutex; pub mod os; -pub mod tcp; -pub mod udp; pub mod pipe; -pub mod helper_signal; pub mod process; +pub mod rwlock; +pub mod sync; +pub mod stack_overflow; +pub mod tcp; +pub mod thread; +pub mod thread_local; pub mod timer; pub mod tty; +pub mod udp; pub mod addrinfo { pub use sys_common::net::get_host_addresses; @@ -130,7 +141,7 @@ pub fn decode_error_detailed(errno: i32) -> IoError { } #[inline] -pub fn retry<I> (f: || -> I) -> I { f() } // PR rust-lang/rust/#17020 +pub fn retry<I, F>(f: F) -> I where F: FnOnce() -> I { f() } // PR rust-lang/rust/#17020 pub fn ms_to_timeval(ms: u64) -> libc::timeval { libc::timeval { diff --git a/src/libstd/sys/windows/mutex.rs b/src/libstd/sys/windows/mutex.rs new file mode 100644 index 00000000000..ddd89070ed5 --- /dev/null +++ b/src/libstd/sys/windows/mutex.rs @@ -0,0 +1,78 @@ +// Copyright 2014 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 sync::atomic; +use alloc::{mod, heap}; + +use libc::DWORD; +use sys::sync as ffi; + +const SPIN_COUNT: DWORD = 4000; + +pub struct Mutex { inner: atomic::AtomicUint } + +pub const MUTEX_INIT: Mutex = Mutex { inner: atomic::INIT_ATOMIC_UINT }; + +#[inline] +pub unsafe fn raw(m: &Mutex) -> ffi::LPCRITICAL_SECTION { + m.get() +} + +impl Mutex { + #[inline] + pub unsafe fn new() -> Mutex { + Mutex { inner: atomic::AtomicUint::new(init_lock() as uint) } + } + #[inline] + pub unsafe fn lock(&self) { + ffi::EnterCriticalSection(self.get()) + } + #[inline] + pub unsafe fn try_lock(&self) -> bool { + ffi::TryEnterCriticalSection(self.get()) != 0 + } + #[inline] + pub unsafe fn unlock(&self) { + ffi::LeaveCriticalSection(self.get()) + } + pub unsafe fn destroy(&self) { + let lock = self.inner.swap(0, atomic::SeqCst); + if lock != 0 { free_lock(lock as ffi::LPCRITICAL_SECTION) } + } + + unsafe fn get(&self) -> ffi::LPCRITICAL_SECTION { + match self.inner.load(atomic::SeqCst) { + 0 => {} + n => return n as ffi::LPCRITICAL_SECTION + } + let lock = init_lock(); + match self.inner.compare_and_swap(0, lock as uint, atomic::SeqCst) { + 0 => return lock as ffi::LPCRITICAL_SECTION, + _ => {} + } + free_lock(lock); + return self.inner.load(atomic::SeqCst) as ffi::LPCRITICAL_SECTION; + } +} + +unsafe fn init_lock() -> ffi::LPCRITICAL_SECTION { + let block = heap::allocate(ffi::CRITICAL_SECTION_SIZE, 8) + as ffi::LPCRITICAL_SECTION; + if block.is_null() { alloc::oom() } + ffi::InitializeCriticalSectionAndSpinCount(block, SPIN_COUNT); + return block; +} + +unsafe fn free_lock(h: ffi::LPCRITICAL_SECTION) { + ffi::DeleteCriticalSection(h); + heap::deallocate(h as *mut _, ffi::CRITICAL_SECTION_SIZE, 8); +} diff --git a/src/libstd/sys/windows/os.rs b/src/libstd/sys/windows/os.rs index aa43b42e746..e007b46b261 100644 --- a/src/libstd/sys/windows/os.rs +++ b/src/libstd/sys/windows/os.rs @@ -8,17 +8,38 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Implementation of `std::os` functionality for Windows + // FIXME: move various extern bindings from here into liblibc or // something similar -use libc; -use libc::{c_int, c_char, c_void}; use prelude::*; + +use fmt; use io::{IoResult, IoError}; -use sys::fs::FileDesc; +use libc::{c_int, c_char, c_void}; +use libc; +use os; +use path::BytesContainer; use ptr; +use sync::atomic::{AtomicInt, INIT_ATOMIC_INT, SeqCst}; +use sys::fs::FileDesc; +use slice; use os::TMPBUF_SZ; +use libc::types::os::arch::extra::DWORD; + +const BUF_BYTES : uint = 2048u; + +/// Return a slice of `v` ending at (and not including) the first NUL +/// (0). +pub fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] { + match v.iter().position(|c| *c == 0) { + // don't include the 0 + Some(i) => v[..i], + None => v + } +} pub fn errno() -> uint { use libc::types::os::arch::extra::DWORD; @@ -76,7 +97,7 @@ pub fn error_string(errnum: i32) -> String { return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err); } - let msg = String::from_utf16(::str::truncate_utf16_at_nul(&buf)); + let msg = String::from_utf16(truncate_utf16_at_nul(&buf)); match msg { Some(msg) => format!("OS Error {}: {}", errnum, msg), None => format!("OS Error {} (FormatMessageW() returned invalid UTF-16)", errnum), @@ -101,3 +122,212 @@ pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> { _ => Err(IoError::last_error()), } } + +pub fn fill_utf16_buf_and_decode(f: |*mut u16, DWORD| -> DWORD) -> Option<String> { + unsafe { + let mut n = TMPBUF_SZ as DWORD; + let mut res = None; + let mut done = false; + while !done { + let mut buf = Vec::from_elem(n as uint, 0u16); + let k = f(buf.as_mut_ptr(), n); + if k == (0 as DWORD) { + done = true; + } else if k == n && + libc::GetLastError() == + libc::ERROR_INSUFFICIENT_BUFFER as DWORD { + n *= 2 as DWORD; + } else if k >= n { + n = k; + } else { + done = true; + } + if k != 0 && done { + let sub = buf.slice(0, k as uint); + // We want to explicitly catch the case when the + // closure returned invalid UTF-16, rather than + // set `res` to None and continue. + let s = String::from_utf16(sub) + .expect("fill_utf16_buf_and_decode: closure created invalid UTF-16"); + res = Some(s) + } + } + return res; + } +} + +pub fn getcwd() -> IoResult<Path> { + use libc::DWORD; + use libc::GetCurrentDirectoryW; + use io::OtherIoError; + + let mut buf = [0 as u16, ..BUF_BYTES]; + unsafe { + if libc::GetCurrentDirectoryW(buf.len() as DWORD, buf.as_mut_ptr()) == 0 as DWORD { + return Err(IoError::last_error()); + } + } + + match String::from_utf16(truncate_utf16_at_nul(&buf)) { + Some(ref cwd) => Ok(Path::new(cwd)), + None => Err(IoError { + kind: OtherIoError, + desc: "GetCurrentDirectoryW returned invalid UTF-16", + detail: None, + }), + } +} + +pub unsafe fn get_env_pairs() -> Vec<Vec<u8>> { + use libc::funcs::extra::kernel32::{ + GetEnvironmentStringsW, + FreeEnvironmentStringsW + }; + let ch = GetEnvironmentStringsW(); + if ch as uint == 0 { + panic!("os::env() failure getting env string from OS: {}", + os::last_os_error()); + } + // Here, we lossily decode the string as UTF16. + // + // The docs suggest that the result should be in Unicode, but + // Windows doesn't guarantee it's actually UTF16 -- it doesn't + // validate the environment string passed to CreateProcess nor + // SetEnvironmentVariable. Yet, it's unlikely that returning a + // raw u16 buffer would be of practical use since the result would + // be inherently platform-dependent and introduce additional + // complexity to this code. + // + // Using the non-Unicode version of GetEnvironmentStrings is even + // worse since the result is in an OEM code page. Characters that + // can't be encoded in the code page would be turned into question + // marks. + let mut result = Vec::new(); + let mut i = 0; + while *ch.offset(i) != 0 { + let p = &*ch.offset(i); + let mut len = 0; + while *(p as *const _).offset(len) != 0 { + len += 1; + } + let p = p as *const u16; + let s = slice::from_raw_buf(&p, len as uint); + result.push(String::from_utf16_lossy(s).into_bytes()); + i += len as int + 1; + } + FreeEnvironmentStringsW(ch); + result +} + +pub fn split_paths(unparsed: &[u8]) -> Vec<Path> { + // On Windows, the PATH environment variable is semicolon separated. Double + // quotes are used as a way of introducing literal semicolons (since + // c:\some;dir is a valid Windows path). Double quotes are not themselves + // permitted in path names, so there is no way to escape a double quote. + // Quoted regions can appear in arbitrary locations, so + // + // c:\foo;c:\som"e;di"r;c:\bar + // + // Should parse as [c:\foo, c:\some;dir, c:\bar]. + // + // (The above is based on testing; there is no clear reference available + // for the grammar.) + + let mut parsed = Vec::new(); + let mut in_progress = Vec::new(); + let mut in_quote = false; + + for b in unparsed.iter() { + match *b { + b';' if !in_quote => { + parsed.push(Path::new(in_progress.as_slice())); + in_progress.truncate(0) + } + b'"' => { + in_quote = !in_quote; + } + _ => { + in_progress.push(*b); + } + } + } + parsed.push(Path::new(in_progress)); + parsed +} + +pub fn join_paths<T: BytesContainer>(paths: &[T]) -> Result<Vec<u8>, &'static str> { + let mut joined = Vec::new(); + let sep = b';'; + + for (i, path) in paths.iter().map(|p| p.container_as_bytes()).enumerate() { + if i > 0 { joined.push(sep) } + if path.contains(&b'"') { + return Err("path segment contains `\"`"); + } else if path.contains(&sep) { + joined.push(b'"'); + joined.push_all(path); + joined.push(b'"'); + } else { + joined.push_all(path); + } + } + + Ok(joined) +} + +pub fn load_self() -> Option<Vec<u8>> { + unsafe { + fill_utf16_buf_and_decode(|buf, sz| { + libc::GetModuleFileNameW(0u as libc::DWORD, buf, sz) + }).map(|s| s.to_string().into_bytes()) + } +} + +pub fn chdir(p: &Path) -> IoResult<()> { + let mut p = p.as_str().unwrap().utf16_units().collect::<Vec<u16>>(); + p.push(0); + + unsafe { + match libc::SetCurrentDirectoryW(p.as_ptr()) != (0 as libc::BOOL) { + true => Ok(()), + false => Err(IoError::last_error()), + } + } +} + +pub fn page_size() -> uint { + use mem; + unsafe { + let mut info = mem::zeroed(); + libc::GetSystemInfo(&mut info); + + return info.dwPageSize as uint; + } +} + +#[cfg(test)] +mod tests { + use super::truncate_utf16_at_nul; + + #[test] + fn test_truncate_utf16_at_nul() { + let v = []; + let b: &[u16] = &[]; + assert_eq!(truncate_utf16_at_nul(&v), b); + + let v = [0, 2, 3]; + assert_eq!(truncate_utf16_at_nul(&v), b); + + let v = [1, 0, 3]; + let b: &[u16] = &[1]; + assert_eq!(truncate_utf16_at_nul(&v), b); + + let v = [1, 2, 0]; + let b: &[u16] = &[1, 2]; + assert_eq!(truncate_utf16_at_nul(&v), b); + + let v = [1, 2, 3]; + let b: &[u16] = &[1, 2, 3]; + assert_eq!(truncate_utf16_at_nul(&v), b); + } +} diff --git a/src/libstd/sys/windows/pipe.rs b/src/libstd/sys/windows/pipe.rs index a623c2cd8e2..bf658d0efd0 100644 --- a/src/libstd/sys/windows/pipe.rs +++ b/src/libstd/sys/windows/pipe.rs @@ -89,8 +89,7 @@ use libc; use c_str::CString; use mem; use ptr; -use sync::atomic; -use rustrt::mutex; +use sync::{atomic, Mutex}; use io::{mod, IoError, IoResult}; use prelude::*; @@ -126,7 +125,7 @@ impl Drop for Event { struct Inner { handle: libc::HANDLE, - lock: mutex::NativeMutex, + lock: Mutex<()>, read_closed: atomic::AtomicBool, write_closed: atomic::AtomicBool, } @@ -135,7 +134,7 @@ impl Inner { fn new(handle: libc::HANDLE) -> Inner { Inner { handle: handle, - lock: unsafe { mutex::NativeMutex::new() }, + lock: Mutex::new(()), read_closed: atomic::AtomicBool::new(false), write_closed: atomic::AtomicBool::new(false), } @@ -329,7 +328,7 @@ impl UnixStream { } } - fn handle(&self) -> libc::HANDLE { self.inner.handle } + pub fn handle(&self) -> libc::HANDLE { self.inner.handle } fn read_closed(&self) -> bool { self.inner.read_closed.load(atomic::SeqCst) @@ -585,6 +584,10 @@ impl UnixListener { }), }) } + + pub fn handle(&self) -> libc::HANDLE { + self.handle + } } impl Drop for UnixListener { @@ -729,6 +732,10 @@ impl UnixAcceptor { Ok(()) } } + + pub fn handle(&self) -> libc::HANDLE { + self.listener.handle() + } } impl Clone for UnixAcceptor { diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index 3fb5ee34356..0c2c76077dd 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -26,20 +26,17 @@ use sys::fs; use sys::{mod, retry, c, wouldblock, set_nonblocking, ms_to_timeval, timer}; use sys::fs::FileDesc; use sys_common::helper_thread::Helper; -use sys_common::{AsFileDesc, mkerr_libc, timeout}; +use sys_common::{AsInner, mkerr_libc, timeout}; use io::fs::PathExtensions; -use string::String; pub use sys_common::ProcessConfig; -/** - * A value representing a child process. - * - * The lifetime of this value is linked to the lifetime of the actual - * process - the Process destructor calls self.finish() which waits - * for the process to terminate. - */ +/// A value representing a child process. +/// +/// The lifetime of this value is linked to the lifetime of the actual +/// process - the Process destructor calls self.finish() which waits +/// for the process to terminate. pub struct Process { /// The unique id of the process (this should never be negative). pid: pid_t, @@ -105,7 +102,7 @@ impl Process { pub fn spawn<K, V, C, P>(cfg: &C, in_fd: Option<P>, out_fd: Option<P>, err_fd: Option<P>) -> IoResult<Process> - where C: ProcessConfig<K, V>, P: AsFileDesc, + where C: ProcessConfig<K, V>, P: AsInner<FileDesc>, K: BytesContainer + Eq + Hash, V: BytesContainer { use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO}; @@ -124,8 +121,8 @@ impl Process { use libc::funcs::extra::msvcrt::get_osfhandle; use mem; - use iter::Iterator; - use str::StrPrelude; + use iter::{Iterator, IteratorExt}; + use str::StrExt; if cfg.gid().is_some() || cfg.uid().is_some() { return Err(IoError { @@ -195,7 +192,7 @@ impl Process { } } Some(ref fd) => { - let orig = get_osfhandle(fd.as_fd().fd()) as HANDLE; + let orig = get_osfhandle(fd.as_inner().fd()) as HANDLE; if orig == INVALID_HANDLE_VALUE { return Err(super::last_error()) } @@ -225,7 +222,7 @@ impl Process { with_envp(cfg.env(), |envp| { with_dirp(cfg.cwd(), |dirp| { - let mut cmd_str: Vec<u16> = cmd_str.as_slice().utf16_units().collect(); + let mut cmd_str: Vec<u16> = cmd_str.utf16_units().collect(); cmd_str.push(0); let created = CreateProcessW(ptr::null(), cmd_str.as_mut_ptr(), @@ -263,16 +260,14 @@ impl Process { } } - /** - * Waits for a process to exit and returns the exit code, failing - * if there is no process with the specified id. - * - * Note that this is private to avoid race conditions on unix where if - * a user calls waitpid(some_process.get_id()) then some_process.finish() - * and some_process.destroy() and some_process.finalize() will then either - * operate on a none-existent process or, even worse, on a newer process - * with the same id. - */ + /// Waits for a process to exit and returns the exit code, failing + /// if there is no process with the specified id. + /// + /// Note that this is private to avoid race conditions on unix where if + /// a user calls waitpid(some_process.get_id()) then some_process.finish() + /// and some_process.destroy() and some_process.finalize() will then either + /// operate on a none-existent process or, even worse, on a newer process + /// with the same id. pub fn wait(&self, deadline: u64) -> IoResult<ProcessExit> { use libc::types::os::arch::extra::DWORD; use libc::consts::os::extra::{ @@ -422,9 +417,8 @@ fn make_command_line(prog: &CString, args: &[CString]) -> String { } } -fn with_envp<K, V, T>(env: Option<&collections::HashMap<K, V>>, - cb: |*mut c_void| -> T) -> T - where K: BytesContainer + Eq + Hash, V: BytesContainer +fn with_envp<K, V, T, F>(env: Option<&collections::HashMap<K, V>>, cb: F) -> T where + K: BytesContainer + Eq + Hash, V: BytesContainer, F: FnOnce(*mut c_void) -> T, { // On Windows we pass an "environment block" which is not a char**, but // rather a concatenation of null-terminated k=v\0 sequences, with a final @@ -435,9 +429,9 @@ fn with_envp<K, V, T>(env: Option<&collections::HashMap<K, V>>, for pair in env.iter() { let kv = format!("{}={}", - pair.ref0().container_as_str().unwrap(), - pair.ref1().container_as_str().unwrap()); - blk.extend(kv.as_slice().utf16_units()); + pair.0.container_as_str().unwrap(), + pair.1.container_as_str().unwrap()); + blk.extend(kv.utf16_units()); blk.push(0); } @@ -449,7 +443,9 @@ fn with_envp<K, V, T>(env: Option<&collections::HashMap<K, V>>, } } -fn with_dirp<T>(d: Option<&CString>, cb: |*const u16| -> T) -> T { +fn with_dirp<T, F>(d: Option<&CString>, cb: F) -> T where + F: FnOnce(*const u16) -> T, +{ match d { Some(dir) => { let dir_str = dir.as_str() @@ -488,24 +484,24 @@ mod tests { assert_eq!( test_wrapper("prog", &["aaa", "bbb", "ccc"]), - "prog aaa bbb ccc".to_string() + "prog aaa bbb ccc" ); assert_eq!( test_wrapper("C:\\Program Files\\blah\\blah.exe", &["aaa"]), - "\"C:\\Program Files\\blah\\blah.exe\" aaa".to_string() + "\"C:\\Program Files\\blah\\blah.exe\" aaa" ); assert_eq!( test_wrapper("C:\\Program Files\\test", &["aa\"bb"]), - "\"C:\\Program Files\\test\" aa\\\"bb".to_string() + "\"C:\\Program Files\\test\" aa\\\"bb" ); assert_eq!( test_wrapper("echo", &["a b c"]), - "echo \"a b c\"".to_string() + "echo \"a b c\"" ); assert_eq!( - test_wrapper("\u03c0\u042f\u97f3\u00e6\u221e", &[]), - "\u03c0\u042f\u97f3\u00e6\u221e".to_string() + test_wrapper("\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}", &[]), + "\u{03c0}\u{042f}\u{97f3}\u{00e6}\u{221e}" ); } } diff --git a/src/libstd/sys/windows/rwlock.rs b/src/libstd/sys/windows/rwlock.rs new file mode 100644 index 00000000000..88ce85c39f6 --- /dev/null +++ b/src/libstd/sys/windows/rwlock.rs @@ -0,0 +1,53 @@ +// Copyright 2014 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 cell::UnsafeCell; +use sys::sync as ffi; + +pub struct RWLock { inner: UnsafeCell<ffi::SRWLOCK> } + +pub const RWLOCK_INIT: RWLock = RWLock { + inner: UnsafeCell { value: ffi::SRWLOCK_INIT } +}; + +impl RWLock { + #[inline] + pub unsafe fn new() -> RWLock { RWLOCK_INIT } + + #[inline] + pub unsafe fn read(&self) { + ffi::AcquireSRWLockShared(self.inner.get()) + } + #[inline] + pub unsafe fn try_read(&self) -> bool { + ffi::TryAcquireSRWLockShared(self.inner.get()) != 0 + } + #[inline] + pub unsafe fn write(&self) { + ffi::AcquireSRWLockExclusive(self.inner.get()) + } + #[inline] + pub unsafe fn try_write(&self) -> bool { + ffi::TryAcquireSRWLockExclusive(self.inner.get()) != 0 + } + #[inline] + pub unsafe fn read_unlock(&self) { + ffi::ReleaseSRWLockShared(self.inner.get()) + } + #[inline] + pub unsafe fn write_unlock(&self) { + ffi::ReleaseSRWLockExclusive(self.inner.get()) + } + + #[inline] + pub unsafe fn destroy(&self) { + // ... + } +} diff --git a/src/libstd/sys/windows/stack_overflow.rs b/src/libstd/sys/windows/stack_overflow.rs new file mode 100644 index 00000000000..bdf2e0bccb1 --- /dev/null +++ b/src/libstd/sys/windows/stack_overflow.rs @@ -0,0 +1,115 @@ +// Copyright 2014 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 rt::util::report_overflow; +use core::prelude::*; +use ptr; +use mem; +use libc; +use libc::types::os::arch::extra::{LPVOID, DWORD, LONG, BOOL}; +use sys_common::{stack, thread_info}; + +pub struct Handler { + _data: *mut libc::c_void +} + +impl Handler { + pub unsafe fn new() -> Handler { + make_handler() + } +} + +impl Drop for Handler { + fn drop(&mut self) {} +} + +// get_task_info is called from an exception / signal handler. +// It returns the guard page of the current task or 0 if that +// guard page doesn't exist. None is returned if there's currently +// no local task. +unsafe fn get_task_guard_page() -> uint { + thread_info::stack_guard() +} + +// This is initialized in init() and only read from after +static mut PAGE_SIZE: uint = 0; + +#[no_stack_check] +extern "system" fn vectored_handler(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG { + unsafe { + let rec = &(*(*ExceptionInfo).ExceptionRecord); + let code = rec.ExceptionCode; + + if code != EXCEPTION_STACK_OVERFLOW { + return EXCEPTION_CONTINUE_SEARCH; + } + + // We're calling into functions with stack checks, + // however stack checks by limit should be disabled on Windows + stack::record_sp_limit(0); + + report_overflow(); + + EXCEPTION_CONTINUE_SEARCH + } +} + +pub unsafe fn init() { + let mut info = mem::zeroed(); + libc::GetSystemInfo(&mut info); + PAGE_SIZE = info.dwPageSize as uint; + + if AddVectoredExceptionHandler(0, vectored_handler) == ptr::null_mut() { + panic!("failed to install exception handler"); + } + + mem::forget(make_handler()); +} + +pub unsafe fn cleanup() { +} + +pub unsafe fn make_handler() -> Handler { + if SetThreadStackGuarantee(&mut 0x5000) == 0 { + panic!("failed to reserve stack space for exception handling"); + } + + Handler { _data: 0i as *mut libc::c_void } +} + +pub struct EXCEPTION_RECORD { + pub ExceptionCode: DWORD, + pub ExceptionFlags: DWORD, + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ExceptionAddress: LPVOID, + pub NumberParameters: DWORD, + pub ExceptionInformation: [LPVOID, ..EXCEPTION_MAXIMUM_PARAMETERS] +} + +pub struct EXCEPTION_POINTERS { + pub ExceptionRecord: *mut EXCEPTION_RECORD, + pub ContextRecord: LPVOID +} + +pub type PVECTORED_EXCEPTION_HANDLER = extern "system" + fn(ExceptionInfo: *mut EXCEPTION_POINTERS) -> LONG; + +pub type ULONG = libc::c_ulong; + +const EXCEPTION_CONTINUE_SEARCH: LONG = 0; +const EXCEPTION_MAXIMUM_PARAMETERS: uint = 15; +const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd; + +extern "system" { + fn AddVectoredExceptionHandler(FirstHandler: ULONG, + VectoredHandler: PVECTORED_EXCEPTION_HANDLER) + -> LPVOID; + fn SetThreadStackGuarantee(StackSizeInBytes: *mut ULONG) -> BOOL; +} diff --git a/src/libstd/sys/windows/sync.rs b/src/libstd/sys/windows/sync.rs new file mode 100644 index 00000000000..cbca47912b5 --- /dev/null +++ b/src/libstd/sys/windows/sync.rs @@ -0,0 +1,58 @@ +// Copyright 2014 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 libc::{BOOL, DWORD, c_void, LPVOID}; +use libc::types::os::arch::extra::BOOLEAN; + +pub type LPCRITICAL_SECTION = *mut c_void; +pub type LPCONDITION_VARIABLE = *mut CONDITION_VARIABLE; +pub type LPSRWLOCK = *mut SRWLOCK; + +#[cfg(target_arch = "x86")] +pub const CRITICAL_SECTION_SIZE: uint = 24; +#[cfg(target_arch = "x86_64")] +pub const CRITICAL_SECTION_SIZE: uint = 40; + +#[repr(C)] +pub struct CONDITION_VARIABLE { pub ptr: LPVOID } +#[repr(C)] +pub struct SRWLOCK { pub ptr: LPVOID } + +pub const CONDITION_VARIABLE_INIT: CONDITION_VARIABLE = CONDITION_VARIABLE { + ptr: 0 as *mut _, +}; +pub const SRWLOCK_INIT: SRWLOCK = SRWLOCK { ptr: 0 as *mut _ }; + +extern "system" { + // critical sections + pub fn InitializeCriticalSectionAndSpinCount( + lpCriticalSection: LPCRITICAL_SECTION, + dwSpinCount: DWORD) -> BOOL; + pub fn DeleteCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); + pub fn EnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); + pub fn LeaveCriticalSection(lpCriticalSection: LPCRITICAL_SECTION); + pub fn TryEnterCriticalSection(lpCriticalSection: LPCRITICAL_SECTION) -> BOOL; + + // condition variables + pub fn SleepConditionVariableCS(ConditionVariable: LPCONDITION_VARIABLE, + CriticalSection: LPCRITICAL_SECTION, + dwMilliseconds: DWORD) -> BOOL; + pub fn WakeConditionVariable(ConditionVariable: LPCONDITION_VARIABLE); + pub fn WakeAllConditionVariable(ConditionVariable: LPCONDITION_VARIABLE); + + // slim rwlocks + pub fn AcquireSRWLockExclusive(SRWLock: LPSRWLOCK); + pub fn AcquireSRWLockShared(SRWLock: LPSRWLOCK); + pub fn ReleaseSRWLockExclusive(SRWLock: LPSRWLOCK); + pub fn ReleaseSRWLockShared(SRWLock: LPSRWLOCK); + pub fn TryAcquireSRWLockExclusive(SRWLock: LPSRWLOCK) -> BOOLEAN; + pub fn TryAcquireSRWLockShared(SRWLock: LPSRWLOCK) -> BOOLEAN; +} + diff --git a/src/libstd/sys/windows/tcp.rs b/src/libstd/sys/windows/tcp.rs index 3baf2be08d2..505e6137bf9 100644 --- a/src/libstd/sys/windows/tcp.rs +++ b/src/libstd/sys/windows/tcp.rs @@ -18,8 +18,7 @@ use super::{last_error, last_net_error, retry, sock_t}; use sync::{Arc, atomic}; use sys::fs::FileDesc; use sys::{mod, c, set_nonblocking, wouldblock, timer}; -use sys_common::{mod, timeout, eof}; -use sys_common::net::*; +use sys_common::{mod, timeout, eof, net}; pub use sys_common::net::TcpStream; @@ -48,37 +47,35 @@ impl Drop for Event { // TCP listeners //////////////////////////////////////////////////////////////////////////////// -pub struct TcpListener { - inner: FileDesc, -} +pub struct TcpListener { sock: sock_t } impl TcpListener { pub fn bind(addr: ip::SocketAddr) -> IoResult<TcpListener> { sys::init_net(); - let fd = try!(socket(addr, libc::SOCK_STREAM)); - let ret = TcpListener { inner: FileDesc::new(fd as libc::c_int, true) }; + let sock = try!(net::socket(addr, libc::SOCK_STREAM)); + let ret = TcpListener { sock: sock }; let mut storage = unsafe { mem::zeroed() }; - let len = addr_to_sockaddr(addr, &mut storage); + let len = net::addr_to_sockaddr(addr, &mut storage); let addrp = &storage as *const _ as *const libc::sockaddr; - match unsafe { libc::bind(fd, addrp, len) } { + match unsafe { libc::bind(sock, addrp, len) } { -1 => Err(last_net_error()), _ => Ok(ret), } } - pub fn fd(&self) -> sock_t { self.inner.fd as sock_t } + pub fn socket(&self) -> sock_t { self.sock } pub fn listen(self, backlog: int) -> IoResult<TcpAcceptor> { - match unsafe { libc::listen(self.fd(), backlog as libc::c_int) } { + match unsafe { libc::listen(self.socket(), backlog as libc::c_int) } { -1 => Err(last_net_error()), _ => { let accept = try!(Event::new()); let ret = unsafe { - c::WSAEventSelect(self.fd(), accept.handle(), c::FD_ACCEPT) + c::WSAEventSelect(self.socket(), accept.handle(), c::FD_ACCEPT) }; if ret != 0 { return Err(last_net_error()) @@ -97,7 +94,13 @@ impl TcpListener { } pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> { - sockname(self.fd(), libc::getsockname) + net::sockname(self.socket(), libc::getsockname) + } +} + +impl Drop for TcpListener { + fn drop(&mut self) { + unsafe { super::close_sock(self.sock); } } } @@ -114,7 +117,7 @@ struct AcceptorInner { } impl TcpAcceptor { - pub fn fd(&self) -> sock_t { self.inner.listener.fd() } + pub fn socket(&self) -> sock_t { self.inner.listener.socket() } pub fn accept(&mut self) -> IoResult<TcpStream> { // Unlink unix, windows cannot invoke `select` on arbitrary file @@ -161,13 +164,13 @@ impl TcpAcceptor { let mut wsaevents: c::WSANETWORKEVENTS = unsafe { mem::zeroed() }; let ret = unsafe { - c::WSAEnumNetworkEvents(self.fd(), events[1], &mut wsaevents) + c::WSAEnumNetworkEvents(self.socket(), events[1], &mut wsaevents) }; if ret != 0 { return Err(last_net_error()) } if wsaevents.lNetworkEvents & c::FD_ACCEPT == 0 { continue } match unsafe { - libc::accept(self.fd(), ptr::null_mut(), ptr::null_mut()) + libc::accept(self.socket(), ptr::null_mut(), ptr::null_mut()) } { -1 if wouldblock() => {} -1 => return Err(last_net_error()), @@ -175,13 +178,13 @@ impl TcpAcceptor { // Accepted sockets inherit the same properties as the caller, // so we need to deregister our event and switch the socket back // to blocking mode - fd => { - let stream = TcpStream::new(fd); + socket => { + let stream = TcpStream::new(socket); let ret = unsafe { - c::WSAEventSelect(fd, events[1], 0) + c::WSAEventSelect(socket, events[1], 0) }; if ret != 0 { return Err(last_net_error()) } - try!(set_nonblocking(fd, false)); + try!(set_nonblocking(socket, false)); return Ok(stream) } } @@ -191,7 +194,7 @@ impl TcpAcceptor { } pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> { - sockname(self.fd(), libc::getsockname) + net::sockname(self.socket(), libc::getsockname) } pub fn set_timeout(&mut self, timeout: Option<u64>) { diff --git a/src/libstd/sys/windows/thread.rs b/src/libstd/sys/windows/thread.rs new file mode 100644 index 00000000000..4498f56c00a --- /dev/null +++ b/src/libstd/sys/windows/thread.rs @@ -0,0 +1,96 @@ +// Copyright 2014 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 core::prelude::*; + +use boxed::Box; +use cmp; +use mem; +use ptr; +use libc; +use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL, + LPVOID, DWORD, LPDWORD, HANDLE}; +use thunk::Thunk; +use sys_common::stack::RED_ZONE; +use sys_common::thread::*; + +pub type rust_thread = HANDLE; +pub type rust_thread_return = DWORD; + +pub type StartFn = extern "system" fn(*mut libc::c_void) -> rust_thread_return; + +#[no_stack_check] +pub extern "system" fn thread_start(main: *mut libc::c_void) -> rust_thread_return { + return start_thread(main); +} + +pub mod guard { + pub unsafe fn main() -> uint { + 0 + } + + pub unsafe fn current() -> uint { + 0 + } + + pub unsafe fn init() { + } +} + +pub unsafe fn create(stack: uint, p: Thunk) -> rust_thread { + let arg: *mut libc::c_void = mem::transmute(box p); + // FIXME On UNIX, we guard against stack sizes that are too small but + // that's because pthreads enforces that stacks are at least + // PTHREAD_STACK_MIN bytes big. Windows has no such lower limit, it's + // just that below a certain threshold you can't do anything useful. + // That threshold is application and architecture-specific, however. + // For now, the only requirement is that it's big enough to hold the + // red zone. Round up to the next 64 kB because that's what the NT + // kernel does, might as well make it explicit. With the current + // 20 kB red zone, that makes for a 64 kB minimum stack. + let stack_size = (cmp::max(stack, RED_ZONE) + 0xfffe) & (-0xfffe - 1); + let ret = CreateThread(ptr::null_mut(), stack_size as libc::size_t, + thread_start, arg, 0, ptr::null_mut()); + + if ret as uint == 0 { + // be sure to not leak the closure + let _p: Box<Thunk> = mem::transmute(arg); + panic!("failed to spawn native thread: {}", ret); + } + return ret; +} + +pub unsafe fn join(native: rust_thread) { + use libc::consts::os::extra::INFINITE; + WaitForSingleObject(native, INFINITE); +} + +pub unsafe fn detach(native: rust_thread) { + assert!(libc::CloseHandle(native) != 0); +} + +pub unsafe fn yield_now() { + // This function will return 0 if there are no other threads to execute, + // but this also means that the yield was useless so this isn't really a + // case that needs to be worried about. + SwitchToThread(); +} + +#[allow(non_snake_case)] +extern "system" { + fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, + dwStackSize: SIZE_T, + lpStartAddress: StartFn, + lpParameter: LPVOID, + dwCreationFlags: DWORD, + lpThreadId: LPDWORD) -> HANDLE; + fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; + fn SwitchToThread() -> BOOL; +} diff --git a/src/libstd/sys/windows/thread_local.rs b/src/libstd/sys/windows/thread_local.rs new file mode 100644 index 00000000000..60b0d584db3 --- /dev/null +++ b/src/libstd/sys/windows/thread_local.rs @@ -0,0 +1,259 @@ +// Copyright 2014 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 libc::types::os::arch::extra::{DWORD, LPVOID, BOOL}; + +use mem; +use rt; +use sys_common::mutex::{MUTEX_INIT, Mutex}; + +pub type Key = DWORD; +pub type Dtor = unsafe extern fn(*mut u8); + +// Turns out, like pretty much everything, Windows is pretty close the +// functionality that Unix provides, but slightly different! In the case of +// TLS, Windows does not provide an API to provide a destructor for a TLS +// variable. This ends up being pretty crucial to this implementation, so we +// need a way around this. +// +// The solution here ended up being a little obscure, but fear not, the +// internet has informed me [1][2] that this solution is not unique (no way +// I could have thought of it as well!). The key idea is to insert some hook +// somewhere to run arbitrary code on thread termination. With this in place +// we'll be able to run anything we like, including all TLS destructors! +// +// To accomplish this feat, we perform a number of tasks, all contained +// within this module: +// +// * All TLS destructors are tracked by *us*, not the windows runtime. This +// means that we have a global list of destructors for each TLS key that +// we know about. +// * When a TLS key is destroyed, we're sure to remove it from the dtor list +// if it's in there. +// * When a thread exits, we run over the entire list and run dtors for all +// non-null keys. This attempts to match Unix semantics in this regard. +// +// This ends up having the overhead of using a global list, having some +// locks here and there, and in general just adding some more code bloat. We +// attempt to optimize runtime by forgetting keys that don't have +// destructors, but this only gets us so far. +// +// For more details and nitty-gritty, see the code sections below! +// +// [1]: http://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way +// [2]: https://github.com/ChromiumWebApps/chromium/blob/master/base +// /threading/thread_local_storage_win.cc#L42 + +// NB these are specifically not types from `std::sync` as they currently rely +// on poisoning and this module needs to operate at a lower level than requiring +// the thread infrastructure to be in place (useful on the borders of +// initialization/destruction). +static DTOR_LOCK: Mutex = MUTEX_INIT; +static mut DTORS: *mut Vec<(Key, Dtor)> = 0 as *mut _; + +// ------------------------------------------------------------------------- +// Native bindings +// +// This section is just raw bindings to the native functions that Windows +// provides, There's a few extra calls to deal with destructors. + +#[inline] +pub unsafe fn create(dtor: Option<Dtor>) -> Key { + const TLS_OUT_OF_INDEXES: DWORD = 0xFFFFFFFF; + let key = TlsAlloc(); + assert!(key != TLS_OUT_OF_INDEXES); + match dtor { + Some(f) => register_dtor(key, f), + None => {} + } + return key; +} + +#[inline] +pub unsafe fn set(key: Key, value: *mut u8) { + let r = TlsSetValue(key, value as LPVOID); + debug_assert!(r != 0); +} + +#[inline] +pub unsafe fn get(key: Key) -> *mut u8 { + TlsGetValue(key) as *mut u8 +} + +#[inline] +pub unsafe fn destroy(key: Key) { + if unregister_dtor(key) { + // FIXME: Currently if a key has a destructor associated with it we + // can't actually ever unregister it. If we were to + // unregister it, then any key destruction would have to be + // serialized with respect to actually running destructors. + // + // We want to avoid a race where right before run_dtors runs + // some destructors TlsFree is called. Allowing the call to + // TlsFree would imply that the caller understands that *all + // known threads* are not exiting, which is quite a difficult + // thing to know! + // + // For now we just leak all keys with dtors to "fix" this. + // Note that source [2] above shows precedent for this sort + // of strategy. + } else { + let r = TlsFree(key); + debug_assert!(r != 0); + } +} + +extern "system" { + fn TlsAlloc() -> DWORD; + fn TlsFree(dwTlsIndex: DWORD) -> BOOL; + fn TlsGetValue(dwTlsIndex: DWORD) -> LPVOID; + fn TlsSetValue(dwTlsIndex: DWORD, lpTlsvalue: LPVOID) -> BOOL; +} + +// ------------------------------------------------------------------------- +// Dtor registration +// +// These functions are associated with registering and unregistering +// destructors. They're pretty simple, they just push onto a vector and scan +// a vector currently. +// +// FIXME: This could probably be at least a little faster with a BTree. + +unsafe fn init_dtors() { + if !DTORS.is_null() { return } + + let dtors = box Vec::<(Key, Dtor)>::new(); + DTORS = mem::transmute(dtors); + + rt::at_exit(move|| { + DTOR_LOCK.lock(); + let dtors = DTORS; + DTORS = 0 as *mut _; + mem::transmute::<_, Box<Vec<(Key, Dtor)>>>(dtors); + assert!(DTORS.is_null()); // can't re-init after destructing + DTOR_LOCK.unlock(); + }); +} + +unsafe fn register_dtor(key: Key, dtor: Dtor) { + DTOR_LOCK.lock(); + init_dtors(); + (*DTORS).push((key, dtor)); + DTOR_LOCK.unlock(); +} + +unsafe fn unregister_dtor(key: Key) -> bool { + DTOR_LOCK.lock(); + init_dtors(); + let ret = { + let dtors = &mut *DTORS; + let before = dtors.len(); + dtors.retain(|&(k, _)| k != key); + dtors.len() != before + }; + DTOR_LOCK.unlock(); + ret +} + +// ------------------------------------------------------------------------- +// Where the Magic (TM) Happens +// +// If you're looking at this code, and wondering "what is this doing?", +// you're not alone! I'll try to break this down step by step: +// +// # What's up with CRT$XLB? +// +// For anything about TLS destructors to work on Windows, we have to be able +// to run *something* when a thread exits. To do so, we place a very special +// static in a very special location. If this is encoded in just the right +// way, the kernel's loader is apparently nice enough to run some function +// of ours whenever a thread exits! How nice of the kernel! +// +// Lots of detailed information can be found in source [1] above, but the +// gist of it is that this is leveraging a feature of Microsoft's PE format +// (executable format) which is not actually used by any compilers today. +// This apparently translates to any callbacks in the ".CRT$XLB" section +// being run on certain events. +// +// So after all that, we use the compiler's #[link_section] feature to place +// a callback pointer into the magic section so it ends up being called. +// +// # What's up with this callback? +// +// The callback specified receives a number of parameters from... someone! +// (the kernel? the runtime? I'm not qute sure!) There are a few events that +// this gets invoked for, but we're currentl only interested on when a +// thread or a process "detaches" (exits). The process part happens for the +// last thread and the thread part happens for any normal thread. +// +// # Ok, what's up with running all these destructors? +// +// This will likely need to be improved over time, but this function +// attempts a "poor man's" destructor callback system. To do this we clone a +// local copy of the dtor list to start out with. This is our fudgy attempt +// to not hold the lock while destructors run and not worry about the list +// changing while we're looking at it. +// +// Once we've got a list of what to run, we iterate over all keys, check +// their values, and then run destructors if the values turn out to be non +// null (setting them to null just beforehand). We do this a few times in a +// loop to basically match Unix semantics. If we don't reach a fixed point +// after a short while then we just inevitably leak something most likely. +// +// # The article mentions crazy stuff about "/INCLUDE"? +// +// It sure does! This seems to work for now, so maybe we'll just run into +// that if we start linking with msvc? + +#[link_section = ".CRT$XLB"] +#[linkage = "external"] +#[allow(warnings)] +pub static p_thread_callback: unsafe extern "system" fn(LPVOID, DWORD, + LPVOID) = + on_tls_callback; + +#[allow(warnings)] +unsafe extern "system" fn on_tls_callback(h: LPVOID, + dwReason: DWORD, + pv: LPVOID) { + const DLL_THREAD_DETACH: DWORD = 3; + const DLL_PROCESS_DETACH: DWORD = 0; + if dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH { + run_dtors(); + } +} + +unsafe fn run_dtors() { + let mut any_run = true; + for _ in range(0, 5i) { + if !any_run { break } + any_run = false; + let dtors = { + DTOR_LOCK.lock(); + let ret = if DTORS.is_null() { + Vec::new() + } else { + (*DTORS).iter().map(|s| *s).collect() + }; + DTOR_LOCK.unlock(); + ret + }; + for &(key, dtor) in dtors.iter() { + let ptr = TlsGetValue(key); + if !ptr.is_null() { + TlsSetValue(key, 0 as *mut _); + dtor(ptr as *mut _); + any_run = true; + } + } + } +} diff --git a/src/libstd/sys/windows/timer.rs b/src/libstd/sys/windows/timer.rs index 9af3a7c8b6e..7e4dd768aa9 100644 --- a/src/libstd/sys/windows/timer.rs +++ b/src/libstd/sys/windows/timer.rs @@ -20,7 +20,7 @@ //! Other than that, the implementation is pretty straightforward in terms of //! the other two implementations of timers with nothing *that* new showing up. -pub use self::Req::*; +use self::Req::*; use libc; use ptr; @@ -32,7 +32,7 @@ use sys_common::helper_thread::Helper; use prelude::*; use io::IoResult; -helper_init!(static HELPER: Helper<Req>) +helper_init! { static HELPER: Helper<Req> } pub trait Callback { fn call(&mut self); diff --git a/src/libstd/sys/windows/tty.rs b/src/libstd/sys/windows/tty.rs index 0e7b06cbb94..f793de5bb57 100644 --- a/src/libstd/sys/windows/tty.rs +++ b/src/libstd/sys/windows/tty.rs @@ -111,9 +111,9 @@ impl TTY { } pub fn write(&mut self, buf: &[u8]) -> IoResult<()> { - let utf16 = match from_utf8(buf) { + let utf16 = match from_utf8(buf).ok() { Some(utf8) => { - utf8.as_slice().utf16_units().collect::<Vec<u16>>() + utf8.utf16_units().collect::<Vec<u16>>() } None => return Err(invalid_encoding()), }; diff --git a/src/libstd/task.rs b/src/libstd/task.rs index 4f5f47e980c..0f08108fee5 100644 --- a/src/libstd/task.rs +++ b/src/libstd/task.rs @@ -8,520 +8,37 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Task creation -//! -//! An executing Rust program consists of a collection of tasks, each -//! with their own stack and local state. -//! -//! Tasks generally have their memory *isolated* from each other by -//! virtue of Rust's owned types (which of course may only be owned by -//! a single task at a time). Communication between tasks is primarily -//! done through [channels](../../std/comm/index.html), Rust's -//! message-passing types, though [other forms of task -//! synchronization](../../std/sync/index.html) are often employed to -//! achieve particular performance goals. In particular, types that -//! are guaranteed to be threadsafe are easily shared between threads -//! using the atomically-reference-counted container, -//! [`Arc`](../../std/sync/struct.Arc.html). -//! -//! Fatal logic errors in Rust cause *task panic*, during which -//! a task will unwind the stack, running destructors and freeing -//! owned resources. Task panic is unrecoverable from within -//! the panicking task (i.e. there is no 'try/catch' in Rust), but -//! panic may optionally be detected from a different task. If -//! the main task panics the application will exit with a non-zero -//! exit code. -//! -//! ## Example -//! -//! ```rust -//! spawn(proc() { -//! println!("Hello, World!"); -//! }) -//! ``` +//! Deprecated in favor of `thread`. -#![unstable = "The task spawning model will be changed as part of runtime reform, and the module \ - will likely be renamed from `task` to `thread`."] +#![deprecated = "use std::thread instead"] use any::Any; -use comm::channel; -use io::{Writer, stdio}; -use kinds::{Send, marker}; -use option::{None, Some, Option}; use boxed::Box; +use thread; +use kinds::Send; use result::Result; -use rustrt::local::Local; -use rustrt::task; -use rustrt::task::Task; -use str::{Str, SendStr, IntoMaybeOwned}; -use string::{String, ToString}; -use sync::Future; +use ops::FnOnce; -/// The task builder type. -/// -/// Provides detailed control over the properties and behavior of new tasks. +/// Deprecate: use `std::thread::Builder` instead. +#[deprecated = "use std::thread::Builder instead"] +pub type TaskBuilder = thread::Builder; -// NB: Builders are designed to be single-use because they do stateful -// things that get weird when reusing - e.g. if you create a result future -// it only applies to a single task, so then you have to maintain Some -// potentially tricky state to ensure that everything behaves correctly -// when you try to reuse the builder to spawn a new task. We'll just -// sidestep that whole issue by making builders uncopyable and making -// the run function move them in. -pub struct TaskBuilder { - // A name for the task-to-be, for identification in panic messages - name: Option<SendStr>, - // The size of the stack for the spawned task - stack_size: Option<uint>, - // Task-local stdout - stdout: Option<Box<Writer + Send>>, - // Task-local stderr - stderr: Option<Box<Writer + Send>>, - // Optionally wrap the eventual task body - gen_body: Option<proc(v: proc():Send):Send -> proc():Send>, - nocopy: marker::NoCopy, +/// Deprecated: use `std::thread::Thread::spawn` and `detach` instead. +#[deprecated = "use std::thread::Thread::spawn and detach instead"] +pub fn spawn<F>(f: F) where F: FnOnce(), F: Send { + thread::Thread::spawn(f).detach(); } -impl TaskBuilder { - /// Generate the base configuration for spawning a task, off of which more - /// configuration methods can be chained. - pub fn new() -> TaskBuilder { - TaskBuilder { - name: None, - stack_size: None, - stdout: None, - stderr: None, - gen_body: None, - nocopy: marker::NoCopy, - } - } +/// Deprecated: use `std::thread::Thread::spawn` and `join` instead. +#[deprecated = "use std::thread::Thread::spawn and join instead"] +pub fn try<T, F>(f: F) -> Result<T, Box<Any + Send>> where + T: Send, F: FnOnce() -> T, F: Send +{ + thread::Thread::spawn(f).join() } -impl TaskBuilder { - /// Name the task-to-be. Currently the name is used for identification - /// only in panic messages. - #[unstable = "IntoMaybeOwned will probably change."] - pub fn named<T: IntoMaybeOwned<'static>>(mut self, name: T) -> TaskBuilder { - self.name = Some(name.into_maybe_owned()); - self - } - - /// Set the size of the stack for the new task. - pub fn stack_size(mut self, size: uint) -> TaskBuilder { - self.stack_size = Some(size); - self - } - - /// Redirect task-local stdout. - #[experimental = "May not want to make stdio overridable here."] - pub fn stdout(mut self, stdout: Box<Writer + Send>) -> TaskBuilder { - self.stdout = Some(stdout); - self - } - - /// Redirect task-local stderr. - #[experimental = "May not want to make stdio overridable here."] - pub fn stderr(mut self, stderr: Box<Writer + Send>) -> TaskBuilder { - self.stderr = Some(stderr); - self - } - - // Where spawning actually happens (whether yielding a future or not) - fn spawn_internal(self, f: proc():Send, - on_exit: Option<proc(Result<(), Box<Any + Send>>):Send>) { - let TaskBuilder { - name, stack_size, stdout, stderr, mut gen_body, nocopy: _ - } = self; - let f = match gen_body.take() { - Some(gen) => gen(f), - None => f - }; - let opts = task::TaskOpts { - on_exit: on_exit, - name: name, - stack_size: stack_size, - }; - if stdout.is_some() || stderr.is_some() { - Task::spawn(opts, proc() { - let _ = stdout.map(stdio::set_stdout); - let _ = stderr.map(stdio::set_stderr); - f(); - }) - } else { - Task::spawn(opts, f) - } - } - - /// Creates and executes a new child task. - /// - /// Sets up a new task with its own call stack and schedules it to run - /// the provided proc. The task has the properties and behavior - /// specified by the `TaskBuilder`. - pub fn spawn(self, f: proc():Send) { - self.spawn_internal(f, None) - } - - /// Execute a proc in a newly-spawned task and return a future representing - /// the task's result. The task has the properties and behavior - /// specified by the `TaskBuilder`. - /// - /// Taking the value of the future will block until the child task - /// terminates. - /// - /// # Return value - /// - /// If the child task executes successfully (without panicking) then the - /// future returns `result::Ok` containing the value returned by the - /// function. If the child task panics then the future returns `result::Err` - /// containing the argument to `panic!(...)` as an `Any` trait object. - #[experimental = "Futures are experimental."] - pub fn try_future<T:Send>(self, f: proc():Send -> T) - -> Future<Result<T, Box<Any + Send>>> { - // currently, the on_exit proc provided by librustrt only works for unit - // results, so we use an additional side-channel to communicate the - // result. - - let (tx_done, rx_done) = channel(); // signal that task has exited - let (tx_retv, rx_retv) = channel(); // return value from task - - let on_exit = proc(res) { let _ = tx_done.send_opt(res); }; - self.spawn_internal(proc() { let _ = tx_retv.send_opt(f()); }, - Some(on_exit)); - - Future::from_fn(proc() { - rx_done.recv().map(|_| rx_retv.recv()) - }) - } - - /// Execute a function in a newly-spawnedtask and block until the task - /// completes or panics. Equivalent to `.try_future(f).unwrap()`. - #[unstable = "Error type may change."] - pub fn try<T:Send>(self, f: proc():Send -> T) -> Result<T, Box<Any + Send>> { - self.try_future(f).unwrap() - } -} - -/* Convenience functions */ - -/// Creates and executes a new child task -/// -/// Sets up a new task with its own call stack and schedules it to run -/// the provided unique closure. -/// -/// This function is equivalent to `TaskBuilder::new().spawn(f)`. -pub fn spawn(f: proc(): Send) { - TaskBuilder::new().spawn(f) -} - -/// Execute a function in a newly-spawned task and return either the return -/// value of the function or an error if the task panicked. -/// -/// This is equivalent to `TaskBuilder::new().try`. -#[unstable = "Error type may change."] -pub fn try<T: Send>(f: proc(): Send -> T) -> Result<T, Box<Any + Send>> { - TaskBuilder::new().try(f) -} - -/// Execute a function in another task and return a future representing the -/// task's result. -/// -/// This is equivalent to `TaskBuilder::new().try_future`. -#[experimental = "Futures are experimental."] -pub fn try_future<T:Send>(f: proc():Send -> T) -> Future<Result<T, Box<Any + Send>>> { - TaskBuilder::new().try_future(f) -} - - -/* Lifecycle functions */ - -/// Read the name of the current task. -#[stable] -pub fn name() -> Option<String> { - use rustrt::task::Task; - - let task = Local::borrow(None::<Task>); - match task.name { - Some(ref name) => Some(name.as_slice().to_string()), - None => None - } -} - -/// Yield control to the task scheduler. -#[unstable = "Name will change."] +/// Deprecated: use `std::thread::Thread::yield_now instead`. +#[deprecated = "use std::thread::Thread::yield_now instead"] pub fn deschedule() { - use rustrt::task::Task; - Task::yield_now(); -} - -/// True if the running task is currently panicking (e.g. will return `true` inside a -/// destructor that is run while unwinding the stack after a call to `panic!()`). -#[unstable = "May move to a different module."] -pub fn failing() -> bool { - use rustrt::task::Task; - Local::borrow(None::<Task>).unwinder.unwinding() -} - -#[cfg(test)] -mod test { - use any::{Any, AnyRefExt}; - use boxed::BoxAny; - use result; - use result::{Ok, Err}; - use string::String; - use std::io::{ChanReader, ChanWriter}; - use prelude::*; - use super::*; - - // !!! These tests are dangerous. If something is buggy, they will hang, !!! - // !!! instead of exiting cleanly. This might wedge the buildbots. !!! - - #[test] - fn test_unnamed_task() { - try(proc() { - assert!(name().is_none()); - }).map_err(|_| ()).unwrap(); - } - - #[test] - fn test_owned_named_task() { - TaskBuilder::new().named("ada lovelace".to_string()).try(proc() { - assert!(name().unwrap() == "ada lovelace".to_string()); - }).map_err(|_| ()).unwrap(); - } - - #[test] - fn test_static_named_task() { - TaskBuilder::new().named("ada lovelace").try(proc() { - assert!(name().unwrap() == "ada lovelace".to_string()); - }).map_err(|_| ()).unwrap(); - } - - #[test] - fn test_send_named_task() { - TaskBuilder::new().named("ada lovelace".into_maybe_owned()).try(proc() { - assert!(name().unwrap() == "ada lovelace".to_string()); - }).map_err(|_| ()).unwrap(); - } - - #[test] - fn test_run_basic() { - let (tx, rx) = channel(); - TaskBuilder::new().spawn(proc() { - tx.send(()); - }); - rx.recv(); - } - - #[test] - fn test_try_future() { - let result = TaskBuilder::new().try_future(proc() {}); - assert!(result.unwrap().is_ok()); - - let result = TaskBuilder::new().try_future(proc() -> () { - panic!(); - }); - assert!(result.unwrap().is_err()); - } - - #[test] - fn test_try_success() { - match try(proc() { - "Success!".to_string() - }).as_ref().map(|s| s.as_slice()) { - result::Ok("Success!") => (), - _ => panic!() - } - } - - #[test] - fn test_try_panic() { - match try(proc() { - panic!() - }) { - result::Err(_) => (), - result::Ok(()) => panic!() - } - } - - #[test] - fn test_spawn_sched() { - use clone::Clone; - - let (tx, rx) = channel(); - - fn f(i: int, tx: Sender<()>) { - let tx = tx.clone(); - spawn(proc() { - if i == 0 { - tx.send(()); - } else { - f(i - 1, tx); - } - }); - - } - f(10, tx); - rx.recv(); - } - - #[test] - fn test_spawn_sched_childs_on_default_sched() { - let (tx, rx) = channel(); - - spawn(proc() { - spawn(proc() { - tx.send(()); - }); - }); - - rx.recv(); - } - - fn avoid_copying_the_body(spawnfn: |v: proc():Send|) { - let (tx, rx) = channel::<uint>(); - - let x = box 1; - let x_in_parent = (&*x) as *const int as uint; - - spawnfn(proc() { - let x_in_child = (&*x) as *const int as uint; - tx.send(x_in_child); - }); - - let x_in_child = rx.recv(); - assert_eq!(x_in_parent, x_in_child); - } - - #[test] - fn test_avoid_copying_the_body_spawn() { - avoid_copying_the_body(spawn); - } - - #[test] - fn test_avoid_copying_the_body_task_spawn() { - avoid_copying_the_body(|f| { - let builder = TaskBuilder::new(); - builder.spawn(proc() { - f(); - }); - }) - } - - #[test] - fn test_avoid_copying_the_body_try() { - avoid_copying_the_body(|f| { - let _ = try(proc() { - f() - }); - }) - } - - #[test] - fn test_child_doesnt_ref_parent() { - // If the child refcounts the parent task, this will stack overflow when - // climbing the task tree to dereference each ancestor. (See #1789) - // (well, it would if the constant were 8000+ - I lowered it to be more - // valgrind-friendly. try this at home, instead..!) - static GENERATIONS: uint = 16; - fn child_no(x: uint) -> proc(): Send { - return proc() { - if x < GENERATIONS { - TaskBuilder::new().spawn(child_no(x+1)); - } - } - } - TaskBuilder::new().spawn(child_no(0)); - } - - #[test] - fn test_simple_newsched_spawn() { - spawn(proc()()) - } - - #[test] - fn test_try_panic_message_static_str() { - match try(proc() { - panic!("static string"); - }) { - Err(e) => { - type T = &'static str; - assert!(e.is::<T>()); - assert_eq!(*e.downcast::<T>().unwrap(), "static string"); - } - Ok(()) => panic!() - } - } - - #[test] - fn test_try_panic_message_owned_str() { - match try(proc() { - panic!("owned string".to_string()); - }) { - Err(e) => { - type T = String; - assert!(e.is::<T>()); - assert_eq!(*e.downcast::<T>().unwrap(), "owned string".to_string()); - } - Ok(()) => panic!() - } - } - - #[test] - fn test_try_panic_message_any() { - match try(proc() { - panic!(box 413u16 as Box<Any + Send>); - }) { - Err(e) => { - type T = Box<Any + Send>; - assert!(e.is::<T>()); - let any = e.downcast::<T>().unwrap(); - assert!(any.is::<u16>()); - assert_eq!(*any.downcast::<u16>().unwrap(), 413u16); - } - Ok(()) => panic!() - } - } - - #[test] - fn test_try_panic_message_unit_struct() { - struct Juju; - - match try(proc() { - panic!(Juju) - }) { - Err(ref e) if e.is::<Juju>() => {} - Err(_) | Ok(()) => panic!() - } - } - - #[test] - fn test_stdout() { - let (tx, rx) = channel(); - let mut reader = ChanReader::new(rx); - let stdout = ChanWriter::new(tx); - - let r = TaskBuilder::new().stdout(box stdout as Box<Writer + Send>) - .try(proc() { - print!("Hello, world!"); - }); - assert!(r.is_ok()); - - let output = reader.read_to_string().unwrap(); - assert_eq!(output, "Hello, world!".to_string()); - } - - // NOTE: the corresponding test for stderr is in run-pass/task-stderr, due - // to the test harness apparently interfering with stderr configuration. -} - -#[test] -fn task_abort_no_kill_runtime() { - use std::io::timer; - use time::Duration; - use mem; - - let tb = TaskBuilder::new(); - let rx = tb.try_future(proc() {}); - mem::drop(rx); - timer::sleep(Duration::milliseconds(1000)); + thread::Thread::yield_now() } diff --git a/src/libstd/thread.rs b/src/libstd/thread.rs new file mode 100644 index 00000000000..89773207347 --- /dev/null +++ b/src/libstd/thread.rs @@ -0,0 +1,654 @@ +// Copyright 2014 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. + +//! Native threads +//! +//! ## The threading model +//! +//! An executing Rust program consists of a collection of native OS threads, +//! each with their own stack and local state. +//! +//! Communication between threads can be done through +//! [channels](../../std/comm/index.html), Rust's message-passing +//! types, along with [other forms of thread +//! synchronization](../../std/sync/index.html) and shared-memory data +//! structures. In particular, types that are guaranteed to be +//! threadsafe are easily shared between threads using the +//! atomically-reference-counted container, +//! [`Arc`](../../std/sync/struct.Arc.html). +//! +//! Fatal logic errors in Rust cause *thread panic*, during which +//! a thread will unwind the stack, running destructors and freeing +//! owned resources. Thread panic is unrecoverable from within +//! the panicking thread (i.e. there is no 'try/catch' in Rust), but +//! panic may optionally be detected from a different thread. If +//! the main thread panics the application will exit with a non-zero +//! exit code. +//! +//! When the main thread of a Rust program terminates, the entire program shuts +//! down, even if other threads are still running. However, this module provides +//! convenient facilities for automatically waiting for the termination of a +//! child thread (i.e., join), described below. +//! +//! ## The `Thread` type +//! +//! Already-running threads are represented via the `Thread` type, which you can +//! get in one of two ways: +//! +//! * By spawning a new thread, e.g. using the `Thread::spawn` constructor; +//! * By requesting the current thread, using the `Thread::current` function. +//! +//! Threads can be named, and provide some built-in support for low-level +//! synchronization described below. +//! +//! The `Thread::current()` function is available even for threads not spawned +//! by the APIs of this module. +//! +//! ## Spawning a thread +//! +//! A new thread can be spawned using the `Thread::spawn` function: +//! +//! ```rust +//! use std::thread::Thread; +//! +//! let guard = Thread::spawn(move || { +//! println!("Hello, World!"); +//! // some computation here +//! }); +//! let result = guard.join(); +//! ``` +//! +//! The `spawn` function doesn't return a `Thread` directly; instead, it returns +//! a *join guard* from which a `Thread` can be extracted. The join guard is an +//! RAII-style guard that will automatically join the child thread (block until +//! it terminates) when it is dropped. You can join the child thread in advance +//! by calling the `join` method on the guard, which will also return the result +//! produced by the thread. +//! +//! If you instead wish to *detach* the child thread, allowing it to outlive its +//! parent, you can use the `detach` method on the guard, +//! +//! A handle to the thread itself is available via the `thread` method on the +//! join guard. +//! +//! ## Configuring threads +//! +//! A new thread can be configured before it is spawned via the `Builder` type, +//! which currently allows you to set the name, stack size, and writers for +//! `println!` and `panic!` for the child thread: +//! +//! ```rust +//! use std::thread; +//! +//! thread::Builder::new().name("child1".to_string()).spawn(move || { +//! println!("Hello, world!") +//! }).detach(); +//! ``` +//! +//! ## Blocking support: park and unpark +//! +//! Every thread is equipped with some basic low-level blocking support, via the +//! `park` and `unpark` functions. +//! +//! Conceptually, each `Thread` handle has an associated token, which is +//! initially not present: +//! +//! * The `Thread::park()` function blocks the current thread unless or until +//! the token is available for its thread handle, at which point It atomically +//! consumes the token. It may also return *spuriously*, without consuming the +//! token. +//! +//! * The `unpark()` method on a `Thread` atomically makes the token available +//! if it wasn't already. +//! +//! In other words, each `Thread` acts a bit like a semaphore with initial count +//! 0, except that the semaphore is *saturating* (the count cannot go above 1), +//! and can return spuriously. +//! +//! The API is typically used by acquiring a handle to the current thread, +//! placing that handle in a shared data structure so that other threads can +//! find it, and then `park`ing. When some desired condition is met, another +//! thread calls `unpark` on the handle. +//! +//! The motivation for this design is twofold: +//! +//! * It avoids the need to allocate mutexes and condvars when building new +//! synchronization primitives; the threads already provide basic blocking/signaling. +//! +//! * It can be implemented highly efficiently on many platforms. + +use any::Any; +use borrow::IntoCow; +use boxed::Box; +use cell::UnsafeCell; +use clone::Clone; +use kinds::Send; +use ops::{Drop, FnOnce}; +use option::Option::{mod, Some, None}; +use result::Result::{Err, Ok}; +use sync::{Mutex, Condvar, Arc}; +use str::Str; +use string::String; +use rt::{mod, unwind}; +use io::{Writer, stdio}; +use thunk::Thunk; + +use sys::thread as imp; +use sys_common::{stack, thread_info}; + +/// Thread configuation. Provides detailed control over the properties +/// and behavior of new threads. +pub struct Builder { + // A name for the thread-to-be, for identification in panic messages + name: Option<String>, + // The size of the stack for the spawned thread + stack_size: Option<uint>, + // Thread-local stdout + stdout: Option<Box<Writer + Send>>, + // Thread-local stderr + stderr: Option<Box<Writer + Send>>, +} + +impl Builder { + /// Generate the base configuration for spawning a thread, from which + /// configuration methods can be chained. + pub fn new() -> Builder { + Builder { + name: None, + stack_size: None, + stdout: None, + stderr: None, + } + } + + /// Name the thread-to-be. Currently the name is used for identification + /// only in panic messages. + pub fn name(mut self, name: String) -> Builder { + self.name = Some(name); + self + } + + /// Deprecated: use `name` instead + #[deprecated = "use name instead"] + pub fn named<T: IntoCow<'static, String, str>>(self, name: T) -> Builder { + self.name(name.into_cow().into_owned()) + } + + /// Set the size of the stack for the new thread. + pub fn stack_size(mut self, size: uint) -> Builder { + self.stack_size = Some(size); + self + } + + /// Redirect thread-local stdout. + #[experimental = "Will likely go away after proc removal"] + pub fn stdout(mut self, stdout: Box<Writer + Send>) -> Builder { + self.stdout = Some(stdout); + self + } + + /// Redirect thread-local stderr. + #[experimental = "Will likely go away after proc removal"] + pub fn stderr(mut self, stderr: Box<Writer + Send>) -> Builder { + self.stderr = Some(stderr); + self + } + + /// Spawn a new joinable thread, and return a JoinGuard guard for it. + /// + /// See `Thead::spawn` and the module doc for more details. + pub fn spawn<T, F>(self, f: F) -> JoinGuard<T> where + T: Send, F: FnOnce() -> T, F: Send + { + self.spawn_inner(Thunk::new(f)) + } + + fn spawn_inner<T: Send>(self, f: Thunk<(), T>) -> JoinGuard<T> { + let my_packet = Arc::new(UnsafeCell::new(None)); + let their_packet = my_packet.clone(); + + let Builder { name, stack_size, stdout, stderr } = self; + + let stack_size = stack_size.unwrap_or(rt::min_stack()); + let my_thread = Thread::new(name); + let their_thread = my_thread.clone(); + + // Spawning a new OS thread guarantees that __morestack will never get + // triggered, but we must manually set up the actual stack bounds once + // this function starts executing. This raises the lower limit by a bit + // because by the time that this function is executing we've already + // consumed at least a little bit of stack (we don't know the exact byte + // address at which our stack started). + let main = move |:| { + let something_around_the_top_of_the_stack = 1; + let addr = &something_around_the_top_of_the_stack as *const int; + let my_stack_top = addr as uint; + let my_stack_bottom = my_stack_top - stack_size + 1024; + unsafe { + stack::record_os_managed_stack_bounds(my_stack_bottom, my_stack_top); + } + thread_info::set( + (my_stack_bottom, my_stack_top), + unsafe { imp::guard::current() }, + their_thread + ); + + let mut output = None; + let f: Thunk<(), T> = if stdout.is_some() || stderr.is_some() { + Thunk::new(move |:| { + let _ = stdout.map(stdio::set_stdout); + let _ = stderr.map(stdio::set_stderr); + f.invoke(()) + }) + } else { + f + }; + + let try_result = { + let ptr = &mut output; + + // There are two primary reasons that general try/catch is + // unsafe. The first is that we do not support nested + // try/catch. The fact that this is happening in a newly-spawned + // thread suffices. The second is that unwinding while unwinding + // is not defined. We take care of that by having an + // 'unwinding' flag in the thread itself. For these reasons, + // this unsafety should be ok. + unsafe { + unwind::try(move || *ptr = Some(f.invoke(()))) + } + }; + unsafe { + *their_packet.get() = Some(match (output, try_result) { + (Some(data), Ok(_)) => Ok(data), + (None, Err(cause)) => Err(cause), + _ => unreachable!() + }); + } + }; + + JoinGuard { + native: unsafe { imp::create(stack_size, Thunk::new(main)) }, + joined: false, + packet: my_packet, + thread: my_thread, + } + } +} + +struct Inner { + name: Option<String>, + lock: Mutex<bool>, // true when there is a buffered unpark + cvar: Condvar, +} + +#[deriving(Clone)] +/// A handle to a thread. +pub struct Thread { + inner: Arc<Inner>, +} + +impl Thread { + // Used only internally to construct a thread object without spawning + fn new(name: Option<String>) -> Thread { + Thread { + inner: Arc::new(Inner { + name: name, + lock: Mutex::new(false), + cvar: Condvar::new(), + }) + } + } + + /// Spawn a new joinable thread, returning a `JoinGuard` for it. + /// + /// The join guard can be used to explicitly join the child thead (via + /// `join`), returning `Result<T>`, or it will implicitly join the child + /// upon being dropped. To detach the child, allowing it to outlive the + /// current thread, use `detach`. See the module documentation for additional details. + pub fn spawn<T, F>(f: F) -> JoinGuard<T> where + T: Send, F: FnOnce() -> T, F: Send + { + Builder::new().spawn(f) + } + + /// Gets a handle to the thread that invokes it. + pub fn current() -> Thread { + thread_info::current_thread() + } + + /// Cooperatively give up a timeslice to the OS scheduler. + pub fn yield_now() { + unsafe { imp::yield_now() } + } + + /// Determines whether the current thread is panicking. + pub fn panicking() -> bool { + unwind::panicking() + } + + /// Block unless or until the current thread's token is made available (may wake spuriously). + /// + /// See the module doc for more detail. + // + // The implementation currently uses the trivial strategy of a Mutex+Condvar + // with wakeup flag, which does not actually allow spurious wakeups. In the + // future, this will be implemented in a more efficient way, perhaps along the lines of + // http://cr.openjdk.java.net/~stefank/6989984.1/raw_files/new/src/os/linux/vm/os_linux.cpp + // or futuxes, and in either case may allow spurious wakeups. + pub fn park() { + let thread = Thread::current(); + let mut guard = thread.inner.lock.lock(); + while !*guard { + thread.inner.cvar.wait(&guard); + } + *guard = false; + } + + /// Atomically makes the handle's token available if it is not already. + /// + /// See the module doc for more detail. + pub fn unpark(&self) { + let mut guard = self.inner.lock.lock(); + if !*guard { + *guard = true; + self.inner.cvar.notify_one(); + } + } + + /// Get the thread's name. + pub fn name(&self) -> Option<&str> { + self.inner.name.as_ref().map(|s| s.as_slice()) + } +} + +// a hack to get around privacy restrictions +impl thread_info::NewThread for Thread { + fn new(name: Option<String>) -> Thread { Thread::new(name) } +} + +/// Indicates the manner in which a thread exited. +/// +/// A thread that completes without panicking is considered to exit successfully. +pub type Result<T> = ::result::Result<T, Box<Any + Send>>; + +#[must_use] +/// An RAII-style guard that will block until thread termination when dropped. +/// +/// The type `T` is the return type for the thread's main function. +pub struct JoinGuard<T> { + native: imp::rust_thread, + thread: Thread, + joined: bool, + packet: Arc<UnsafeCell<Option<Result<T>>>>, +} + +impl<T: Send> JoinGuard<T> { + /// Extract a handle to the thread this guard will join on. + pub fn thread(&self) -> &Thread { + &self.thread + } + + /// Wait for the associated thread to finish, returning the result of the thread's + /// calculation. + /// + /// If the child thread panics, `Err` is returned with the parameter given + /// to `panic`. + pub fn join(mut self) -> Result<T> { + assert!(!self.joined); + unsafe { imp::join(self.native) }; + self.joined = true; + unsafe { + (*self.packet.get()).take().unwrap() + } + } + + /// Detaches the child thread, allowing it to outlive its parent. + pub fn detach(mut self) { + unsafe { imp::detach(self.native) }; + self.joined = true; // avoid joining in the destructor + } +} + +#[unsafe_destructor] +impl<T: Send> Drop for JoinGuard<T> { + fn drop(&mut self) { + if !self.joined { + unsafe { imp::join(self.native) }; + } + } +} + +#[cfg(test)] +mod test { + use prelude::*; + use any::{Any, AnyRefExt}; + use boxed::BoxAny; + use result; + use std::io::{ChanReader, ChanWriter}; + use thunk::Thunk; + use super::{Thread, Builder}; + + // !!! These tests are dangerous. If something is buggy, they will hang, !!! + // !!! instead of exiting cleanly. This might wedge the buildbots. !!! + + #[test] + fn test_unnamed_thread() { + Thread::spawn(move|| { + assert!(Thread::current().name().is_none()); + }).join().map_err(|_| ()).unwrap(); + } + + #[test] + fn test_named_thread() { + Builder::new().name("ada lovelace".to_string()).spawn(move|| { + assert!(Thread::current().name().unwrap() == "ada lovelace".to_string()); + }).join().map_err(|_| ()).unwrap(); + } + + #[test] + fn test_run_basic() { + let (tx, rx) = channel(); + Thread::spawn(move|| { + tx.send(()); + }).detach(); + rx.recv(); + } + + #[test] + fn test_join_success() { + match Thread::spawn(move|| -> String { + "Success!".to_string() + }).join().as_ref().map(|s| s.as_slice()) { + result::Result::Ok("Success!") => (), + _ => panic!() + } + } + + #[test] + fn test_join_panic() { + match Thread::spawn(move|| { + panic!() + }).join() { + result::Result::Err(_) => (), + result::Result::Ok(()) => panic!() + } + } + + #[test] + fn test_spawn_sched() { + use clone::Clone; + + let (tx, rx) = channel(); + + fn f(i: int, tx: Sender<()>) { + let tx = tx.clone(); + Thread::spawn(move|| { + if i == 0 { + tx.send(()); + } else { + f(i - 1, tx); + } + }).detach(); + + } + f(10, tx); + rx.recv(); + } + + #[test] + fn test_spawn_sched_childs_on_default_sched() { + let (tx, rx) = channel(); + + Thread::spawn(move|| { + Thread::spawn(move|| { + tx.send(()); + }).detach(); + }).detach(); + + rx.recv(); + } + + fn avoid_copying_the_body<F>(spawnfn: F) where F: FnOnce(Thunk) { + let (tx, rx) = channel::<uint>(); + + let x = box 1; + let x_in_parent = (&*x) as *const int as uint; + + spawnfn(Thunk::new(move|| { + let x_in_child = (&*x) as *const int as uint; + tx.send(x_in_child); + })); + + let x_in_child = rx.recv(); + assert_eq!(x_in_parent, x_in_child); + } + + #[test] + fn test_avoid_copying_the_body_spawn() { + avoid_copying_the_body(|v| { + Thread::spawn(move || v.invoke(())).detach(); + }); + } + + #[test] + fn test_avoid_copying_the_body_thread_spawn() { + avoid_copying_the_body(|f| { + Thread::spawn(move|| { + f.invoke(()); + }).detach(); + }) + } + + #[test] + fn test_avoid_copying_the_body_join() { + avoid_copying_the_body(|f| { + let _ = Thread::spawn(move|| { + f.invoke(()) + }).join(); + }) + } + + #[test] + fn test_child_doesnt_ref_parent() { + // If the child refcounts the parent task, this will stack overflow when + // climbing the task tree to dereference each ancestor. (See #1789) + // (well, it would if the constant were 8000+ - I lowered it to be more + // valgrind-friendly. try this at home, instead..!) + static GENERATIONS: uint = 16; + fn child_no(x: uint) -> Thunk { + return Thunk::new(move|| { + if x < GENERATIONS { + Thread::spawn(move|| child_no(x+1).invoke(())).detach(); + } + }); + } + Thread::spawn(|| child_no(0).invoke(())).detach(); + } + + #[test] + fn test_simple_newsched_spawn() { + Thread::spawn(move || {}).detach(); + } + + #[test] + fn test_try_panic_message_static_str() { + match Thread::spawn(move|| { + panic!("static string"); + }).join() { + Err(e) => { + type T = &'static str; + assert!(e.is::<T>()); + assert_eq!(*e.downcast::<T>().unwrap(), "static string"); + } + Ok(()) => panic!() + } + } + + #[test] + fn test_try_panic_message_owned_str() { + match Thread::spawn(move|| { + panic!("owned string".to_string()); + }).join() { + Err(e) => { + type T = String; + assert!(e.is::<T>()); + assert_eq!(*e.downcast::<T>().unwrap(), "owned string".to_string()); + } + Ok(()) => panic!() + } + } + + #[test] + fn test_try_panic_message_any() { + match Thread::spawn(move|| { + panic!(box 413u16 as Box<Any + Send>); + }).join() { + Err(e) => { + type T = Box<Any + Send>; + assert!(e.is::<T>()); + let any = e.downcast::<T>().unwrap(); + assert!(any.is::<u16>()); + assert_eq!(*any.downcast::<u16>().unwrap(), 413u16); + } + Ok(()) => panic!() + } + } + + #[test] + fn test_try_panic_message_unit_struct() { + struct Juju; + + match Thread::spawn(move|| { + panic!(Juju) + }).join() { + Err(ref e) if e.is::<Juju>() => {} + Err(_) | Ok(()) => panic!() + } + } + + #[test] + fn test_stdout() { + let (tx, rx) = channel(); + let mut reader = ChanReader::new(rx); + let stdout = ChanWriter::new(tx); + + let r = Builder::new().stdout(box stdout as Box<Writer + Send>).spawn(move|| { + print!("Hello, world!"); + }).join(); + assert!(r.is_ok()); + + let output = reader.read_to_string().unwrap(); + assert_eq!(output, "Hello, world!".to_string()); + } + + // NOTE: the corresponding test for stderr is in run-pass/task-stderr, due + // to the test harness apparently interfering with stderr configuration. +} diff --git a/src/libstd/thread_local/mod.rs b/src/libstd/thread_local/mod.rs new file mode 100644 index 00000000000..04718dcc6ae --- /dev/null +++ b/src/libstd/thread_local/mod.rs @@ -0,0 +1,653 @@ +// Copyright 2014 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. + +//! Thread local storage +//! +//! This module provides an implementation of thread local storage for Rust +//! programs. Thread local storage is a method of storing data into a global +//! variable which each thread in the program will have its own copy of. +//! Threads do not share this data, so accesses do not need to be synchronized. +//! +//! At a high level, this module provides two variants of storage: +//! +//! * Owning thread local storage. This is a type of thread local key which +//! owns the value that it contains, and will destroy the value when the +//! thread exits. This variant is created with the `thread_local!` macro and +//! can contain any value which is `'static` (no borrowed pointers. +//! +//! * Scoped thread local storage. This type of key is used to store a reference +//! to a value into local storage temporarily for the scope of a function +//! call. There are no restrictions on what types of values can be placed +//! into this key. +//! +//! Both forms of thread local storage provide an accessor function, `with`, +//! which will yield a shared reference to the value to the specified +//! closure. Thread local keys only allow shared access to values as there is no +//! way to guarantee uniqueness if a mutable borrow was allowed. Most values +//! will want to make use of some form of **interior mutability** through the +//! `Cell` or `RefCell` types. + +#![macro_escape] +#![experimental] + +use prelude::*; + +use cell::UnsafeCell; + +// Sure wish we had macro hygiene, no? +#[doc(hidden)] pub use self::imp::Key as KeyInner; +#[doc(hidden)] pub use self::imp::destroy_value; +#[doc(hidden)] pub use sys_common::thread_local::INIT_INNER as OS_INIT_INNER; +#[doc(hidden)] pub use sys_common::thread_local::StaticKey as OsStaticKey; + +pub mod scoped; + +/// A thread local storage key which owns its contents. +/// +/// This key uses the fastest possible implementation available to it for the +/// target platform. It is instantiated with the `thread_local!` macro and the +/// primary method is the `with` method. +/// +/// The `with` method yields a reference to the contained value which cannot be +/// sent across tasks or escape the given closure. +/// +/// # Initialization and Destruction +/// +/// Initialization is dynamically performed on the first call to `with()` +/// within a thread, and values support destructors which will be run when a +/// thread exits. +/// +/// # Example +/// +/// ``` +/// use std::cell::RefCell; +/// use std::thread::Thread; +/// +/// thread_local!(static FOO: RefCell<uint> = RefCell::new(1)); +/// +/// FOO.with(|f| { +/// assert_eq!(*f.borrow(), 1); +/// *f.borrow_mut() = 2; +/// }); +/// +/// // each thread starts out with the initial value of 1 +/// Thread::spawn(move|| { +/// FOO.with(|f| { +/// assert_eq!(*f.borrow(), 1); +/// *f.borrow_mut() = 3; +/// }); +/// }).detach(); +/// +/// // we retain our original value of 2 despite the child thread +/// FOO.with(|f| { +/// assert_eq!(*f.borrow(), 2); +/// }); +/// ``` +pub struct Key<T> { + // The key itself may be tagged with #[thread_local], and this `Key` is + // stored as a `static`, and it's not valid for a static to reference the + // address of another thread_local static. For this reason we kinda wonkily + // work around this by generating a shim function which will give us the + // address of the inner TLS key at runtime. + // + // This is trivially devirtualizable by LLVM because we never store anything + // to this field and rustc can declare the `static` as constant as well. + #[doc(hidden)] + pub inner: fn() -> &'static KeyInner<UnsafeCell<Option<T>>>, + + // initialization routine to invoke to create a value + #[doc(hidden)] + pub init: fn() -> T, +} + +/// Declare a new thread local storage key of type `std::thread_local::Key`. +#[macro_export] +#[doc(hidden)] +macro_rules! thread_local { + (static $name:ident: $t:ty = $init:expr) => ( + static $name: ::std::thread_local::Key<$t> = { + use std::cell::UnsafeCell as __UnsafeCell; + use std::thread_local::KeyInner as __KeyInner; + use std::option::Option as __Option; + use std::option::Option::None as __None; + + __thread_local_inner!(static __KEY: __UnsafeCell<__Option<$t>> = { + __UnsafeCell { value: __None } + }); + fn __init() -> $t { $init } + fn __getit() -> &'static __KeyInner<__UnsafeCell<__Option<$t>>> { + &__KEY + } + ::std::thread_local::Key { inner: __getit, init: __init } + }; + ); + (pub static $name:ident: $t:ty = $init:expr) => ( + pub static $name: ::std::thread_local::Key<$t> = { + use std::cell::UnsafeCell as __UnsafeCell; + use std::thread_local::KeyInner as __KeyInner; + use std::option::Option as __Option; + use std::option::Option::None as __None; + + __thread_local_inner!(static __KEY: __UnsafeCell<__Option<$t>> = { + __UnsafeCell { value: __None } + }); + fn __init() -> $t { $init } + fn __getit() -> &'static __KeyInner<__UnsafeCell<__Option<$t>>> { + &__KEY + } + ::std::thread_local::Key { inner: __getit, init: __init } + }; + ); +} + +// Macro pain #4586: +// +// When cross compiling, rustc will load plugins and macros from the *host* +// platform before search for macros from the target platform. This is primarily +// done to detect, for example, plugins. Ideally the macro below would be +// defined once per module below, but unfortunately this means we have the +// following situation: +// +// 1. We compile libstd for x86_64-unknown-linux-gnu, this thread_local!() macro +// will inject #[thread_local] statics. +// 2. We then try to compile a program for arm-linux-androideabi +// 3. The compiler has a host of linux and a target of android, so it loads +// macros from the *linux* libstd. +// 4. The macro generates a #[thread_local] field, but the android libstd does +// not use #[thread_local] +// 5. Compile error about structs with wrong fields. +// +// To get around this, we're forced to inject the #[cfg] logic into the macro +// itself. Woohoo. + +#[macro_export] +macro_rules! __thread_local_inner { + (static $name:ident: $t:ty = $init:expr) => ( + #[cfg_attr(any(target_os = "macos", target_os = "linux"), thread_local)] + static $name: ::std::thread_local::KeyInner<$t> = + __thread_local_inner!($init, $t); + ); + (pub static $name:ident: $t:ty = $init:expr) => ( + #[cfg_attr(any(target_os = "macos", target_os = "linux"), thread_local)] + pub static $name: ::std::thread_local::KeyInner<$t> = + __thread_local_inner!($init, $t); + ); + ($init:expr, $t:ty) => ({ + #[cfg(any(target_os = "macos", target_os = "linux"))] + const INIT: ::std::thread_local::KeyInner<$t> = { + ::std::thread_local::KeyInner { + inner: ::std::cell::UnsafeCell { value: $init }, + dtor_registered: ::std::cell::UnsafeCell { value: false }, + dtor_running: ::std::cell::UnsafeCell { value: false }, + } + }; + + #[cfg(all(stage0, not(any(target_os = "macos", target_os = "linux"))))] + const INIT: ::std::thread_local::KeyInner<$t> = { + unsafe extern fn __destroy(ptr: *mut u8) { + ::std::thread_local::destroy_value::<$t>(ptr); + } + + ::std::thread_local::KeyInner { + inner: ::std::cell::UnsafeCell { value: $init }, + os: ::std::thread_local::OsStaticKey { + inner: ::std::thread_local::OS_INIT_INNER, + dtor: ::std::option::Option::Some(__destroy), + }, + } + }; + + #[cfg(all(not(stage0), not(any(target_os = "macos", target_os = "linux"))))] + const INIT: ::std::thread_local::KeyInner<$t> = { + unsafe extern fn __destroy(ptr: *mut u8) { + ::std::thread_local::destroy_value::<$t>(ptr); + } + + ::std::thread_local::KeyInner { + inner: ::std::cell::UnsafeCell { value: $init }, + os: ::std::thread_local::OsStaticKey { + inner: ::std::thread_local::OS_INIT_INNER, + dtor: ::std::option::Option::Some(__destroy as unsafe extern fn(*mut u8)), + }, + } + }; + + INIT + }); +} + +impl<T: 'static> Key<T> { + /// Acquire a reference to the value in this TLS key. + /// + /// This will lazily initialize the value if this thread has not referenced + /// this key yet. + /// + /// # Panics + /// + /// This function will `panic!()` if the key currently has its + /// destructor running, and it **may** panic if the destructor has + /// previously been run for this thread. + pub fn with<F, R>(&'static self, f: F) -> R + where F: FnOnce(&T) -> R { + let slot = (self.inner)(); + unsafe { + let slot = slot.get().expect("cannot access a TLS value during or \ + after it is destroyed"); + if (*slot.get()).is_none() { + *slot.get() = Some((self.init)()); + } + f((*slot.get()).as_ref().unwrap()) + } + } + + /// Test this TLS key to determine whether its value has been destroyed for + /// the current thread or not. + /// + /// This will not initialize the key if it is not already initialized. + pub fn destroyed(&'static self) -> bool { + unsafe { (self.inner)().get().is_none() } + } +} + +#[cfg(any(target_os = "macos", target_os = "linux"))] +mod imp { + use prelude::*; + + use cell::UnsafeCell; + use intrinsics; + use ptr; + + #[doc(hidden)] + pub struct Key<T> { + // Place the inner bits in an `UnsafeCell` to currently get around the + // "only Sync statics" restriction. This allows any type to be placed in + // the cell. + // + // Note that all access requires `T: 'static` so it can't be a type with + // any borrowed pointers still. + pub inner: UnsafeCell<T>, + + // Metadata to keep track of the state of the destructor. Remember that + // these variables are thread-local, not global. + pub dtor_registered: UnsafeCell<bool>, // should be Cell + pub dtor_running: UnsafeCell<bool>, // should be Cell + } + + #[doc(hidden)] + impl<T> Key<T> { + pub unsafe fn get(&'static self) -> Option<&'static T> { + if intrinsics::needs_drop::<T>() && *self.dtor_running.get() { + return None + } + self.register_dtor(); + Some(&*self.inner.get()) + } + + unsafe fn register_dtor(&self) { + if !intrinsics::needs_drop::<T>() || *self.dtor_registered.get() { + return + } + + register_dtor(self as *const _ as *mut u8, + destroy_value::<T>); + *self.dtor_registered.get() = true; + } + } + + // Since what appears to be glibc 2.18 this symbol has been shipped which + // GCC and clang both use to invoke destructors in thread_local globals, so + // let's do the same! + // + // Note, however, that we run on lots older linuxes, as well as cross + // compiling from a newer linux to an older linux, so we also have a + // fallback implementation to use as well. + // + // Due to rust-lang/rust#18804, make sure this is not generic! + #[cfg(target_os = "linux")] + unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { + use mem; + use libc; + use sys_common::thread_local as os; + + extern { + static __dso_handle: *mut u8; + #[linkage = "extern_weak"] + static __cxa_thread_atexit_impl: *const (); + } + if !__cxa_thread_atexit_impl.is_null() { + type F = unsafe extern fn(dtor: unsafe extern fn(*mut u8), + arg: *mut u8, + dso_handle: *mut u8) -> libc::c_int; + mem::transmute::<*const (), F>(__cxa_thread_atexit_impl) + (dtor, t, __dso_handle); + return + } + + // The fallback implementation uses a vanilla OS-based TLS key to track + // the list of destructors that need to be run for this thread. The key + // then has its own destructor which runs all the other destructors. + // + // The destructor for DTORS is a little special in that it has a `while` + // loop to continuously drain the list of registered destructors. It + // *should* be the case that this loop always terminates because we + // provide the guarantee that a TLS key cannot be set after it is + // flagged for destruction. + #[cfg(not(stage0))] + static DTORS: os::StaticKey = os::StaticKey { + inner: os::INIT_INNER, + dtor: Some(run_dtors as unsafe extern "C" fn(*mut u8)), + }; + #[cfg(stage0)] + static DTORS: os::StaticKey = os::StaticKey { + inner: os::INIT_INNER, + dtor: Some(run_dtors), + }; + type List = Vec<(*mut u8, unsafe extern fn(*mut u8))>; + if DTORS.get().is_null() { + let v: Box<List> = box Vec::new(); + DTORS.set(mem::transmute(v)); + } + let list: &mut List = &mut *(DTORS.get() as *mut List); + list.push((t, dtor)); + + unsafe extern fn run_dtors(mut ptr: *mut u8) { + while !ptr.is_null() { + let list: Box<List> = mem::transmute(ptr); + for &(ptr, dtor) in list.iter() { + dtor(ptr); + } + ptr = DTORS.get(); + DTORS.set(0 as *mut _); + } + } + } + + // OSX's analog of the above linux function is this _tlv_atexit function. + // The disassembly of thread_local globals in C++ (at least produced by + // clang) will have this show up in the output. + #[cfg(target_os = "macos")] + unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { + extern { + fn _tlv_atexit(dtor: unsafe extern fn(*mut u8), + arg: *mut u8); + } + _tlv_atexit(dtor, t); + } + + #[doc(hidden)] + pub unsafe extern fn destroy_value<T>(ptr: *mut u8) { + let ptr = ptr as *mut Key<T>; + // Right before we run the user destructor be sure to flag the + // destructor as running for this thread so calls to `get` will return + // `None`. + *(*ptr).dtor_running.get() = true; + ptr::read((*ptr).inner.get() as *const T); + } +} + +#[cfg(not(any(target_os = "macos", target_os = "linux")))] +mod imp { + use prelude::*; + + use cell::UnsafeCell; + use mem; + use sys_common::thread_local::StaticKey as OsStaticKey; + + #[doc(hidden)] + pub struct Key<T> { + // Statically allocated initialization expression, using an `UnsafeCell` + // for the same reasons as above. + pub inner: UnsafeCell<T>, + + // OS-TLS key that we'll use to key off. + pub os: OsStaticKey, + } + + struct Value<T: 'static> { + key: &'static Key<T>, + value: T, + } + + #[doc(hidden)] + impl<T> Key<T> { + pub unsafe fn get(&'static self) -> Option<&'static T> { + self.ptr().map(|p| &*p) + } + + unsafe fn ptr(&'static self) -> Option<*mut T> { + let ptr = self.os.get() as *mut Value<T>; + if !ptr.is_null() { + if ptr as uint == 1 { + return None + } + return Some(&mut (*ptr).value as *mut T); + } + + // If the lookup returned null, we haven't initialized our own local + // copy, so do that now. + // + // Also note that this transmute_copy should be ok because the value + // `inner` is already validated to be a valid `static` value, so we + // should be able to freely copy the bits. + let ptr: Box<Value<T>> = box Value { + key: self, + value: mem::transmute_copy(&self.inner), + }; + let ptr: *mut Value<T> = mem::transmute(ptr); + self.os.set(ptr as *mut u8); + Some(&mut (*ptr).value as *mut T) + } + } + + #[doc(hidden)] + pub unsafe extern fn destroy_value<T: 'static>(ptr: *mut u8) { + // The OS TLS ensures that this key contains a NULL value when this + // destructor starts to run. We set it back to a sentinel value of 1 to + // ensure that any future calls to `get` for this thread will return + // `None`. + // + // Note that to prevent an infinite loop we reset it back to null right + // before we return from the destructor ourselves. + let ptr: Box<Value<T>> = mem::transmute(ptr); + let key = ptr.key; + key.os.set(1 as *mut u8); + drop(ptr); + key.os.set(0 as *mut u8); + } +} + +#[cfg(test)] +mod tests { + use prelude::*; + + use cell::UnsafeCell; + use thread::Thread; + + struct Foo(Sender<()>); + + impl Drop for Foo { + fn drop(&mut self) { + let Foo(ref s) = *self; + s.send(()); + } + } + + #[test] + fn smoke_no_dtor() { + thread_local!(static FOO: UnsafeCell<int> = UnsafeCell { value: 1 }); + + FOO.with(|f| unsafe { + assert_eq!(*f.get(), 1); + *f.get() = 2; + }); + let (tx, rx) = channel(); + spawn(move|| { + FOO.with(|f| unsafe { + assert_eq!(*f.get(), 1); + }); + tx.send(()); + }); + rx.recv(); + + FOO.with(|f| unsafe { + assert_eq!(*f.get(), 2); + }); + } + + #[test] + fn smoke_dtor() { + thread_local!(static FOO: UnsafeCell<Option<Foo>> = UnsafeCell { + value: None + }); + + let (tx, rx) = channel(); + spawn(move|| unsafe { + let mut tx = Some(tx); + FOO.with(|f| { + *f.get() = Some(Foo(tx.take().unwrap())); + }); + }); + rx.recv(); + } + + #[test] + fn circular() { + struct S1; + struct S2; + thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell { + value: None + }); + thread_local!(static K2: UnsafeCell<Option<S2>> = UnsafeCell { + value: None + }); + static mut HITS: uint = 0; + + impl Drop for S1 { + fn drop(&mut self) { + unsafe { + HITS += 1; + if K2.destroyed() { + assert_eq!(HITS, 3); + } else { + if HITS == 1 { + K2.with(|s| *s.get() = Some(S2)); + } else { + assert_eq!(HITS, 3); + } + } + } + } + } + impl Drop for S2 { + fn drop(&mut self) { + unsafe { + HITS += 1; + assert!(!K1.destroyed()); + assert_eq!(HITS, 2); + K1.with(|s| *s.get() = Some(S1)); + } + } + } + + Thread::spawn(move|| { + drop(S1); + }).join(); + } + + #[test] + fn self_referential() { + struct S1; + thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell { + value: None + }); + + impl Drop for S1 { + fn drop(&mut self) { + assert!(K1.destroyed()); + } + } + + Thread::spawn(move|| unsafe { + K1.with(|s| *s.get() = Some(S1)); + }).join(); + } + + #[test] + fn dtors_in_dtors_in_dtors() { + struct S1(Sender<()>); + thread_local!(static K1: UnsafeCell<Option<S1>> = UnsafeCell { + value: None + }); + thread_local!(static K2: UnsafeCell<Option<Foo>> = UnsafeCell { + value: None + }); + + impl Drop for S1 { + fn drop(&mut self) { + let S1(ref tx) = *self; + unsafe { + if !K2.destroyed() { + K2.with(|s| *s.get() = Some(Foo(tx.clone()))); + } + } + } + } + + let (tx, rx) = channel(); + spawn(move|| unsafe { + let mut tx = Some(tx); + K1.with(|s| *s.get() = Some(S1(tx.take().unwrap()))); + }); + rx.recv(); + } +} + +#[cfg(test)] +mod dynamic_tests { + use prelude::*; + + use cell::RefCell; + use collections::HashMap; + + #[test] + fn smoke() { + fn square(i: int) -> int { i * i } + thread_local!(static FOO: int = square(3)); + + FOO.with(|f| { + assert_eq!(*f, 9); + }); + } + + #[test] + fn hashmap() { + fn map() -> RefCell<HashMap<int, int>> { + let mut m = HashMap::new(); + m.insert(1, 2); + RefCell::new(m) + } + thread_local!(static FOO: RefCell<HashMap<int, int>> = map()); + + FOO.with(|map| { + assert_eq!(map.borrow()[1], 2); + }); + } + + #[test] + fn refcell_vec() { + thread_local!(static FOO: RefCell<Vec<uint>> = RefCell::new(vec![1, 2, 3])); + + FOO.with(|vec| { + assert_eq!(vec.borrow().len(), 3); + vec.borrow_mut().push(4); + assert_eq!(vec.borrow()[3], 4); + }); + } +} diff --git a/src/libstd/thread_local/scoped.rs b/src/libstd/thread_local/scoped.rs new file mode 100644 index 00000000000..643a0f55e74 --- /dev/null +++ b/src/libstd/thread_local/scoped.rs @@ -0,0 +1,264 @@ +// Copyright 2014 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. + +//! Scoped thread-local storage +//! +//! This module provides the ability to generate *scoped* thread-local +//! variables. In this sense, scoped indicates that thread local storage +//! actually stores a reference to a value, and this reference is only placed +//! in storage for a scoped amount of time. +//! +//! There are no restrictions on what types can be placed into a scoped +//! variable, but all scoped variables are initialized to the equivalent of +//! null. Scoped thread local storage is useful when a value is present for a known +//! period of time and it is not required to relinquish ownership of the +//! contents. +//! +//! # Example +//! +//! ``` +//! scoped_thread_local!(static FOO: uint); +//! +//! // Initially each scoped slot is empty. +//! assert!(!FOO.is_set()); +//! +//! // When inserting a value, the value is only in place for the duration +//! // of the closure specified. +//! FOO.set(&1, || { +//! FOO.with(|slot| { +//! assert_eq!(*slot, 1); +//! }); +//! }); +//! ``` + +#![macro_escape] + +use prelude::*; + +// macro hygiene sure would be nice, wouldn't it? +#[doc(hidden)] pub use self::imp::KeyInner; +#[doc(hidden)] pub use sys_common::thread_local::INIT as OS_INIT; + +/// Type representing a thread local storage key corresponding to a reference +/// to the type parameter `T`. +/// +/// Keys are statically allocated and can contain a reference to an instance of +/// type `T` scoped to a particular lifetime. Keys provides two methods, `set` +/// and `with`, both of which currently use closures to control the scope of +/// their contents. +pub struct Key<T> { #[doc(hidden)] pub inner: KeyInner<T> } + +/// Declare a new scoped thread local storage key. +/// +/// This macro declares a `static` item on which methods are used to get and +/// set the value stored within. +#[macro_export] +macro_rules! scoped_thread_local { + (static $name:ident: $t:ty) => ( + __scoped_thread_local_inner!(static $name: $t) + ); + (pub static $name:ident: $t:ty) => ( + __scoped_thread_local_inner!(pub static $name: $t) + ); +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __scoped_thread_local_inner { + (static $name:ident: $t:ty) => ( + #[cfg_attr(not(any(windows, target_os = "android", target_os = "ios")), + thread_local)] + static $name: ::std::thread_local::scoped::Key<$t> = + __scoped_thread_local_inner!($t); + ); + (pub static $name:ident: $t:ty) => ( + #[cfg_attr(not(any(windows, target_os = "android", target_os = "ios")), + thread_local)] + pub static $name: ::std::thread_local::scoped::Key<$t> = + __scoped_thread_local_inner!($t); + ); + ($t:ty) => ({ + use std::thread_local::scoped::Key as __Key; + + #[cfg(not(any(windows, target_os = "android", target_os = "ios")))] + const INIT: __Key<$t> = __Key { + inner: ::std::thread_local::scoped::KeyInner { + inner: ::std::cell::UnsafeCell { value: 0 as *mut _ }, + } + }; + + #[cfg(any(windows, target_os = "android", target_os = "ios"))] + const INIT: __Key<$t> = __Key { + inner: ::std::thread_local::scoped::KeyInner { + inner: ::std::thread_local::scoped::OS_INIT, + marker: ::std::kinds::marker::InvariantType, + } + }; + + INIT + }) +} + +impl<T> Key<T> { + /// Insert a value into this scoped thread local storage slot for a + /// duration of a closure. + /// + /// While `cb` is running, the value `t` will be returned by `get` unless + /// this function is called recursively inside of `cb`. + /// + /// Upon return, this function will restore the previous value, if any + /// was available. + /// + /// # Example + /// + /// ``` + /// scoped_thread_local!(static FOO: uint); + /// + /// FOO.set(&100, || { + /// let val = FOO.with(|v| *v); + /// assert_eq!(val, 100); + /// + /// // set can be called recursively + /// FOO.set(&101, || { + /// // ... + /// }); + /// + /// // Recursive calls restore the previous value. + /// let val = FOO.with(|v| *v); + /// assert_eq!(val, 100); + /// }); + /// ``` + pub fn set<R, F>(&'static self, t: &T, cb: F) -> R where + F: FnOnce() -> R, + { + struct Reset<'a, T: 'a> { + key: &'a KeyInner<T>, + val: *mut T, + } + #[unsafe_destructor] + impl<'a, T> Drop for Reset<'a, T> { + fn drop(&mut self) { + unsafe { self.key.set(self.val) } + } + } + + let prev = unsafe { + let prev = self.inner.get(); + self.inner.set(t as *const T as *mut T); + prev + }; + + let _reset = Reset { key: &self.inner, val: prev }; + cb() + } + + /// Get a value out of this scoped variable. + /// + /// This function takes a closure which receives the value of this + /// variable. + /// + /// # Panics + /// + /// This function will panic if `set` has not previously been called. + /// + /// # Example + /// + /// ```no_run + /// scoped_thread_local!(static FOO: uint); + /// + /// FOO.with(|slot| { + /// // work with `slot` + /// }); + /// ``` + pub fn with<R, F>(&'static self, cb: F) -> R where + F: FnOnce(&T) -> R + { + unsafe { + let ptr = self.inner.get(); + assert!(!ptr.is_null(), "cannot access a scoped thread local \ + variable without calling `set` first"); + cb(&*ptr) + } + } + + /// Test whether this TLS key has been `set` for the current thread. + pub fn is_set(&'static self) -> bool { + unsafe { !self.inner.get().is_null() } + } +} + +#[cfg(not(any(windows, target_os = "android", target_os = "ios")))] +mod imp { + use std::cell::UnsafeCell; + + // FIXME: Should be a `Cell`, but that's not `Sync` + #[doc(hidden)] + pub struct KeyInner<T> { pub inner: UnsafeCell<*mut T> } + + #[doc(hidden)] + impl<T> KeyInner<T> { + #[doc(hidden)] + pub unsafe fn set(&self, ptr: *mut T) { *self.inner.get() = ptr; } + #[doc(hidden)] + pub unsafe fn get(&self) -> *mut T { *self.inner.get() } + } +} + +#[cfg(any(windows, target_os = "android", target_os = "ios"))] +mod imp { + use kinds::marker; + use sys_common::thread_local::StaticKey as OsStaticKey; + + #[doc(hidden)] + pub struct KeyInner<T> { + pub inner: OsStaticKey, + pub marker: marker::InvariantType<T>, + } + + #[doc(hidden)] + impl<T> KeyInner<T> { + #[doc(hidden)] + pub unsafe fn set(&self, ptr: *mut T) { self.inner.set(ptr as *mut _) } + #[doc(hidden)] + pub unsafe fn get(&self) -> *mut T { self.inner.get() as *mut _ } + } +} + + +#[cfg(test)] +mod tests { + use cell::Cell; + use prelude::*; + + #[test] + fn smoke() { + scoped_thread_local!(static BAR: uint); + + assert!(!BAR.is_set()); + BAR.set(&1, || { + assert!(BAR.is_set()); + BAR.with(|slot| { + assert_eq!(*slot, 1); + }); + }); + assert!(!BAR.is_set()); + } + + #[test] + fn cell_allowed() { + scoped_thread_local!(static BAR: Cell<uint>); + + BAR.set(&Cell::new(1), || { + BAR.with(|slot| { + assert_eq!(slot.get(), 1); + }); + }); + } +} diff --git a/src/libstd/thunk.rs b/src/libstd/thunk.rs new file mode 100644 index 00000000000..067926042f1 --- /dev/null +++ b/src/libstd/thunk.rs @@ -0,0 +1,55 @@ +// Copyright 2014 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. + +// Because this module is temporary... +#![allow(missing_docs)] + +use alloc::boxed::Box; +use core::kinds::Send; +use core::ops::FnOnce; + +pub struct Thunk<A=(),R=()> { + invoke: Box<Invoke<A,R>+Send> +} + +impl<R> Thunk<(),R> { + pub fn new<F>(func: F) -> Thunk<(),R> + where F : FnOnce() -> R, F : Send + { + Thunk::with_arg(move|: ()| func()) + } +} + +impl<A,R> Thunk<A,R> { + pub fn with_arg<F>(func: F) -> Thunk<A,R> + where F : FnOnce(A) -> R, F : Send + { + Thunk { + invoke: box func + } + } + + pub fn invoke(self, arg: A) -> R { + self.invoke.invoke(arg) + } +} + +pub trait Invoke<A=(),R=()> { + fn invoke(self: Box<Self>, arg: A) -> R; +} + +impl<A,R,F> Invoke<A,R> for F + where F : FnOnce(A) -> R +{ + fn invoke(self: Box<F>, arg: A) -> R { + let f = *self; + f(arg) + } +} diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index ec2d62ff85c..f7351c9580f 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -13,10 +13,12 @@ #![experimental] use {fmt, i64}; -use ops::{Add, Sub, Mul, Div, Neg}; -use option::{Option, Some, None}; +use ops::{Add, Sub, Mul, Div, Neg, FnOnce}; +use option::Option; +use option::Option::{Some, None}; use num::Int; -use result::{Result, Ok, Err}; +use result::Result; +use result::Result::{Ok, Err}; /// The number of nanoseconds in a microsecond. const NANOS_PER_MICRO: i32 = 1000; @@ -37,14 +39,14 @@ const SECS_PER_DAY: i64 = 86400; /// The number of (non-leap) seconds in a week. const SECS_PER_WEEK: i64 = 604800; -macro_rules! try_opt( +macro_rules! try_opt { ($e:expr) => (match $e { Some(v) => v, None => return None }) -) +} /// ISO 8601 time duration with nanosecond precision. /// This also allows for the negative duration; see individual methods for details. -#[deriving(Clone, PartialEq, Eq, PartialOrd, Ord)] +#[deriving(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct Duration { secs: i64, nanos: i32, // Always 0 <= nanos < NANOS_PER_SEC @@ -136,7 +138,7 @@ impl Duration { /// Runs a closure, returning the duration of time it took to run the /// closure. - pub fn span(f: ||) -> Duration { + pub fn span<F>(f: F) -> Duration where F: FnOnce() { let before = super::precise_time_ns(); f(); Duration::nanoseconds((super::precise_time_ns() - before) as i64) @@ -260,6 +262,8 @@ impl Duration { } } +// NOTE(stage0): Remove impl after a snapshot +#[cfg(stage0)] impl Neg<Duration> for Duration { #[inline] fn neg(&self) -> Duration { @@ -271,8 +275,20 @@ impl Neg<Duration> for Duration { } } -impl Add<Duration,Duration> for Duration { - fn add(&self, rhs: &Duration) -> Duration { +#[cfg(not(stage0))] // NOTE(stage0): Remove cfg after a snapshot +impl Neg<Duration> for Duration { + #[inline] + fn neg(self) -> Duration { + if self.nanos == 0 { + Duration { secs: -self.secs, nanos: 0 } + } else { + Duration { secs: -self.secs - 1, nanos: NANOS_PER_SEC - self.nanos } + } + } +} + +impl Add<Duration, Duration> for Duration { + fn add(self, rhs: Duration) -> Duration { let mut secs = self.secs + rhs.secs; let mut nanos = self.nanos + rhs.nanos; if nanos >= NANOS_PER_SEC { @@ -283,8 +299,8 @@ impl Add<Duration,Duration> for Duration { } } -impl Sub<Duration,Duration> for Duration { - fn sub(&self, rhs: &Duration) -> Duration { +impl Sub<Duration, Duration> for Duration { + fn sub(self, rhs: Duration) -> Duration { let mut secs = self.secs - rhs.secs; let mut nanos = self.nanos - rhs.nanos; if nanos < 0 { @@ -295,22 +311,22 @@ impl Sub<Duration,Duration> for Duration { } } -impl Mul<i32,Duration> for Duration { - fn mul(&self, rhs: &i32) -> Duration { +impl Mul<i32, Duration> for Duration { + fn mul(self, rhs: i32) -> Duration { // Multiply nanoseconds as i64, because it cannot overflow that way. - let total_nanos = self.nanos as i64 * *rhs as i64; + let total_nanos = self.nanos as i64 * rhs as i64; let (extra_secs, nanos) = div_mod_floor_64(total_nanos, NANOS_PER_SEC as i64); - let secs = self.secs * *rhs as i64 + extra_secs; + let secs = self.secs * rhs as i64 + extra_secs; Duration { secs: secs, nanos: nanos as i32 } } } -impl Div<i32,Duration> for Duration { - fn div(&self, rhs: &i32) -> Duration { - let mut secs = self.secs / *rhs as i64; - let carry = self.secs - secs * *rhs as i64; - let extra_nanos = carry * NANOS_PER_SEC as i64 / *rhs as i64; - let mut nanos = self.nanos / *rhs + extra_nanos as i32; +impl Div<i32, Duration> for Duration { + fn div(self, rhs: i32) -> Duration { + let mut secs = self.secs / rhs as i64; + let carry = self.secs - secs * rhs as i64; + let extra_nanos = carry * NANOS_PER_SEC as i64 / rhs as i64; + let mut nanos = self.nanos / rhs + extra_nanos as i32; if nanos >= NANOS_PER_SEC { nanos -= NANOS_PER_SEC; secs += 1; @@ -387,7 +403,7 @@ fn div_rem_64(this: i64, other: i64) -> (i64, i64) { mod tests { use super::{Duration, MIN, MAX}; use {i32, i64}; - use option::{Some, None}; + use option::Option::{Some, None}; use string::ToString; #[test] @@ -538,20 +554,20 @@ mod tests { #[test] fn test_duration_fmt() { - assert_eq!(Duration::zero().to_string(), "PT0S".to_string()); - assert_eq!(Duration::days(42).to_string(), "P42D".to_string()); - assert_eq!(Duration::days(-42).to_string(), "-P42D".to_string()); - assert_eq!(Duration::seconds(42).to_string(), "PT42S".to_string()); - assert_eq!(Duration::milliseconds(42).to_string(), "PT0.042S".to_string()); - assert_eq!(Duration::microseconds(42).to_string(), "PT0.000042S".to_string()); - assert_eq!(Duration::nanoseconds(42).to_string(), "PT0.000000042S".to_string()); + assert_eq!(Duration::zero().to_string(), "PT0S"); + assert_eq!(Duration::days(42).to_string(), "P42D"); + assert_eq!(Duration::days(-42).to_string(), "-P42D"); + assert_eq!(Duration::seconds(42).to_string(), "PT42S"); + assert_eq!(Duration::milliseconds(42).to_string(), "PT0.042S"); + assert_eq!(Duration::microseconds(42).to_string(), "PT0.000042S"); + assert_eq!(Duration::nanoseconds(42).to_string(), "PT0.000000042S"); assert_eq!((Duration::days(7) + Duration::milliseconds(6543)).to_string(), - "P7DT6.543S".to_string()); - assert_eq!(Duration::seconds(-86401).to_string(), "-P1DT1S".to_string()); - assert_eq!(Duration::nanoseconds(-1).to_string(), "-PT0.000000001S".to_string()); + "P7DT6.543S"); + assert_eq!(Duration::seconds(-86401).to_string(), "-P1DT1S"); + assert_eq!(Duration::nanoseconds(-1).to_string(), "-PT0.000000001S"); // the format specifier should have no effect on `Duration` assert_eq!(format!("{:30}", Duration::days(1) + Duration::milliseconds(2345)), - "P1DT2.345S".to_string()); + "P1DT2.345S"); } } diff --git a/src/libstd/tuple.rs b/src/libstd/tuple.rs new file mode 100644 index 00000000000..5cd60d6e153 --- /dev/null +++ b/src/libstd/tuple.rs @@ -0,0 +1,66 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Operations on tuples +//! +//! To access a single element of a tuple one can use the following +//! methods: +//! +//! * `valN` - returns a value of _N_-th element +//! * `refN` - returns a reference to _N_-th element +//! * `mutN` - returns a mutable reference to _N_-th element +//! +//! Indexing starts from zero, so `val0` returns first value, `val1` +//! returns second value, and so on. In general, a tuple with _S_ +//! elements provides aforementioned methods suffixed with numbers +//! from `0` to `S-1`. Traits which contain these methods are +//! implemented for tuples with up to 12 elements. +//! +//! If every type inside a tuple implements one of the following +//! traits, then a tuple itself also implements it. +//! +//! * `Clone` +//! * `PartialEq` +//! * `Eq` +//! * `PartialOrd` +//! * `Ord` +//! * `Default` +//! +//! # Examples +//! +//! Using methods: +//! +//! ``` +//! #[allow(deprecated)] +//! # fn main() { +//! let pair = ("pi", 3.14f64); +//! assert_eq!(pair.val0(), "pi"); +//! assert_eq!(pair.val1(), 3.14f64); +//! # } +//! ``` +//! +//! Using traits implemented for tuples: +//! +//! ``` +//! use std::default::Default; +//! +//! let a = (1i, 2i); +//! let b = (3i, 4i); +//! assert!(a != b); +//! +//! let c = b.clone(); +//! assert!(b == c); +//! +//! let d : (u32, f32) = Default::default(); +//! assert_eq!(d, (0u32, 0.0f32)); +//! ``` + +#![doc(primitive = "tuple")] +#![stable] diff --git a/src/libstd/unit.rs b/src/libstd/unit.rs new file mode 100644 index 00000000000..012b175b031 --- /dev/null +++ b/src/libstd/unit.rs @@ -0,0 +1,45 @@ +// Copyright 2014 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. + +#![doc(primitive = "unit")] +#![stable] + +//! The `()` type, sometimes called "unit" or "nil". +//! +//! The `()` type has exactly one value `()`, and is used when there +//! is no other meaningful value that could be returned. `()` is most +//! commonly seen implicitly: functions without a `-> ...` implicitly +//! have return type `()`, that is, these are equivalent: +//! +//! ```rust +//! fn long() -> () {} +//! +//! fn short() {} +//! ``` +//! +//! The semicolon `;` can be used to discard the result of an +//! expression at the end of a block, making the expression (and thus +//! the block) evaluate to `()`. For example, +//! +//! ```rust +//! fn returns_i64() -> i64 { +//! 1i64 +//! } +//! fn returns_unit() { +//! 1i64; +//! } +//! +//! let is_i64 = { +//! returns_i64() +//! }; +//! let is_unit = { +//! returns_i64(); +//! }; +//! ``` |
