diff options
Diffstat (limited to 'src/libstd')
108 files changed, 3571 insertions, 3751 deletions
diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index 29bd28b6160..eded6e24f3e 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -8,17 +8,19 @@ build = "build.rs" name = "std" path = "lib.rs" crate-type = ["dylib", "rlib"] -test = false [dependencies] alloc = { path = "../liballoc" } alloc_jemalloc = { path = "../liballoc_jemalloc", optional = true } alloc_system = { path = "../liballoc_system" } +panic_unwind = { path = "../libpanic_unwind" } +panic_abort = { path = "../libpanic_abort" } collections = { path = "../libcollections" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } rand = { path = "../librand" } rustc_unicode = { path = "../librustc_unicode" } +unwind = { path = "../libunwind" } [build-dependencies] build_helper = { path = "../build_helper" } diff --git a/src/libstd/build.rs b/src/libstd/build.rs index c32bca82bd5..9c408366f8b 100644 --- a/src/libstd/build.rs +++ b/src/libstd/build.rs @@ -8,11 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![deny(warnings)] + extern crate gcc; extern crate build_helper; use std::env; -use std::fs; use std::path::PathBuf; use std::process::Command; @@ -20,6 +21,7 @@ use build_helper::run; fn main() { println!("cargo:rustc-cfg=cargobuild"); + println!("cargo:rerun-if-changed=build.rs"); let target = env::var("TARGET").unwrap(); let host = env::var("HOST").unwrap(); @@ -28,9 +30,7 @@ fn main() { } if target.contains("linux") { - if target.contains("musl") && (target.contains("x86_64") || target.contains("i686")) { - println!("cargo:rustc-link-lib=static=unwind"); - } else if target.contains("android") { + if target.contains("android") { println!("cargo:rustc-link-lib=dl"); println!("cargo:rustc-link-lib=log"); println!("cargo:rustc-link-lib=gcc"); @@ -38,27 +38,13 @@ fn main() { println!("cargo:rustc-link-lib=dl"); println!("cargo:rustc-link-lib=rt"); println!("cargo:rustc-link-lib=pthread"); - println!("cargo:rustc-link-lib=gcc_s"); } } else if target.contains("freebsd") { println!("cargo:rustc-link-lib=execinfo"); println!("cargo:rustc-link-lib=pthread"); - println!("cargo:rustc-link-lib=gcc_s"); } else if target.contains("dragonfly") || target.contains("bitrig") || target.contains("netbsd") || target.contains("openbsd") { println!("cargo:rustc-link-lib=pthread"); - - if target.contains("rumprun") { - println!("cargo:rustc-link-lib=unwind"); - } else if target.contains("netbsd") { - println!("cargo:rustc-link-lib=gcc_s"); - } else if target.contains("openbsd") { - println!("cargo:rustc-link-lib=gcc"); - } else if target.contains("bitrig") { - println!("cargo:rustc-link-lib=c++abi"); - } else if target.contains("dragonfly") { - println!("cargo:rustc-link-lib=gcc_pic"); - } } else if target.contains("apple-darwin") { println!("cargo:rustc-link-lib=System"); } else if target.contains("apple-ios") { @@ -67,9 +53,6 @@ fn main() { println!("cargo:rustc-link-lib=framework=Security"); println!("cargo:rustc-link-lib=framework=Foundation"); } else if target.contains("windows") { - if target.contains("windows-gnu") { - println!("cargo:rustc-link-lib=gcc_eh"); - } println!("cargo:rustc-link-lib=advapi32"); println!("cargo:rustc-link-lib=ws2_32"); println!("cargo:rustc-link-lib=userenv"); @@ -84,12 +67,21 @@ fn build_libbacktrace(host: &str, target: &str) { println!("cargo:rustc-link-lib=static=backtrace"); println!("cargo:rustc-link-search=native={}/.libs", build_dir.display()); - if fs::metadata(&build_dir.join(".libs/libbacktrace.a")).is_ok() { - return + let mut stack = src_dir.read_dir().unwrap() + .map(|e| e.unwrap()) + .collect::<Vec<_>>(); + while let Some(entry) = stack.pop() { + let path = entry.path(); + if entry.file_type().unwrap().is_dir() { + stack.extend(path.read_dir().unwrap().map(|e| e.unwrap())); + } else { + println!("cargo:rerun-if-changed={}", path.display()); + } } let compiler = gcc::Config::new().get_compiler(); - let ar = build_helper::cc2ar(compiler.path(), target); + // only msvc returns None for ar so unwrap is okay + let ar = build_helper::cc2ar(compiler.path(), target).unwrap(); let cflags = compiler.args().iter().map(|s| s.to_str().unwrap()) .collect::<Vec<_>>().join(" "); run(Command::new("sh") diff --git a/src/libstd/collections/hash/bench.rs b/src/libstd/collections/hash/bench.rs index 9fae9af2d54..a1275d23d57 100644 --- a/src/libstd/collections/hash/bench.rs +++ b/src/libstd/collections/hash/bench.rs @@ -11,7 +11,6 @@ #![cfg(test)] extern crate test; -use prelude::v1::*; use self::test::Bencher; diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index abb33b33f3f..fd7b0a2e6bb 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -14,7 +14,7 @@ use self::VacantEntryState::*; use borrow::Borrow; use cmp::max; use fmt::{self, Debug}; -use hash::{Hash, SipHasher, BuildHasher}; +use hash::{Hash, Hasher, BuildHasher, SipHasher13}; use iter::FromIterator; use mem::{self, replace}; use ops::{Deref, Index}; @@ -202,8 +202,10 @@ fn test_resize_policy() { /// The hashes are all keyed by the thread-local random number generator /// on creation by default. This means that the ordering of the keys is /// randomized, but makes the tables more resistant to -/// denial-of-service attacks (Hash DoS). This behavior can be -/// overridden with one of the constructors. +/// denial-of-service attacks (Hash DoS). No guarantees are made to the +/// quality of the random data. The implementation uses the best available +/// random data from your platform at the time of creation. This behavior +/// can be overridden with one of the constructors. /// /// It is required that the keys implement the `Eq` and `Hash` traits, although /// this can frequently be achieved by using `#[derive(PartialEq, Eq, Hash)]`. @@ -862,7 +864,6 @@ impl<K, V, S> HashMap<K, V, S> /// # Examples /// /// ``` - /// # #![feature(map_values_mut)] /// use std::collections::HashMap; /// /// let mut map = HashMap::new(); @@ -876,11 +877,11 @@ impl<K, V, S> HashMap<K, V, S> /// } /// /// for val in map.values() { - /// print!("{}", val); + /// println!("{}", val); /// } /// ``` - #[unstable(feature = "map_values_mut", reason = "recently added", issue = "32551")] - pub fn values_mut<'a>(&'a mut self) -> ValuesMut<K, V> { + #[stable(feature = "map_values_mut", since = "1.10.0")] + pub fn values_mut(&mut self) -> ValuesMut<K, V> { ValuesMut { inner: self.iter_mut() } } @@ -1286,7 +1287,7 @@ pub struct Drain<'a, K: 'a, V: 'a> { } /// Mutable HashMap values iterator. -#[unstable(feature = "map_values_mut", reason = "recently added", issue = "32551")] +#[stable(feature = "map_values_mut", since = "1.10.0")] pub struct ValuesMut<'a, K: 'a, V: 'a> { inner: IterMut<'a, K, V> } @@ -1335,6 +1336,10 @@ impl<'a, K, V> InternalEntry<K, V, &'a mut RawTable<K, V>> { } /// A view into a single location in a map, which may be vacant or occupied. +/// This enum is constructed from the [`entry`] method on [`HashMap`]. +/// +/// [`HashMap`]: struct.HashMap.html +/// [`entry`]: struct.HashMap.html#method.entry #[stable(feature = "rust1", since = "1.0.0")] pub enum Entry<'a, K: 'a, V: 'a> { /// An occupied Entry. @@ -1350,14 +1355,44 @@ pub enum Entry<'a, K: 'a, V: 'a> { ), } +#[stable(feature= "debug_hash_map", since = "1.12.0")] +impl<'a, K: 'a + Debug, V: 'a + Debug> Debug for Entry<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Vacant(ref v) => f.debug_tuple("Entry") + .field(v) + .finish(), + Occupied(ref o) => f.debug_tuple("Entry") + .field(o) + .finish(), + } + } +} + /// A view into a single occupied location in a HashMap. +/// It is part of the [`Entry`] enum. +/// +/// [`Entry`]: enum.Entry.html #[stable(feature = "rust1", since = "1.0.0")] pub struct OccupiedEntry<'a, K: 'a, V: 'a> { key: Option<K>, elem: FullBucket<K, V, &'a mut RawTable<K, V>>, } +#[stable(feature= "debug_hash_map", since = "1.12.0")] +impl<'a, K: 'a + Debug, V: 'a + Debug> Debug for OccupiedEntry<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("OccupiedEntry") + .field("key", self.key()) + .field("value", self.get()) + .finish() + } +} + /// A view into a single empty location in a HashMap. +/// It is part of the [`Entry`] enum. +/// +/// [`Entry`]: enum.Entry.html #[stable(feature = "rust1", since = "1.0.0")] pub struct VacantEntry<'a, K: 'a, V: 'a> { hash: SafeHash, @@ -1365,6 +1400,15 @@ pub struct VacantEntry<'a, K: 'a, V: 'a> { elem: VacantEntryState<K, V, &'a mut RawTable<K, V>>, } +#[stable(feature= "debug_hash_map", since = "1.12.0")] +impl<'a, K: 'a + Debug, V: 'a> Debug for VacantEntry<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("VacantEntry") + .field(self.key()) + .finish() + } +} + /// Possible states of a VacantEntry. enum VacantEntryState<K, V, M> { /// The index is occupied, but the key to insert has precedence, @@ -1489,14 +1533,14 @@ impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> { #[inline] fn len(&self) -> usize { self.inner.len() } } -#[unstable(feature = "map_values_mut", reason = "recently added", issue = "32551")] +#[stable(feature = "map_values_mut", since = "1.10.0")] impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { type Item = &'a mut V; #[inline] fn next(&mut self) -> Option<(&'a mut V)> { self.inner.next().map(|(_, v)| v) } #[inline] fn size_hint(&self) -> (usize, Option<usize>) { self.inner.size_hint() } } -#[unstable(feature = "map_values_mut", reason = "recently added", issue = "32551")] +#[stable(feature = "map_values_mut", since = "1.10.0")] impl<'a, K, V> ExactSizeIterator for ValuesMut<'a, K, V> { #[inline] fn len(&self) -> usize { self.inner.len() } } @@ -1517,6 +1561,20 @@ impl<'a, K, V> Entry<'a, K, V> { #[stable(feature = "rust1", since = "1.0.0")] /// Ensures a value is in the entry by inserting the default if empty, and returns /// a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// + /// *map.entry("poneyland").or_insert(12) += 10; + /// assert_eq!(map["poneyland"], 22); + /// ``` pub fn or_insert(self, default: V) -> &'a mut V { match self { Occupied(entry) => entry.into_mut(), @@ -1527,6 +1585,19 @@ impl<'a, K, V> Entry<'a, K, V> { #[stable(feature = "rust1", since = "1.0.0")] /// Ensures a value is in the entry by inserting the result of the default function if empty, /// and returns a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, String> = HashMap::new(); + /// let s = "hoho".to_owned(); + /// + /// map.entry("poneyland").or_insert_with(|| s); + /// + /// assert_eq!(map["poneyland"], "hoho".to_owned()); + /// ``` pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V { match self { Occupied(entry) => entry.into_mut(), @@ -1535,7 +1606,16 @@ impl<'a, K, V> Entry<'a, K, V> { } /// Returns a reference to this entry's key. - #[unstable(feature = "map_entry_keys", issue = "32281")] + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` + #[stable(feature = "map_entry_keys", since = "1.10.0")] pub fn key(&self) -> &K { match *self { Occupied(ref entry) => entry.key(), @@ -1546,31 +1626,130 @@ impl<'a, K, V> Entry<'a, K, V> { impl<'a, K, V> OccupiedEntry<'a, K, V> { /// Gets a reference to the key in the entry. - #[unstable(feature = "map_entry_keys", issue = "32281")] + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` + #[stable(feature = "map_entry_keys", since = "1.10.0")] pub fn key(&self) -> &K { self.elem.read().0 } + /// Take the ownership of the key and value from the map. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_entry_recover_keys)] + /// + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// // We delete the entry from the map. + /// o.remove_pair(); + /// } + /// + /// assert_eq!(map.contains_key("poneyland"), false); + /// ``` + #[unstable(feature = "map_entry_recover_keys", issue = "34285")] + pub fn remove_pair(self) -> (K, V) { + pop_internal(self.elem) + } + /// Gets a reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// assert_eq!(o.get(), &12); + /// } + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn get(&self) -> &V { self.elem.read().1 } /// Gets a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// if let Entry::Occupied(mut o) = map.entry("poneyland") { + /// *o.get_mut() += 10; + /// } + /// + /// assert_eq!(map["poneyland"], 22); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn get_mut(&mut self) -> &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 + /// with a lifetime bound to the map itself. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// *o.into_mut() += 10; + /// } + /// + /// assert_eq!(map["poneyland"], 22); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn into_mut(self) -> &'a mut V { self.elem.into_mut_refs().1 } - /// Sets the value of the entry, and returns the entry's old value + /// Sets the value of the entry, and returns the entry's old value. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(mut o) = map.entry("poneyland") { + /// assert_eq!(o.insert(15), 12); + /// } + /// + /// assert_eq!(map["poneyland"], 15); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn insert(&mut self, mut value: V) -> V { let old_value = self.get_mut(); @@ -1578,11 +1757,28 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { value } - /// Takes the value out of the entry, and returns it + /// Takes the value out of the entry, and returns it. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// assert_eq!(o.remove(), 12); + /// } + /// + /// assert_eq!(map.contains_key("poneyland"), false); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn remove(self) -> V { pop_internal(self.elem).1 } + /// Returns a key that was used for search. /// /// The key was retained for further use. @@ -1593,14 +1789,58 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { /// Gets a reference to the key that would be used when inserting a value - /// through the VacantEntry. - #[unstable(feature = "map_entry_keys", issue = "32281")] + /// through the `VacantEntry`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` + #[stable(feature = "map_entry_keys", since = "1.10.0")] pub fn key(&self) -> &K { &self.key } + /// Take ownership of the key. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_entry_recover_keys)] + /// + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// if let Entry::Vacant(v) = map.entry("poneyland") { + /// v.into_key(); + /// } + /// ``` + #[unstable(feature = "map_entry_recover_keys", issue = "34285")] + pub fn into_key(self) -> K { + self.key + } + /// Sets the value of the entry with the VacantEntry's key, - /// and returns a mutable reference to it + /// and returns a mutable reference to it. + /// + /// # Examples + /// + /// ``` + /// use std::collections::HashMap; + /// use std::collections::hash_map::Entry; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// + /// if let Entry::Vacant(o) = map.entry("poneyland") { + /// o.insert(37); + /// } + /// assert_eq!(map["poneyland"], 37); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn insert(self, value: V) -> &'a mut V { match self.elem { @@ -1652,6 +1892,17 @@ impl<'a, K, V, S> Extend<(&'a K, &'a V)> for HashMap<K, V, S> /// A particular instance `RandomState` will create the same instances of /// `Hasher`, but the hashers created by two different `RandomState` /// instances are unlikely to produce the same result for the same values. +/// +/// # Examples +/// +/// ``` +/// use std::collections::HashMap; +/// use std::collections::hash_map::RandomState; +/// +/// let s = RandomState::new(); +/// let mut map = HashMap::with_hasher(s); +/// map.insert(1, 2); +/// ``` #[derive(Clone)] #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub struct RandomState { @@ -1661,21 +1912,74 @@ pub struct RandomState { impl RandomState { /// Constructs a new `RandomState` that is initialized with random keys. + /// + /// # Examples + /// + /// ``` + /// use std::collections::hash_map::RandomState; + /// + /// let s = RandomState::new(); + /// ``` #[inline] #[allow(deprecated)] // rand #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] pub fn new() -> RandomState { - let mut r = rand::thread_rng(); - RandomState { k0: r.gen(), k1: r.gen() } + // Historically this function did not cache keys from the OS and instead + // simply always called `rand::thread_rng().gen()` twice. In #31356 it + // was discovered, however, that because we re-seed the thread-local RNG + // from the OS periodically that this can cause excessive slowdown when + // many hash maps are created on a thread. To solve this performance + // trap we cache the first set of randomly generated keys per-thread. + // + // In doing this, however, we lose the property that all hash maps have + // nondeterministic iteration order as all of those created on the same + // thread would have the same hash keys. This property has been nice in + // the past as it allows for maximal flexibility in the implementation + // of `HashMap` itself. + // + // The constraint here (if there even is one) is just that maps created + // on the same thread have the same iteration order, and that *may* be + // relied upon even though it is not a documented guarantee at all of + // the `HashMap` type. In any case we've decided that this is reasonable + // for now, so caching keys thread-locally seems fine. + thread_local!(static KEYS: (u64, u64) = { + let r = rand::OsRng::new(); + let mut r = r.expect("failed to create an OS RNG"); + (r.gen(), r.gen()) + }); + + KEYS.with(|&(k0, k1)| { + RandomState { k0: k0, k1: k1 } + }) } } #[stable(feature = "hashmap_build_hasher", since = "1.7.0")] impl BuildHasher for RandomState { - type Hasher = SipHasher; + type Hasher = DefaultHasher; + #[inline] + fn build_hasher(&self) -> DefaultHasher { + DefaultHasher(SipHasher13::new_with_keys(self.k0, self.k1)) + } +} + +/// The default `Hasher` used by `RandomState`. +/// +/// The internal algorithm is not specified, and so it and its hashes should +/// not be relied upon over releases. +#[unstable(feature = "hashmap_default_hasher", issue = "0")] +pub struct DefaultHasher(SipHasher13); + +#[unstable(feature = "hashmap_default_hasher", issue = "0")] +impl Hasher for DefaultHasher { + #[inline] + fn write(&mut self, msg: &[u8]) { + self.0.write(msg) + } + #[inline] - fn build_hasher(&self) -> SipHasher { - SipHasher::new_with_keys(self.k0, self.k1) + fn finish(&self) -> u64 { + self.0.finish() } } diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index cf64e5d3336..c4c4cb45313 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -8,10 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use alloc::heap::{allocate, deallocate, EMPTY}; +use alloc::heap::{EMPTY, allocate, deallocate}; use cmp; -use hash::{Hash, Hasher, BuildHasher}; +use hash::{BuildHasher, Hash, Hasher}; use intrinsics::needs_drop; use marker; use mem::{align_of, size_of}; @@ -62,12 +62,12 @@ const EMPTY_BUCKET: u64 = 0; #[unsafe_no_drop_flag] pub struct RawTable<K, V> { capacity: usize, - size: usize, - hashes: Unique<u64>, + size: usize, + hashes: Unique<u64>, // Because K/V do not appear directly in any of the types in the struct, // inform rustc that in fact instances of K and V are reachable from here. - marker: marker::PhantomData<(K,V)>, + marker: marker::PhantomData<(K, V)>, } unsafe impl<K: Send, V: Send> Send for RawTable<K, V> {} @@ -77,44 +77,48 @@ struct RawBucket<K, V> { hash: *mut u64, // We use *const to ensure covariance with respect to K and V - key: *const K, - val: *const V, - _marker: marker::PhantomData<(K,V)>, + key: *const K, + val: *const V, + _marker: marker::PhantomData<(K, V)>, } -impl<K,V> Copy for RawBucket<K,V> {} -impl<K,V> Clone for RawBucket<K,V> { - fn clone(&self) -> RawBucket<K, V> { *self } +impl<K, V> Copy for RawBucket<K, V> {} +impl<K, V> Clone for RawBucket<K, V> { + fn clone(&self) -> RawBucket<K, V> { + *self + } } pub struct Bucket<K, V, M> { - raw: RawBucket<K, V>, - idx: usize, - table: M + raw: RawBucket<K, V>, + idx: usize, + table: M, } -impl<K,V,M:Copy> Copy for Bucket<K,V,M> {} -impl<K,V,M:Copy> Clone for Bucket<K,V,M> { - fn clone(&self) -> Bucket<K,V,M> { *self } +impl<K, V, M: Copy> Copy for Bucket<K, V, M> {} +impl<K, V, M: Copy> Clone for Bucket<K, V, M> { + fn clone(&self) -> Bucket<K, V, M> { + *self + } } pub struct EmptyBucket<K, V, M> { - raw: RawBucket<K, V>, - idx: usize, - table: M + raw: RawBucket<K, V>, + idx: usize, + table: M, } pub struct FullBucket<K, V, M> { - raw: RawBucket<K, V>, - idx: usize, - table: M + raw: RawBucket<K, V>, + idx: usize, + table: M, } pub type EmptyBucketImm<'table, K, V> = EmptyBucket<K, V, &'table RawTable<K, V>>; -pub type FullBucketImm<'table, K, V> = FullBucket<K, V, &'table RawTable<K, V>>; +pub type FullBucketImm<'table, K, V> = FullBucket<K, V, &'table RawTable<K, V>>; pub type EmptyBucketMut<'table, K, V> = EmptyBucket<K, V, &'table mut RawTable<K, V>>; -pub type FullBucketMut<'table, K, V> = FullBucket<K, V, &'table mut RawTable<K, V>>; +pub type FullBucketMut<'table, K, V> = FullBucket<K, V, &'table mut RawTable<K, V>>; pub enum BucketState<K, V, M> { Empty(EmptyBucket<K, V, M>), @@ -139,14 +143,17 @@ pub struct SafeHash { impl SafeHash { /// Peek at the hash value, which is guaranteed to be non-zero. #[inline(always)] - pub fn inspect(&self) -> u64 { self.hash } + pub fn inspect(&self) -> u64 { + self.hash + } } /// We need to remove hashes of 0. That's reserved for empty buckets. /// This function wraps up `hash_keyed` to be the only way outside this /// module to generate a SafeHash. pub fn make_hash<T: ?Sized, S>(hash_state: &S, t: &T) -> SafeHash - where T: Hash, S: BuildHasher + where T: Hash, + S: BuildHasher { let mut state = hash_state.build_hasher(); t.hash(&mut state); @@ -175,8 +182,8 @@ impl<K, V> RawBucket<K, V> { unsafe fn offset(self, count: isize) -> RawBucket<K, V> { RawBucket { hash: self.hash.offset(count), - key: self.key.offset(count), - val: self.val.offset(count), + key: self.key.offset(count), + val: self.val.offset(count), _marker: marker::PhantomData, } } @@ -212,7 +219,9 @@ impl<K, V, M> Bucket<K, V, M> { } } -impl<K, V, M> Deref for FullBucket<K, V, M> where M: Deref<Target=RawTable<K, V>> { +impl<K, V, M> Deref for FullBucket<K, V, M> + where M: Deref<Target = RawTable<K, V>> +{ type Target = RawTable<K, V>; fn deref(&self) -> &RawTable<K, V> { &self.table @@ -232,19 +241,23 @@ impl<'t, K, V> Put<K, V> for &'t mut RawTable<K, V> { } } -impl<K, V, M> Put<K, V> for Bucket<K, V, M> where M: Put<K, V> { +impl<K, V, M> Put<K, V> for Bucket<K, V, M> + where M: Put<K, V> +{ unsafe fn borrow_table_mut(&mut self) -> &mut RawTable<K, V> { self.table.borrow_table_mut() } } -impl<K, V, M> Put<K, V> for FullBucket<K, V, M> where M: Put<K, V> { +impl<K, V, M> Put<K, V> for FullBucket<K, V, M> + where M: Put<K, V> +{ unsafe fn borrow_table_mut(&mut self) -> &mut RawTable<K, V> { self.table.borrow_table_mut() } } -impl<K, V, M: Deref<Target=RawTable<K, V>>> Bucket<K, V, M> { +impl<K, V, M: Deref<Target = RawTable<K, V>>> Bucket<K, V, M> { pub fn new(table: M, hash: SafeHash) -> Bucket<K, V, M> { Bucket::at_index(table, hash.inspect() as usize) } @@ -252,14 +265,13 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> Bucket<K, V, M> { pub fn at_index(table: M, ib_index: usize) -> Bucket<K, V, M> { // if capacity is 0, then the RawBucket will be populated with bogus pointers. // This is an uncommon case though, so avoid it in release builds. - debug_assert!(table.capacity() > 0, "Table should have capacity at this point"); + debug_assert!(table.capacity() > 0, + "Table should have capacity at this point"); let ib_index = ib_index & (table.capacity() - 1); Bucket { - raw: unsafe { - table.first_bucket_raw().offset(ib_index as isize) - }, + raw: unsafe { table.first_bucket_raw().offset(ib_index as isize) }, idx: ib_index, - table: table + table: table, } } @@ -267,7 +279,7 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> Bucket<K, V, M> { Bucket { raw: table.first_bucket_raw(), idx: 0, - table: table + table: table, } } @@ -277,18 +289,20 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> Bucket<K, V, M> { /// this module. pub fn peek(self) -> BucketState<K, V, M> { match unsafe { *self.raw.hash } { - EMPTY_BUCKET => + EMPTY_BUCKET => { Empty(EmptyBucket { raw: self.raw, idx: self.idx, - table: self.table - }), - _ => + table: self.table, + }) + } + _ => { Full(FullBucket { raw: self.raw, idx: self.idx, - table: self.table + table: self.table, }) + } } } @@ -308,7 +322,7 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> Bucket<K, V, M> { } } -impl<K, V, M: Deref<Target=RawTable<K, V>>> EmptyBucket<K, V, M> { +impl<K, V, M: Deref<Target = RawTable<K, V>>> EmptyBucket<K, V, M> { #[inline] pub fn next(self) -> Bucket<K, V, M> { let mut bucket = self.into_bucket(); @@ -321,7 +335,7 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> EmptyBucket<K, V, M> { Bucket { raw: self.raw, idx: self.idx, - table: self.table + table: self.table, } } @@ -329,22 +343,24 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> EmptyBucket<K, V, M> { let gap = EmptyBucket { raw: self.raw, idx: self.idx, - table: () + table: (), }; match self.next().peek() { Full(bucket) => { Some(GapThenFull { gap: gap, - full: bucket + full: bucket, }) } - Empty(..) => None + Empty(..) => None, } } } -impl<K, V, M> EmptyBucket<K, V, M> where M: Put<K, V> { +impl<K, V, M> EmptyBucket<K, V, M> + where M: Put<K, V> +{ /// Puts given key and value pair, along with the key's hash, /// into this bucket in the hashtable. Note how `self` is 'moved' into /// this function, because this slot will no longer be empty when @@ -352,8 +368,7 @@ impl<K, V, M> EmptyBucket<K, V, M> where M: Put<K, V> { /// the newly-filled slot in the hashtable. /// /// Use `make_hash` to construct a `SafeHash` to pass to this function. - pub fn put(mut self, hash: SafeHash, key: K, value: V) - -> FullBucket<K, V, M> { + pub fn put(mut self, hash: SafeHash, key: K, value: V) -> FullBucket<K, V, M> { unsafe { *self.raw.hash = hash.inspect(); ptr::write(self.raw.key as *mut K, key); @@ -362,11 +377,15 @@ impl<K, V, M> EmptyBucket<K, V, M> where M: Put<K, V> { self.table.borrow_table_mut().size += 1; } - FullBucket { raw: self.raw, idx: self.idx, table: self.table } + FullBucket { + raw: self.raw, + idx: self.idx, + table: self.table, + } } } -impl<K, V, M: Deref<Target=RawTable<K, V>>> FullBucket<K, V, M> { +impl<K, V, M: Deref<Target = RawTable<K, V>>> FullBucket<K, V, M> { #[inline] pub fn next(self) -> Bucket<K, V, M> { let mut bucket = self.into_bucket(); @@ -379,7 +398,7 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> FullBucket<K, V, M> { Bucket { raw: self.raw, idx: self.idx, - table: self.table + table: self.table, } } @@ -407,19 +426,12 @@ impl<K, V, M: Deref<Target=RawTable<K, V>>> FullBucket<K, V, M> { #[inline] pub fn hash(&self) -> SafeHash { - unsafe { - SafeHash { - hash: *self.raw.hash - } - } + unsafe { SafeHash { hash: *self.raw.hash } } } /// Gets references to the key and value at a given index. pub fn read(&self) -> (&K, &V) { - unsafe { - (&*self.raw.key, - &*self.raw.val) - } + unsafe { (&*self.raw.key, &*self.raw.val) } } } @@ -436,69 +448,68 @@ impl<'t, K, V> FullBucket<K, V, &'t mut RawTable<K, V>> { unsafe { *self.raw.hash = EMPTY_BUCKET; - ( - EmptyBucket { - raw: self.raw, - idx: self.idx, - table: self.table - }, - ptr::read(self.raw.key), - ptr::read(self.raw.val) - ) + (EmptyBucket { + raw: self.raw, + idx: self.idx, + table: self.table, + }, + ptr::read(self.raw.key), + ptr::read(self.raw.val)) } } } // This use of `Put` is misleading and restrictive, but safe and sufficient for our use cases // where `M` is a full bucket or table reference type with mutable access to the table. -impl<K, V, M> FullBucket<K, V, M> where M: Put<K, V> { +impl<K, V, M> FullBucket<K, V, M> + where M: Put<K, V> +{ pub fn replace(&mut self, h: SafeHash, k: K, v: V) -> (SafeHash, K, V) { unsafe { let old_hash = ptr::replace(self.raw.hash as *mut SafeHash, h); - let old_key = ptr::replace(self.raw.key as *mut K, k); - let old_val = ptr::replace(self.raw.val as *mut V, v); + let old_key = ptr::replace(self.raw.key as *mut K, k); + let old_val = ptr::replace(self.raw.val as *mut V, v); (old_hash, old_key, old_val) } } } -impl<K, V, M> FullBucket<K, V, M> where M: Deref<Target=RawTable<K, V>> + DerefMut { +impl<K, V, M> FullBucket<K, V, M> + where M: Deref<Target = RawTable<K, V>> + DerefMut +{ /// Gets mutable references to the key and value at a given index. pub fn read_mut(&mut self) -> (&mut K, &mut V) { - unsafe { - (&mut *(self.raw.key as *mut K), - &mut *(self.raw.val as *mut V)) - } + unsafe { (&mut *(self.raw.key as *mut K), &mut *(self.raw.val as *mut V)) } } } -impl<'t, K, V, M> FullBucket<K, V, M> where M: Deref<Target=RawTable<K, V>> + 't { +impl<'t, K, V, M> FullBucket<K, V, M> + where M: Deref<Target = RawTable<K, V>> + 't +{ /// Exchange a bucket state for immutable references into the table. /// Because the underlying reference to the table is also consumed, /// no further changes to the structure of the table are possible; /// in exchange for this, the returned references have a longer lifetime /// than the references returned by `read()`. pub fn into_refs(self) -> (&'t K, &'t V) { - unsafe { - (&*self.raw.key, - &*self.raw.val) - } + unsafe { (&*self.raw.key, &*self.raw.val) } } } -impl<'t, K, V, M> FullBucket<K, V, M> where M: Deref<Target=RawTable<K, V>> + DerefMut + 't { +impl<'t, K, V, M> FullBucket<K, V, M> + where M: Deref<Target = RawTable<K, V>> + DerefMut + 't +{ /// This works similarly to `into_refs`, exchanging a bucket state /// for mutable references into the table. pub fn into_mut_refs(self) -> (&'t mut K, &'t mut V) { - unsafe { - (&mut *(self.raw.key as *mut K), - &mut *(self.raw.val as *mut V)) - } + unsafe { (&mut *(self.raw.key as *mut K), &mut *(self.raw.val as *mut V)) } } } -impl<K, V, M> GapThenFull<K, V, M> where M: Deref<Target=RawTable<K, V>> { +impl<K, V, M> GapThenFull<K, V, M> + where M: Deref<Target = RawTable<K, V>> +{ #[inline] pub fn full(&self) -> &FullBucket<K, V, M> { &self.full @@ -522,7 +533,7 @@ impl<K, V, M> GapThenFull<K, V, M> where M: Deref<Target=RawTable<K, V>> { Some(self) } - Empty(..) => None + Empty(..) => None, } } } @@ -554,7 +565,8 @@ fn test_rounding() { // from the start of a mallocated array. #[inline] fn calculate_offsets(hashes_size: usize, - keys_size: usize, keys_align: usize, + keys_size: usize, + keys_align: usize, vals_align: usize) -> (usize, usize, bool) { let keys_offset = round_up_to_next(hashes_size, keys_align); @@ -567,14 +579,15 @@ fn calculate_offsets(hashes_size: usize, // Returns a tuple of (minimum required malloc alignment, hash_offset, // array_size), from the start of a mallocated array. -fn calculate_allocation(hash_size: usize, hash_align: usize, - keys_size: usize, keys_align: usize, - vals_size: usize, vals_align: usize) +fn calculate_allocation(hash_size: usize, + hash_align: usize, + keys_size: usize, + keys_align: usize, + vals_size: usize, + vals_align: usize) -> (usize, usize, usize, bool) { let hash_offset = 0; - let (_, vals_offset, oflo) = calculate_offsets(hash_size, - keys_size, keys_align, - vals_align); + let (_, vals_offset, oflo) = calculate_offsets(hash_size, keys_size, keys_align, vals_align); let (end_of_vals, oflo2) = vals_offset.overflowing_add(vals_size); let align = cmp::max(hash_align, cmp::max(keys_align, vals_align)); @@ -584,12 +597,13 @@ fn calculate_allocation(hash_size: usize, hash_align: usize, #[test] fn test_offset_calculation() { - assert_eq!(calculate_allocation(128, 8, 15, 1, 4, 4), (8, 0, 148, false)); - assert_eq!(calculate_allocation(3, 1, 2, 1, 1, 1), (1, 0, 6, false)); - assert_eq!(calculate_allocation(6, 2, 12, 4, 24, 8), (8, 0, 48, false)); + assert_eq!(calculate_allocation(128, 8, 15, 1, 4, 4), + (8, 0, 148, false)); + assert_eq!(calculate_allocation(3, 1, 2, 1, 1, 1), (1, 0, 6, false)); + assert_eq!(calculate_allocation(6, 2, 12, 4, 24, 8), (8, 0, 48, false)); assert_eq!(calculate_offsets(128, 15, 1, 4), (128, 144, false)); - assert_eq!(calculate_offsets(3, 2, 1, 1), (3, 5, false)); - assert_eq!(calculate_offsets(6, 12, 4, 8), (8, 24, false)); + assert_eq!(calculate_offsets(3, 2, 1, 1), (3, 5, false)); + assert_eq!(calculate_offsets(6, 12, 4, 8), (8, 24, false)); } impl<K, V> RawTable<K, V> { @@ -608,8 +622,8 @@ impl<K, V> RawTable<K, V> { // No need for `checked_mul` before a more restrictive check performed // later in this method. let hashes_size = capacity * size_of::<u64>(); - let keys_size = capacity * size_of::< K >(); - let vals_size = capacity * size_of::< V >(); + let keys_size = capacity * size_of::<K>(); + let vals_size = capacity * size_of::<V>(); // Allocating hashmaps is a little tricky. We need to allocate three // arrays, but since we know their sizes and alignments up front, @@ -619,31 +633,38 @@ impl<K, V> RawTable<K, V> { // This is great in theory, but in practice getting the alignment // right is a little subtle. Therefore, calculating offsets has been // factored out into a different function. - let (malloc_alignment, hash_offset, size, oflo) = - calculate_allocation( - hashes_size, align_of::<u64>(), - keys_size, align_of::< K >(), - vals_size, align_of::< V >()); + let (malloc_alignment, hash_offset, size, oflo) = calculate_allocation(hashes_size, + align_of::<u64>(), + keys_size, + align_of::<K>(), + vals_size, + align_of::<V>()); assert!(!oflo, "capacity overflow"); // One check for overflow that covers calculation and rounding of size. - let size_of_bucket = size_of::<u64>().checked_add(size_of::<K>()).unwrap() - .checked_add(size_of::<V>()).unwrap(); - assert!(size >= capacity.checked_mul(size_of_bucket) - .expect("capacity overflow"), + let size_of_bucket = size_of::<u64>() + .checked_add(size_of::<K>()) + .unwrap() + .checked_add(size_of::<V>()) + .unwrap(); + assert!(size >= + capacity.checked_mul(size_of_bucket) + .expect("capacity overflow"), "capacity overflow"); let buffer = allocate(size, malloc_alignment); - if buffer.is_null() { ::alloc::oom() } + if buffer.is_null() { + ::alloc::oom() + } let hashes = buffer.offset(hash_offset as isize) as *mut u64; RawTable { capacity: capacity, - size: 0, - hashes: Unique::new(hashes), - marker: marker::PhantomData, + size: 0, + hashes: Unique::new(hashes), + marker: marker::PhantomData, } } @@ -652,16 +673,16 @@ impl<K, V> RawTable<K, V> { let keys_size = self.capacity * size_of::<K>(); let buffer = *self.hashes as *const u8; - let (keys_offset, vals_offset, oflo) = - calculate_offsets(hashes_size, - keys_size, align_of::<K>(), - align_of::<V>()); + let (keys_offset, vals_offset, oflo) = calculate_offsets(hashes_size, + keys_size, + align_of::<K>(), + align_of::<V>()); debug_assert!(!oflo, "capacity overflow"); unsafe { RawBucket { hash: *self.hashes, - key: buffer.offset(keys_offset as isize) as *const K, - val: buffer.offset(vals_offset as isize) as *const V, + key: buffer.offset(keys_offset as isize) as *const K, + val: buffer.offset(vals_offset as isize) as *const V, _marker: marker::PhantomData, } } @@ -691,9 +712,7 @@ impl<K, V> RawTable<K, V> { fn raw_buckets(&self) -> RawBuckets<K, V> { RawBuckets { raw: self.first_bucket_raw(), - hashes_end: unsafe { - self.hashes.offset(self.capacity as isize) - }, + hashes_end: unsafe { self.hashes.offset(self.capacity as isize) }, marker: marker::PhantomData, } } @@ -747,7 +766,7 @@ impl<K, V> RawTable<K, V> { raw: raw_bucket.offset(self.capacity as isize), hashes_end: raw_bucket.hash, elems_left: self.size, - marker: marker::PhantomData, + marker: marker::PhantomData, } } } @@ -827,10 +846,7 @@ impl<'a, K, V> Iterator for RevMoveBuckets<'a, K, V> { if *self.raw.hash != EMPTY_BUCKET { self.elems_left -= 1; - return Some(( - ptr::read(self.raw.key), - ptr::read(self.raw.val) - )); + return Some((ptr::read(self.raw.key), ptr::read(self.raw.val))); } } } @@ -851,7 +867,7 @@ impl<'a, K, V> Clone for Iter<'a, K, V> { fn clone(&self) -> Iter<'a, K, V> { Iter { iter: self.iter.clone(), - elems_left: self.elems_left + elems_left: self.elems_left, } } } @@ -873,7 +889,7 @@ unsafe impl<'a, K: Send, V: Send> Send for IterMut<'a, K, V> {} /// Iterator over the entries in a table, consuming the table. pub struct IntoIter<K, V> { table: RawTable<K, V>, - iter: RawBuckets<'static, K, V> + iter: RawBuckets<'static, K, V>, } unsafe impl<K: Sync, V: Sync> Sync for IntoIter<K, V> {} @@ -894,10 +910,7 @@ impl<'a, K, V> Iterator for Iter<'a, K, V> { fn next(&mut self) -> Option<(&'a K, &'a V)> { self.iter.next().map(|bucket| { self.elems_left -= 1; - unsafe { - (&*bucket.key, - &*bucket.val) - } + unsafe { (&*bucket.key, &*bucket.val) } }) } @@ -906,7 +919,9 @@ impl<'a, K, V> Iterator for Iter<'a, K, V> { } } impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> { - fn len(&self) -> usize { self.elems_left } + fn len(&self) -> usize { + self.elems_left + } } impl<'a, K, V> Iterator for IterMut<'a, K, V> { @@ -915,10 +930,7 @@ impl<'a, K, V> Iterator for IterMut<'a, K, V> { fn next(&mut self) -> Option<(&'a K, &'a mut V)> { self.iter.next().map(|bucket| { self.elems_left -= 1; - unsafe { - (&*bucket.key, - &mut *(bucket.val as *mut V)) - } + unsafe { (&*bucket.key, &mut *(bucket.val as *mut V)) } }) } @@ -927,7 +939,9 @@ impl<'a, K, V> Iterator for IterMut<'a, K, V> { } } impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> { - fn len(&self) -> usize { self.elems_left } + fn len(&self) -> usize { + self.elems_left + } } impl<K, V> Iterator for IntoIter<K, V> { @@ -937,13 +951,7 @@ impl<K, V> Iterator for IntoIter<K, V> { self.iter.next().map(|bucket| { self.table.size -= 1; unsafe { - ( - SafeHash { - hash: *bucket.hash, - }, - ptr::read(bucket.key), - ptr::read(bucket.val) - ) + (SafeHash { hash: *bucket.hash }, ptr::read(bucket.key), ptr::read(bucket.val)) } }) } @@ -954,7 +962,9 @@ impl<K, V> Iterator for IntoIter<K, V> { } } impl<K, V> ExactSizeIterator for IntoIter<K, V> { - fn len(&self) -> usize { self.table.size() } + fn len(&self) -> usize { + self.table.size() + } } impl<'a, K, V> Iterator for Drain<'a, K, V> { @@ -965,13 +975,9 @@ impl<'a, K, V> Iterator for Drain<'a, K, V> { self.iter.next().map(|bucket| { self.table.size -= 1; unsafe { - ( - SafeHash { - hash: ptr::replace(bucket.hash, EMPTY_BUCKET), - }, - ptr::read(bucket.key), - ptr::read(bucket.val) - ) + (SafeHash { hash: ptr::replace(bucket.hash, EMPTY_BUCKET) }, + ptr::read(bucket.key), + ptr::read(bucket.val)) } }) } @@ -982,7 +988,9 @@ impl<'a, K, V> Iterator for Drain<'a, K, V> { } } impl<'a, K, V> ExactSizeIterator for Drain<'a, K, V> { - fn len(&self) -> usize { self.table.size() } + fn len(&self) -> usize { + self.table.size() + } } impl<'a, K: 'a, V: 'a> Drop for Drain<'a, K, V> { @@ -1040,7 +1048,8 @@ impl<K, V> Drop for RawTable<K, V> { // dropping empty tables such as on resize. // Also avoid double drop of elements that have been already moved out. unsafe { - if needs_drop::<(K, V)>() { // avoid linear runtime for types that don't need drop + if needs_drop::<(K, V)>() { + // avoid linear runtime for types that don't need drop for _ in self.rev_move_buckets() {} } } @@ -1048,10 +1057,12 @@ impl<K, V> Drop for RawTable<K, V> { let hashes_size = self.capacity * size_of::<u64>(); let keys_size = self.capacity * size_of::<K>(); let vals_size = self.capacity * size_of::<V>(); - let (align, _, size, oflo) = - calculate_allocation(hashes_size, align_of::<u64>(), - keys_size, align_of::<K>(), - vals_size, align_of::<V>()); + let (align, _, size, oflo) = calculate_allocation(hashes_size, + align_of::<u64>(), + keys_size, + align_of::<K>(), + vals_size, + align_of::<V>()); debug_assert!(!oflo, "should be impossible"); diff --git a/src/libstd/collections/mod.rs b/src/libstd/collections/mod.rs index 44613d77671..9b13d542300 100644 --- a/src/libstd/collections/mod.rs +++ b/src/libstd/collections/mod.rs @@ -429,14 +429,16 @@ mod hash; #[stable(feature = "rust1", since = "1.0.0")] pub mod hash_map { - //! A hashmap + //! A hash map implementation which uses linear probing with Robin + //! Hood bucket stealing. #[stable(feature = "rust1", since = "1.0.0")] pub use super::hash::map::*; } #[stable(feature = "rust1", since = "1.0.0")] pub mod hash_set { - //! A hashset + //! An implementation of a hash set using the underlying representation of a + //! HashMap where the value is (). #[stable(feature = "rust1", since = "1.0.0")] pub use super::hash::set::*; } diff --git a/src/libstd/env.rs b/src/libstd/env.rs index 9dc6a26cdee..d6d62ce79d4 100644 --- a/src/libstd/env.rs +++ b/src/libstd/env.rs @@ -452,16 +452,16 @@ pub fn home_dir() -> Option<PathBuf> { /// Returns the path of 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 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), it returns +/// `/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 of the Windows directory. This -/// behavior is identical to that of [GetTempPath][msdn], which this function -/// uses internally. +/// On Windows, returns the value of, in order, the `TMP`, `TEMP`, +/// `USERPROFILE` environment variable if any are set and not the empty +/// string. Otherwise, `temp_dir` returns the path of the Windows directory. +/// This behavior is identical to that of [`GetTempPath`][msdn], which this +/// function uses internally. /// /// [msdn]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa364992(v=vs.85).aspx /// @@ -493,6 +493,44 @@ pub fn temp_dir() -> PathBuf { /// that can fail for a good number of reasons. Some errors can include, but not /// be limited to, filesystem operations failing or general syscall failures. /// +/// # Security +/// +/// The output of this function should not be used in anything that might have +/// security implications. For example: +/// +/// ``` +/// fn main() { +/// println!("{:?}", std::env::current_exe()); +/// } +/// ``` +/// +/// On Linux systems, if this is compiled as `foo`: +/// +/// ```bash +/// $ rustc foo.rs +/// $ ./foo +/// Ok("/home/alex/foo") +/// ``` +/// +/// And you make a symbolic link of the program: +/// +/// ```bash +/// $ ln foo bar +/// ``` +/// +/// When you run it, you won't get the original executable, you'll get the +/// symlink: +/// +/// ```bash +/// $ ./bar +/// Ok("/home/alex/bar") +/// ``` +/// +/// This sort of behavior has been known to [lead to privledge escalation] when +/// used incorrectly, for example. +/// +/// [lead to privledge escalation]: http://securityvulns.com/Wdocument183.html +/// /// # Examples /// /// ``` @@ -587,6 +625,13 @@ impl ExactSizeIterator for Args { fn len(&self) -> usize { self.inner.len() } } +#[stable(feature = "env_iterators", since = "1.11.0")] +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option<String> { + self.inner.next_back().map(|s| s.into_string().unwrap()) + } +} + #[stable(feature = "env", since = "1.0.0")] impl Iterator for ArgsOs { type Item = OsString; @@ -599,6 +644,10 @@ impl ExactSizeIterator for ArgsOs { fn len(&self) -> usize { self.inner.len() } } +#[stable(feature = "env_iterators", since = "1.11.0")] +impl DoubleEndedIterator for ArgsOs { + fn next_back(&mut self) -> Option<OsString> { self.inner.next_back() } +} /// Constants associated with the current target #[stable(feature = "env", since = "1.0.0")] pub mod consts { diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 35cd4a5ec52..1459420cdc0 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -66,10 +66,80 @@ pub trait Error: Debug + Display + Reflect { /// The description should not contain newlines or sentence-ending /// punctuation, to facilitate embedding in larger user-facing /// strings. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// + /// match "xc".parse::<u32>() { + /// Err(e) => { + /// println!("Error: {}", e.description()); + /// } + /// _ => println!("No error"), + /// } + /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn description(&self) -> &str; /// The lower-level cause of this error, if any. + /// + /// # Examples + /// + /// ``` + /// use std::error::Error; + /// use std::fmt; + /// + /// #[derive(Debug)] + /// struct SuperError { + /// side: SuperErrorSideKick, + /// } + /// + /// impl fmt::Display for SuperError { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "SuperError is here!") + /// } + /// } + /// + /// impl Error for SuperError { + /// fn description(&self) -> &str { + /// "I'm the superhero of errors!" + /// } + /// + /// fn cause(&self) -> Option<&Error> { + /// Some(&self.side) + /// } + /// } + /// + /// #[derive(Debug)] + /// struct SuperErrorSideKick; + /// + /// impl fmt::Display for SuperErrorSideKick { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "SuperErrorSideKick is here!") + /// } + /// } + /// + /// impl Error for SuperErrorSideKick { + /// fn description(&self) -> &str { + /// "I'm SuperError side kick!" + /// } + /// } + /// + /// fn get_super_error() -> Result<(), SuperError> { + /// Err(SuperError { side: SuperErrorSideKick }) + /// } + /// + /// fn main() { + /// match get_super_error() { + /// Err(e) => { + /// println!("Error: {}", e.description()); + /// println!("Caused by: {}", e.cause().unwrap()); + /// } + /// _ => println!("No error"), + /// } + /// } + /// ``` #[stable(feature = "rust1", since = "1.0.0")] fn cause(&self) -> Option<&Error> { None } @@ -159,6 +229,13 @@ impl Error for num::ParseIntError { } } +#[unstable(feature = "try_from", issue = "33417")] +impl Error for num::TryFromIntError { + fn description(&self) -> &str { + self.__description() + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl Error for num::ParseFloatError { fn description(&self) -> &str { @@ -205,6 +282,13 @@ impl<T: Error> Error for Box<T> { } } +#[stable(feature = "fmt_error", since = "1.11.0")] +impl Error for fmt::Error { + fn description(&self) -> &str { + "an error occurred when formatting an argument" + } +} + // copied from any.rs impl Error + 'static { /// Returns true if the boxed type is the same as `T` diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 52d7bb128d5..0d3e18f9b96 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -159,6 +159,12 @@ pub struct CStr { #[stable(feature = "rust1", since = "1.0.0")] pub struct NulError(usize, Vec<u8>); +/// An error returned from `CStr::from_bytes_with_nul` to indicate that a nul +/// byte was found too early in the slice provided or one wasn't found at all. +#[derive(Clone, PartialEq, Debug)] +#[stable(feature = "cstr_from_bytes", since = "1.10.0")] +pub struct FromBytesWithNulError { _a: () } + /// An error returned from `CString::into_string` to indicate that a UTF-8 error /// was encountered during the conversion. #[derive(Clone, PartialEq, Debug)] @@ -326,6 +332,22 @@ impl fmt::Debug for CStr { } } +#[stable(feature = "cstr_default", since = "1.10.0")] +impl<'a> Default for &'a CStr { + fn default() -> &'a CStr { + static SLICE: &'static [c_char] = &[0]; + unsafe { CStr::from_ptr(SLICE.as_ptr()) } + } +} + +#[stable(feature = "cstr_default", since = "1.10.0")] +impl Default for CString { + fn default() -> CString { + let a: &CStr = Default::default(); + a.to_owned() + } +} + #[stable(feature = "cstr_borrow", since = "1.3.0")] impl Borrow<CStr> for CString { fn borrow(&self) -> &CStr { self } @@ -445,20 +467,18 @@ impl CStr { /// # Examples /// /// ``` - /// # #![feature(cstr_from_bytes)] /// use std::ffi::CStr; /// - /// # fn main() { /// let cstr = CStr::from_bytes_with_nul(b"hello\0"); - /// assert!(cstr.is_some()); - /// # } + /// assert!(cstr.is_ok()); /// ``` - #[unstable(feature = "cstr_from_bytes", reason = "recently added", issue = "31190")] - pub fn from_bytes_with_nul(bytes: &[u8]) -> Option<&CStr> { + #[stable(feature = "cstr_from_bytes", since = "1.10.0")] + pub fn from_bytes_with_nul(bytes: &[u8]) + -> Result<&CStr, FromBytesWithNulError> { if bytes.is_empty() || memchr::memchr(0, &bytes) != Some(bytes.len() - 1) { - None + Err(FromBytesWithNulError { _a: () }) } else { - Some(unsafe { Self::from_bytes_with_nul_unchecked(bytes) }) + Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) }) } } @@ -471,18 +491,15 @@ impl CStr { /// # Examples /// /// ``` - /// # #![feature(cstr_from_bytes)] /// use std::ffi::{CStr, CString}; /// - /// # fn main() { /// unsafe { /// let cstring = CString::new("hello").unwrap(); /// let cstr = CStr::from_bytes_with_nul_unchecked(cstring.to_bytes_with_nul()); /// assert_eq!(cstr, &*cstring); /// } - /// # } /// ``` - #[unstable(feature = "cstr_from_bytes", reason = "recently added", issue = "31190")] + #[stable(feature = "cstr_from_bytes", since = "1.10.0")] pub unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { mem::transmute(bytes) } @@ -492,6 +509,38 @@ impl CStr { /// The returned pointer will be valid for as long as `self` is and points /// to a contiguous region of memory terminated with a 0 byte to represent /// the end of the string. + /// + /// **WARNING** + /// + /// It is your responsibility to make sure that the underlying memory is not + /// freed too early. For example, the following code will cause undefined + /// behaviour when `ptr` is used inside the `unsafe` block: + /// + /// ```no_run + /// use std::ffi::{CString}; + /// + /// let ptr = CString::new("Hello").unwrap().as_ptr(); + /// unsafe { + /// // `ptr` is dangling + /// *ptr; + /// } + /// ``` + /// + /// This happens because the pointer returned by `as_ptr` does not carry any + /// lifetime information and the string is deallocated immediately after + /// the `CString::new("Hello").unwrap().as_ptr()` expression is evaluated. + /// To fix the problem, bind the string to a local variable: + /// + /// ```no_run + /// use std::ffi::{CString}; + /// + /// let hello = CString::new("Hello").unwrap(); + /// let ptr = hello.as_ptr(); + /// unsafe { + /// // `ptr` is valid because `hello` is in scope + /// *ptr; + /// } + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn as_ptr(&self) -> *const c_char { self.inner.as_ptr() @@ -726,12 +775,14 @@ mod tests { fn from_bytes_with_nul() { let data = b"123\0"; let cstr = CStr::from_bytes_with_nul(data); - assert_eq!(cstr.map(CStr::to_bytes), Some(&b"123"[..])); - assert_eq!(cstr.map(CStr::to_bytes_with_nul), Some(&b"123\0"[..])); + assert_eq!(cstr.map(CStr::to_bytes), Ok(&b"123"[..])); + let cstr = CStr::from_bytes_with_nul(data); + assert_eq!(cstr.map(CStr::to_bytes_with_nul), Ok(&b"123\0"[..])); unsafe { + let cstr = CStr::from_bytes_with_nul(data); let cstr_unchecked = CStr::from_bytes_with_nul_unchecked(data); - assert_eq!(cstr, Some(cstr_unchecked)); + assert_eq!(cstr, Ok(cstr_unchecked)); } } @@ -739,13 +790,13 @@ mod tests { fn from_bytes_with_nul_unterminated() { let data = b"123"; let cstr = CStr::from_bytes_with_nul(data); - assert!(cstr.is_none()); + assert!(cstr.is_err()); } #[test] fn from_bytes_with_nul_interior() { let data = b"1\023\0"; let cstr = CStr::from_bytes_with_nul(data); - assert!(cstr.is_none()); + assert!(cstr.is_err()); } } diff --git a/src/libstd/ffi/mod.rs b/src/libstd/ffi/mod.rs index bfd6ab52289..ca1ff18f1ca 100644 --- a/src/libstd/ffi/mod.rs +++ b/src/libstd/ffi/mod.rs @@ -14,6 +14,8 @@ #[stable(feature = "rust1", since = "1.0.0")] pub use self::c_str::{CString, CStr, NulError, IntoStringError}; +#[stable(feature = "cstr_from_bytes", since = "1.10.0")] +pub use self::c_str::{FromBytesWithNulError}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::os_str::{OsString, OsStr}; diff --git a/src/libstd/fs.rs b/src/libstd/fs.rs index 03ebaa59ca5..48753ccf1c3 100644 --- a/src/libstd/fs.rs +++ b/src/libstd/fs.rs @@ -32,6 +32,8 @@ use time::SystemTime; /// it was opened with. Files also implement `Seek` to alter the logical cursor /// that the file contains internally. /// +/// Files are automatically closed when they go out of scope. +/// /// # Examples /// /// ```no_run @@ -56,28 +58,37 @@ pub struct File { /// Metadata information about a file. /// -/// This structure is returned from the `metadata` function or method and +/// This structure is returned from the [`metadata`] function or method and /// represents known metadata about a file such as its permissions, size, /// modification times, etc. +/// +/// [`metadata`]: fn.metadata.html #[stable(feature = "rust1", since = "1.0.0")] #[derive(Clone)] pub struct Metadata(fs_imp::FileAttr); /// Iterator over the entries in a directory. /// -/// This iterator is returned from the `read_dir` function of this module and -/// will yield instances of `io::Result<DirEntry>`. Through a `DirEntry` +/// This iterator is returned from the [`read_dir`] function of this module and +/// will yield instances of `io::Result<DirEntry>`. Through a [`DirEntry`] /// information like the entry's path and possibly other metadata can be /// learned. /// +/// [`read_dir`]: fn.read_dir.html +/// [`DirEntry`]: struct.DirEntry.html +/// /// # Errors /// -/// This `io::Result` will be an `Err` if there's some sort of intermittent +/// This [`io::Result`] will be an `Err` if there's some sort of intermittent /// IO error during iteration. +/// +/// [`io::Result`]: ../io/type.Result.html #[stable(feature = "rust1", since = "1.0.0")] pub struct ReadDir(fs_imp::ReadDir); -/// Entries returned by the `ReadDir` iterator. +/// Entries returned by the [`ReadDir`] iterator. +/// +/// [`ReadDir`]: struct.ReadDir.html /// /// An instance of `DirEntry` represents an entry inside of a directory on the /// filesystem. Each entry can be inspected via methods to learn about the full @@ -87,17 +98,23 @@ pub struct DirEntry(fs_imp::DirEntry); /// Options and flags which can be used to configure how a file is opened. /// -/// This builder exposes the ability to configure how a `File` is opened and -/// what operations are permitted on the open file. The `File::open` and -/// `File::create` methods are aliases for commonly used options using this +/// This builder exposes the ability to configure how a [`File`] is opened and +/// what operations are permitted on the open file. The [`File::open`] and +/// [`File::create`] methods are aliases for commonly used options using this /// builder. /// -/// Generally speaking, when using `OpenOptions`, you'll first call `new()`, -/// then chain calls to methods to set each option, then call `open()`, passing -/// the path of the file you're trying to open. This will give you a +/// [`File`]: struct.File.html +/// [`File::open`]: struct.File.html#method.open +/// [`File::create`]: struct.File.html#method.create +/// +/// Generally speaking, when using `OpenOptions`, you'll first call [`new()`], +/// then chain calls to methods to set each option, then call [`open()`], +/// passing the path of the file you're trying to open. This will give you a /// [`io::Result`][result] with a [`File`][file] inside that you can further /// operate on. /// +/// [`new()`]: struct.OpenOptions.html#method.new +/// [`open()`]: struct.OpenOptions.html#method.open /// [result]: ../io/type.Result.html /// [file]: struct.File.html /// @@ -129,17 +146,19 @@ pub struct OpenOptions(fs_imp::OpenOptions); /// Representation of the various permissions on a file. /// -/// This module only currently provides one bit of information, `readonly`, +/// This module only currently provides one bit of information, [`readonly`], /// which is exposed on all currently supported platforms. Unix-specific /// functionality, such as mode bits, is available through the /// `os::unix::PermissionsExt` trait. +/// +/// [`readonly`]: struct.Permissions.html#method.readonly #[derive(Clone, PartialEq, Eq, Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Permissions(fs_imp::FilePermissions); /// An structure representing a type of file with accessors for each file type. #[stable(feature = "file_type", since = "1.1.0")] -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct FileType(fs_imp::FileType); /// A builder used to create directories in various manners. @@ -154,12 +173,14 @@ pub struct DirBuilder { impl File { /// Attempts to open a file in read-only mode. /// - /// See the `OpenOptions::open` method for more details. + /// See the [`OpenOptions::open`] method for more details. /// /// # Errors /// /// This function will return an error if `path` does not already exist. - /// Other errors may also be returned according to `OpenOptions::open`. + /// Other errors may also be returned according to [`OpenOptions::open`]. + /// + /// [`OpenOptions::open`]: struct.OpenOptions.html#method.open /// /// # Examples /// @@ -181,7 +202,9 @@ impl File { /// This function will create a file if it does not exist, /// and will truncate it if it does. /// - /// See the `OpenOptions::open` function for more details. + /// See the [`OpenOptions::open`] function for more details. + /// + /// [`OpenOptions::open`]: struct.OpenOptions.html#method.open /// /// # Examples /// @@ -222,7 +245,7 @@ impl File { self.inner.fsync() } - /// This function is similar to `sync_all`, except that it may not + /// This function is similar to [`sync_all`], except that it may not /// synchronize file metadata to the filesystem. /// /// This is intended for use cases that must synchronize content, but don't @@ -230,7 +253,9 @@ impl File { /// operations. /// /// Note that some platforms may simply implement this in terms of - /// `sync_all`. + /// [`sync_all`]. + /// + /// [`sync_all`]: struct.File.html#method.sync_all /// /// # Examples /// @@ -302,6 +327,18 @@ impl File { /// The returned `File` is a reference to the same state that this object /// references. Both handles will read and write with the same cursor /// position. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs::File; + /// + /// # fn foo() -> std::io::Result<()> { + /// let mut f = try!(File::open("foo.txt")); + /// let file_copy = try!(f.try_clone()); + /// # Ok(()) + /// # } + /// ``` #[stable(feature = "file_try_clone", since = "1.9.0")] pub fn try_clone(&self) -> io::Result<File> { Ok(File { @@ -510,7 +547,7 @@ impl OpenOptions { /// No file is allowed to exist at the target location, also no (dangling) /// symlink. /// - /// This option is useful because it as atomic. Otherwise between checking + /// This option is useful because it is atomic. Otherwise between checking /// whether a file exists and creating a new one, the file may have been /// created by another process (a TOCTOU race condition / attack). /// @@ -657,7 +694,7 @@ impl Metadata { /// /// This field may not be available on all platforms, and will return an /// `Err` on platforms where it is not available. - #[unstable(feature = "fs_time", issue = "31399")] + #[stable(feature = "fs_time", since = "1.10.0")] pub fn modified(&self) -> io::Result<SystemTime> { self.0.modified().map(FromInner::from_inner) } @@ -675,7 +712,7 @@ impl Metadata { /// /// This field may not be available on all platforms, and will return an /// `Err` on platforms where it is not available. - #[unstable(feature = "fs_time", issue = "31399")] + #[stable(feature = "fs_time", since = "1.10.0")] pub fn accessed(&self) -> io::Result<SystemTime> { self.0.accessed().map(FromInner::from_inner) } @@ -689,7 +726,7 @@ impl Metadata { /// /// This field may not be available on all platforms, and will return an /// `Err` on platforms where it is not available. - #[unstable(feature = "fs_time", issue = "31399")] + #[stable(feature = "fs_time", since = "1.10.0")] pub fn created(&self) -> io::Result<SystemTime> { self.0.created().map(FromInner::from_inner) } @@ -789,8 +826,8 @@ impl Iterator for ReadDir { impl DirEntry { /// Returns the full path to the file that this entry represents. /// - /// The full path is created by joining the original path to `read_dir` or - /// `walk_dir` with the filename of this entry. + /// The full path is created by joining the original path to `read_dir` + /// with the filename of this entry. /// /// # Examples /// @@ -827,6 +864,26 @@ impl DirEntry { /// On Windows this function is cheap to call (no extra system calls /// needed), but on Unix platforms this function is the equivalent of /// calling `symlink_metadata` on the path. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// + /// if let Ok(entries) = fs::read_dir(".") { + /// for entry in entries { + /// if let Ok(entry) = entry { + /// // Here, `entry` is a `DirEntry`. + /// if let Ok(metadata) = entry.metadata() { + /// // Now let's show our entry's permissions! + /// println!("{:?}: {:?}", entry.path(), metadata.permissions()); + /// } else { + /// println!("Couldn't get metadata for {:?}", entry.path()); + /// } + /// } + /// } + /// } + /// ``` #[stable(feature = "dir_entry_ext", since = "1.1.0")] pub fn metadata(&self) -> io::Result<Metadata> { self.0.metadata().map(Metadata) @@ -842,6 +899,26 @@ impl DirEntry { /// On Windows and most Unix platforms this function is free (no extra /// system calls needed), but some Unix platforms may require the equivalent /// call to `symlink_metadata` to learn about the target file type. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// + /// if let Ok(entries) = fs::read_dir(".") { + /// for entry in entries { + /// if let Ok(entry) = entry { + /// // Here, `entry` is a `DirEntry`. + /// if let Ok(file_type) = entry.file_type() { + /// // Now let's show our entry's file type! + /// println!("{:?}: {:?}", entry.path(), file_type); + /// } else { + /// println!("Couldn't get file type for {:?}", entry.path()); + /// } + /// } + /// } + /// } + /// ``` #[stable(feature = "dir_entry_ext", since = "1.1.0")] pub fn file_type(&self) -> io::Result<FileType> { self.0.file_type().map(FileType) @@ -849,6 +926,21 @@ impl DirEntry { /// Returns the bare file name of this directory entry without any other /// leading path component. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// + /// if let Ok(entries) = fs::read_dir(".") { + /// for entry in entries { + /// if let Ok(entry) = entry { + /// // Here, `entry` is a `DirEntry`. + /// println!("{:?}", entry.file_name()); + /// } + /// } + /// } + /// ``` #[stable(feature = "dir_entry_ext", since = "1.1.0")] pub fn file_name(&self) -> OsString { self.0.file_name() @@ -1338,11 +1430,12 @@ pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { /// /// // one possible implementation of walking a directory only visiting files /// fn visit_dirs(dir: &Path, cb: &Fn(&DirEntry)) -> io::Result<()> { -/// if try!(fs::metadata(dir)).is_dir() { +/// if dir.is_dir() { /// for entry in try!(fs::read_dir(dir)) { /// let entry = try!(entry); -/// if try!(fs::metadata(entry.path())).is_dir() { -/// try!(visit_dirs(&entry.path(), cb)); +/// let path = entry.path(); +/// if path.is_dir() { +/// try!(visit_dirs(&path, cb)); /// } else { /// cb(&entry); /// } @@ -1394,6 +1487,14 @@ pub fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) impl DirBuilder { /// Creates a new set of options with default mode/security settings for all /// platforms and also non-recursive. + /// + /// # Examples + /// + /// ``` + /// use std::fs::DirBuilder; + /// + /// let builder = DirBuilder::new(); + /// ``` #[stable(feature = "dir_builder", since = "1.6.0")] pub fn new() -> DirBuilder { DirBuilder { @@ -1406,7 +1507,16 @@ impl DirBuilder { /// all parent directories if they do not exist with the same security and /// permissions settings. /// - /// This option defaults to `false` + /// This option defaults to `false`. + /// + /// # Examples + /// + /// ``` + /// use std::fs::DirBuilder; + /// + /// let mut builder = DirBuilder::new(); + /// builder.recursive(true); + /// ``` #[stable(feature = "dir_builder", since = "1.6.0")] pub fn recursive(&mut self, recursive: bool) -> &mut Self { self.recursive = recursive; @@ -1543,7 +1653,7 @@ mod tests { let result = File::open(filename); if cfg!(unix) { - error!(result, "o such file or directory"); + error!(result, "No such file or directory"); } if cfg!(windows) { error!(result, "The system cannot find the file specified"); @@ -1558,7 +1668,7 @@ mod tests { let result = fs::remove_file(filename); if cfg!(unix) { - error!(result, "o such file or directory"); + error!(result, "No such file or directory"); } if cfg!(windows) { error!(result, "The system cannot find the file specified"); @@ -1768,11 +1878,20 @@ mod tests { } #[test] + fn file_create_new_already_exists_error() { + let tmpdir = tmpdir(); + let file = &tmpdir.join("file_create_new_error_exists"); + check!(fs::File::create(file)); + let e = fs::OpenOptions::new().write(true).create_new(true).open(file).unwrap_err(); + assert_eq!(e.kind(), ErrorKind::AlreadyExists); + } + + #[test] fn mkdir_path_already_exists_error() { let tmpdir = tmpdir(); let dir = &tmpdir.join("mkdir_error_twice"); check!(fs::create_dir(dir)); - let e = fs::create_dir(dir).err().unwrap(); + let e = fs::create_dir(dir).unwrap_err(); assert_eq!(e.kind(), ErrorKind::AlreadyExists); } diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 632ef3db804..a92ca95f4ee 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -1127,7 +1127,7 @@ mod tests { let mut writer = BufWriter::new(PanicWriter); let _ = writer.write(b"hello world"); let _ = writer.flush(); - }).join().err().unwrap(); + }).join().unwrap_err(); assert_eq!(WRITES.load(Ordering::SeqCst), 1); } diff --git a/src/libstd/io/cursor.rs b/src/libstd/io/cursor.rs index a1002fdb645..2d780559db1 100644 --- a/src/libstd/io/cursor.rs +++ b/src/libstd/io/cursor.rs @@ -230,6 +230,7 @@ impl<T> BufRead for Cursor<T> where T: AsRef<[u8]> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a> Write for Cursor<&'a mut [u8]> { + #[inline] fn write(&mut self, data: &[u8]) -> io::Result<usize> { let pos = cmp::min(self.pos, self.inner.len() as u64); let amt = (&mut self.inner[(pos as usize)..]).write(data)?; @@ -269,6 +270,7 @@ impl Write for Cursor<Vec<u8>> { #[stable(feature = "cursor_box_slice", since = "1.5.0")] impl Write for Cursor<Box<[u8]>> { + #[inline] fn write(&mut self, buf: &[u8]) -> io::Result<usize> { let pos = cmp::min(self.pos, self.inner.len() as u64); let amt = (&mut self.inner[(pos as usize)..]).write(buf)?; diff --git a/src/libstd/io/error.rs b/src/libstd/io/error.rs index 9a605fc7bbf..05ae8ed5b0b 100644 --- a/src/libstd/io/error.rs +++ b/src/libstd/io/error.rs @@ -214,6 +214,30 @@ impl Error { } /// Creates a new instance of an `Error` from a particular OS error code. + /// + /// # Examples + /// + /// On Linux: + /// + /// ``` + /// # if cfg!(target_os = "linux") { + /// use std::io; + /// + /// let error = io::Error::from_raw_os_error(98); + /// assert_eq!(error.kind(), io::ErrorKind::AddrInUse); + /// # } + /// ``` + /// + /// On Windows: + /// + /// ``` + /// # if cfg!(windows) { + /// use std::io; + /// + /// let error = io::Error::from_raw_os_error(10048); + /// assert_eq!(error.kind(), io::ErrorKind::AddrInUse); + /// # } + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn from_raw_os_error(code: i32) -> Error { Error { repr: Repr::Os(code) } @@ -350,7 +374,6 @@ mod test { use prelude::v1::*; use super::{Error, ErrorKind}; use error; - use error::Error as error_Error; use fmt; use sys::os::error_string; diff --git a/src/libstd/io/lazy.rs b/src/libstd/io/lazy.rs index 65667f24dda..11551601207 100644 --- a/src/libstd/io/lazy.rs +++ b/src/libstd/io/lazy.rs @@ -12,11 +12,12 @@ use prelude::v1::*; use cell::Cell; use ptr; -use sync::{StaticMutex, Arc}; +use sync::Arc; use sys_common; +use sys_common::mutex::Mutex; pub struct Lazy<T> { - lock: StaticMutex, + lock: Mutex, ptr: Cell<*mut Arc<T>>, init: fn() -> Arc<T>, } @@ -26,23 +27,25 @@ unsafe impl<T> Sync for Lazy<T> {} impl<T: Send + Sync + 'static> Lazy<T> { pub const fn new(init: fn() -> Arc<T>) -> Lazy<T> { Lazy { - lock: StaticMutex::new(), + lock: Mutex::new(), ptr: Cell::new(ptr::null_mut()), init: init } } pub fn get(&'static self) -> Option<Arc<T>> { - let _g = self.lock.lock(); - let ptr = self.ptr.get(); unsafe { - if ptr.is_null() { + self.lock.lock(); + let ptr = self.ptr.get(); + let ret = if ptr.is_null() { Some(self.init()) } else if ptr as usize == 1 { None } else { Some((*ptr).clone()) - } + }; + self.lock.unlock(); + return ret } } @@ -52,10 +55,10 @@ impl<T: Send + Sync + 'static> Lazy<T> { // the at exit handler). Otherwise we just return the freshly allocated // `Arc`. let registered = sys_common::at_exit(move || { - let g = self.lock.lock(); + self.lock.lock(); let ptr = self.ptr.get(); self.ptr.set(1 as *mut _); - drop(g); + self.lock.unlock(); drop(Box::from_raw(ptr)) }); let ret = (self.init)(); diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index d914d143e70..d5b255ee573 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -182,11 +182,10 @@ //! //! # fn foo() -> io::Result<()> { //! let f = try!(File::open("foo.txt")); -//! let mut reader = BufReader::new(f); +//! let reader = BufReader::new(f); //! //! for line in reader.lines() { -//! let line = try!(line); -//! println!("{}", line); +//! println!("{}", try!(line)); //! } //! //! # Ok(()) @@ -195,7 +194,7 @@ //! //! ## Functions //! -//! There are a number of [functions][functions] that offer access to various +//! There are a number of [functions][functions-list] that offer access to various //! features. For example, we can use three of these functions to copy everything //! from standard input to standard output: //! @@ -208,7 +207,7 @@ //! # } //! ``` //! -//! [functions]: #functions +//! [functions-list]: #functions-1 //! //! ## io::Result //! @@ -1523,6 +1522,18 @@ impl<T: BufRead> BufRead for Take<T> { } } +fn read_one_byte(reader: &mut Read) -> Option<Result<u8>> { + let mut buf = [0]; + loop { + return match reader.read(&mut buf) { + Ok(0) => None, + Ok(..) => Some(Ok(buf[0])), + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => Some(Err(e)), + }; + } +} + /// An iterator over `u8` values of a reader. /// /// This struct is generally created by calling [`bytes()`][bytes] on a reader. @@ -1539,12 +1550,7 @@ impl<R: Read> Iterator for Bytes<R> { type Item = Result<u8>; fn next(&mut self) -> Option<Result<u8>> { - let mut buf = [0]; - match self.inner.read(&mut buf) { - Ok(0) => None, - Ok(..) => Some(Ok(buf[0])), - Err(e) => Some(Err(e)), - } + read_one_byte(&mut self.inner) } } @@ -1580,11 +1586,10 @@ impl<R: Read> Iterator for Chars<R> { type Item = result::Result<char, CharsError>; fn next(&mut self) -> Option<result::Result<char, CharsError>> { - let mut buf = [0]; - let first_byte = match self.inner.read(&mut buf) { - Ok(0) => return None, - Ok(..) => buf[0], - Err(e) => return Some(Err(CharsError::Other(e))), + let first_byte = match read_one_byte(&mut self.inner) { + None => return None, + Some(Ok(b)) => b, + Some(Err(e)) => return Some(Err(CharsError::Other(e))), }; let width = core_str::utf8_char_width(first_byte); if width == 1 { return Some(Ok(first_byte as char)) } @@ -1596,6 +1601,7 @@ impl<R: Read> Iterator for Chars<R> { match self.inner.read(&mut buf[start..width]) { Ok(0) => return Some(Err(CharsError::NotUtf8)), Ok(n) => start += n, + Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, Err(e) => return Some(Err(CharsError::Other(e))), } } diff --git a/src/libstd/io/util.rs b/src/libstd/io/util.rs index 2815c0163d6..c8b52fc0467 100644 --- a/src/libstd/io/util.rs +++ b/src/libstd/io/util.rs @@ -78,14 +78,11 @@ pub struct Empty { _priv: () } /// A slightly sad example of not reading anything into a buffer: /// /// ``` -/// use std::io; -/// use std::io::Read; +/// use std::io::{self, Read}; /// -/// # fn foo() -> io::Result<String> { /// let mut buffer = String::new(); -/// try!(io::empty().read_to_string(&mut buffer)); -/// # Ok(buffer) -/// # } +/// io::empty().read_to_string(&mut buffer).unwrap(); +/// assert!(buffer.is_empty()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn empty() -> Empty { Empty { _priv: () } } @@ -113,6 +110,16 @@ pub struct Repeat { byte: u8 } /// /// All reads from this reader will succeed by filling the specified buffer with /// the given byte. +/// +/// # Examples +/// +/// ``` +/// use std::io::{self, Read}; +/// +/// let mut buffer = [0; 3]; +/// io::repeat(0b101).read_exact(&mut buffer).unwrap(); +/// assert_eq!(buffer, [0b101, 0b101, 0b101]); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn repeat(byte: u8) -> Repeat { Repeat { byte: byte } } @@ -139,6 +146,16 @@ pub struct Sink { _priv: () } /// /// All calls to `write` on the returned instance will return `Ok(buf.len())` /// and the contents of the buffer will not be inspected. +/// +/// # Examples +/// +/// ```rust +/// use std::io::{self, Write}; +/// +/// let buffer = vec![1, 2, 3, 5, 8]; +/// let num_bytes = io::sink().write(&buffer).unwrap(); +/// assert_eq!(num_bytes, 5); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn sink() -> Sink { Sink { _priv: () } } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index e14a31453d3..d05a5a09614 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -210,6 +210,8 @@ test(no_crate_inject, attr(deny(warnings))), test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))))] +#![needs_panic_runtime] + #![feature(alloc)] #![feature(allow_internal_unstable)] #![feature(asm)] @@ -245,6 +247,7 @@ #![feature(on_unimplemented)] #![feature(oom)] #![feature(optin_builtin_traits)] +#![feature(panic_unwind)] #![feature(placement_in_syntax)] #![feature(rand)] #![feature(raw)] @@ -252,6 +255,7 @@ #![feature(reflect_marker)] #![feature(rustc_attrs)] #![feature(shared)] +#![feature(sip_hash_13)] #![feature(slice_bytes)] #![feature(slice_concat_ext)] #![feature(slice_patterns)] @@ -270,6 +274,8 @@ #![feature(vec_push_all)] #![feature(zero_one)] #![feature(question_mark)] +#![feature(try_from)] +#![feature(needs_panic_runtime)] // Issue# 30592: Systematically use alloc_system during stage0 since jemalloc // might be unavailable or disabled @@ -300,6 +306,9 @@ extern crate alloc; extern crate rustc_unicode; extern crate libc; +// We always need an unwinder currently for backtraces +extern crate unwind; + #[cfg(stage0)] extern crate alloc_system; diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 39adda1066a..6f0f6ecab5b 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -17,9 +17,9 @@ /// The entry point for panic of Rust threads. /// /// This macro is used to inject panic into a Rust thread, causing the thread to -/// unwind and panic entirely. Each thread's panic can be reaped as the -/// `Box<Any>` type, and the single-argument form of the `panic!` macro will be -/// the value which is transmitted. +/// panic entirely. Each thread's panic can be reaped as the `Box<Any>` type, +/// and the single-argument form of the `panic!` macro will be the value which +/// is transmitted. /// /// The multi-argument form of this macro panics with a string and has the /// `format!` syntax for building a string. @@ -41,14 +41,14 @@ macro_rules! panic { panic!("explicit panic") }); ($msg:expr) => ({ - $crate::rt::begin_unwind($msg, { + $crate::rt::begin_panic($msg, { // static requires less code at runtime, more constant data static _FILE_LINE: (&'static str, u32) = (file!(), line!()); &_FILE_LINE }) }); ($fmt:expr, $($arg:tt)+) => ({ - $crate::rt::begin_unwind_fmt(format_args!($fmt, $($arg)+), { + $crate::rt::begin_panic_fmt(&format_args!($fmt, $($arg)+), { // The leading _'s are to avoid dead code warnings if this is // used inside a dead function. Just `#[allow(dead_code)]` is // insufficient, since the user may have @@ -98,7 +98,9 @@ macro_rules! print { ($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*))); } -/// Macro for printing to the standard output, with a newline. +/// Macro for printing to the standard output, with a newline. On all +/// platforms, the newline is the LINE FEED character (`\n`/`U+000A`) alone +/// (no additional CARRIAGE RETURN (`\r`/`U+000D`). /// /// Use the `format!` syntax to write data to the standard output. /// See `std::fmt` for more information. @@ -276,7 +278,7 @@ pub mod builtin { /// // fn concat_idents!(new, fun, name) { } // not usable in this way! /// # } /// ``` - #[stable(feature = "rust1", since = "1.0.0")] + #[unstable(feature = "concat_idents", issue = "29599")] #[macro_export] macro_rules! concat_idents { ($($e:ident),*) => ({ /* compiler built-in */ }) diff --git a/src/libstd/memchr.rs b/src/libstd/memchr.rs index 1d97611eabb..a408b4378e1 100644 --- a/src/libstd/memchr.rs +++ b/src/libstd/memchr.rs @@ -239,7 +239,7 @@ mod fallback { text[..offset].iter().rposition(|elt| *elt == x) } - // test fallback implementations on all plattforms + // test fallback implementations on all platforms #[test] fn matches_one() { assert_eq!(Some(0), memchr(b'a', b"a")); diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs index d510339f1c5..b93ca8277e6 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -344,6 +344,9 @@ impl hash::Hash for SocketAddrV6 { /// some other type (e.g. a string) just for it to be converted back to /// `SocketAddr` in constructor methods is pointless. /// +/// Addresses returned by the operating system that are not IP addresses are +/// silently ignored. +/// /// Some examples: /// /// ```no_run @@ -448,12 +451,7 @@ impl ToSocketAddrs for (Ipv6Addr, u16) { fn resolve_socket_addr(s: &str, p: u16) -> io::Result<vec::IntoIter<SocketAddr>> { let ips = lookup_host(s)?; - let v: Vec<_> = ips.map(|a| { - a.map(|mut a| { - a.set_port(p); - a - }) - }).collect()?; + let v: Vec<_> = ips.map(|mut a| { a.set_port(p); a }).collect(); Ok(v.into_iter()) } diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index adceee6d73e..2a8bd0c88be 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -59,6 +59,66 @@ pub enum Ipv6MulticastScope { Global } +impl IpAddr { + /// Returns true for the special 'unspecified' address ([IPv4], [IPv6]). + /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_unspecified + /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_unspecified + #[unstable(feature="ip", issue="27709", + reason="recently added and depends on unstable Ipv4Addr.is_unspecified()")] + pub fn is_unspecified(&self) -> bool { + match *self { + IpAddr::V4(ref a) => a.is_unspecified(), + IpAddr::V6(ref a) => a.is_unspecified(), + } + } + + /// Returns true if this is a loopback address ([IPv4], [IPv6]). + /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_loopback + /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_loopback + #[unstable(feature="ip", reason="recently added", issue="27709")] + pub fn is_loopback(&self) -> bool { + match *self { + IpAddr::V4(ref a) => a.is_loopback(), + IpAddr::V6(ref a) => a.is_loopback(), + } + } + + /// Returns true if the address appears to be globally routable ([IPv4], [IPv6]). + /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_global + /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_global + #[unstable(feature="ip", issue="27709", + reason="recently added and depends on unstable Ip{v4,v6}Addr.is_global()")] + pub fn is_global(&self) -> bool { + match *self { + IpAddr::V4(ref a) => a.is_global(), + IpAddr::V6(ref a) => a.is_global(), + } + } + + /// Returns true if this is a multicast address ([IPv4], [IPv6]). + /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_multicast + /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_multicast + #[unstable(feature="ip", reason="recently added", issue="27709")] + pub fn is_multicast(&self) -> bool { + match *self { + IpAddr::V4(ref a) => a.is_multicast(), + IpAddr::V6(ref a) => a.is_multicast(), + } + } + + /// Returns true if this address is in a range designated for documentation ([IPv4], [IPv6]). + /// [IPv4]: ../../std/net/struct.Ipv4Addr.html#method.is_documentation + /// [IPv6]: ../../std/net/struct.Ipv6Addr.html#method.is_documentation + #[unstable(feature="ip", issue="27709", + reason="recently added and depends on unstable Ipv6Addr.is_documentation()")] + pub fn is_documentation(&self) -> bool { + match *self { + IpAddr::V4(ref a) => a.is_documentation(), + IpAddr::V6(ref a) => a.is_documentation(), + } + } +} + impl Ipv4Addr { /// Creates a new IPv4 address from four eight-bit octets. /// @@ -82,14 +142,19 @@ impl Ipv4Addr { [(bits >> 24) as u8, (bits >> 16) as u8, (bits >> 8) as u8, bits as u8] } - /// Returns true for the special 'unspecified' address 0.0.0.0. + /// Returns true for the special 'unspecified' address (0.0.0.0). + /// + /// This property is defined in _UNIX Network Programming, Second Edition_, + /// W. Richard Stevens, p. 891; see also [ip7] + /// [ip7][http://man7.org/linux/man-pages/man7/ip.7.html] pub fn is_unspecified(&self) -> bool { self.inner.s_addr == 0 } /// Returns true if this is a loopback address (127.0.0.0/8). /// - /// This property is defined by RFC 6890 + /// This property is defined by [RFC 1122]. + /// [RFC 1122]: https://tools.ietf.org/html/rfc1122 #[stable(since = "1.7.0", feature = "ip_17")] pub fn is_loopback(&self) -> bool { self.octets()[0] == 127 @@ -97,7 +162,8 @@ impl Ipv4Addr { /// Returns true if this is a private address. /// - /// The private address ranges are defined in RFC1918 and include: + /// The private address ranges are defined in [RFC 1918] and include: + /// [RFC 1918]: https://tools.ietf.org/html/rfc1918 /// /// - 10.0.0.0/8 /// - 172.16.0.0/12 @@ -114,7 +180,8 @@ impl Ipv4Addr { /// Returns true if the address is link-local (169.254.0.0/16). /// - /// This property is defined by RFC 6890 + /// This property is defined by [RFC 3927]. + /// [RFC 3927]: https://tools.ietf.org/html/rfc3927 #[stable(since = "1.7.0", feature = "ip_17")] pub fn is_link_local(&self) -> bool { self.octets()[0] == 169 && self.octets()[1] == 254 @@ -137,18 +204,20 @@ impl Ipv4Addr { !self.is_broadcast() && !self.is_documentation() && !self.is_unspecified() } - /// Returns true if this is a multicast address. + /// Returns true if this is a multicast address (224.0.0.0/4). /// /// Multicast addresses have a most significant octet between 224 and 239, - /// and is defined by RFC 5771 + /// and is defined by [RFC 5771]. + /// [RFC 5771]: https://tools.ietf.org/html/rfc5771 #[stable(since = "1.7.0", feature = "ip_17")] pub fn is_multicast(&self) -> bool { self.octets()[0] >= 224 && self.octets()[0] <= 239 } - /// Returns true if this is a broadcast address. + /// Returns true if this is a broadcast address (255.255.255.255). /// - /// A broadcast address has all octets set to 255 as defined in RFC 919. + /// A broadcast address has all octets set to 255 as defined in [RFC 919]. + /// [RFC 919]: https://tools.ietf.org/html/rfc919 #[stable(since = "1.7.0", feature = "ip_17")] pub fn is_broadcast(&self) -> bool { self.octets()[0] == 255 && self.octets()[1] == 255 && @@ -157,7 +226,8 @@ impl Ipv4Addr { /// Returns true if this address is in a range designated for documentation. /// - /// This is defined in RFC 5737: + /// This is defined in [RFC 5737]: + /// [RFC 5737]: https://tools.ietf.org/html/rfc5737 /// /// - 192.0.2.0/24 (TEST-NET-1) /// - 198.51.100.0/24 (TEST-NET-2) @@ -251,7 +321,7 @@ impl PartialOrd for Ipv4Addr { #[stable(feature = "rust1", since = "1.0.0")] impl Ord for Ipv4Addr { fn cmp(&self, other: &Ipv4Addr) -> Ordering { - self.octets().cmp(&other.octets()) + ntoh(self.inner.s_addr).cmp(&ntoh(other.inner.s_addr)) } } @@ -321,9 +391,10 @@ impl Ipv6Addr { ] } - /// Returns true for the special 'unspecified' address ::. + /// Returns true for the special 'unspecified' address (::). /// - /// This property is defined in RFC 6890. + /// This property is defined in [RFC 4291]. + /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 #[stable(since = "1.7.0", feature = "ip_17")] pub fn is_unspecified(&self) -> bool { self.segments() == [0, 0, 0, 0, 0, 0, 0, 0] @@ -331,7 +402,8 @@ impl Ipv6Addr { /// Returns true if this is a loopback address (::1). /// - /// This property is defined in RFC 6890. + /// This property is defined in [RFC 4291]. + /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 #[stable(since = "1.7.0", feature = "ip_17")] pub fn is_loopback(&self) -> bool { self.segments() == [0, 0, 0, 0, 0, 0, 0, 1] @@ -352,26 +424,33 @@ impl Ipv6Addr { } } - /// Returns true if this is a unique local address (IPv6). + /// Returns true if this is a unique local address (fc00::/7). /// - /// Unique local addresses are defined in RFC4193 and have the form fc00::/7. + /// This property is defined in [RFC 4193]. + /// [RFC 4193]: https://tools.ietf.org/html/rfc4193 pub fn is_unique_local(&self) -> bool { (self.segments()[0] & 0xfe00) == 0xfc00 } /// Returns true if the address is unicast and link-local (fe80::/10). + /// + /// This property is defined in [RFC 4291]. + /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 pub fn is_unicast_link_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfe80 } - /// Returns true if this is a deprecated unicast site-local address (IPv6 - /// fec0::/10). + /// Returns true if this is a deprecated unicast site-local address + /// (fec0::/10). pub fn is_unicast_site_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfec0 } /// Returns true if this is an address reserved for documentation - /// This is defined to be 2001:db8::/32 in RFC RFC 3849 + /// (2001:db8::/32). + /// + /// This property is defined in [RFC 3849]. + /// [RFC 3849]: https://tools.ietf.org/html/rfc3849 pub fn is_documentation(&self) -> bool { (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) } @@ -411,10 +490,10 @@ impl Ipv6Addr { } } - /// Returns true if this is a multicast address. + /// Returns true if this is a multicast address (ff00::/8). /// - /// Multicast addresses have the form ff00::/8, and this property is defined - /// by RFC 3956. + /// This property is defined by [RFC 4291]. + /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 #[stable(since = "1.7.0", feature = "ip_17")] pub fn is_multicast(&self) -> bool { (self.segments()[0] & 0xff00) == 0xff00 @@ -742,6 +821,67 @@ mod tests { } #[test] + fn ip_properties() { + fn check4(octets: &[u8; 4], unspec: bool, loopback: bool, + global: bool, multicast: bool, documentation: bool) { + let ip = IpAddr::V4(Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3])); + assert_eq!(ip.is_unspecified(), unspec); + assert_eq!(ip.is_loopback(), loopback); + assert_eq!(ip.is_global(), global); + assert_eq!(ip.is_multicast(), multicast); + assert_eq!(ip.is_documentation(), documentation); + } + + fn check6(str_addr: &str, unspec: bool, loopback: bool, + global: bool, u_doc: bool, mcast: bool) { + let ip = IpAddr::V6(str_addr.parse().unwrap()); + assert_eq!(ip.is_unspecified(), unspec); + assert_eq!(ip.is_loopback(), loopback); + assert_eq!(ip.is_global(), global); + assert_eq!(ip.is_documentation(), u_doc); + assert_eq!(ip.is_multicast(), mcast); + } + + // address unspec loopbk global multicast doc + check4(&[0, 0, 0, 0], true, false, false, false, false); + check4(&[0, 0, 0, 1], false, false, true, false, false); + check4(&[0, 1, 0, 0], false, false, true, false, false); + check4(&[10, 9, 8, 7], false, false, false, false, false); + check4(&[127, 1, 2, 3], false, true, false, false, false); + check4(&[172, 31, 254, 253], false, false, false, false, false); + check4(&[169, 254, 253, 242], false, false, false, false, false); + check4(&[192, 0, 2, 183], false, false, false, false, true); + check4(&[192, 1, 2, 183], false, false, true, false, false); + check4(&[192, 168, 254, 253], false, false, false, false, false); + check4(&[198, 51, 100, 0], false, false, false, false, true); + check4(&[203, 0, 113, 0], false, false, false, false, true); + check4(&[203, 2, 113, 0], false, false, true, false, false); + check4(&[224, 0, 0, 0], false, false, true, true, false); + check4(&[239, 255, 255, 255], false, false, true, true, false); + check4(&[255, 255, 255, 255], false, false, false, false, false); + + // address unspec loopbk global doc mcast + check6("::", true, false, false, false, false); + check6("::1", false, true, false, false, false); + check6("::0.0.0.2", false, false, true, false, false); + check6("1::", false, false, true, false, false); + check6("fc00::", false, false, false, false, false); + check6("fdff:ffff::", false, false, false, false, false); + check6("fe80:ffff::", false, false, false, false, false); + check6("febf:ffff::", false, false, false, false, false); + check6("fec0::", false, false, false, false, false); + check6("ff01::", false, false, false, false, true); + check6("ff02::", false, false, false, false, true); + check6("ff03::", false, false, false, false, true); + check6("ff04::", false, false, false, false, true); + check6("ff05::", false, false, false, false, true); + check6("ff08::", false, false, false, false, true); + check6("ff0e::", false, false, true, false, true); + check6("2001:db8:85a3::8a2e:370:7334", false, false, false, true, false); + check6("102:304:506:708:90a:b0c:d0e:f10", false, false, true, false, false); + } + + #[test] fn ipv4_properties() { fn check(octets: &[u8; 4], unspec: bool, loopback: bool, private: bool, link_local: bool, global: bool, diff --git a/src/libstd/net/mod.rs b/src/libstd/net/mod.rs index 45070460282..ac13b23ebee 100644 --- a/src/libstd/net/mod.rs +++ b/src/libstd/net/mod.rs @@ -98,8 +98,8 @@ pub struct LookupHost(net_imp::LookupHost); addresses", issue = "27705")] impl Iterator for LookupHost { - type Item = io::Result<SocketAddr>; - fn next(&mut self) -> Option<io::Result<SocketAddr>> { self.0.next() } + type Item = SocketAddr; + fn next(&mut self) -> Option<SocketAddr> { self.0.next() } } /// Resolve the host specified by `host` as a number of `SocketAddr` instances. @@ -107,6 +107,9 @@ impl Iterator for LookupHost { /// This method may perform a DNS query to resolve `host` and may also inspect /// system configuration to resolve the specified hostname. /// +/// The returned iterator will skip over any unknown addresses returned by the +/// operating system. +/// /// # Examples /// /// ```no_run @@ -116,7 +119,7 @@ impl Iterator for LookupHost { /// /// # fn foo() -> std::io::Result<()> { /// for host in try!(net::lookup_host("rust-lang.org")) { -/// println!("found address: {}", try!(host)); +/// println!("found address: {}", host); /// } /// # Ok(()) /// # } diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 94aa3d6b513..7a676c041ad 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -110,6 +110,7 @@ mod cmath { } #[inline] + #[allow(deprecated)] pub unsafe fn frexpf(x: c_float, value: &mut c_int) -> c_float { let (a, b) = f64::frexp(x as f64); *value = b as c_int; @@ -117,6 +118,7 @@ mod cmath { } #[inline] + #[allow(deprecated)] pub unsafe fn ldexpf(x: c_float, n: c_int) -> c_float { f64::ldexp(x as f64, n as isize) as c_float } @@ -217,7 +219,7 @@ impl f32 { /// // Values between `0` and `min` are Subnormal. /// assert!(!lower_than_min.is_normal()); /// ``` - /// [subnormal]: http://en.wikipedia.org/wiki/Denormal_number + /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn is_normal(self) -> bool { num::Float::is_normal(self) } @@ -265,7 +267,11 @@ impl f32 { /// [floating-point]: ../reference.html#machine-types #[unstable(feature = "float_extras", reason = "signature is undecided", issue = "27752")] + #[rustc_deprecated(since = "1.11.0", + reason = "never really came to fruition and easily \ + implementable outside the standard library")] #[inline] + #[allow(deprecated)] pub fn integer_decode(self) -> (u64, i16, i8) { num::Float::integer_decode(self) } @@ -718,6 +724,9 @@ impl f32 { #[unstable(feature = "float_extras", reason = "pending integer conventions", issue = "27752")] + #[rustc_deprecated(since = "1.11.0", + reason = "never really came to fruition and easily \ + implementable outside the standard library")] #[inline] pub fn ldexp(x: f32, exp: isize) -> f32 { unsafe { cmath::ldexpf(x, exp as c_int) } @@ -747,6 +756,9 @@ impl f32 { #[unstable(feature = "float_extras", reason = "pending integer conventions", issue = "27752")] + #[rustc_deprecated(since = "1.11.0", + reason = "never really came to fruition and easily \ + implementable outside the standard library")] #[inline] pub fn frexp(self) -> (f32, isize) { unsafe { @@ -773,6 +785,9 @@ impl f32 { #[unstable(feature = "float_extras", reason = "unsure about its place in the world", issue = "27752")] + #[rustc_deprecated(since = "1.11.0", + reason = "never really came to fruition and easily \ + implementable outside the standard library")] #[inline] pub fn next_after(self, other: f32) -> f32 { unsafe { cmath::nextafterf(self, other) } @@ -829,6 +844,13 @@ impl f32 { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] + #[rustc_deprecated(since = "1.10.0", + reason = "you probably meant `(self - other).abs()`: \ + this operation is `(self - other).max(0.0)` (also \ + known as `fdimf` in C). If you truly need the positive \ + difference, consider using that expression or the C function \ + `fdimf`, depending on how you wish to handle NaN (please consider \ + filing an issue describing your use-case too).")] pub fn abs_sub(self, other: f32) -> f32 { unsafe { cmath::fdimf(self, other) } } @@ -916,12 +938,12 @@ impl f32 { /// Computes the tangent of a number (in radians). /// /// ``` - /// use std::f64; + /// use std::f32; /// - /// let x = f64::consts::PI/4.0; + /// let x = f32::consts::PI / 4.0; /// let abs_difference = (x.tan() - 1.0).abs(); /// - /// assert!(abs_difference < 1e-10); + /// assert!(abs_difference <= f32::EPSILON); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -939,7 +961,7 @@ impl f32 { /// let f = f32::consts::PI / 2.0; /// /// // asin(sin(pi/2)) - /// let abs_difference = f.sin().asin().abs_sub(f32::consts::PI / 2.0); + /// let abs_difference = (f.sin().asin() - f32::consts::PI / 2.0).abs(); /// /// assert!(abs_difference <= f32::EPSILON); /// ``` @@ -959,7 +981,7 @@ impl f32 { /// let f = f32::consts::PI / 4.0; /// /// // acos(cos(pi/4)) - /// let abs_difference = f.cos().acos().abs_sub(f32::consts::PI / 4.0); + /// let abs_difference = (f.cos().acos() - f32::consts::PI / 4.0).abs(); /// /// assert!(abs_difference <= f32::EPSILON); /// ``` @@ -978,7 +1000,7 @@ impl f32 { /// let f = 1.0f32; /// /// // atan(tan(1)) - /// let abs_difference = f.tan().atan().abs_sub(1.0); + /// let abs_difference = (f.tan().atan() - 1.0).abs(); /// /// assert!(abs_difference <= f32::EPSILON); /// ``` @@ -1045,12 +1067,14 @@ impl f32 { /// number is close to zero. /// /// ``` - /// let x = 7.0f64; + /// use std::f32; + /// + /// let x = 6.0f32; /// - /// // e^(ln(7)) - 1 - /// let abs_difference = x.ln().exp_m1().abs_sub(6.0); + /// // e^(ln(6)) - 1 + /// let abs_difference = (x.ln().exp_m1() - 5.0).abs(); /// - /// assert!(abs_difference < 1e-10); + /// assert!(abs_difference <= f32::EPSILON); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -1108,7 +1132,7 @@ impl f32 { /// let f = x.cosh(); /// // Solving cosh() at 1 gives this result /// let g = (e*e + 1.0)/(2.0*e); - /// let abs_difference = f.abs_sub(g); + /// let abs_difference = (f - g).abs(); /// /// // Same result /// assert!(abs_difference <= f32::EPSILON); @@ -1191,9 +1215,9 @@ impl f32 { /// let e = f32::consts::E; /// let f = e.tanh().atanh(); /// - /// let abs_difference = f.abs_sub(e); + /// let abs_difference = (f - e).abs(); /// - /// assert!(abs_difference <= f32::EPSILON); + /// assert!(abs_difference <= 1e-5); /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] @@ -1375,7 +1399,7 @@ mod tests { } #[test] - #[rustc_no_mir] // FIXME #27840 MIR NAN ends up negative. + #[allow(deprecated)] fn test_integer_decode() { assert_eq!(3.14159265359f32.integer_decode(), (13176795, -22, 1)); assert_eq!((-8573.5918555f32).integer_decode(), (8779358, -10, -1)); @@ -1384,7 +1408,11 @@ mod tests { assert_eq!((-0f32).integer_decode(), (0, -150, -1)); assert_eq!(INFINITY.integer_decode(), (8388608, 105, 1)); assert_eq!(NEG_INFINITY.integer_decode(), (8388608, 105, -1)); - assert_eq!(NAN.integer_decode(), (12582912, 105, 1)); + + // Ignore the "sign" (quiet / signalling flag) of NAN. + // It can vary between runtime operations and LLVM folding. + let (nan_m, nan_e, _nan_s) = NAN.integer_decode(); + assert_eq!((nan_m, nan_e), (12582912, 105)); } #[test] @@ -1699,6 +1727,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn test_ldexp() { let f1 = 2.0f32.powi(-123); let f2 = 2.0f32.powi(-111); @@ -1719,6 +1748,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn test_frexp() { let f1 = 2.0f32.powi(-123); let f2 = 2.0f32.powi(-111); @@ -1738,6 +1768,7 @@ mod tests { } #[test] #[cfg_attr(windows, ignore)] // FIXME #8755 + #[allow(deprecated)] fn test_frexp_nowin() { let inf: f32 = f32::INFINITY; let neg_inf: f32 = f32::NEG_INFINITY; @@ -1748,24 +1779,6 @@ mod tests { } #[test] - fn test_abs_sub() { - assert_eq!((-1f32).abs_sub(1f32), 0f32); - assert_eq!(1f32.abs_sub(1f32), 0f32); - assert_eq!(1f32.abs_sub(0f32), 1f32); - assert_eq!(1f32.abs_sub(-1f32), 2f32); - assert_eq!(NEG_INFINITY.abs_sub(0f32), 0f32); - assert_eq!(INFINITY.abs_sub(1f32), INFINITY); - assert_eq!(0f32.abs_sub(NEG_INFINITY), INFINITY); - assert_eq!(0f32.abs_sub(INFINITY), 0f32); - } - - #[test] - fn test_abs_sub_nowin() { - assert!(NAN.abs_sub(-1f32).is_nan()); - assert!(1f32.abs_sub(NAN).is_nan()); - } - - #[test] fn test_asinh() { assert_eq!(0.0f32.asinh(), 0.0f32); assert_eq!((-0.0f32).asinh(), -0.0f32); diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index 2beffb64d3d..67a1c302483 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -147,23 +147,23 @@ impl f64 { /// [subnormal][subnormal], or `NaN`. /// /// ``` - /// use std::f32; + /// use std::f64; /// - /// let min = f32::MIN_POSITIVE; // 1.17549435e-38f64 - /// let max = f32::MAX; - /// let lower_than_min = 1.0e-40_f32; - /// let zero = 0.0f32; + /// let min = f64::MIN_POSITIVE; // 2.2250738585072014e-308f64 + /// let max = f64::MAX; + /// let lower_than_min = 1.0e-308_f64; + /// let zero = 0.0f64; /// /// assert!(min.is_normal()); /// assert!(max.is_normal()); /// /// assert!(!zero.is_normal()); - /// assert!(!f32::NAN.is_normal()); - /// assert!(!f32::INFINITY.is_normal()); + /// assert!(!f64::NAN.is_normal()); + /// assert!(!f64::INFINITY.is_normal()); /// // Values between `0` and `min` are Subnormal. /// assert!(!lower_than_min.is_normal()); /// ``` - /// [subnormal]: http://en.wikipedia.org/wiki/Denormal_number + /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn is_normal(self) -> bool { num::Float::is_normal(self) } @@ -209,7 +209,11 @@ impl f64 { /// [floating-point]: ../reference.html#machine-types #[unstable(feature = "float_extras", reason = "signature is undecided", issue = "27752")] + #[rustc_deprecated(since = "1.11.0", + reason = "never really came to fruition and easily \ + implementable outside the standard library")] #[inline] + #[allow(deprecated)] pub fn integer_decode(self) -> (u64, i16, i8) { num::Float::integer_decode(self) } /// Returns the largest integer less than or equal to a number. @@ -613,6 +617,9 @@ impl f64 { #[unstable(feature = "float_extras", reason = "pending integer conventions", issue = "27752")] + #[rustc_deprecated(since = "1.11.0", + reason = "never really came to fruition and easily \ + implementable outside the standard library")] #[inline] pub fn ldexp(x: f64, exp: isize) -> f64 { unsafe { cmath::ldexp(x, exp as c_int) } @@ -640,6 +647,9 @@ impl f64 { #[unstable(feature = "float_extras", reason = "pending integer conventions", issue = "27752")] + #[rustc_deprecated(since = "1.11.0", + reason = "never really came to fruition and easily \ + implementable outside the standard library")] #[inline] pub fn frexp(self) -> (f64, isize) { unsafe { @@ -655,15 +665,18 @@ impl f64 { /// ``` /// #![feature(float_extras)] /// - /// let x = 1.0f32; + /// let x = 1.0f64; /// - /// let abs_diff = (x.next_after(2.0) - 1.00000011920928955078125_f32).abs(); + /// let abs_diff = (x.next_after(2.0) - 1.0000000000000002220446049250313_f64).abs(); /// /// assert!(abs_diff < 1e-10); /// ``` #[unstable(feature = "float_extras", reason = "unsure about its place in the world", issue = "27752")] + #[rustc_deprecated(since = "1.11.0", + reason = "never really came to fruition and easily \ + implementable outside the standard library")] #[inline] pub fn next_after(self, other: f64) -> f64 { unsafe { cmath::nextafter(self, other) } @@ -718,9 +731,16 @@ impl f64 { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn abs_sub(self, other: f64) -> f64 { - unsafe { cmath::fdim(self, other) } - } + #[rustc_deprecated(since = "1.10.0", + reason = "you probably meant `(self - other).abs()`: \ + this operation is `(self - other).max(0.0)` (also \ + known as `fdim` in C). If you truly need the positive \ + difference, consider using that expression or the C function \ + `fdim`, depending on how you wish to handle NaN (please consider \ + filing an issue describing your use-case too).")] + pub fn abs_sub(self, other: f64) -> f64 { + unsafe { cmath::fdim(self, other) } + } /// Takes the cubic root of a number. /// @@ -1270,7 +1290,7 @@ mod tests { } #[test] - #[rustc_no_mir] // FIXME #27840 MIR NAN ends up negative. + #[allow(deprecated)] fn test_integer_decode() { assert_eq!(3.14159265359f64.integer_decode(), (7074237752028906, -51, 1)); assert_eq!((-8573.5918555f64).integer_decode(), (4713381968463931, -39, -1)); @@ -1279,7 +1299,11 @@ mod tests { assert_eq!((-0f64).integer_decode(), (0, -1075, -1)); assert_eq!(INFINITY.integer_decode(), (4503599627370496, 972, 1)); assert_eq!(NEG_INFINITY.integer_decode(), (4503599627370496, 972, -1)); - assert_eq!(NAN.integer_decode(), (6755399441055744, 972, 1)); + + // Ignore the "sign" (quiet / signalling flag) of NAN. + // It can vary between runtime operations and LLVM folding. + let (nan_m, nan_e, _nan_s) = NAN.integer_decode(); + assert_eq!((nan_m, nan_e), (6755399441055744, 972)); } #[test] @@ -1594,6 +1618,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn test_ldexp() { let f1 = 2.0f64.powi(-123); let f2 = 2.0f64.powi(-111); @@ -1614,6 +1639,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn test_frexp() { let f1 = 2.0f64.powi(-123); let f2 = 2.0f64.powi(-111); @@ -1633,6 +1659,7 @@ mod tests { } #[test] #[cfg_attr(windows, ignore)] // FIXME #8755 + #[allow(deprecated)] fn test_frexp_nowin() { let inf: f64 = INFINITY; let neg_inf: f64 = NEG_INFINITY; @@ -1643,24 +1670,6 @@ mod tests { } #[test] - fn test_abs_sub() { - assert_eq!((-1f64).abs_sub(1f64), 0f64); - assert_eq!(1f64.abs_sub(1f64), 0f64); - assert_eq!(1f64.abs_sub(0f64), 1f64); - assert_eq!(1f64.abs_sub(-1f64), 2f64); - assert_eq!(NEG_INFINITY.abs_sub(0f64), 0f64); - assert_eq!(INFINITY.abs_sub(1f64), INFINITY); - assert_eq!(0f64.abs_sub(NEG_INFINITY), INFINITY); - assert_eq!(0f64.abs_sub(INFINITY), 0f64); - } - - #[test] - fn test_abs_sub_nowin() { - assert!(NAN.abs_sub(-1f64).is_nan()); - assert!(1f64.abs_sub(NAN).is_nan()); - } - - #[test] fn test_asinh() { assert_eq!(0.0f64.asinh(), 0.0f64); assert_eq!((-0.0f64).asinh(), -0.0f64); diff --git a/src/libstd/num/mod.rs b/src/libstd/num/mod.rs index 1886b4fdf59..20804d62dfa 100644 --- a/src/libstd/num/mod.rs +++ b/src/libstd/num/mod.rs @@ -17,9 +17,10 @@ #![allow(missing_docs)] #[stable(feature = "rust1", since = "1.0.0")] +#[allow(deprecated)] pub use core::num::{Zero, One}; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::num::{FpCategory, ParseIntError, ParseFloatError}; +pub use core::num::{FpCategory, ParseIntError, ParseFloatError, TryFromIntError}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::num::Wrapping; @@ -46,7 +47,6 @@ pub fn test_num<T>(ten: T, two: T) where #[cfg(test)] mod tests { - use super::*; use u8; use u16; use u32; @@ -198,15 +198,14 @@ mod tests { #[test] fn test_pow() { - fn naive_pow<T: Mul<Output=T> + One + Copy>(base: T, exp: usize) -> T { - let one: T = T::one(); + fn naive_pow<T: Mul<Output=T> + Copy>(one: T, base: T, exp: usize) -> T { (0..exp).fold(one, |acc, _| acc * base) } 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_eq!(result, naive_pow(1, $num, $exp)); }} } assert_pow!((3u32, 0 ) => 1); diff --git a/src/libstd/os/android/raw.rs b/src/libstd/os/android/raw.rs index ce6e810592c..5e473a933a6 100644 --- a/src/libstd/os/android/raw.rs +++ b/src/libstd/os/android/raw.rs @@ -20,7 +20,8 @@ use os::raw::c_long; -#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = c_long; +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = c_long; #[doc(inline)] #[stable(feature = "raw_ext", since = "1.1.0")] diff --git a/src/libstd/os/bitrig/raw.rs b/src/libstd/os/bitrig/raw.rs index 3fc3c5937f4..28958575eb2 100644 --- a/src/libstd/os/bitrig/raw.rs +++ b/src/libstd/os/bitrig/raw.rs @@ -31,7 +31,8 @@ use os::unix::raw::{uid_t, gid_t}; #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64; #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; -#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize; +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = usize; #[repr(C)] #[derive(Clone)] diff --git a/src/libstd/os/dragonfly/raw.rs b/src/libstd/os/dragonfly/raw.rs index 83e0be0d158..5da2540ceee 100644 --- a/src/libstd/os/dragonfly/raw.rs +++ b/src/libstd/os/dragonfly/raw.rs @@ -30,7 +30,8 @@ use os::raw::c_long; #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64; #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; -#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize; +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = usize; #[repr(C)] #[derive(Clone)] diff --git a/src/libstd/os/emscripten/raw.rs b/src/libstd/os/emscripten/raw.rs index 9da400a6913..bcd85f8e29a 100644 --- a/src/libstd/os/emscripten/raw.rs +++ b/src/libstd/os/emscripten/raw.rs @@ -25,7 +25,8 @@ use os::raw::{c_long, c_short, c_uint, c_ulong}; #[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64; #[stable(feature = "raw_ext", since = "1.1.0")] pub type mode_t = u32; -#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = c_ulong; +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = c_ulong; #[doc(inline)] #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64; diff --git a/src/libstd/os/freebsd/raw.rs b/src/libstd/os/freebsd/raw.rs index 989eef63d82..c755e5af8d9 100644 --- a/src/libstd/os/freebsd/raw.rs +++ b/src/libstd/os/freebsd/raw.rs @@ -30,7 +30,8 @@ use os::raw::c_long; #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64; #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; -#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize; +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = usize; #[repr(C)] #[derive(Clone)] diff --git a/src/libstd/os/ios/raw.rs b/src/libstd/os/ios/raw.rs index 5a2de14b28b..8b34a932a17 100644 --- a/src/libstd/os/ios/raw.rs +++ b/src/libstd/os/ios/raw.rs @@ -29,7 +29,8 @@ use os::raw::c_long; #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64; #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; -#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize; +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = usize; #[repr(C)] #[derive(Clone)] diff --git a/src/libstd/os/linux/raw.rs b/src/libstd/os/linux/raw.rs index 4113966841b..1be76961fea 100644 --- a/src/libstd/os/linux/raw.rs +++ b/src/libstd/os/linux/raw.rs @@ -23,7 +23,8 @@ use os::raw::c_ulong; #[stable(feature = "raw_ext", since = "1.1.0")] pub type dev_t = u64; #[stable(feature = "raw_ext", since = "1.1.0")] pub type mode_t = u32; -#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = c_ulong; +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = c_ulong; #[doc(inline)] #[stable(feature = "raw_ext", since = "1.1.0")] diff --git a/src/libstd/os/macos/raw.rs b/src/libstd/os/macos/raw.rs index 2148670ccbd..8f9b29462c4 100644 --- a/src/libstd/os/macos/raw.rs +++ b/src/libstd/os/macos/raw.rs @@ -29,7 +29,8 @@ use os::raw::c_long; #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64; #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; -#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize; +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = usize; #[repr(C)] #[derive(Clone)] diff --git a/src/libstd/os/nacl/raw.rs b/src/libstd/os/nacl/raw.rs index 9a10fbcc30b..3c3d4410a2a 100644 --- a/src/libstd/os/nacl/raw.rs +++ b/src/libstd/os/nacl/raw.rs @@ -30,7 +30,8 @@ #[stable(feature = "raw_ext", since = "1.1.0")] pub type blksize_t = u64; #[stable(feature = "raw_ext", since = "1.1.0")] pub type blkcnt_t = u64; -#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize; +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = usize; #[repr(C)] #[derive(Copy, Clone)] diff --git a/src/libstd/os/netbsd/raw.rs b/src/libstd/os/netbsd/raw.rs index bc30c1a7f48..9b2e037e59a 100644 --- a/src/libstd/os/netbsd/raw.rs +++ b/src/libstd/os/netbsd/raw.rs @@ -31,7 +31,8 @@ use os::unix::raw::{uid_t, gid_t}; #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64; #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; -#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize; +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = usize; #[repr(C)] #[derive(Clone)] diff --git a/src/libstd/os/openbsd/raw.rs b/src/libstd/os/openbsd/raw.rs index 0e9a2128bc2..6142f036218 100644 --- a/src/libstd/os/openbsd/raw.rs +++ b/src/libstd/os/openbsd/raw.rs @@ -30,7 +30,8 @@ use os::raw::c_long; #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64; #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; -#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize; +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = usize; #[repr(C)] #[derive(Clone)] diff --git a/src/libstd/os/solaris/raw.rs b/src/libstd/os/solaris/raw.rs index 6a75b36bd6b..b84fdba9ca2 100644 --- a/src/libstd/os/solaris/raw.rs +++ b/src/libstd/os/solaris/raw.rs @@ -31,7 +31,8 @@ use os::unix::raw::{uid_t, gid_t}; #[stable(feature = "raw_ext", since = "1.1.0")] pub type off_t = u64; #[stable(feature = "raw_ext", since = "1.1.0")] pub type time_t = i64; -#[unstable(feature = "pthread_t", issue = "29791")] pub type pthread_t = usize; +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = usize; #[repr(C)] #[derive(Clone)] diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index bbd89af01a7..ba18d15f5c4 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -16,37 +16,23 @@ use any::Any; use boxed::Box; use cell::UnsafeCell; use ops::{Deref, DerefMut}; +use panicking; use ptr::{Unique, Shared}; use rc::Rc; use sync::{Arc, Mutex, RwLock}; -use sys_common::unwind; use thread::Result; -#[unstable(feature = "panic_handler", issue = "30449")] +#[stable(feature = "panic_hooks", since = "1.10.0")] pub use panicking::{take_hook, set_hook, PanicInfo, Location}; -/// -#[rustc_deprecated(since = "1.9.0", reason = "renamed to set_hook")] -#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] -pub fn set_handler<F>(handler: F) where F: Fn(&PanicInfo) + 'static + Sync + Send { - set_hook(Box::new(handler)) -} - -/// -#[rustc_deprecated(since = "1.9.0", reason = "renamed to take_hook")] -#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] -pub fn take_handler() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> { - take_hook() -} - /// A marker trait which represents "panic safe" types in Rust. /// /// This trait is implemented by default for many types and behaves similarly in /// terms of inference of implementation to the `Send` and `Sync` traits. The -/// purpose of this trait is to encode what types are safe to cross a `recover` -/// boundary with no fear of panic safety. +/// purpose of this trait is to encode what types are safe to cross a `catch_unwind` +/// boundary with no fear of unwind safety. /// -/// ## What is panic safety? +/// ## What is unwind safety? /// /// In Rust a function can "return" early if it either panics or calls a /// function which transitively panics. This sort of control flow is not always @@ -59,74 +45,66 @@ pub fn take_handler() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> { /// /// Typically in Rust, it is difficult to perform step (2) because catching a /// panic involves either spawning a thread (which in turns makes it difficult -/// to later witness broken invariants) or using the `recover` function in this +/// to later witness broken invariants) or using the `catch_unwind` function in this /// module. Additionally, even if an invariant is witnessed, it typically isn't a -/// problem in Rust because there's no uninitialized values (like in C or C++). +/// problem in Rust because there are no uninitialized values (like in C or C++). /// /// It is possible, however, for **logical** invariants to be broken in Rust, -/// which can end up causing behavioral bugs. Another key aspect of panic safety +/// which can end up causing behavioral bugs. Another key aspect of unwind safety /// in Rust is that, in the absence of `unsafe` code, a panic cannot lead to /// memory unsafety. /// -/// That was a bit of a whirlwind tour of panic safety, but for more information -/// about panic safety and how it applies to Rust, see an [associated RFC][rfc]. +/// That was a bit of a whirlwind tour of unwind safety, but for more information +/// about unwind safety and how it applies to Rust, see an [associated RFC][rfc]. /// /// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md /// -/// ## What is `RecoverSafe`? +/// ## What is `UnwindSafe`? /// -/// Now that we've got an idea of what panic safety is in Rust, it's also +/// Now that we've got an idea of what unwind safety is in Rust, it's also /// important to understand what this trait represents. As mentioned above, one -/// way to witness broken invariants is through the `recover` function in this +/// way to witness broken invariants is through the `catch_unwind` function in this /// module as it allows catching a panic and then re-using the environment of /// the closure. /// -/// Simply put, a type `T` implements `RecoverSafe` if it cannot easily allow -/// witnessing a broken invariant through the use of `recover` (catching a +/// Simply put, a type `T` implements `UnwindSafe` if it cannot easily allow +/// witnessing a broken invariant through the use of `catch_unwind` (catching a /// panic). This trait is a marker trait, so it is automatically implemented for -/// many types, and it is also structurally composed (e.g. a struct is recover -/// safe if all of its components are recover safe). +/// many types, and it is also structurally composed (e.g. a struct is unwind +/// safe if all of its components are unwind safe). /// /// Note, however, that this is not an unsafe trait, so there is not a succinct /// contract that this trait is providing. Instead it is intended as more of a -/// "speed bump" to alert users of `recover` that broken invariants may be +/// "speed bump" to alert users of `catch_unwind` that broken invariants may be /// witnessed and may need to be accounted for. /// /// ## Who implements `UnwindSafe`? /// /// Types such as `&mut T` and `&RefCell<T>` are examples which are **not** -/// recover safe. The general idea is that any mutable state which can be shared -/// across `recover` is not recover safe by default. This is because it is very -/// easy to witness a broken invariant outside of `recover` as the data is +/// unwind safe. The general idea is that any mutable state which can be shared +/// across `catch_unwind` is not unwind safe by default. This is because it is very +/// easy to witness a broken invariant outside of `catch_unwind` as the data is /// simply accessed as usual. /// -/// Types like `&Mutex<T>`, however, are recover safe because they implement +/// Types like `&Mutex<T>`, however, are unwind safe because they implement /// poisoning by default. They still allow witnessing a broken invariant, but /// they already provide their own "speed bumps" to do so. /// /// ## When should `UnwindSafe` be used? /// /// Is not intended that most types or functions need to worry about this trait. -/// It is only used as a bound on the `recover` function and as mentioned above, -/// the lack of `unsafe` means it is mostly an advisory. The `AssertRecoverSafe` +/// It is only used as a bound on the `catch_unwind` function and as mentioned above, +/// the lack of `unsafe` means it is mostly an advisory. The `AssertUnwindSafe` /// wrapper struct in this module can be used to force this trait to be -/// implemented for any closed over variables passed to the `recover` function +/// implemented for any closed over variables passed to the `catch_unwind` function /// (more on this below). #[stable(feature = "catch_unwind", since = "1.9.0")] #[rustc_on_unimplemented = "the type {Self} may not be safely transferred \ - across a recover boundary"] + across an unwind boundary"] pub trait UnwindSafe {} -/// Deprecated, renamed to UnwindSafe -#[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")] -#[rustc_deprecated(reason = "renamed to `UnwindSafe`", since = "1.9.0")] -pub trait RecoverSafe {} -#[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")] -#[allow(deprecated)] -impl<T: UnwindSafe> RecoverSafe for T {} - /// A marker trait representing types where a shared reference is considered -/// recover safe. +/// unwind safe. /// /// This trait is namely not implemented by `UnsafeCell`, the root of all /// interior mutability. @@ -136,23 +114,23 @@ impl<T: UnwindSafe> RecoverSafe for T {} #[stable(feature = "catch_unwind", since = "1.9.0")] #[rustc_on_unimplemented = "the type {Self} contains interior mutability \ and a reference may not be safely transferrable \ - across a recover boundary"] + across a catch_unwind boundary"] pub trait RefUnwindSafe {} -/// A simple wrapper around a type to assert that it is panic safe. +/// A simple wrapper around a type to assert that it is unwind safe. /// -/// When using `recover` it may be the case that some of the closed over -/// variables are not panic safe. For example if `&mut T` is captured the -/// compiler will generate a warning indicating that it is not panic safe. It +/// When using `catch_unwind` it may be the case that some of the closed over +/// variables are not unwind safe. For example if `&mut T` is captured the +/// compiler will generate a warning indicating that it is not unwind safe. It /// may not be the case, however, that this is actually a problem due to the -/// specific usage of `recover` if panic safety is specifically taken into +/// specific usage of `catch_unwind` if unwind safety is specifically taken into /// account. This wrapper struct is useful for a quick and lightweight -/// annotation that a variable is indeed panic safe. +/// annotation that a variable is indeed unwind safe. /// /// # Examples /// /// One way to use `AssertUnwindSafe` is to assert that the entire closure -/// itself is recover safe, bypassing all checks for all variables: +/// itself is unwind safe, bypassing all checks for all variables: /// /// ``` /// use std::panic::{self, AssertUnwindSafe}; @@ -160,7 +138,7 @@ pub trait RefUnwindSafe {} /// let mut variable = 4; /// /// // This code will not compile because the closure captures `&mut variable` -/// // which is not considered panic safe by default. +/// // which is not considered unwind safe by default. /// /// // panic::catch_unwind(|| { /// // variable += 3; @@ -202,11 +180,6 @@ pub struct AssertUnwindSafe<T>( pub T ); -/// Deprecated, renamed to `AssertUnwindSafe` -#[unstable(feature = "recover", issue = "27719")] -#[rustc_deprecated(reason = "renamed to `AssertUnwindSafe`", since = "1.9.0")] -pub struct AssertRecoverSafe<T>(pub T); - // Implementations of the `UnwindSafe` trait: // // * By default everything is unwind safe @@ -234,19 +207,16 @@ impl<T: ?Sized> UnwindSafe for Mutex<T> {} impl<T: ?Sized> UnwindSafe for RwLock<T> {} #[stable(feature = "catch_unwind", since = "1.9.0")] impl<T> UnwindSafe for AssertUnwindSafe<T> {} -#[unstable(feature = "recover", issue = "27719")] -#[allow(deprecated)] -impl<T> UnwindSafe for AssertRecoverSafe<T> {} // not covered via the Shared impl above b/c the inner contents use -// Cell/AtomicUsize, but the usage here is recover safe so we can lift the +// Cell/AtomicUsize, but the usage here is unwind safe so we can lift the // impl up one level to Arc/Rc itself #[stable(feature = "catch_unwind", since = "1.9.0")] impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Rc<T> {} #[stable(feature = "catch_unwind", since = "1.9.0")] impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Arc<T> {} -// Pretty simple implementations for the `RefRecoverSafe` marker trait, +// Pretty simple implementations for the `RefUnwindSafe` marker trait, // basically just saying that this is a marker trait and `UnsafeCell` is the // only thing which doesn't implement it (which then transitively applies to // everything else). @@ -256,9 +226,11 @@ impl RefUnwindSafe for .. {} impl<T: ?Sized> !RefUnwindSafe for UnsafeCell<T> {} #[stable(feature = "catch_unwind", since = "1.9.0")] impl<T> RefUnwindSafe for AssertUnwindSafe<T> {} -#[unstable(feature = "recover", issue = "27719")] -#[allow(deprecated)] -impl<T> RefUnwindSafe for AssertRecoverSafe<T> {} + +#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] +impl<T: ?Sized> RefUnwindSafe for Mutex<T> {} +#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")] +impl<T: ?Sized> RefUnwindSafe for RwLock<T> {} #[stable(feature = "catch_unwind", since = "1.9.0")] impl<T> Deref for AssertUnwindSafe<T> { @@ -285,53 +257,6 @@ impl<R, F: FnOnce() -> R> FnOnce<()> for AssertUnwindSafe<F> { } } -#[allow(deprecated)] -impl<T> AssertRecoverSafe<T> { - /// Creates a new `AssertRecoverSafe` wrapper around the provided type. - #[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")] - #[rustc_deprecated(reason = "the type's field is now public, construct it directly", - since = "1.9.0")] - pub fn new(t: T) -> AssertRecoverSafe<T> { - AssertRecoverSafe(t) - } - - /// Consumes the `AssertRecoverSafe`, returning the wrapped value. - #[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")] - #[rustc_deprecated(reason = "the type's field is now public, access it directly", - since = "1.9.0")] - pub fn into_inner(self) -> T { - self.0 - } -} - -#[unstable(feature = "recover", issue = "27719")] -#[allow(deprecated)] -impl<T> Deref for AssertRecoverSafe<T> { - type Target = T; - - fn deref(&self) -> &T { - &self.0 - } -} - -#[unstable(feature = "recover", issue = "27719")] -#[allow(deprecated)] -impl<T> DerefMut for AssertRecoverSafe<T> { - fn deref_mut(&mut self) -> &mut T { - &mut self.0 - } -} - -#[unstable(feature = "recover", issue = "27719")] -#[allow(deprecated)] -impl<R, F: FnOnce() -> R> FnOnce<()> for AssertRecoverSafe<F> { - type Output = R; - - extern "rust-call" fn call_once(self, _args: ()) -> R { - (self.0)() - } -} - /// Invokes a closure, capturing the cause of an unwinding panic if one occurs. /// /// This function will return `Ok` with the closure's result if the closure @@ -352,9 +277,9 @@ impl<R, F: FnOnce() -> R> FnOnce<()> for AssertRecoverSafe<F> { /// that all captured variables are safe to cross this boundary. The purpose of /// this bound is to encode the concept of [exception safety][rfc] in the type /// system. Most usage of this function should not need to worry about this -/// bound as programs are naturally panic safe without `unsafe` code. If it +/// bound as programs are naturally unwind safe without `unsafe` code. If it /// becomes a problem the associated `AssertUnwindSafe` wrapper type in this -/// module can be used to quickly assert that the usage here is indeed exception +/// module can be used to quickly assert that the usage here is indeed unwind /// safe. /// /// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md @@ -383,22 +308,12 @@ impl<R, F: FnOnce() -> R> FnOnce<()> for AssertRecoverSafe<F> { /// ``` #[stable(feature = "catch_unwind", since = "1.9.0")] pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> { - let mut result = None; unsafe { - let result = &mut result; - unwind::try(move || *result = Some(f()))? + panicking::try(f) } - Ok(result.unwrap()) } -/// Deprecated, renamed to `catch_unwind` -#[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")] -#[rustc_deprecated(reason = "renamed to `catch_unwind`", since = "1.9.0")] -pub fn recover<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> { - catch_unwind(f) -} - -/// Triggers a panic without invoking the panic handler. +/// Triggers a panic without invoking the panic hook. /// /// This is designed to be used in conjunction with `catch_unwind` to, for /// example, carry a panic across a layer of C code. @@ -425,12 +340,5 @@ pub fn recover<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> { /// ``` #[stable(feature = "resume_unwind", since = "1.9.0")] pub fn resume_unwind(payload: Box<Any + Send>) -> ! { - unwind::rust_panic(payload) -} - -/// Deprecated, use resume_unwind instead -#[unstable(feature = "panic_propagate", reason = "awaiting feedback", issue = "30752")] -#[rustc_deprecated(reason = "renamed to `resume_unwind`", since = "1.9.0")] -pub fn propagate(payload: Box<Any + Send>) -> ! { - resume_unwind(payload) + panicking::rust_panic(payload) } diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index fd6a15b0f69..d73e9542d21 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -8,14 +8,26 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Implementation of various bits and pieces of the `panic!` macro and +//! associated runtime pieces. +//! +//! Specifically, this module contains the implementation of: +//! +//! * Panic hooks +//! * Executing a panic up to doing the actual implementation +//! * Shims around "try" + use prelude::v1::*; use io::prelude::*; use any::Any; use cell::Cell; use cell::RefCell; +use fmt; use intrinsics; -use sync::StaticRwLock; +use mem; +use raw; +use sys_common::rwlock::RWLock; use sync::atomic::{AtomicBool, Ordering}; use sys::stdio::Stderr; use sys_common::backtrace; @@ -23,30 +35,51 @@ use sys_common::thread_info; use sys_common::util; use thread; -thread_local! { pub static PANIC_COUNT: Cell<usize> = Cell::new(0) } - thread_local! { pub static LOCAL_STDERR: RefCell<Option<Box<Write + Send>>> = { RefCell::new(None) } } +thread_local! { pub static PANIC_COUNT: Cell<usize> = Cell::new(0) } + +// Binary interface to the panic runtime that the standard library depends on. +// +// The standard library is tagged with `#![needs_panic_runtime]` (introduced in +// RFC 1513) to indicate that it requires some other crate tagged with +// `#![panic_runtime]` to exist somewhere. Each panic runtime is intended to +// implement these symbols (with the same signatures) so we can get matched up +// to them. +// +// One day this may look a little less ad-hoc with the compiler helping out to +// hook up these functions, but it is not this day! +#[allow(improper_ctypes)] +extern { + fn __rust_maybe_catch_panic(f: fn(*mut u8), + data: *mut u8, + data_ptr: *mut usize, + vtable_ptr: *mut usize) -> u32; + #[unwind] + fn __rust_start_panic(data: usize, vtable: usize) -> u32; +} + #[derive(Copy, Clone)] enum Hook { Default, Custom(*mut (Fn(&PanicInfo) + 'static + Sync + Send)), } -static HOOK_LOCK: StaticRwLock = StaticRwLock::new(); +static HOOK_LOCK: RWLock = RWLock::new(); static mut HOOK: Hook = Hook::Default; static FIRST_PANIC: AtomicBool = AtomicBool::new(true); /// Registers a custom panic hook, replacing any that was previously registered. /// -/// The panic hook is invoked when a thread panics, but before it begins -/// unwinding the stack. The default hook prints a message to standard error -/// and generates a backtrace if requested, but this behavior can be customized -/// with the `set_hook` and `take_hook` functions. +/// The panic hook is invoked when a thread panics, but before the panic runtime +/// is invoked. As such, the hook will run with both the aborting and unwinding +/// runtimes. The default hook prints a message to standard error and generates +/// a backtrace if requested, but this behavior can be customized with the +/// `set_hook` and `take_hook` functions. /// /// The hook is provided with a `PanicInfo` struct which contains information /// about the origin of the panic, including the payload passed to `panic!` and @@ -57,17 +90,17 @@ static FIRST_PANIC: AtomicBool = AtomicBool::new(true); /// # Panics /// /// Panics if called from a panicking thread. -#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +#[stable(feature = "panic_hooks", since = "1.10.0")] pub fn set_hook(hook: Box<Fn(&PanicInfo) + 'static + Sync + Send>) { if thread::panicking() { panic!("cannot modify the panic hook from a panicking thread"); } unsafe { - let lock = HOOK_LOCK.write(); + HOOK_LOCK.write(); let old_hook = HOOK; HOOK = Hook::Custom(Box::into_raw(hook)); - drop(lock); + HOOK_LOCK.write_unlock(); if let Hook::Custom(ptr) = old_hook { Box::from_raw(ptr); @@ -82,17 +115,17 @@ pub fn set_hook(hook: Box<Fn(&PanicInfo) + 'static + Sync + Send>) { /// # Panics /// /// Panics if called from a panicking thread. -#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +#[stable(feature = "panic_hooks", since = "1.10.0")] pub fn take_hook() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> { if thread::panicking() { panic!("cannot modify the panic hook from a panicking thread"); } unsafe { - let lock = HOOK_LOCK.write(); + HOOK_LOCK.write(); let hook = HOOK; HOOK = Hook::Default; - drop(lock); + HOOK_LOCK.write_unlock(); match hook { Hook::Default => Box::new(default_hook), @@ -102,7 +135,7 @@ pub fn take_hook() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> { } /// A struct providing information about a panic. -#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +#[stable(feature = "panic_hooks", since = "1.10.0")] pub struct PanicInfo<'a> { payload: &'a (Any + Send), location: Location<'a>, @@ -112,7 +145,7 @@ impl<'a> PanicInfo<'a> { /// Returns the payload associated with the panic. /// /// This will commonly, but not always, be a `&'static str` or `String`. - #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + #[stable(feature = "panic_hooks", since = "1.10.0")] pub fn payload(&self) -> &(Any + Send) { self.payload } @@ -122,14 +155,14 @@ impl<'a> PanicInfo<'a> { /// /// This method will currently always return `Some`, but this may change /// in future versions. - #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + #[stable(feature = "panic_hooks", since = "1.10.0")] pub fn location(&self) -> Option<&Location> { Some(&self.location) } } /// A struct containing information about the location of a panic. -#[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] +#[stable(feature = "panic_hooks", since = "1.10.0")] pub struct Location<'a> { file: &'a str, line: u32, @@ -137,20 +170,20 @@ pub struct Location<'a> { impl<'a> Location<'a> { /// Returns the name of the source file from which the panic originated. - #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + #[stable(feature = "panic_hooks", since = "1.10.0")] pub fn file(&self) -> &str { self.file } /// Returns the line number from which the panic originated. - #[unstable(feature = "panic_handler", reason = "awaiting feedback", issue = "30449")] + #[stable(feature = "panic_hooks", since = "1.10.0")] pub fn line(&self) -> u32 { self.line } } fn default_hook(info: &PanicInfo) { - let panics = PANIC_COUNT.with(|s| s.get()); + let panics = PANIC_COUNT.with(|c| c.get()); // If this is a double panic, make sure that we print a backtrace // for this panic. Otherwise only print it if logging is enabled. @@ -195,41 +228,152 @@ fn default_hook(info: &PanicInfo) { } } -pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) { - let panics = PANIC_COUNT.with(|s| { - let count = s.get() + 1; - s.set(count); - count +/// Invoke a closure, capturing the cause of an unwinding panic if one occurs. +pub unsafe fn try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<Any + Send>> { + let mut slot = None; + let mut f = Some(f); + let ret = PANIC_COUNT.with(|s| { + let prev = s.get(); + s.set(0); + + let mut to_run = || { + slot = Some(f.take().unwrap()()); + }; + let fnptr = get_call(&mut to_run); + let dataptr = &mut to_run as *mut _ as *mut u8; + let mut any_data = 0; + let mut any_vtable = 0; + let fnptr = mem::transmute::<fn(&mut _), fn(*mut u8)>(fnptr); + let r = __rust_maybe_catch_panic(fnptr, + dataptr, + &mut any_data, + &mut any_vtable); + s.set(prev); + + if r == 0 { + Ok(()) + } else { + Err(mem::transmute(raw::TraitObject { + data: any_data as *mut _, + vtable: any_vtable as *mut _, + })) + } + }); + + return ret.map(|()| { + slot.take().unwrap() }); - // If this is the third nested call, on_panic triggered the last panic, - // otherwise the double-panic check would have aborted the process. - // Even if it is likely that on_panic was unable to log the backtrace, - // abort immediately to avoid infinite recursion, so that attaching a - // debugger provides a useable stacktrace. - if panics >= 3 { + fn get_call<F: FnMut()>(_: &mut F) -> fn(&mut F) { + call + } + + fn call<F: FnMut()>(f: &mut F) { + f() + } +} + +/// Determines whether the current thread is unwinding because of panic. +pub fn panicking() -> bool { + PANIC_COUNT.with(|c| c.get() != 0) +} + +/// Entry point of panic from the libcore crate. +#[cfg(not(test))] +#[lang = "panic_fmt"] +#[unwind] +pub extern fn rust_begin_panic(msg: fmt::Arguments, + file: &'static str, + line: u32) -> ! { + begin_panic_fmt(&msg, &(file, line)) +} + +/// The entry point for panicking 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. +#[unstable(feature = "libstd_sys_internals", + reason = "used by the panic! macro", + issue = "0")] +#[inline(never)] #[cold] +pub fn begin_panic_fmt(msg: &fmt::Arguments, + file_line: &(&'static str, u32)) -> ! { + use fmt::Write; + + // 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_panic + // below). + + let mut s = String::new(); + let _ = s.write_fmt(*msg); + begin_panic(s, file_line) +} + +/// This is the entry point of panicking for panic!() and assert!(). +#[unstable(feature = "libstd_sys_internals", + reason = "used by the panic! macro", + issue = "0")] +#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible +pub fn begin_panic<M: Any + Send>(msg: M, file_line: &(&'static str, u32)) -> ! { + // 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 thread instead of the thread that's + // panicking. + + rust_panic_with_hook(Box::new(msg), file_line) +} + +/// Executes the primary logic for a panic, including checking for recursive +/// panics and panic hooks. +/// +/// This is the entry point or panics from libcore, formatted panics, and +/// `Box<Any>` panics. Here we'll verify that we're not panicking recursively, +/// run panic hooks, and then delegate to the actual implementation of panics. +#[inline(never)] +#[cold] +fn rust_panic_with_hook(msg: Box<Any + Send>, + file_line: &(&'static str, u32)) -> ! { + let (file, line) = *file_line; + + let panics = PANIC_COUNT.with(|c| { + let prev = c.get(); + c.set(prev + 1); + prev + }); + + // If this is the third nested call (e.g. panics == 2, this is 0-indexed), + // the panic hook probably triggered the last panic, otherwise the + // double-panic check would have aborted the process. In this case abort the + // process real quickly as we don't want to try calling it again as it'll + // probably just panic again. + if panics > 1 { util::dumb_print(format_args!("thread panicked while processing \ panic. aborting.\n")); unsafe { intrinsics::abort() } } - let info = PanicInfo { - payload: obj, - location: Location { - file: file, - line: line, - }, - }; - unsafe { - let _lock = HOOK_LOCK.read(); + let info = PanicInfo { + payload: &*msg, + location: Location { + file: file, + line: line, + }, + }; + HOOK_LOCK.read(); match HOOK { Hook::Default => default_hook(&info), Hook::Custom(ptr) => (*ptr)(&info), } + HOOK_LOCK.read_unlock(); } - if panics >= 2 { + if panics > 0 { // 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 @@ -238,4 +382,17 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) { aborting.\n")); unsafe { intrinsics::abort() } } + + rust_panic(msg) +} + +/// A private no-mangle function on which to slap yer breakpoints. +#[no_mangle] +#[allow(private_no_mangle_fns)] // yes we get it, but we like breakpoints +pub fn rust_panic(msg: Box<Any + Send>) -> ! { + let code = unsafe { + let obj = mem::transmute::<_, raw::TraitObject>(msg); + __rust_start_panic(obj.data as usize, obj.vtable as usize) + }; + rtabort!("failed to initiate panic, error {}", code) } diff --git a/src/libstd/path.rs b/src/libstd/path.rs index f413bed86a8..2d19561139b 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -525,6 +525,26 @@ impl<'a> Hash for PrefixComponent<'a> { /// /// See the module documentation for an in-depth explanation of components and /// their role in the API. +/// +/// This `enum` is created from iterating over the [`path::Components`] +/// `struct`. +/// +/// # Examples +/// +/// ```rust +/// use std::path::{Component, Path}; +/// +/// let path = Path::new("/tmp/foo/bar.txt"); +/// let components = path.components().collect::<Vec<_>>(); +/// assert_eq!(&components, &[ +/// Component::RootDir, +/// Component::Normal("tmp".as_ref()), +/// Component::Normal("foo".as_ref()), +/// Component::Normal("bar.txt".as_ref()), +/// ]); +/// ``` +/// +/// [`path::Components`]: struct.Components.html #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[stable(feature = "rust1", since = "1.0.0")] pub enum Component<'a> { @@ -579,6 +599,8 @@ impl<'a> AsRef<OsStr> for Component<'a> { /// See the module documentation for an in-depth explanation of components and /// their role in the API. /// +/// This `struct` is created by the [`path::Path::components`] method. +/// /// # Examples /// /// ``` @@ -590,6 +612,8 @@ impl<'a> AsRef<OsStr> for Component<'a> { /// println!("{:?}", component); /// } /// ``` +/// +/// [`path::Path::components`]: struct.Path.html#method.components #[derive(Clone)] #[stable(feature = "rust1", since = "1.0.0")] pub struct Components<'a> { @@ -1033,7 +1057,6 @@ impl PathBuf { self._push(path.as_ref()) } - #[allow(deprecated)] fn _push(&mut self, path: &Path) { // in general, a separator is needed if the rightmost byte is not a separator let mut need_sep = self.as_mut_vec().last().map(|c| !is_sep_byte(*c)).unwrap_or(false); @@ -1419,8 +1442,7 @@ impl Path { /// `is_absolute` and `has_root` are equivalent. /// /// * On Windows, a path is absolute if it has a prefix and starts with the - /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not. In - /// other words, `path.is_absolute() == path.prefix().is_some() && path.has_root()`. + /// root: `c:\windows` is absolute, while `c:temp` and `\temp` are not. /// /// # Examples /// @@ -1507,8 +1529,7 @@ impl Path { /// The final component of the path, if it is a normal file. /// - /// If the path terminates in `.`, `..`, or consists solely of a root of - /// prefix, `file_name` will return `None`. + /// If the path terminates in `..`, `file_name` will return `None`. /// /// # Examples /// @@ -1521,6 +1542,17 @@ impl Path { /// /// assert_eq!(Some(os_str), path.file_name()); /// ``` + /// + /// # Other examples + /// + /// ``` + /// use std::path::Path; + /// use std::ffi::OsStr; + /// + /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.").file_name()); + /// assert_eq!(Some(OsStr::new("foo.txt")), Path::new("foo.txt/.//").file_name()); + /// assert_eq!(None, Path::new("foo.txt/..").file_name()); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn file_name(&self) -> Option<&OsStr> { self.components().next_back().and_then(|p| { @@ -1781,7 +1813,9 @@ impl Path { /// This function will traverse symbolic links to query information about the /// destination file. /// - /// This is an alias to `fs::metadata`. + /// This is an alias to [`fs::metadata`]. + /// + /// [`fs::metadata`]: ../fs/fn.metadata.html #[stable(feature = "path_ext", since = "1.5.0")] pub fn metadata(&self) -> io::Result<fs::Metadata> { fs::metadata(self) @@ -1789,7 +1823,9 @@ impl Path { /// Query the metadata about a file without following symlinks. /// - /// This is an alias to `fs::symlink_metadata`. + /// This is an alias to [`fs::symlink_metadata`]. + /// + /// [`fs::symlink_metadata`]: ../fs/fn.symlink_metadata.html #[stable(feature = "path_ext", since = "1.5.0")] pub fn symlink_metadata(&self) -> io::Result<fs::Metadata> { fs::symlink_metadata(self) @@ -1798,7 +1834,9 @@ impl Path { /// Returns the canonical form of the path with all intermediate components /// normalized and symbolic links resolved. /// - /// This is an alias to `fs::canonicalize`. + /// This is an alias to [`fs::canonicalize`]. + /// + /// [`fs::canonicalize`]: ../fs/fn.canonicalize.html #[stable(feature = "path_ext", since = "1.5.0")] pub fn canonicalize(&self) -> io::Result<PathBuf> { fs::canonicalize(self) @@ -1806,7 +1844,9 @@ impl Path { /// Reads a symbolic link, returning the file that the link points to. /// - /// This is an alias to `fs::read_link`. + /// This is an alias to [`fs::read_link`]. + /// + /// [`fs::read_link`]: ../fs/fn.read_link.html #[stable(feature = "path_ext", since = "1.5.0")] pub fn read_link(&self) -> io::Result<PathBuf> { fs::read_link(self) @@ -1817,7 +1857,9 @@ impl Path { /// The iterator will yield instances of `io::Result<DirEntry>`. New errors may /// be encountered after an iterator is initially constructed. /// - /// This is an alias to `fs::read_dir`. + /// This is an alias to [`fs::read_dir`]. + /// + /// [`fs::read_dir`]: ../fs/fn.read_dir.html #[stable(feature = "path_ext", since = "1.5.0")] pub fn read_dir(&self) -> io::Result<fs::ReadDir> { fs::read_dir(self) diff --git a/src/libstd/primitive_docs.rs b/src/libstd/primitive_docs.rs index e083605a2ac..de891ea8918 100644 --- a/src/libstd/primitive_docs.rs +++ b/src/libstd/primitive_docs.rs @@ -28,7 +28,7 @@ /// ``` /// /// [`assert!`]: macro.assert!.html -/// [`if` conditionals]: ../book/if.html +/// [`if`]: ../book/if.html /// [`BitAnd`]: ops/trait.BitAnd.html /// [`BitOr`]: ops/trait.BitOr.html /// [`Not`]: ops/trait.Not.html @@ -490,9 +490,6 @@ mod prim_tuple { } /// /// *[See also the `std::f32` module](f32/index.html).* /// -/// However, please note that examples are shared between the `f64` and `f32` -/// primitive types. So it's normal if you see usage of `f64` in there. -/// mod prim_f32 { } #[doc(primitive = "f64")] @@ -501,9 +498,6 @@ mod prim_f32 { } /// /// *[See also the `std::f64` module](f64/index.html).* /// -/// However, please note that examples are shared between the `f64` and `f32` -/// primitive types. So it's normal if you see usage of `f32` in there. -/// mod prim_f64 { } #[doc(primitive = "i8")] @@ -512,6 +506,9 @@ mod prim_f64 { } /// /// *[See also the `std::i8` module](i8/index.html).* /// +/// However, please note that examples are shared between primitive integer +/// types. So it's normal if you see usage of types like `i64` in there. +/// mod prim_i8 { } #[doc(primitive = "i16")] @@ -520,6 +517,9 @@ mod prim_i8 { } /// /// *[See also the `std::i16` module](i16/index.html).* /// +/// However, please note that examples are shared between primitive integer +/// types. So it's normal if you see usage of types like `i32` in there. +/// mod prim_i16 { } #[doc(primitive = "i32")] @@ -528,6 +528,9 @@ mod prim_i16 { } /// /// *[See also the `std::i32` module](i32/index.html).* /// +/// However, please note that examples are shared between primitive integer +/// types. So it's normal if you see usage of types like `i16` in there. +/// mod prim_i32 { } #[doc(primitive = "i64")] @@ -536,6 +539,9 @@ mod prim_i32 { } /// /// *[See also the `std::i64` module](i64/index.html).* /// +/// However, please note that examples are shared between primitive integer +/// types. So it's normal if you see usage of types like `i8` in there. +/// mod prim_i64 { } #[doc(primitive = "u8")] @@ -544,6 +550,9 @@ mod prim_i64 { } /// /// *[See also the `std::u8` module](u8/index.html).* /// +/// However, please note that examples are shared between primitive integer +/// types. So it's normal if you see usage of types like `u64` in there. +/// mod prim_u8 { } #[doc(primitive = "u16")] @@ -552,6 +561,9 @@ mod prim_u8 { } /// /// *[See also the `std::u16` module](u16/index.html).* /// +/// However, please note that examples are shared between primitive integer +/// types. So it's normal if you see usage of types like `u32` in there. +/// mod prim_u16 { } #[doc(primitive = "u32")] @@ -560,6 +572,9 @@ mod prim_u16 { } /// /// *[See also the `std::u32` module](u32/index.html).* /// +/// However, please note that examples are shared between primitive integer +/// types. So it's normal if you see usage of types like `u16` in there. +/// mod prim_u32 { } #[doc(primitive = "u64")] @@ -568,6 +583,9 @@ mod prim_u32 { } /// /// *[See also the `std::u64` module](u64/index.html).* /// +/// However, please note that examples are shared between primitive integer +/// types. So it's normal if you see usage of types like `u8` in there. +/// mod prim_u64 { } #[doc(primitive = "isize")] @@ -576,6 +594,9 @@ mod prim_u64 { } /// /// *[See also the `std::isize` module](isize/index.html).* /// +/// However, please note that examples are shared between primitive integer +/// types. So it's normal if you see usage of types like `usize` in there. +/// mod prim_isize { } #[doc(primitive = "usize")] @@ -584,4 +605,7 @@ mod prim_isize { } /// /// *[See also the `std::usize` module](usize/index.html).* /// +/// However, please note that examples are shared between primitive integer +/// types. So it's normal if you see usage of types like `isize` in there. +/// mod prim_usize { } diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 1d2516a4c40..660c098d30b 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -27,8 +27,9 @@ use sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; /// Representation of a running or exited child process. /// /// This structure is used to represent and manage child processes. A child -/// process is created via the `Command` struct, which configures the spawning -/// process and can itself be constructed using a builder-style interface. +/// process is created via the [`Command`] struct, which configures the +/// spawning process and can itself be constructed using a builder-style +/// interface. /// /// # Examples /// @@ -48,13 +49,18 @@ use sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; /// /// # Note /// -/// Take note that there is no implementation of -/// [`Drop`](../../core/ops/trait.Drop.html) for child processes, so if you -/// do not ensure the `Child` has exited then it will continue to run, even -/// after the `Child` handle to the child process has gone out of scope. +/// Take note that there is no implementation of [`Drop`] for child processes, +/// so if you do not ensure the `Child` has exited then it will continue to +/// run, even after the `Child` handle to the child process has gone out of +/// scope. /// -/// Calling `wait` (or other functions that wrap around it) will make the -/// parent process wait until the child has actually exited before continuing. +/// Calling [`wait`][`wait`] (or other functions that wrap around it) will make +/// the parent process wait until the child has actually exited before +/// continuing. +/// +/// [`Command`]: struct.Command.html +/// [`Drop`]: ../../core/ops/trait.Drop.html +/// [`wait`]: #method.wait #[stable(feature = "process", since = "1.0.0")] pub struct Child { handle: imp::Process, @@ -91,7 +97,11 @@ impl IntoInner<imp::Process> for Child { fn into_inner(self) -> imp::Process { self.handle } } -/// A handle to a child process's stdin +/// A handle to a child process's stdin. This struct is used in the [`stdin`] +/// field on [`Child`]. +/// +/// [`Child`]: struct.Child.html +/// [`stdin`]: struct.Child.html#structfield.stdin #[stable(feature = "process", since = "1.0.0")] pub struct ChildStdin { inner: AnonPipe @@ -122,7 +132,11 @@ impl FromInner<AnonPipe> for ChildStdin { } } -/// A handle to a child process's stdout +/// A handle to a child process's stdout. This struct is used in the [`stdout`] +/// field on [`Child`]. +/// +/// [`Child`]: struct.Child.html +/// [`stdout`]: struct.Child.html#structfield.stdout #[stable(feature = "process", since = "1.0.0")] pub struct ChildStdout { inner: AnonPipe @@ -152,7 +166,11 @@ impl FromInner<AnonPipe> for ChildStdout { } } -/// A handle to a child process's stderr +/// A handle to a child process's stderr. This struct is used in the [`stderr`] +/// field on [`Child`]. +/// +/// [`Child`]: struct.Child.html +/// [`stderr`]: struct.Child.html#structfield.stderr #[stable(feature = "process", since = "1.0.0")] pub struct ChildStderr { inner: AnonPipe @@ -182,8 +200,10 @@ impl FromInner<AnonPipe> for ChildStderr { } } -/// The `Command` type acts as a process builder, providing fine-grained control -/// over how a new process should be spawned. A default configuration can be +/// A process builder, providing fine-grained control +/// over how a new process should be spawned. +/// +/// A default configuration can be /// generated using `Command::new(program)`, where `program` gives a path to the /// program to be executed. Additional builder methods allow the configuration /// to be changed (for example, by adding arguments) prior to spawning: @@ -195,7 +215,7 @@ impl FromInner<AnonPipe> for ChildStderr { /// .arg("-c") /// .arg("echo hello") /// .output() -/// .expect("failed to execute proces"); +/// .expect("failed to execute process"); /// /// let hello = output.stdout; /// ``` @@ -215,12 +235,38 @@ impl Command { /// /// Builder methods are provided to change these defaults and /// otherwise configure the process. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("sh") + /// .spawn() + /// .expect("sh command failed to start"); + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn new<S: AsRef<OsStr>>(program: S) -> Command { Command { inner: imp::Command::new(program.as_ref()) } } /// Add an argument to pass to the program. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .arg("-l") + /// .arg("-a") + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command { self.inner.arg(arg.as_ref()); @@ -228,6 +274,19 @@ impl Command { } /// Add multiple arguments to pass to the program. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .args(&["-l", "-a"]) + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn args<S: AsRef<OsStr>>(&mut self, args: &[S]) -> &mut Command { for arg in args { @@ -240,6 +299,19 @@ impl Command { /// /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, /// and case-sensitive on all other platforms. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .env("PATH", "/bin") + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Command where K: AsRef<OsStr>, V: AsRef<OsStr> @@ -249,6 +321,19 @@ impl Command { } /// Removes an environment variable mapping. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .env_remove("PATH") + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Command { self.inner.env_remove(key.as_ref()); @@ -256,6 +341,19 @@ impl Command { } /// Clears the entire environment map for the child process. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .env_clear() + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn env_clear(&mut self) -> &mut Command { self.inner.env_clear(); @@ -263,6 +361,19 @@ impl Command { } /// Sets the working directory for the child process. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .current_dir("/bin") + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command { self.inner.cwd(dir.as_ref().as_ref()); @@ -270,6 +381,19 @@ impl Command { } /// Configuration for the child process's stdin handle (file descriptor 0). + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// Command::new("ls") + /// .stdin(Stdio::null()) + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn stdin(&mut self, cfg: Stdio) -> &mut Command { self.inner.stdin(cfg.0); @@ -277,6 +401,19 @@ impl Command { } /// Configuration for the child process's stdout handle (file descriptor 1). + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// Command::new("ls") + /// .stdout(Stdio::null()) + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn stdout(&mut self, cfg: Stdio) -> &mut Command { self.inner.stdout(cfg.0); @@ -284,6 +421,19 @@ impl Command { } /// Configuration for the child process's stderr handle (file descriptor 2). + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::{Command, Stdio}; + /// + /// Command::new("ls") + /// .stderr(Stdio::null()) + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn stderr(&mut self, cfg: Stdio) -> &mut Command { self.inner.stderr(cfg.0); @@ -293,6 +443,18 @@ impl Command { /// Executes the command as a child process, returning a handle to it. /// /// By default, stdin, stdout and stderr are inherited from the parent. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// Command::new("ls") + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn spawn(&mut self) -> io::Result<Child> { self.inner.spawn(imp::Stdio::Inherit, true).map(Child::from_inner) @@ -308,8 +470,10 @@ impl Command { /// /// ```should_panic /// use std::process::Command; - /// let output = Command::new("/bin/cat").arg("file.txt").output() - /// .expect("failed to execute process"); + /// let output = Command::new("/bin/cat") + /// .arg("file.txt") + /// .output() + /// .expect("failed to execute process"); /// /// println!("status: {}", output.status); /// println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); @@ -333,8 +497,10 @@ impl Command { /// ```should_panic /// use std::process::Command; /// - /// let status = Command::new("/bin/cat").arg("file.txt").status() - /// .expect("failed to execute process"); + /// let status = Command::new("/bin/cat") + /// .arg("file.txt") + /// .status() + /// .expect("failed to execute process"); /// /// println!("process exited with: {}", status); /// @@ -439,6 +605,23 @@ pub struct ExitStatus(imp::ExitStatus); impl ExitStatus { /// Was termination successful? Signal termination not considered a success, /// and success is defined as a zero exit status. + /// + /// # Examples + /// + /// ```rust,no_run + /// use std::process::Command; + /// + /// let status = Command::new("mkdir") + /// .arg("projects") + /// .status() + /// .expect("failed to execute mkdir"); + /// + /// if status.success() { + /// println!("'projects/' directory created"); + /// } else { + /// println!("failed to create 'projects/' directory"); + /// } + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn success(&self) -> bool { self.0.success() @@ -459,6 +642,12 @@ impl AsInner<imp::ExitStatus> for ExitStatus { fn as_inner(&self) -> &imp::ExitStatus { &self.0 } } +impl FromInner<imp::ExitStatus> for ExitStatus { + fn from_inner(s: imp::ExitStatus) -> ExitStatus { + ExitStatus(s) + } +} + #[stable(feature = "process", since = "1.0.0")] impl fmt::Display for ExitStatus { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -469,12 +658,42 @@ impl fmt::Display for ExitStatus { impl Child { /// Forces the child to exit. This is equivalent to sending a /// SIGKILL on unix platforms. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// let mut command = Command::new("yes"); + /// if let Ok(mut child) = command.spawn() { + /// child.kill().expect("command wasn't running"); + /// } else { + /// println!("yes command didn't start"); + /// } + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn kill(&mut self) -> io::Result<()> { self.handle.kill() } /// Returns the OS-assigned process identifier associated with this child. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// let mut command = Command::new("ls"); + /// if let Ok(child) = command.spawn() { + /// println!("Child's id is {}", child.id()); + /// } else { + /// println!("ls command didn't start"); + /// } + /// ``` #[stable(feature = "process_id", since = "1.3.0")] pub fn id(&self) -> u32 { self.handle.id() @@ -488,6 +707,22 @@ impl Child { /// before waiting. This helps avoid deadlock: it ensures that the /// child does not block waiting for input from the parent, while /// the parent waits for the child to exit. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// use std::process::Command; + /// + /// let mut command = Command::new("ls"); + /// if let Ok(mut child) = command.spawn() { + /// child.wait().expect("command wasn't running"); + /// println!("Child has finished its execution!"); + /// } else { + /// println!("ls command didn't start"); + /// } + /// ``` #[stable(feature = "process", since = "1.0.0")] pub fn wait(&mut self) -> io::Result<ExitStatus> { drop(self.stdin.take()); @@ -513,16 +748,17 @@ impl Child { /// ```should_panic /// use std::process::{Command, Stdio}; /// - /// let mut child = Command::new("/bin/cat") - /// .arg("file.txt") - /// .stdout(Stdio::piped()) - /// .spawn() - /// .expect("failed to execute child"); + /// let child = Command::new("/bin/cat") + /// .arg("file.txt") + /// .stdout(Stdio::piped()) + /// .spawn() + /// .expect("failed to execute child"); /// - /// let ecode = child.wait_with_output() - /// .expect("failed to wait on child"); + /// let output = child + /// .wait_with_output() + /// .expect("failed to wait on child"); /// - /// assert!(ecode.status.success()); + /// assert!(output.status.success()); /// ``` /// #[stable(feature = "process", since = "1.0.0")] diff --git a/src/libstd/rt.rs b/src/libstd/rt.rs index 83091c72c0d..5a7c0fe4816 100644 --- a/src/libstd/rt.rs +++ b/src/libstd/rt.rs @@ -25,12 +25,7 @@ // Reexport some of our utilities which are expected by other crates. -pub use sys_common::unwind::{begin_unwind, begin_unwind_fmt}; - -// Rust runtime's startup objects depend on these symbols, so they must be public. -// Since sys_common isn't public, we have to re-export them here. -#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] -pub use sys_common::unwind::imp::eh_frame_registry::*; +pub use panicking::{begin_panic, begin_panic_fmt}; #[cfg(not(test))] #[lang = "start"] @@ -53,7 +48,7 @@ fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize { // 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_owned())); + let thread: Thread = NewThread::new(Some("main".to_owned())); thread_info::set(main_guard, thread); // Store our args if necessary in a squirreled away location diff --git a/src/libstd/sync/barrier.rs b/src/libstd/sync/barrier.rs index b543240c15a..b1267acdee6 100644 --- a/src/libstd/sync/barrier.rs +++ b/src/libstd/sync/barrier.rs @@ -71,7 +71,7 @@ impl Barrier { } } - /// Blocks the current thread until all threads has rendezvoused here. + /// Blocks the current thread until all threads have rendezvoused here. /// /// Barriers are re-usable after all threads have rendezvoused once, and can /// be used continuously. diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs index 57932f03796..3c52ebc72f2 100644 --- a/src/libstd/sync/condvar.rs +++ b/src/libstd/sync/condvar.rs @@ -15,7 +15,7 @@ use sync::{mutex, MutexGuard, PoisonError}; use sys_common::condvar as sys; use sys_common::mutex as sys_mutex; use sys_common::poison::{self, LockResult}; -use time::{Instant, Duration}; +use time::Duration; /// A type indicating whether a timed wait on a condition variable returned /// due to a time out or not. @@ -72,46 +72,19 @@ impl WaitTimeoutResult { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] -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. -/// -/// # Examples -/// -/// ``` -/// #![feature(static_condvar)] -/// -/// use std::sync::{StaticCondvar, CONDVAR_INIT}; -/// -/// static CVAR: StaticCondvar = CONDVAR_INIT; -/// ``` -#[unstable(feature = "static_condvar", - reason = "may be merged with Condvar in the future", - issue = "27717")] -pub struct StaticCondvar { - inner: sys::Condvar, +pub struct Condvar { + inner: Box<sys::Condvar>, mutex: AtomicUsize, } -/// Constant initializer for a statically allocated condition variable. -#[unstable(feature = "static_condvar", - reason = "may be merged with Condvar in the future", - issue = "27717")] -pub const CONDVAR_INIT: StaticCondvar = StaticCondvar::new(); - impl Condvar { /// Creates a new condition variable which is ready to be waited on and /// notified. #[stable(feature = "rust1", since = "1.0.0")] pub fn new() -> Condvar { Condvar { - inner: box StaticCondvar { - inner: sys::Condvar::new(), - mutex: AtomicUsize::new(0), - } + inner: box sys::Condvar::new(), + mutex: AtomicUsize::new(0), } } @@ -144,9 +117,16 @@ impl Condvar { #[stable(feature = "rust1", since = "1.0.0")] pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult<MutexGuard<'a, T>> { - unsafe { - let me: &'static Condvar = &*(self as *const _); - me.inner.wait(guard) + let poisoned = unsafe { + let lock = mutex::guard_lock(&guard); + self.verify(lock); + self.inner.wait(lock); + mutex::guard_poison(&guard).get() + }; + if poisoned { + Err(PoisonError::new(guard)) + } else { + Ok(guard) } } @@ -193,9 +173,16 @@ impl Condvar { pub fn wait_timeout<'a, T>(&self, guard: MutexGuard<'a, T>, dur: Duration) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> { - unsafe { - let me: &'static Condvar = &*(self as *const _); - me.inner.wait_timeout(guard, dur) + let (poisoned, result) = unsafe { + let lock = mutex::guard_lock(&guard); + self.verify(lock); + let success = self.inner.wait_timeout(lock, dur); + (mutex::guard_poison(&guard).get(), WaitTimeoutResult(!success)) + }; + if poisoned { + Err(PoisonError::new((guard, result))) + } else { + Ok((guard, result)) } } @@ -207,7 +194,9 @@ impl Condvar { /// /// To wake up all threads, see `notify_all()`. #[stable(feature = "rust1", since = "1.0.0")] - pub fn notify_one(&self) { unsafe { self.inner.inner.notify_one() } } + pub fn notify_one(&self) { + unsafe { self.inner.notify_one() } + } /// Wakes up all blocked threads on this condvar. /// @@ -217,159 +206,8 @@ impl Condvar { /// /// To wake up only one thread, see `notify_one()`. #[stable(feature = "rust1", since = "1.0.0")] - pub fn notify_all(&self) { unsafe { self.inner.inner.notify_all() } } -} - -#[stable(feature = "condvar_default", since = "1.9.0")] -impl Default for Condvar { - fn default() -> Condvar { - Condvar::new() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Drop for Condvar { - fn drop(&mut self) { - unsafe { self.inner.inner.destroy() } - } -} - -impl StaticCondvar { - /// Creates a new condition variable - #[unstable(feature = "static_condvar", - reason = "may be merged with Condvar in the future", - issue = "27717")] - pub const fn new() -> StaticCondvar { - StaticCondvar { - inner: sys::Condvar::new(), - mutex: AtomicUsize::new(0), - } - } - - /// Blocks the current thread until this condition variable receives a - /// notification. - /// - /// See `Condvar::wait`. - #[unstable(feature = "static_condvar", - reason = "may be merged with Condvar in the future", - issue = "27717")] - pub fn wait<'a, T>(&'static self, guard: MutexGuard<'a, T>) - -> LockResult<MutexGuard<'a, T>> { - let poisoned = unsafe { - let lock = mutex::guard_lock(&guard); - self.verify(lock); - self.inner.wait(lock); - mutex::guard_poison(&guard).get() - }; - if poisoned { - Err(PoisonError::new(guard)) - } else { - Ok(guard) - } - } - - /// Waits on this condition variable for a notification, timing out after a - /// specified duration. - /// - /// See `Condvar::wait_timeout`. - #[unstable(feature = "static_condvar", - reason = "may be merged with Condvar in the future", - issue = "27717")] - pub fn wait_timeout<'a, T>(&'static self, - guard: MutexGuard<'a, T>, - timeout: Duration) - -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> { - let (poisoned, result) = unsafe { - let lock = mutex::guard_lock(&guard); - self.verify(lock); - let success = self.inner.wait_timeout(lock, timeout); - (mutex::guard_poison(&guard).get(), WaitTimeoutResult(!success)) - }; - if poisoned { - Err(PoisonError::new((guard, result))) - } else { - Ok((guard, result)) - } - } - - /// Waits on this condition variable for a notification, timing out after a - /// specified duration. - /// - /// The implementation will repeatedly wait while the duration has not - /// passed and the function returns `false`. - /// - /// See `Condvar::wait_timeout_with`. - #[unstable(feature = "static_condvar", - reason = "may be merged with Condvar in the future", - issue = "27717")] - pub fn wait_timeout_with<'a, T, F>(&'static self, - guard: MutexGuard<'a, T>, - dur: Duration, - mut f: F) - -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> - where F: FnMut(LockResult<&mut T>) -> bool { - // This could be made more efficient by pushing the implementation into - // sys::condvar - let start = Instant::now(); - let mut guard_result: LockResult<MutexGuard<'a, T>> = Ok(guard); - while !f(guard_result - .as_mut() - .map(|g| &mut **g) - .map_err(|e| PoisonError::new(&mut **e.get_mut()))) { - let consumed = start.elapsed(); - let guard = guard_result.unwrap_or_else(|e| e.into_inner()); - let (new_guard_result, timed_out) = if consumed > dur { - (Ok(guard), WaitTimeoutResult(true)) - } else { - match self.wait_timeout(guard, dur - consumed) { - Ok((new_guard, timed_out)) => (Ok(new_guard), timed_out), - Err(err) => { - let (new_guard, no_timeout) = err.into_inner(); - (Err(PoisonError::new(new_guard)), no_timeout) - } - } - }; - guard_result = new_guard_result; - if timed_out.timed_out() { - let result = f(guard_result - .as_mut() - .map(|g| &mut **g) - .map_err(|e| PoisonError::new(&mut **e.get_mut()))); - let result = WaitTimeoutResult(!result); - return poison::map_result(guard_result, |g| (g, result)); - } - } - - poison::map_result(guard_result, |g| (g, WaitTimeoutResult(false))) - } - - /// Wakes up one blocked thread on this condvar. - /// - /// See `Condvar::notify_one`. - #[unstable(feature = "static_condvar", - reason = "may be merged with Condvar in the future", - issue = "27717")] - pub fn notify_one(&'static self) { unsafe { self.inner.notify_one() } } - - /// Wakes up all blocked threads on this condvar. - /// - /// See `Condvar::notify_all`. - #[unstable(feature = "static_condvar", - reason = "may be merged with Condvar in the future", - issue = "27717")] - pub fn notify_all(&'static self) { unsafe { self.inner.notify_all() } } - - /// Deallocates 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. - #[unstable(feature = "static_condvar", - reason = "may be merged with Condvar in the future", - issue = "27717")] - pub unsafe fn destroy(&'static self) { - self.inner.destroy() + pub fn notify_all(&self) { + unsafe { self.inner.notify_all() } } fn verify(&self, mutex: &sys_mutex::Mutex) { @@ -391,14 +229,26 @@ impl StaticCondvar { } } +#[stable(feature = "condvar_default", since = "1.9.0")] +impl Default for Condvar { + fn default() -> Condvar { + Condvar::new() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Drop for Condvar { + fn drop(&mut self) { + unsafe { self.inner.destroy() } + } +} + #[cfg(test)] mod tests { use prelude::v1::*; - use super::StaticCondvar; use sync::mpsc::channel; - use sync::{StaticMutex, Condvar, Mutex, Arc}; - use sync::atomic::{AtomicUsize, Ordering}; + use sync::{Condvar, Mutex, Arc}; use thread; use time::Duration; use u32; @@ -411,26 +261,19 @@ mod tests { } #[test] - fn static_smoke() { - static C: StaticCondvar = StaticCondvar::new(); - C.notify_one(); - C.notify_all(); - unsafe { C.destroy(); } - } - - #[test] fn notify_one() { - static C: StaticCondvar = StaticCondvar::new(); - static M: StaticMutex = StaticMutex::new(); + let m = Arc::new(Mutex::new(())); + let m2 = m.clone(); + let c = Arc::new(Condvar::new()); + let c2 = c.clone(); - let g = M.lock().unwrap(); + let g = m.lock().unwrap(); let _t = thread::spawn(move|| { - let _g = M.lock().unwrap(); - C.notify_one(); + let _g = m2.lock().unwrap(); + c2.notify_one(); }); - let g = C.wait(g).unwrap(); + let g = c.wait(g).unwrap(); drop(g); - unsafe { C.destroy(); M.destroy(); } } #[test] @@ -471,84 +314,41 @@ mod tests { #[test] fn wait_timeout_ms() { - static C: StaticCondvar = StaticCondvar::new(); - static M: StaticMutex = StaticMutex::new(); + let m = Arc::new(Mutex::new(())); + let m2 = m.clone(); + let c = Arc::new(Condvar::new()); + let c2 = c.clone(); - let g = M.lock().unwrap(); - let (g, _no_timeout) = C.wait_timeout(g, Duration::from_millis(1)).unwrap(); + let g = m.lock().unwrap(); + let (g, _no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap(); // spurious wakeups mean this isn't necessarily true // assert!(!no_timeout); let _t = thread::spawn(move || { - let _g = M.lock().unwrap(); - C.notify_one(); + let _g = m2.lock().unwrap(); + c2.notify_one(); }); - let (g, timeout_res) = C.wait_timeout(g, Duration::from_millis(u32::MAX as u64)).unwrap(); + let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u32::MAX as u64)).unwrap(); assert!(!timeout_res.timed_out()); drop(g); - unsafe { C.destroy(); M.destroy(); } - } - - #[test] - fn wait_timeout_with() { - static C: StaticCondvar = StaticCondvar::new(); - static M: StaticMutex = StaticMutex::new(); - static S: AtomicUsize = AtomicUsize::new(0); - - let g = M.lock().unwrap(); - let (g, timed_out) = C.wait_timeout_with(g, Duration::new(0, 1000), |_| { - false - }).unwrap(); - assert!(timed_out.timed_out()); - - let (tx, rx) = channel(); - let _t = thread::spawn(move || { - rx.recv().unwrap(); - let g = M.lock().unwrap(); - S.store(1, Ordering::SeqCst); - C.notify_one(); - drop(g); - - rx.recv().unwrap(); - let g = M.lock().unwrap(); - S.store(2, Ordering::SeqCst); - C.notify_one(); - drop(g); - - rx.recv().unwrap(); - let _g = M.lock().unwrap(); - S.store(3, Ordering::SeqCst); - C.notify_one(); - }); - - let mut state = 0; - let day = 24 * 60 * 60; - let (_g, timed_out) = C.wait_timeout_with(g, Duration::new(day, 0), |_| { - assert_eq!(state, S.load(Ordering::SeqCst)); - tx.send(()).unwrap(); - state += 1; - match state { - 1|2 => false, - _ => true, - } - }).unwrap(); - assert!(!timed_out.timed_out()); } #[test] #[should_panic] fn two_mutexes() { - static M1: StaticMutex = StaticMutex::new(); - static M2: StaticMutex = StaticMutex::new(); - static C: StaticCondvar = StaticCondvar::new(); + let m = Arc::new(Mutex::new(())); + let m2 = m.clone(); + let c = Arc::new(Condvar::new()); + let c2 = c.clone(); - let mut g = M1.lock().unwrap(); + let mut g = m.lock().unwrap(); let _t = thread::spawn(move|| { - let _g = M1.lock().unwrap(); - C.notify_one(); + let _g = m2.lock().unwrap(); + c2.notify_one(); }); - g = C.wait(g).unwrap(); + g = c.wait(g).unwrap(); drop(g); - let _ = C.wait(M2.lock().unwrap()).unwrap(); + let m = Mutex::new(()); + let _ = c.wait(m.lock().unwrap()).unwrap(); } } diff --git a/src/libstd/sync/mod.rs b/src/libstd/sync/mod.rs index c20b422d40c..289b47b3484 100644 --- a/src/libstd/sync/mod.rs +++ b/src/libstd/sync/mod.rs @@ -25,19 +25,15 @@ pub use core::sync::atomic; #[stable(feature = "rust1", since = "1.0.0")] pub use self::barrier::{Barrier, BarrierWaitResult}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::condvar::{Condvar, StaticCondvar, WaitTimeoutResult, CONDVAR_INIT}; +pub use self::condvar::{Condvar, WaitTimeoutResult}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::mutex::MUTEX_INIT; +pub use self::mutex::{Mutex, MutexGuard}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::mutex::{Mutex, MutexGuard, StaticMutex}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::once::{Once, ONCE_INIT}; +pub use self::once::{Once, OnceState, ONCE_INIT}; #[stable(feature = "rust1", since = "1.0.0")] pub use sys_common::poison::{PoisonError, TryLockError, TryLockResult, LockResult}; #[stable(feature = "rust1", since = "1.0.0")] -pub use self::rwlock::{RwLockReadGuard, RwLockWriteGuard}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use self::rwlock::{RwLock, StaticRwLock, RW_LOCK_INIT}; +pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; pub mod mpsc; diff --git a/src/libstd/sync/mpsc/blocking.rs b/src/libstd/sync/mpsc/blocking.rs index 0e5a9859116..4a70de0e7d8 100644 --- a/src/libstd/sync/mpsc/blocking.rs +++ b/src/libstd/sync/mpsc/blocking.rs @@ -16,6 +16,7 @@ use sync::Arc; use marker::{Sync, Send}; use mem; use clone::Clone; +use time::Instant; struct Inner { thread: Thread, @@ -74,7 +75,6 @@ impl SignalToken { pub unsafe fn cast_from_usize(signal_ptr: usize) -> SignalToken { SignalToken { inner: mem::transmute(signal_ptr) } } - } impl WaitToken { @@ -83,4 +83,16 @@ impl WaitToken { thread::park() } } + + /// Returns true if we wake up normally, false otherwise. + pub fn wait_max_until(self, end: Instant) -> bool { + while !self.inner.woken.load(Ordering::SeqCst) { + let now = Instant::now(); + if now >= end { + return false; + } + thread::park_timeout(end - now) + } + true + } } diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs index dbcc2bc95bc..11f785dffd1 100644 --- a/src/libstd/sync/mpsc/mod.rs +++ b/src/libstd/sync/mpsc/mod.rs @@ -134,9 +134,9 @@ // senders. Under the hood, however, there are actually three flavors of // channels in play. // -// * Flavor::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. +// * Flavor::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 @@ -148,9 +148,9 @@ // // ## 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. +// 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 @@ -271,6 +271,7 @@ use fmt; use mem; use cell::UnsafeCell; use marker::Reflect; +use time::{Duration, Instant}; #[unstable(feature = "mpsc_select", issue = "27800")] pub use self::select::{Select, Handle}; @@ -310,6 +311,17 @@ pub struct Iter<'a, T: 'a> { rx: &'a Receiver<T> } +/// An iterator that attempts to yield all pending values for a receiver. +/// `None` will be returned when there are no pending values remaining or +/// if the corresponding channel has hung up. +/// +/// This Iterator will never block the caller in order to wait for data to +/// become available. Instead, it will return `None`. +#[unstable(feature = "receiver_try_iter", issue = "34931")] +pub struct TryIter<'a, T: 'a> { + rx: &'a Receiver<T> +} + /// An owning 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. @@ -379,6 +391,19 @@ pub enum TryRecvError { Disconnected, } +/// This enumeration is the list of possible errors that `recv_timeout` could +/// not return data when called. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[unstable(feature = "mpsc_recv_timeout", issue = "34029")] +pub enum RecvTimeoutError { + /// This channel is currently empty, but the sender(s) have not yet + /// disconnected, so data may yet become available. + Timeout, + /// 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. #[stable(feature = "rust1", since = "1.0.0")] @@ -535,7 +560,7 @@ impl<T> Sender<T> { /// /// // This send will fail because the receiver is gone /// drop(rx); - /// assert_eq!(tx.send(1).err().unwrap().0, 1); + /// assert_eq!(tx.send(1).unwrap_err().0, 1); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn send(&self, t: T) -> Result<(), SendError<T>> { @@ -838,30 +863,30 @@ impl<T> Receiver<T> { loop { let new_port = match *unsafe { self.inner() } { Flavor::Oneshot(ref p) => { - match unsafe { (*p.get()).recv() } { + match unsafe { (*p.get()).recv(None) } { Ok(t) => return Ok(t), - Err(oneshot::Empty) => return unreachable!(), Err(oneshot::Disconnected) => return Err(RecvError), Err(oneshot::Upgraded(rx)) => rx, + Err(oneshot::Empty) => unreachable!(), } } Flavor::Stream(ref p) => { - match unsafe { (*p.get()).recv() } { + match unsafe { (*p.get()).recv(None) } { Ok(t) => return Ok(t), - Err(stream::Empty) => return unreachable!(), Err(stream::Disconnected) => return Err(RecvError), Err(stream::Upgraded(rx)) => rx, + Err(stream::Empty) => unreachable!(), } } Flavor::Shared(ref p) => { - match unsafe { (*p.get()).recv() } { + match unsafe { (*p.get()).recv(None) } { Ok(t) => return Ok(t), - Err(shared::Empty) => return unreachable!(), Err(shared::Disconnected) => return Err(RecvError), + Err(shared::Empty) => unreachable!(), } } Flavor::Sync(ref p) => return unsafe { - (*p.get()).recv().map_err(|()| RecvError) + (*p.get()).recv(None).map_err(|_| RecvError) } }; unsafe { @@ -870,12 +895,114 @@ impl<T> Receiver<T> { } } + /// Attempts to wait for a value on this receiver, returning an error if the + /// corresponding channel has hung up, or if it waits more than `timeout`. + /// + /// This function will always block the current thread if there is no data + /// available and it's possible for more data to be sent. Once a message is + /// sent to the corresponding `Sender`, then this receiver will wake up and + /// return that message. + /// + /// If the corresponding `Sender` has disconnected, or it disconnects while + /// this call is blocking, this call will wake up and return `Err` to + /// indicate that no more messages can ever be received on this channel. + /// However, since channels are buffered, messages sent before the disconnect + /// will still be properly received. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(mpsc_recv_timeout)] + /// + /// use std::sync::mpsc::{self, RecvTimeoutError}; + /// use std::time::Duration; + /// + /// let (send, recv) = mpsc::channel::<()>(); + /// + /// let timeout = Duration::from_millis(100); + /// assert_eq!(Err(RecvTimeoutError::Timeout), recv.recv_timeout(timeout)); + /// ``` + #[unstable(feature = "mpsc_recv_timeout", issue = "34029")] + pub fn recv_timeout(&self, timeout: Duration) -> Result<T, RecvTimeoutError> { + // Do an optimistic try_recv to avoid the performance impact of + // Instant::now() in the full-channel case. + match self.try_recv() { + Ok(result) + => Ok(result), + Err(TryRecvError::Disconnected) + => Err(RecvTimeoutError::Disconnected), + Err(TryRecvError::Empty) + => self.recv_max_until(Instant::now() + timeout) + } + } + + fn recv_max_until(&self, deadline: Instant) -> Result<T, RecvTimeoutError> { + use self::RecvTimeoutError::*; + + loop { + let port_or_empty = match *unsafe { self.inner() } { + Flavor::Oneshot(ref p) => { + match unsafe { (*p.get()).recv(Some(deadline)) } { + Ok(t) => return Ok(t), + Err(oneshot::Disconnected) => return Err(Disconnected), + Err(oneshot::Upgraded(rx)) => Some(rx), + Err(oneshot::Empty) => None, + } + } + Flavor::Stream(ref p) => { + match unsafe { (*p.get()).recv(Some(deadline)) } { + Ok(t) => return Ok(t), + Err(stream::Disconnected) => return Err(Disconnected), + Err(stream::Upgraded(rx)) => Some(rx), + Err(stream::Empty) => None, + } + } + Flavor::Shared(ref p) => { + match unsafe { (*p.get()).recv(Some(deadline)) } { + Ok(t) => return Ok(t), + Err(shared::Disconnected) => return Err(Disconnected), + Err(shared::Empty) => None, + } + } + Flavor::Sync(ref p) => { + match unsafe { (*p.get()).recv(Some(deadline)) } { + Ok(t) => return Ok(t), + Err(sync::Disconnected) => return Err(Disconnected), + Err(sync::Empty) => None, + } + } + }; + + if let Some(new_port) = port_or_empty { + unsafe { + mem::swap(self.inner_mut(), new_port.inner_mut()); + } + } + + // If we're already passed the deadline, and we're here without + // data, return a timeout, else try again. + if Instant::now() >= deadline { + return Err(Timeout); + } + } + } + /// Returns an iterator that will block waiting for messages, but never /// `panic!`. It will return `None` when the channel has hung up. #[stable(feature = "rust1", since = "1.0.0")] pub fn iter(&self) -> Iter<T> { Iter { rx: self } } + + /// Returns an iterator that will attempt to yield all pending values. + /// It will return `None` if there are no more pending values or if the + /// channel has hung up. The iterator will never `panic!` or block the + /// user by waiting for values. + #[unstable(feature = "receiver_try_iter", issue = "34931")] + pub fn try_iter(&self) -> TryIter<T> { + TryIter { rx: self } + } + } impl<T> select::Packet for Receiver<T> { @@ -971,6 +1098,13 @@ impl<'a, T> Iterator for Iter<'a, T> { fn next(&mut self) -> Option<T> { self.rx.recv().ok() } } +#[unstable(feature = "receiver_try_iter", issue = "34931")] +impl<'a, T> Iterator for TryIter<'a, T> { + type Item = T; + + fn next(&mut self) -> Option<T> { self.rx.try_recv().ok() } +} + #[stable(feature = "receiver_into_iter", since = "1.1.0")] impl<'a, T> IntoIterator for &'a Receiver<T> { type Item = T; @@ -1141,6 +1275,7 @@ mod tests { use env; use super::*; use thread; + use time::{Duration, Instant}; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { @@ -1540,6 +1675,87 @@ mod tests { } #[test] + fn oneshot_single_thread_recv_timeout() { + let (tx, rx) = channel(); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); + } + + #[test] + fn stress_recv_timeout_two_threads() { + let (tx, rx) = channel(); + let stress = stress_factor() + 100; + let timeout = Duration::from_millis(100); + + thread::spawn(move || { + for i in 0..stress { + if i % 2 == 0 { + thread::sleep(timeout * 2); + } + tx.send(1usize).unwrap(); + } + }); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(timeout) { + Ok(n) => { + assert_eq!(n, 1usize); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, stress); + } + + #[test] + fn recv_timeout_upgrade() { + let (tx, rx) = channel::<()>(); + let timeout = Duration::from_millis(1); + let _tx_clone = tx.clone(); + + let start = Instant::now(); + assert_eq!(rx.recv_timeout(timeout), Err(RecvTimeoutError::Timeout)); + assert!(Instant::now() >= start + timeout); + } + + #[test] + fn stress_recv_timeout_shared() { + let (tx, rx) = channel(); + let stress = stress_factor() + 100; + + for i in 0..stress { + let tx = tx.clone(); + thread::spawn(move || { + thread::sleep(Duration::from_millis(i as u64 * 10)); + tx.send(1usize).unwrap(); + }); + } + + drop(tx); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(Duration::from_millis(10)) { + Ok(n) => { + assert_eq!(n, 1usize); + recv_count += 1; + } + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, stress); + } + + #[test] fn recv_a_lot() { // Regression test that we don't run out of stack in scheduler context let (tx, rx) = channel(); @@ -1548,6 +1764,24 @@ mod tests { } #[test] + fn shared_recv_timeout() { + let (tx, rx) = channel(); + let total = 5; + for _ in 0..total { + let tx = tx.clone(); + thread::spawn(move|| { + tx.send(()).unwrap(); + }); + } + + for _ in 0..total { rx.recv().unwrap(); } + + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(()).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(())); + } + + #[test] fn shared_chan_stress() { let (tx, rx) = channel(); let total = stress_factor() + 100; @@ -1609,6 +1843,34 @@ mod tests { } #[test] + fn test_recv_try_iter() { + let (request_tx, request_rx) = channel(); + let (response_tx, response_rx) = channel(); + + // Request `x`s until we have `6`. + let t = thread::spawn(move|| { + let mut count = 0; + loop { + for x in response_rx.try_iter() { + count += x; + if count == 6 { + return count; + } + } + request_tx.send(()).unwrap(); + } + }); + + for _ in request_rx.iter() { + if response_tx.send(2).is_err() { + break; + } + } + + assert_eq!(t.join().unwrap(), 6); + } + + #[test] fn test_recv_into_iter_owned() { let mut iter = { let (tx, rx) = channel::<i32>(); @@ -1689,6 +1951,7 @@ mod sync_tests { use env; use thread; use super::*; + use time::Duration; pub fn stress_factor() -> usize { match env::var("RUST_TEST_STRESS") { @@ -1721,6 +1984,14 @@ mod sync_tests { } #[test] + fn recv_timeout() { + let (tx, rx) = sync_channel::<i32>(1); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Err(RecvTimeoutError::Timeout)); + tx.send(1).unwrap(); + assert_eq!(rx.recv_timeout(Duration::from_millis(1)), Ok(1)); + } + + #[test] fn smoke_threads() { let (tx, rx) = sync_channel::<i32>(0); let _t = thread::spawn(move|| { @@ -1802,6 +2073,67 @@ mod sync_tests { } #[test] + fn stress_recv_timeout_two_threads() { + let (tx, rx) = sync_channel::<i32>(0); + + thread::spawn(move|| { + for _ in 0..10000 { tx.send(1).unwrap(); } + }); + + let mut recv_count = 0; + loop { + match rx.recv_timeout(Duration::from_millis(1)) { + Ok(v) => { + assert_eq!(v, 1); + recv_count += 1; + }, + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, 10000); + } + + #[test] + fn stress_recv_timeout_shared() { + const AMT: u32 = 1000; + const NTHREADS: u32 = 8; + let (tx, rx) = sync_channel::<i32>(0); + let (dtx, drx) = sync_channel::<()>(0); + + thread::spawn(move|| { + let mut recv_count = 0; + loop { + match rx.recv_timeout(Duration::from_millis(10)) { + Ok(v) => { + assert_eq!(v, 1); + recv_count += 1; + }, + Err(RecvTimeoutError::Timeout) => continue, + Err(RecvTimeoutError::Disconnected) => break, + } + } + + assert_eq!(recv_count, AMT * NTHREADS); + assert!(rx.try_recv().is_err()); + + dtx.send(()).unwrap(); + }); + + for _ in 0..NTHREADS { + let tx = tx.clone(); + thread::spawn(move|| { + for _ in 0..AMT { tx.send(1).unwrap(); } + }); + } + + drop(tx); + + drx.recv().unwrap(); + } + + #[test] fn stress_shared() { const AMT: u32 = 1000; const NTHREADS: u32 = 8; @@ -1905,6 +2237,15 @@ mod sync_tests { } #[test] + fn oneshot_single_thread_try_recv_closed_with_data() { + let (tx, rx) = sync_channel::<i32>(1); + tx.send(10).unwrap(); + drop(tx); + assert_eq!(rx.try_recv(), Ok(10)); + assert_eq!(rx.try_recv(), Err(TryRecvError::Disconnected)); + } + + #[test] fn oneshot_single_thread_peek_data() { let (tx, rx) = sync_channel::<i32>(1); assert_eq!(rx.try_recv(), Err(TryRecvError::Empty)); diff --git a/src/libstd/sync/mpsc/oneshot.rs b/src/libstd/sync/mpsc/oneshot.rs index cb930280964..7a35ea6bbaa 100644 --- a/src/libstd/sync/mpsc/oneshot.rs +++ b/src/libstd/sync/mpsc/oneshot.rs @@ -41,6 +41,7 @@ use sync::mpsc::Receiver; use sync::mpsc::blocking::{self, SignalToken}; use core::mem; use sync::atomic::{AtomicUsize, Ordering}; +use time::Instant; // Various states you can find a port in. const EMPTY: usize = 0; // initial state: no data, no blocked receiver @@ -136,7 +137,7 @@ impl<T> Packet<T> { } } - pub fn recv(&mut self) -> Result<T, Failure<T>> { + pub fn recv(&mut self, deadline: Option<Instant>) -> Result<T, Failure<T>> { // Attempt to not block the thread (it's a little expensive). If it looks // like we're not empty, then immediately go through to `try_recv`. if self.state.load(Ordering::SeqCst) == EMPTY { @@ -145,8 +146,16 @@ impl<T> Packet<T> { // race with senders to enter the blocking state if self.state.compare_and_swap(EMPTY, ptr, Ordering::SeqCst) == EMPTY { - wait_token.wait(); - debug_assert!(self.state.load(Ordering::SeqCst) != EMPTY); + if let Some(deadline) = deadline { + let timed_out = !wait_token.wait_max_until(deadline); + // Try to reset the state + if timed_out { + try!(self.abort_selection().map_err(Upgraded)); + } + } else { + wait_token.wait(); + debug_assert!(self.state.load(Ordering::SeqCst) != EMPTY); + } } else { // drop the signal token, since we never blocked drop(unsafe { SignalToken::cast_from_usize(ptr) }); diff --git a/src/libstd/sync/mpsc/shared.rs b/src/libstd/sync/mpsc/shared.rs index a3779931c7b..baa4db7e5c0 100644 --- a/src/libstd/sync/mpsc/shared.rs +++ b/src/libstd/sync/mpsc/shared.rs @@ -30,6 +30,7 @@ use sync::mpsc::select::StartResult::*; use sync::mpsc::select::StartResult; use sync::{Mutex, MutexGuard}; use thread; +use time::Instant; const DISCONNECTED: isize = isize::MIN; const FUDGE: isize = 1024; @@ -66,7 +67,7 @@ impl<T> 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 { + Packet { queue: mpsc::Queue::new(), cnt: AtomicIsize::new(0), steals: 0, @@ -75,8 +76,7 @@ impl<T> Packet<T> { port_dropped: AtomicBool::new(false), sender_drain: AtomicIsize::new(0), select_lock: Mutex::new(()), - }; - return p; + } } // This function should be used after newly created Packet @@ -216,7 +216,7 @@ impl<T> Packet<T> { Ok(()) } - pub fn recv(&mut self) -> Result<T, Failure> { + pub fn recv(&mut self, deadline: Option<Instant>) -> Result<T, Failure> { // This code is essentially the exact same as that found in the stream // case (see stream.rs) match self.try_recv() { @@ -226,7 +226,14 @@ impl<T> Packet<T> { let (wait_token, signal_token) = blocking::tokens(); if self.decrement(signal_token) == Installed { - wait_token.wait() + if let Some(deadline) = deadline { + let timed_out = !wait_token.wait_max_until(deadline); + if timed_out { + self.abort_selection(false); + } + } else { + wait_token.wait(); + } } match self.try_recv() { diff --git a/src/libstd/sync/mpsc/spsc_queue.rs b/src/libstd/sync/mpsc/spsc_queue.rs index ffd33f8518f..02506e7c2f3 100644 --- a/src/libstd/sync/mpsc/spsc_queue.rs +++ b/src/libstd/sync/mpsc/spsc_queue.rs @@ -265,15 +265,18 @@ mod tests { // Ensure the borrowchecker works match queue.peek() { - Some(vec) => match &**vec { - // Note that `pop` is not allowed here due to borrow - [1] => {} - _ => return + Some(vec) => { + assert_eq!(&*vec, &[1]); }, None => unreachable!() } - queue.pop(); + match queue.pop() { + Some(vec) => { + assert_eq!(&*vec, &[1]); + }, + None => unreachable!() + } } } diff --git a/src/libstd/sync/mpsc/stream.rs b/src/libstd/sync/mpsc/stream.rs index e8012ca470b..aa1254c8641 100644 --- a/src/libstd/sync/mpsc/stream.rs +++ b/src/libstd/sync/mpsc/stream.rs @@ -25,6 +25,7 @@ use self::Message::*; use core::cmp; use core::isize; use thread; +use time::Instant; use sync::atomic::{AtomicIsize, AtomicUsize, Ordering, AtomicBool}; use sync::mpsc::Receiver; @@ -172,7 +173,7 @@ impl<T> Packet<T> { Err(unsafe { SignalToken::cast_from_usize(ptr) }) } - pub fn recv(&mut self) -> Result<T, Failure<T>> { + pub fn recv(&mut self, deadline: Option<Instant>) -> Result<T, Failure<T>> { // Optimistic preflight check (scheduling is expensive). match self.try_recv() { Err(Empty) => {} @@ -183,7 +184,15 @@ impl<T> Packet<T> { // initiate the blocking protocol. let (wait_token, signal_token) = blocking::tokens(); if self.decrement(signal_token).is_ok() { - wait_token.wait() + if let Some(deadline) = deadline { + let timed_out = !wait_token.wait_max_until(deadline); + if timed_out { + try!(self.abort_selection(/* was_upgrade = */ false) + .map_err(Upgraded)); + } + } else { + wait_token.wait(); + } } match self.try_recv() { @@ -332,7 +341,7 @@ impl<T> Packet<T> { // the internal state. match self.queue.peek() { Some(&mut GoUp(..)) => { - match self.recv() { + match self.recv(None) { Err(Upgraded(port)) => Err(port), _ => unreachable!(), } diff --git a/src/libstd/sync/mpsc/sync.rs b/src/libstd/sync/mpsc/sync.rs index b98fc2859af..9d13a71ff95 100644 --- a/src/libstd/sync/mpsc/sync.rs +++ b/src/libstd/sync/mpsc/sync.rs @@ -44,6 +44,7 @@ use sync::atomic::{Ordering, AtomicUsize}; use sync::mpsc::blocking::{self, WaitToken, SignalToken}; use sync::mpsc::select::StartResult::{self, Installed, Abort}; use sync::{Mutex, MutexGuard}; +use time::Instant; pub struct Packet<T> { /// Only field outside of the mutex. Just done for kicks, but mainly because @@ -126,6 +127,38 @@ fn wait<'a, 'b, T>(lock: &'a Mutex<State<T>>, lock.lock().unwrap() // relock } +/// Same as wait, but waiting at most until `deadline`. +fn wait_timeout_receiver<'a, 'b, T>(lock: &'a Mutex<State<T>>, + deadline: Instant, + mut guard: MutexGuard<'b, State<T>>, + success: &mut bool) + -> MutexGuard<'a, State<T>> +{ + let (wait_token, signal_token) = blocking::tokens(); + match mem::replace(&mut guard.blocker, BlockedReceiver(signal_token)) { + NoneBlocked => {} + _ => unreachable!(), + } + drop(guard); // unlock + *success = wait_token.wait_max_until(deadline); // block + let mut new_guard = lock.lock().unwrap(); // relock + if !*success { + abort_selection(&mut new_guard); + } + new_guard +} + +fn abort_selection<'a, T>(guard: &mut MutexGuard<'a , State<T>>) -> bool { + match mem::replace(&mut guard.blocker, NoneBlocked) { + NoneBlocked => true, + BlockedSender(token) => { + guard.blocker = BlockedSender(token); + true + } + BlockedReceiver(token) => { drop(token); false } + } +} + /// 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 thread *outside* of the mutex @@ -238,22 +271,37 @@ impl<T> Packet<T> { // // When reading this, remember that there can only ever be one receiver at // time. - pub fn recv(&self) -> Result<T, ()> { + pub fn recv(&self, deadline: Option<Instant>) -> Result<T, Failure> { let mut guard = self.lock.lock().unwrap(); - // 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; + let mut woke_up_after_waiting = false; + // Wait for the buffer to have something in it. No need for a + // while loop because we're the only receiver. if !guard.disconnected && guard.buf.size() == 0 { - guard = wait(&self.lock, guard, BlockedReceiver); - waited = true; + if let Some(deadline) = deadline { + guard = wait_timeout_receiver(&self.lock, + deadline, + guard, + &mut woke_up_after_waiting); + } else { + guard = wait(&self.lock, guard, BlockedReceiver); + woke_up_after_waiting = true; + } + } + + // NB: Channel could be disconnected while waiting, so the order of + // these conditionals is important. + if guard.disconnected && guard.buf.size() == 0 { + return Err(Disconnected); } - 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); + assert!(guard.buf.size() > 0 || (deadline.is_some() && !woke_up_after_waiting)); + + if guard.buf.size() == 0 { return Err(Empty); } + let ret = guard.buf.dequeue(); - self.wakeup_senders(waited, guard); + self.wakeup_senders(woke_up_after_waiting, guard); Ok(ret) } @@ -261,7 +309,7 @@ impl<T> Packet<T> { let mut guard = self.lock.lock().unwrap(); // Easy cases first - if guard.disconnected { return Err(Disconnected) } + if guard.disconnected && guard.buf.size() == 0 { return Err(Disconnected) } if guard.buf.size() == 0 { return Err(Empty) } // Be sure to wake up neighbors @@ -392,14 +440,7 @@ impl<T> Packet<T> { // The return value indicates whether there's data on this port. pub fn abort_selection(&self) -> bool { let mut guard = self.lock.lock().unwrap(); - match mem::replace(&mut guard.blocker, NoneBlocked) { - NoneBlocked => true, - BlockedSender(token) => { - guard.blocker = BlockedSender(token); - true - } - BlockedReceiver(token) => { drop(token); false } - } + abort_selection(&mut guard) } } diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs index 90cc79dad66..6bc458397f1 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -114,12 +114,13 @@ use sys_common::poison::{self, TryLockError, TryLockResult, LockResult}; /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct Mutex<T: ?Sized> { - // 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>, + // Note that this 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<sys::Mutex>, + poison: poison::Flag, data: UnsafeCell<T>, } @@ -130,37 +131,6 @@ unsafe impl<T: ?Sized + Send> Send for Mutex<T> { } #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<T: ?Sized + Send> Sync for Mutex<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. -/// -/// # Examples -/// -/// ``` -/// #![feature(static_mutex)] -/// -/// use std::sync::{StaticMutex, MUTEX_INIT}; -/// -/// static LOCK: StaticMutex = MUTEX_INIT; -/// -/// { -/// let _g = LOCK.lock().unwrap(); -/// // do some productive work -/// } -/// // lock is unlocked here. -/// ``` -#[unstable(feature = "static_mutex", - reason = "may be merged with Mutex in the future", - issue = "27717")] -pub struct StaticMutex { - lock: sys::Mutex, - poison: 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. /// @@ -171,29 +141,26 @@ pub struct StaticMutex { pub struct MutexGuard<'a, T: ?Sized + 'a> { // funny underscores due to how Deref/DerefMut currently work (they // disregard field privacy). - __lock: &'a StaticMutex, - __data: &'a mut T, + __lock: &'a Mutex<T>, __poison: poison::Guard, } #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T: ?Sized> !marker::Send for MutexGuard<'a, T> {} -/// Static initialization of a mutex. This constant can be used to initialize -/// other mutex constants. -#[unstable(feature = "static_mutex", - reason = "may be merged with Mutex in the future", - issue = "27717")] -pub const MUTEX_INIT: StaticMutex = StaticMutex::new(); - impl<T> Mutex<T> { /// Creates a new mutex in an unlocked state ready for use. #[stable(feature = "rust1", since = "1.0.0")] pub fn new(t: T) -> Mutex<T> { - Mutex { - inner: box StaticMutex::new(), + let mut m = Mutex { + inner: box sys::Mutex::new(), + poison: poison::Flag::new(), data: UnsafeCell::new(t), + }; + unsafe { + m.inner.init(); } + m } } @@ -221,8 +188,8 @@ impl<T: ?Sized> Mutex<T> { #[stable(feature = "rust1", since = "1.0.0")] pub fn lock(&self) -> LockResult<MutexGuard<T>> { unsafe { - self.inner.lock.lock(); - MutexGuard::new(&*self.inner, &self.data) + self.inner.lock(); + MutexGuard::new(self) } } @@ -242,8 +209,8 @@ impl<T: ?Sized> Mutex<T> { #[stable(feature = "rust1", since = "1.0.0")] pub fn try_lock(&self) -> TryLockResult<MutexGuard<T>> { unsafe { - if self.inner.lock.try_lock() { - Ok(MutexGuard::new(&*self.inner, &self.data)?) + if self.inner.try_lock() { + Ok(MutexGuard::new(self)?) } else { Err(TryLockError::WouldBlock) } @@ -258,7 +225,7 @@ impl<T: ?Sized> Mutex<T> { #[inline] #[stable(feature = "sync_poison", since = "1.2.0")] pub fn is_poisoned(&self) -> bool { - self.inner.poison.get() + self.poison.get() } /// Consumes this mutex, returning the underlying data. @@ -270,21 +237,22 @@ impl<T: ?Sized> Mutex<T> { #[stable(feature = "mutex_into_inner", since = "1.6.0")] pub fn into_inner(self) -> LockResult<T> where T: Sized { // We know statically that there are no outstanding references to - // `self` so there's no need to lock the inner StaticMutex. + // `self` so there's no need to lock the inner lock. // // To get the inner value, we'd like to call `data.into_inner()`, // but because `Mutex` impl-s `Drop`, we can't move out of it, so // we'll have to destructure it manually instead. unsafe { - // Like `let Mutex { inner, data } = self`. - let (inner, data) = { - let Mutex { ref inner, ref data } = self; - (ptr::read(inner), ptr::read(data)) + // Like `let Mutex { inner, poison, data } = self`. + let (inner, poison, data) = { + let Mutex { ref inner, ref poison, ref data } = self; + (ptr::read(inner), ptr::read(poison), ptr::read(data)) }; mem::forget(self); - inner.lock.destroy(); // Keep in sync with the `Drop` impl. + inner.destroy(); // Keep in sync with the `Drop` impl. + drop(inner); - poison::map_result(inner.poison.borrow(), |_| data.into_inner()) + poison::map_result(poison.borrow(), |_| data.into_inner()) } } @@ -300,9 +268,9 @@ impl<T: ?Sized> Mutex<T> { #[stable(feature = "mutex_get_mut", since = "1.6.0")] pub fn get_mut(&mut self) -> LockResult<&mut T> { // We know statically that there are no other references to `self`, so - // there's no need to lock the inner StaticMutex. + // there's no need to lock the inner lock. let data = unsafe { &mut *self.data.get() }; - poison::map_result(self.inner.poison.borrow(), |_| data ) + poison::map_result(self.poison.borrow(), |_| data ) } } @@ -315,7 +283,7 @@ impl<T: ?Sized> Drop for Mutex<T> { // dropped, that's not our job) // // IMPORTANT: This code must be kept in sync with `Mutex::into_inner`. - unsafe { self.inner.lock.destroy() } + unsafe { self.inner.destroy() } } } @@ -339,66 +307,11 @@ impl<T: ?Sized + fmt::Debug> fmt::Debug for Mutex<T> { } } -struct Dummy(UnsafeCell<()>); -unsafe impl Sync for Dummy {} -static DUMMY: Dummy = Dummy(UnsafeCell::new(())); - -#[unstable(feature = "static_mutex", - reason = "may be merged with Mutex in the future", - issue = "27717")] -impl StaticMutex { - /// Creates a new mutex in an unlocked state ready for use. - pub const fn new() -> StaticMutex { - StaticMutex { - lock: sys::Mutex::new(), - poison: poison::Flag::new(), - } - } - - /// Acquires this lock, see `Mutex::lock` - #[inline] - pub fn lock(&'static self) -> LockResult<MutexGuard<()>> { - unsafe { - self.lock.lock(); - MutexGuard::new(self, &DUMMY.0) - } - } - - /// Attempts to grab this lock, see `Mutex::try_lock` - #[inline] - pub fn try_lock(&'static self) -> TryLockResult<MutexGuard<()>> { - unsafe { - if self.lock.try_lock() { - Ok(MutexGuard::new(self, &DUMMY.0)?) - } else { - Err(TryLockError::WouldBlock) - } - } - } - - /// 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: ?Sized> MutexGuard<'mutex, T> { - - unsafe fn new(lock: &'mutex StaticMutex, data: &'mutex UnsafeCell<T>) - -> LockResult<MutexGuard<'mutex, T>> { + unsafe fn new(lock: &'mutex Mutex<T>) -> LockResult<MutexGuard<'mutex, T>> { poison::map_result(lock.poison.borrow(), |guard| { MutexGuard { __lock: lock, - __data: &mut *data.get(), __poison: guard, } }) @@ -409,12 +322,16 @@ impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> { impl<'mutex, T: ?Sized> Deref for MutexGuard<'mutex, T> { type Target = T; - fn deref(&self) -> &T {self.__data } + fn deref(&self) -> &T { + unsafe { &*self.__lock.data.get() } + } } #[stable(feature = "rust1", since = "1.0.0")] impl<'mutex, T: ?Sized> DerefMut for MutexGuard<'mutex, T> { - fn deref_mut(&mut self) -> &mut T { self.__data } + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.__lock.data.get() } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -423,13 +340,13 @@ impl<'a, T: ?Sized> Drop for MutexGuard<'a, T> { fn drop(&mut self) { unsafe { self.__lock.poison.done(&self.__poison); - self.__lock.lock.unlock(); + self.__lock.inner.unlock(); } } } pub fn guard_lock<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a sys::Mutex { - &guard.__lock.lock + &guard.__lock.inner } pub fn guard_poison<'a, T: ?Sized>(guard: &MutexGuard<'a, T>) -> &'a poison::Flag { @@ -441,7 +358,7 @@ mod tests { use prelude::v1::*; use sync::mpsc::channel; - use sync::{Arc, Mutex, StaticMutex, Condvar}; + use sync::{Arc, Mutex, Condvar}; use sync::atomic::{AtomicUsize, Ordering}; use thread; @@ -461,47 +378,33 @@ mod tests { } #[test] - fn smoke_static() { - static M: StaticMutex = StaticMutex::new(); - unsafe { - drop(M.lock().unwrap()); - drop(M.lock().unwrap()); - M.destroy(); - } - } - - #[test] fn lots_and_lots() { - static M: StaticMutex = StaticMutex::new(); - static mut CNT: u32 = 0; const J: u32 = 1000; const K: u32 = 3; - fn inc() { + let m = Arc::new(Mutex::new(0)); + + fn inc(m: &Mutex<u32>) { for _ in 0..J { - unsafe { - let _g = M.lock().unwrap(); - CNT += 1; - } + *m.lock().unwrap() += 1; } } let (tx, rx) = channel(); for _ in 0..K { let tx2 = tx.clone(); - thread::spawn(move|| { inc(); tx2.send(()).unwrap(); }); + let m2 = m.clone(); + thread::spawn(move|| { inc(&m2); tx2.send(()).unwrap(); }); let tx2 = tx.clone(); - thread::spawn(move|| { inc(); tx2.send(()).unwrap(); }); + let m2 = m.clone(); + thread::spawn(move|| { inc(&m2); tx2.send(()).unwrap(); }); } drop(tx); for _ in 0..2 * K { rx.recv().unwrap(); } - assert_eq!(unsafe {CNT}, J * K * 2); - unsafe { - M.destroy(); - } + assert_eq!(*m.lock().unwrap(), J * K * 2); } #[test] diff --git a/src/libstd/sync/once.rs b/src/libstd/sync/once.rs index e228d236a3c..54c1fe6c564 100644 --- a/src/libstd/sync/once.rs +++ b/src/libstd/sync/once.rs @@ -65,6 +65,7 @@ // it! use marker; +use ptr; use sync::atomic::{AtomicUsize, AtomicBool, Ordering}; use thread::{self, Thread}; @@ -101,7 +102,7 @@ unsafe impl Send for Once {} /// State yielded to the `call_once_force` method which can be used to query /// whether the `Once` was previously poisoned or not. -#[unstable(feature = "once_poison", issue = "31688")] +#[unstable(feature = "once_poison", issue = "33577")] pub struct OnceState { poisoned: bool, } @@ -218,8 +219,7 @@ impl Once { /// The closure `f` is yielded a structure which can be used to query the /// state of this `Once` (whether initialization has previously panicked or /// not). - /// poisoned or not. - #[unstable(feature = "once_poison", issue = "31688")] + #[unstable(feature = "once_poison", issue = "33577")] pub fn call_once_force<F>(&'static self, f: F) where F: FnOnce(&OnceState) { // same as above, just with a different parameter to `call_inner`. if self.state.load(Ordering::SeqCst) == COMPLETE { @@ -298,7 +298,7 @@ impl Once { let mut node = Waiter { thread: Some(thread::current()), signaled: AtomicBool::new(false), - next: 0 as *mut Waiter, + next: ptr::null_mut(), }; let me = &mut node as *mut Waiter as usize; assert!(me & STATE_MASK == 0); @@ -361,7 +361,7 @@ impl OnceState { /// /// Once an initalization routine for a `Once` has panicked it will forever /// indicate to future forced initialization routines that it is poisoned. - #[unstable(feature = "once_poison", issue = "31688")] + #[unstable(feature = "once_poison", issue = "33577")] pub fn poisoned(&self) -> bool { self.poisoned } diff --git a/src/libstd/sync/rwlock.rs b/src/libstd/sync/rwlock.rs index 09b1b0a939d..65b5686de86 100644 --- a/src/libstd/sync/rwlock.rs +++ b/src/libstd/sync/rwlock.rs @@ -38,8 +38,8 @@ use sys_common::rwlock as sys; /// /// # Poisoning /// -/// 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 +/// An `RwLock`, like `Mutex`, will become poisoned on a panic. 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. /// @@ -67,7 +67,8 @@ use sys_common::rwlock as sys; /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub struct RwLock<T: ?Sized> { - inner: Box<StaticRwLock>, + inner: Box<sys::RWLock>, + poison: poison::Flag, data: UnsafeCell<T>, } @@ -76,52 +77,12 @@ unsafe impl<T: ?Sized + Send + Sync> Send for RwLock<T> {} #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<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. -/// -/// # Examples -/// -/// ``` -/// #![feature(static_rwlock)] -/// -/// use std::sync::{StaticRwLock, RW_LOCK_INIT}; -/// -/// static LOCK: StaticRwLock = RW_LOCK_INIT; -/// -/// { -/// let _g = LOCK.read().unwrap(); -/// // ... shared read access -/// } -/// { -/// let _g = LOCK.write().unwrap(); -/// // ... exclusive write access -/// } -/// unsafe { LOCK.destroy() } // free all resources -/// ``` -#[unstable(feature = "static_rwlock", - reason = "may be merged with RwLock in the future", - issue = "27717")] -pub struct StaticRwLock { - lock: sys::RWLock, - poison: poison::Flag, -} - -/// Constant initialization for a statically-initialized rwlock. -#[unstable(feature = "static_rwlock", - reason = "may be merged with RwLock in the future", - issue = "27717")] -pub const RW_LOCK_INIT: StaticRwLock = StaticRwLock::new(); - /// RAII structure used to release the shared read access of a lock when /// dropped. #[must_use] #[stable(feature = "rust1", since = "1.0.0")] pub struct RwLockReadGuard<'a, T: ?Sized + 'a> { - __lock: &'a StaticRwLock, - __data: &'a T, + __lock: &'a RwLock<T>, } #[stable(feature = "rust1", since = "1.0.0")] @@ -132,8 +93,7 @@ impl<'a, T: ?Sized> !marker::Send for RwLockReadGuard<'a, T> {} #[must_use] #[stable(feature = "rust1", since = "1.0.0")] pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> { - __lock: &'a StaticRwLock, - __data: &'a mut T, + __lock: &'a RwLock<T>, __poison: poison::Guard, } @@ -152,7 +112,11 @@ impl<T> RwLock<T> { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn new(t: T) -> RwLock<T> { - RwLock { inner: box StaticRwLock::new(), data: UnsafeCell::new(t) } + RwLock { + inner: box sys::RWLock::new(), + poison: poison::Flag::new(), + data: UnsafeCell::new(t), + } } } @@ -178,8 +142,8 @@ impl<T: ?Sized> RwLock<T> { #[stable(feature = "rust1", since = "1.0.0")] pub fn read(&self) -> LockResult<RwLockReadGuard<T>> { unsafe { - self.inner.lock.read(); - RwLockReadGuard::new(&*self.inner, &self.data) + self.inner.read(); + RwLockReadGuard::new(self) } } @@ -204,8 +168,8 @@ impl<T: ?Sized> RwLock<T> { #[stable(feature = "rust1", since = "1.0.0")] pub fn try_read(&self) -> TryLockResult<RwLockReadGuard<T>> { unsafe { - if self.inner.lock.try_read() { - Ok(RwLockReadGuard::new(&*self.inner, &self.data)?) + if self.inner.try_read() { + Ok(RwLockReadGuard::new(self)?) } else { Err(TryLockError::WouldBlock) } @@ -230,8 +194,8 @@ impl<T: ?Sized> RwLock<T> { #[stable(feature = "rust1", since = "1.0.0")] pub fn write(&self) -> LockResult<RwLockWriteGuard<T>> { unsafe { - self.inner.lock.write(); - RwLockWriteGuard::new(&*self.inner, &self.data) + self.inner.write(); + RwLockWriteGuard::new(self) } } @@ -256,8 +220,8 @@ impl<T: ?Sized> RwLock<T> { #[stable(feature = "rust1", since = "1.0.0")] pub fn try_write(&self) -> TryLockResult<RwLockWriteGuard<T>> { unsafe { - if self.inner.lock.try_write() { - Ok(RwLockWriteGuard::new(&*self.inner, &self.data)?) + if self.inner.try_write() { + Ok(RwLockWriteGuard::new(self)?) } else { Err(TryLockError::WouldBlock) } @@ -272,7 +236,7 @@ impl<T: ?Sized> RwLock<T> { #[inline] #[stable(feature = "sync_poison", since = "1.2.0")] pub fn is_poisoned(&self) -> bool { - self.inner.poison.get() + self.poison.get() } /// Consumes this `RwLock`, returning the underlying data. @@ -286,21 +250,22 @@ impl<T: ?Sized> RwLock<T> { #[stable(feature = "rwlock_into_inner", since = "1.6.0")] pub fn into_inner(self) -> LockResult<T> where T: Sized { // We know statically that there are no outstanding references to - // `self` so there's no need to lock the inner StaticRwLock. + // `self` so there's no need to lock the inner lock. // // To get the inner value, we'd like to call `data.into_inner()`, // but because `RwLock` impl-s `Drop`, we can't move out of it, so // we'll have to destructure it manually instead. unsafe { - // Like `let RwLock { inner, data } = self`. - let (inner, data) = { - let RwLock { ref inner, ref data } = self; - (ptr::read(inner), ptr::read(data)) + // Like `let RwLock { inner, poison, data } = self`. + let (inner, poison, data) = { + let RwLock { ref inner, ref poison, ref data } = self; + (ptr::read(inner), ptr::read(poison), ptr::read(data)) }; mem::forget(self); - inner.lock.destroy(); // Keep in sync with the `Drop` impl. + inner.destroy(); // Keep in sync with the `Drop` impl. + drop(inner); - poison::map_result(inner.poison.borrow(), |_| data.into_inner()) + poison::map_result(poison.borrow(), |_| data.into_inner()) } } @@ -318,9 +283,9 @@ impl<T: ?Sized> RwLock<T> { #[stable(feature = "rwlock_get_mut", since = "1.6.0")] pub fn get_mut(&mut self) -> LockResult<&mut T> { // We know statically that there are no other references to `self`, so - // there's no need to lock the inner StaticRwLock. + // there's no need to lock the inner lock. let data = unsafe { &mut *self.data.get() }; - poison::map_result(self.inner.poison.borrow(), |_| data ) + poison::map_result(self.poison.borrow(), |_| data) } } @@ -329,7 +294,7 @@ impl<T: ?Sized> Drop for RwLock<T> { #[unsafe_destructor_blind_to_params] fn drop(&mut self) { // IMPORTANT: This code needs to be kept in sync with `RwLock::into_inner`. - unsafe { self.inner.lock.destroy() } + unsafe { self.inner.destroy() } } } @@ -353,223 +318,58 @@ impl<T: Default> Default for RwLock<T> { } } -struct Dummy(UnsafeCell<()>); -unsafe impl Sync for Dummy {} -static DUMMY: Dummy = Dummy(UnsafeCell::new(())); - -#[unstable(feature = "static_rwlock", - reason = "may be merged with RwLock in the future", - issue = "27717")] -impl StaticRwLock { - /// Creates a new rwlock. - pub const fn new() -> StaticRwLock { - StaticRwLock { - lock: sys::RWLock::new(), - poison: poison::Flag::new(), - } - } - - /// 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) -> LockResult<RwLockReadGuard<'static, ()>> { - unsafe { - self.lock.read(); - RwLockReadGuard::new(self, &DUMMY.0) - } - } - - /// Attempts to acquire this lock with shared read access. - /// - /// See `RwLock::try_read`. - #[inline] - pub fn try_read(&'static self) - -> TryLockResult<RwLockReadGuard<'static, ()>> { - unsafe { - if self.lock.try_read(){ - Ok(RwLockReadGuard::new(self, &DUMMY.0)?) - } else { - Err(TryLockError::WouldBlock) - } - } - } - - /// Locks this rwlock with exclusive write access, blocking the current - /// thread until it can be acquired. - /// - /// See `RwLock::write`. - #[inline] - pub fn write(&'static self) -> LockResult<RwLockWriteGuard<'static, ()>> { - unsafe { - self.lock.write(); - RwLockWriteGuard::new(self, &DUMMY.0) - } - } - - /// Attempts to lock this rwlock with exclusive write access. - /// - /// See `RwLock::try_write`. - #[inline] - pub fn try_write(&'static self) - -> TryLockResult<RwLockWriteGuard<'static, ()>> { - unsafe { - if self.lock.try_write() { - Ok(RwLockWriteGuard::new(self, &DUMMY.0)?) - } else { - Err(TryLockError::WouldBlock) - } - } - } - - /// Deallocates 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.lock.destroy() - } -} - impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> { - unsafe fn new(lock: &'rwlock StaticRwLock, data: &'rwlock UnsafeCell<T>) - -> LockResult<RwLockReadGuard<'rwlock, T>> { + unsafe fn new(lock: &'rwlock RwLock<T>) + -> LockResult<RwLockReadGuard<'rwlock, T>> { poison::map_result(lock.poison.borrow(), |_| { RwLockReadGuard { __lock: lock, - __data: &*data.get(), } }) } - - /// Transform this guard to hold a sub-borrow of the original data. - /// - /// Applies the supplied closure to the data, returning a new lock - /// guard referencing the borrow returned by the closure. - /// - /// # Examples - /// - /// ```rust - /// # #![feature(guard_map)] - /// # use std::sync::{RwLockReadGuard, RwLock}; - /// let x = RwLock::new(vec![1, 2]); - /// - /// let y = RwLockReadGuard::map(x.read().unwrap(), |v| &v[0]); - /// assert_eq!(*y, 1); - /// ``` - #[unstable(feature = "guard_map", - reason = "recently added, needs RFC for stabilization, - questionable interaction with Condvar", - issue = "27746")] - #[rustc_deprecated(since = "1.8.0", - reason = "unsound on Mutex because of Condvar and \ - RwLock may also with to be used with Condvar \ - one day")] - pub fn map<U: ?Sized, F>(this: Self, cb: F) -> RwLockReadGuard<'rwlock, U> - where F: FnOnce(&T) -> &U - { - let new = RwLockReadGuard { - __lock: this.__lock, - __data: cb(this.__data) - }; - - mem::forget(this); - - new - } } impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> { - unsafe fn new(lock: &'rwlock StaticRwLock, data: &'rwlock UnsafeCell<T>) - -> LockResult<RwLockWriteGuard<'rwlock, T>> { + unsafe fn new(lock: &'rwlock RwLock<T>) + -> LockResult<RwLockWriteGuard<'rwlock, T>> { poison::map_result(lock.poison.borrow(), |guard| { RwLockWriteGuard { __lock: lock, - __data: &mut *data.get(), __poison: guard, } }) } - - /// Transform this guard to hold a sub-borrow of the original data. - /// - /// Applies the supplied closure to the data, returning a new lock - /// guard referencing the borrow returned by the closure. - /// - /// # Examples - /// - /// ```rust - /// # #![feature(guard_map)] - /// # use std::sync::{RwLockWriteGuard, RwLock}; - /// let x = RwLock::new(vec![1, 2]); - /// - /// { - /// let mut y = RwLockWriteGuard::map(x.write().unwrap(), |v| &mut v[0]); - /// assert_eq!(*y, 1); - /// - /// *y = 10; - /// } - /// - /// assert_eq!(&**x.read().unwrap(), &[10, 2]); - /// ``` - #[unstable(feature = "guard_map", - reason = "recently added, needs RFC for stabilization, - questionable interaction with Condvar", - issue = "27746")] - #[rustc_deprecated(since = "1.8.0", - reason = "unsound on Mutex because of Condvar and \ - RwLock may also with to be used with Condvar \ - one day")] - pub fn map<U: ?Sized, F>(this: Self, cb: F) -> RwLockWriteGuard<'rwlock, U> - where F: FnOnce(&mut T) -> &mut U - { - // Compute the new data while still owning the original lock - // in order to correctly poison if the callback panics. - let data = unsafe { ptr::read(&this.__data) }; - let new_data = cb(data); - - // We don't want to unlock the lock by running the destructor of the - // original lock, so just read the fields we need and forget it. - let (poison, lock) = unsafe { - (ptr::read(&this.__poison), ptr::read(&this.__lock)) - }; - mem::forget(this); - - RwLockWriteGuard { - __lock: lock, - __data: new_data, - __poison: poison - } - } } #[stable(feature = "rust1", since = "1.0.0")] impl<'rwlock, T: ?Sized> Deref for RwLockReadGuard<'rwlock, T> { type Target = T; - fn deref(&self) -> &T { self.__data } + fn deref(&self) -> &T { + unsafe { &*self.__lock.data.get() } + } } #[stable(feature = "rust1", since = "1.0.0")] impl<'rwlock, T: ?Sized> Deref for RwLockWriteGuard<'rwlock, T> { type Target = T; - fn deref(&self) -> &T { self.__data } + fn deref(&self) -> &T { + unsafe { &*self.__lock.data.get() } + } } #[stable(feature = "rust1", since = "1.0.0")] impl<'rwlock, T: ?Sized> DerefMut for RwLockWriteGuard<'rwlock, T> { - fn deref_mut(&mut self) -> &mut T { self.__data + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.__lock.data.get() } } } #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T: ?Sized> Drop for RwLockReadGuard<'a, T> { fn drop(&mut self) { - unsafe { self.__lock.lock.read_unlock(); } + unsafe { self.__lock.inner.read_unlock(); } } } @@ -577,7 +377,7 @@ impl<'a, T: ?Sized> Drop for RwLockReadGuard<'a, T> { impl<'a, T: ?Sized> Drop for RwLockWriteGuard<'a, T> { fn drop(&mut self) { self.__lock.poison.done(&self.__poison); - unsafe { self.__lock.lock.write_unlock(); } + unsafe { self.__lock.inner.write_unlock(); } } } @@ -590,7 +390,7 @@ mod tests { use rand::{self, Rng}; use sync::mpsc::channel; use thread; - use sync::{Arc, RwLock, StaticRwLock, TryLockError, RwLockWriteGuard}; + use sync::{Arc, RwLock, TryLockError}; use sync::atomic::{AtomicUsize, Ordering}; #[derive(Eq, PartialEq, Debug)] @@ -606,31 +406,23 @@ mod tests { } #[test] - fn static_smoke() { - static R: StaticRwLock = StaticRwLock::new(); - drop(R.read().unwrap()); - drop(R.write().unwrap()); - drop((R.read().unwrap(), R.read().unwrap())); - drop(R.write().unwrap()); - unsafe { R.destroy(); } - } - - #[test] fn frob() { - static R: StaticRwLock = StaticRwLock::new(); const N: usize = 10; const M: usize = 1000; + let r = Arc::new(RwLock::new(())); + let (tx, rx) = channel::<()>(); for _ in 0..N { let tx = tx.clone(); - thread::spawn(move|| { + let r = r.clone(); + thread::spawn(move || { let mut rng = rand::thread_rng(); for _ in 0..M { if rng.gen_weighted_bool(N) { - drop(R.write().unwrap()); + drop(r.write().unwrap()); } else { - drop(R.read().unwrap()); + drop(r.read().unwrap()); } } drop(tx); @@ -638,7 +430,6 @@ mod tests { } drop(tx); let _ = rx.recv(); - unsafe { R.destroy(); } } #[test] @@ -838,20 +629,4 @@ mod tests { Ok(x) => panic!("get_mut of poisoned RwLock is Ok: {:?}", x), } } - - #[test] - fn test_rwlock_write_map_poison() { - let rwlock = Arc::new(RwLock::new(vec![1, 2])); - let rwlock2 = rwlock.clone(); - - thread::spawn(move || { - let _ = RwLockWriteGuard::map::<usize, _>(rwlock2.write().unwrap(), |_| panic!()); - }).join().unwrap_err(); - - match rwlock.read() { - Ok(r) => panic!("Read lock on poisioned RwLock is Ok: {:?}", &*r), - Err(_) => {} - }; - } } - diff --git a/src/libstd/sys/common/args.rs b/src/libstd/sys/common/args.rs index 58417540664..e877391fb8b 100644 --- a/src/libstd/sys/common/args.rs +++ b/src/libstd/sys/common/args.rs @@ -48,32 +48,36 @@ mod imp { use mem; use ffi::CStr; - use sync::StaticMutex; + use sys_common::mutex::Mutex; static mut GLOBAL_ARGS_PTR: usize = 0; - static LOCK: StaticMutex = StaticMutex::new(); + static LOCK: Mutex = Mutex::new(); pub unsafe fn init(argc: isize, argv: *const *const u8) { let args = (0..argc).map(|i| { CStr::from_ptr(*argv.offset(i) as *const c_char).to_bytes().to_vec() }).collect(); - let _guard = LOCK.lock(); + LOCK.lock(); let ptr = get_global_ptr(); assert!((*ptr).is_none()); (*ptr) = Some(box args); + LOCK.unlock(); } pub unsafe fn cleanup() { - let _guard = LOCK.lock(); + LOCK.lock(); *get_global_ptr() = None; + LOCK.unlock(); } pub fn clone() -> Option<Vec<Vec<u8>>> { - let _guard = LOCK.lock(); unsafe { + LOCK.lock(); let ptr = get_global_ptr(); - (*ptr).as_ref().map(|s| (**s).clone()) + let ret = (*ptr).as_ref().map(|s| (**s).clone()); + LOCK.unlock(); + return ret } } diff --git a/src/libstd/sys/common/backtrace.rs b/src/libstd/sys/common/backtrace.rs index 6f185437e50..4c23ceb63f2 100644 --- a/src/libstd/sys/common/backtrace.rs +++ b/src/libstd/sys/common/backtrace.rs @@ -170,7 +170,9 @@ pub fn demangle(writer: &mut Write, s: &str) -> io::Result<()> { "$u20$", => b" ", "$u27$", => b"'", "$u5b$", => b"[", - "$u5d$", => b"]" + "$u5d$", => b"]", + "$u7b$", => b"{", + "$u7d$", => b"}" ) } else { let idx = match rest.find('$') { diff --git a/src/libstd/sys/common/dwarf/eh.rs b/src/libstd/sys/common/dwarf/eh.rs deleted file mode 100644 index 319be245bde..00000000000 --- a/src/libstd/sys/common/dwarf/eh.rs +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2015 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. - -//! Parsing of GCC-style Language-Specific Data Area (LSDA) -//! For details see: -//! http://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html -//! http://mentorembedded.github.io/cxx-abi/exceptions.pdf -//! http://www.airs.com/blog/archives/460 -//! http://www.airs.com/blog/archives/464 -//! -//! A reference implementation may be found in the GCC source tree -//! (<root>/libgcc/unwind-c.c as of this writing) - -#![allow(non_upper_case_globals)] -#![allow(unused)] - -use prelude::v1::*; -use sys_common::dwarf::DwarfReader; -use core::mem; - -pub const DW_EH_PE_omit : u8 = 0xFF; -pub const DW_EH_PE_absptr : u8 = 0x00; - -pub const DW_EH_PE_uleb128 : u8 = 0x01; -pub const DW_EH_PE_udata2 : u8 = 0x02; -pub const DW_EH_PE_udata4 : u8 = 0x03; -pub const DW_EH_PE_udata8 : u8 = 0x04; -pub const DW_EH_PE_sleb128 : u8 = 0x09; -pub const DW_EH_PE_sdata2 : u8 = 0x0A; -pub const DW_EH_PE_sdata4 : u8 = 0x0B; -pub const DW_EH_PE_sdata8 : u8 = 0x0C; - -pub const DW_EH_PE_pcrel : u8 = 0x10; -pub const DW_EH_PE_textrel : u8 = 0x20; -pub const DW_EH_PE_datarel : u8 = 0x30; -pub const DW_EH_PE_funcrel : u8 = 0x40; -pub const DW_EH_PE_aligned : u8 = 0x50; - -pub const DW_EH_PE_indirect : u8 = 0x80; - -#[derive(Copy, Clone)] -pub struct EHContext { - pub ip: usize, // Current instruction pointer - pub func_start: usize, // Address of the current function - pub text_start: usize, // Address of the code section - pub data_start: usize, // Address of the data section -} - -pub unsafe fn find_landing_pad(lsda: *const u8, context: &EHContext) - -> Option<usize> { - if lsda.is_null() { - return None; - } - - let func_start = context.func_start; - let mut reader = DwarfReader::new(lsda); - - let start_encoding = reader.read::<u8>(); - // base address for landing pad offsets - let lpad_base = if start_encoding != DW_EH_PE_omit { - read_encoded_pointer(&mut reader, context, start_encoding) - } else { - func_start - }; - - let ttype_encoding = reader.read::<u8>(); - if ttype_encoding != DW_EH_PE_omit { - // Rust doesn't analyze exception types, so we don't care about the type table - reader.read_uleb128(); - } - - let call_site_encoding = reader.read::<u8>(); - let call_site_table_length = reader.read_uleb128(); - let action_table = reader.ptr.offset(call_site_table_length as isize); - // Return addresses point 1 byte past the call instruction, which could - // be in the next IP range. - let ip = context.ip-1; - - while reader.ptr < action_table { - let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding); - let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding); - let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding); - let cs_action = reader.read_uleb128(); - // Callsite table is sorted by cs_start, so if we've passed the ip, we - // may stop searching. - if ip < func_start + cs_start { - break - } - if ip < func_start + cs_start + cs_len { - if cs_lpad != 0 { - return Some(lpad_base + cs_lpad); - } else { - return None; - } - } - } - // IP range not found: gcc's C++ personality calls terminate() here, - // however the rest of the languages treat this the same as cs_lpad == 0. - // We follow this suit. - None -} - -#[inline] -fn round_up(unrounded: usize, align: usize) -> usize { - assert!(align.is_power_of_two()); - (unrounded + align - 1) & !(align - 1) -} - -unsafe fn read_encoded_pointer(reader: &mut DwarfReader, - context: &EHContext, - encoding: u8) -> usize { - assert!(encoding != DW_EH_PE_omit); - - // DW_EH_PE_aligned implies it's an absolute pointer value - if encoding == DW_EH_PE_aligned { - reader.ptr = round_up(reader.ptr as usize, - mem::size_of::<usize>()) as *const u8; - return reader.read::<usize>(); - } - - let mut result = match encoding & 0x0F { - DW_EH_PE_absptr => reader.read::<usize>(), - DW_EH_PE_uleb128 => reader.read_uleb128() as usize, - DW_EH_PE_udata2 => reader.read::<u16>() as usize, - DW_EH_PE_udata4 => reader.read::<u32>() as usize, - DW_EH_PE_udata8 => reader.read::<u64>() as usize, - DW_EH_PE_sleb128 => reader.read_sleb128() as usize, - DW_EH_PE_sdata2 => reader.read::<i16>() as usize, - DW_EH_PE_sdata4 => reader.read::<i32>() as usize, - DW_EH_PE_sdata8 => reader.read::<i64>() as usize, - _ => panic!() - }; - - result += match encoding & 0x70 { - DW_EH_PE_absptr => 0, - // relative to address of the encoded value, despite the name - DW_EH_PE_pcrel => reader.ptr as usize, - DW_EH_PE_textrel => { assert!(context.text_start != 0); - context.text_start }, - DW_EH_PE_datarel => { assert!(context.data_start != 0); - context.data_start }, - DW_EH_PE_funcrel => { assert!(context.func_start != 0); - context.func_start }, - _ => panic!() - }; - - if encoding & DW_EH_PE_indirect != 0 { - result = *(result as *const usize); - } - - result -} diff --git a/src/libstd/sys/common/dwarf/mod.rs b/src/libstd/sys/common/dwarf/mod.rs deleted file mode 100644 index 822826bcc83..00000000000 --- a/src/libstd/sys/common/dwarf/mod.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2015 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. - -//! Utilities for parsing DWARF-encoded data streams. -//! See http://www.dwarfstd.org, -//! DWARF-4 standard, Section 7 - "Data Representation" - -// This module is used only by x86_64-pc-windows-gnu for now, but we -// are compiling it everywhere to avoid regressions. -#![allow(unused)] - -pub mod eh; - -use prelude::v1::*; -use core::mem; - -pub struct DwarfReader { - pub ptr : *const u8 -} - -#[repr(C,packed)] -struct Unaligned<T>(T); - -impl DwarfReader { - - pub fn new(ptr : *const u8) -> DwarfReader { - DwarfReader { - ptr : ptr - } - } - - // DWARF streams are packed, so e.g. a u32 would not necessarily be aligned - // on a 4-byte boundary. This may cause problems on platforms with strict - // alignment requirements. By wrapping data in a "packed" struct, we are - // telling the backend to generate "misalignment-safe" code. - pub unsafe fn read<T:Copy>(&mut self) -> T { - let Unaligned(result) = *(self.ptr as *const Unaligned<T>); - self.ptr = self.ptr.offset(mem::size_of::<T>() as isize); - result - } - - // ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable - // Length Data". - pub unsafe fn read_uleb128(&mut self) -> u64 { - let mut shift : usize = 0; - let mut result : u64 = 0; - let mut byte : u8; - loop { - byte = self.read::<u8>(); - result |= ((byte & 0x7F) as u64) << shift; - shift += 7; - if byte & 0x80 == 0 { - break; - } - } - result - } - - pub unsafe fn read_sleb128(&mut self) -> i64 { - let mut shift : usize = 0; - let mut result : u64 = 0; - let mut byte : u8; - loop { - byte = self.read::<u8>(); - result |= ((byte & 0x7F) as u64) << shift; - shift += 7; - if byte & 0x80 == 0 { - break; - } - } - // sign-extend - if shift < 8 * mem::size_of::<u64>() && (byte & 0x40) != 0 { - result |= (!0 as u64) << shift; - } - result as i64 - } -} - -#[test] -fn dwarf_reader() { - let encoded: &[u8] = &[1, - 2, 3, - 4, 5, 6, 7, - 0xE5, 0x8E, 0x26, - 0x9B, 0xF1, 0x59, - 0xFF, 0xFF]; - - let mut reader = DwarfReader::new(encoded.as_ptr()); - - unsafe { - assert!(reader.read::<u8>() == u8::to_be(1u8)); - assert!(reader.read::<u16>() == u16::to_be(0x0203)); - assert!(reader.read::<u32>() == u32::to_be(0x04050607)); - - assert!(reader.read_uleb128() == 624485); - assert!(reader.read_sleb128() == -624485); - - assert!(reader.read::<i8>() == i8::to_be(-1)); - } -} diff --git a/src/libstd/sys/common/gnu/libbacktrace.rs b/src/libstd/sys/common/gnu/libbacktrace.rs index db719ccce61..b5802afc109 100644 --- a/src/libstd/sys/common/gnu/libbacktrace.rs +++ b/src/libstd/sys/common/gnu/libbacktrace.rs @@ -15,7 +15,6 @@ use sys_common::backtrace::{output, output_fileline}; pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void, symaddr: *mut libc::c_void) -> io::Result<()> { - use env; use ffi::CStr; use ptr; @@ -110,46 +109,22 @@ pub fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void, // 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. + // Things don't work so well on not-Linux since libbacktrace can't track + // down that executable this is. We at one point used env::current_exe but + // it turns out that there are some serious security issues with that + // approach. // - // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't - // tested if this is required or not. + // Specifically, on certain platforms like BSDs, a malicious actor can cause + // an arbitrary file to be placed at the path returned by current_exe. + // libbacktrace does not behave defensively in the presence of ill-formed + // DWARF information, and has been demonstrated to segfault in at least one + // case. There is no evidence at the moment to suggest that a more carefully + // constructed file can't cause arbitrary code execution. As a result of all + // of this, we don't hint libbacktrace with the path to the current process. unsafe fn init_state() -> *mut backtrace_state { static mut STATE: *mut backtrace_state = ptr::null_mut(); - 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") || - cfg!(target_os = "bitrig") || - cfg!(target_os = "openbsd") || - cfg!(target_os = "windows") { - env::current_exe().ok() - } else { - None - }; - let filename = match selfname.as_ref().and_then(|s| s.to_str()) { - Some(path) => { - let bytes = path.as_bytes(); - 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, + STATE = backtrace_create_state(ptr::null(), 0, error_cb, ptr::null_mut()); STATE } diff --git a/src/libstd/sys/common/libunwind.rs b/src/libstd/sys/common/libunwind.rs deleted file mode 100644 index c1e9782852a..00000000000 --- a/src/libstd/sys/common/libunwind.rs +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2014-2015 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)] -#[derive(Copy, Clone)] -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)] -#[derive(Copy, Clone)] -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)] -#[derive(Copy, Clone)] -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: usize = 5; - -#[cfg(target_arch = "x86_64")] -pub const unwinder_private_data_size: usize = 6; - -#[cfg(all(target_arch = "arm", not(target_os = "ios")))] -pub const unwinder_private_data_size: usize = 20; - -#[cfg(all(target_arch = "arm", target_os = "ios"))] -pub const unwinder_private_data_size: usize = 5; - -#[cfg(target_arch = "aarch64")] -pub const unwinder_private_data_size: usize = 2; - -#[cfg(target_arch = "mips")] -pub const unwinder_private_data_size: usize = 2; - -#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] -pub const unwinder_private_data_size: usize = 2; - -#[cfg(target_arch = "asmjs")] -// FIXME: Copied from arm. Need to confirm. -pub const unwinder_private_data_size: usize = 20; - -#[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_attr(any(all(target_os = "linux", not(target_env = "musl")), - target_os = "freebsd", - target_os = "solaris", - all(target_os = "linux", - target_env = "musl", - not(target_arch = "x86"), - not(target_arch = "x86_64"))), - link(name = "gcc_s"))] -#[cfg_attr(all(target_os = "linux", - target_env = "musl", - any(target_arch = "x86", target_arch = "x86_64"), - not(test)), - link(name = "unwind", kind = "static"))] -#[cfg_attr(any(target_os = "android", target_os = "openbsd"), - link(name = "gcc"))] -#[cfg_attr(all(target_os = "netbsd", not(target_vendor = "rumprun")), - link(name = "gcc"))] -#[cfg_attr(all(target_os = "netbsd", target_vendor = "rumprun"), - link(name = "unwind"))] -#[cfg_attr(target_os = "dragonfly", - link(name = "gcc_pic"))] -#[cfg_attr(target_os = "bitrig", - link(name = "c++abi"))] -#[cfg_attr(all(target_os = "windows", target_env="gnu"), - link(name = "gcc_eh"))] -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")))] - #[unwind] - pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) - -> _Unwind_Reason_Code; - - #[cfg(all(target_os = "ios", target_arch = "arm"))] - #[unwind] - fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) - -> _Unwind_Reason_Code; - - pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception); - - #[unwind] - pub fn _Unwind_Resume(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/sys/common/mod.rs b/src/libstd/sys/common/mod.rs index 56628a4c754..c9279883ae5 100644 --- a/src/libstd/sys/common/mod.rs +++ b/src/libstd/sys/common/mod.rs @@ -30,9 +30,7 @@ pub mod args; pub mod at_exit_imp; pub mod backtrace; pub mod condvar; -pub mod dwarf; pub mod io; -pub mod libunwind; pub mod mutex; pub mod net; pub mod poison; @@ -41,7 +39,6 @@ pub mod rwlock; pub mod thread; pub mod thread_info; pub mod thread_local; -pub mod unwind; pub mod util; pub mod wtf8; diff --git a/src/libstd/sys/common/mutex.rs b/src/libstd/sys/common/mutex.rs index 5a6dfe7fb1a..7a2183c522f 100644 --- a/src/libstd/sys/common/mutex.rs +++ b/src/libstd/sys/common/mutex.rs @@ -27,6 +27,12 @@ impl Mutex { /// first used with any of the functions below. pub const fn new() -> Mutex { Mutex(imp::Mutex::new()) } + /// Prepare the mutex for use. + /// + /// This should be called once the mutex is at a stable memory address. + #[inline] + pub unsafe fn init(&mut self) { self.0.init() } + /// Locks the mutex blocking the current thread until it is available. /// /// Behavior is undefined if the mutex has been moved between this and any diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs index 54b9b466c42..442618c55b3 100644 --- a/src/libstd/sys/common/net.rs +++ b/src/libstd/sys/common/net.rs @@ -25,19 +25,23 @@ use time::Duration; #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", - target_os = "openbsd", target_os = "netbsd"))] + target_os = "openbsd", target_os = "netbsd", + target_os = "solaris"))] use sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; #[cfg(not(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", - target_os = "openbsd", target_os = "netbsd")))] + target_os = "openbsd", target_os = "netbsd", + target_os = "solaris")))] use sys::net::netc::IPV6_ADD_MEMBERSHIP; #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", - target_os = "openbsd", target_os = "netbsd"))] + target_os = "openbsd", target_os = "netbsd", + target_os = "solaris"))] use sys::net::netc::IPV6_LEAVE_GROUP as IPV6_DROP_MEMBERSHIP; #[cfg(not(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", target_os = "macos", - target_os = "openbsd", target_os = "netbsd")))] + target_os = "openbsd", target_os = "netbsd", + target_os = "solaris")))] use sys::net::netc::IPV6_DROP_MEMBERSHIP; //////////////////////////////////////////////////////////////////////////////// @@ -119,14 +123,22 @@ pub struct LookupHost { } impl Iterator for LookupHost { - type Item = io::Result<SocketAddr>; - fn next(&mut self) -> Option<io::Result<SocketAddr>> { - unsafe { - if self.cur.is_null() { return None } - let ret = sockaddr_to_addr(mem::transmute((*self.cur).ai_addr), - (*self.cur).ai_addrlen as usize); - self.cur = (*self.cur).ai_next as *mut c::addrinfo; - Some(ret) + type Item = SocketAddr; + fn next(&mut self) -> Option<SocketAddr> { + loop { + unsafe { + let cur = match self.cur.as_ref() { + None => return None, + Some(c) => c, + }; + self.cur = cur.ai_next; + match sockaddr_to_addr(mem::transmute(cur.ai_addr), + cur.ai_addrlen as usize) + { + Ok(addr) => return Some(addr), + Err(_) => continue, + } + } } } } @@ -144,9 +156,19 @@ pub fn lookup_host(host: &str) -> io::Result<LookupHost> { init(); let c_host = CString::new(host)?; + let hints = c::addrinfo { + ai_flags: 0, + ai_family: 0, + ai_socktype: c::SOCK_STREAM, + ai_protocol: 0, + ai_addrlen: 0, + ai_addr: ptr::null_mut(), + ai_canonname: ptr::null_mut(), + ai_next: ptr::null_mut() + }; let mut res = ptr::null_mut(); unsafe { - cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), ptr::null(), + cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res))?; Ok(LookupHost { original: res, cur: res }) } @@ -583,3 +605,22 @@ impl fmt::Debug for UdpSocket { .finish() } } + +#[cfg(test)] +mod tests { + use prelude::v1::*; + + use super::*; + use collections::HashMap; + + #[test] + fn no_lookup_host_duplicates() { + let mut addrs = HashMap::new(); + let lh = match lookup_host("localhost") { + Ok(lh) => lh, + Err(e) => panic!("couldn't resolve `localhost': {}", e) + }; + let _na = lh.map(|sa| *addrs.entry(sa).or_insert(0) += 1).count(); + assert!(addrs.values().filter(|&&v| v > 1).count() == 0); + } +} diff --git a/src/libstd/sys/common/poison.rs b/src/libstd/sys/common/poison.rs index 83780a31cce..55212bf35d6 100644 --- a/src/libstd/sys/common/poison.rs +++ b/src/libstd/sys/common/poison.rs @@ -8,22 +8,28 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use cell::Cell; use error::{Error}; use fmt; use marker::Reflect; +use sync::atomic::{AtomicBool, Ordering}; use thread; -pub struct Flag { failed: Cell<bool> } +pub struct Flag { failed: AtomicBool } -// This flag is only ever accessed with a lock previously held. Note that this -// a totally private structure. -unsafe impl Send for Flag {} -unsafe impl Sync for Flag {} +// Note that the Ordering uses to access the `failed` field of `Flag` below is +// always `Relaxed`, and that's because this isn't actually protecting any data, +// it's just a flag whether we've panicked or not. +// +// The actual location that this matters is when a mutex is **locked** which is +// where we have external synchronization ensuring that we see memory +// reads/writes to this flag. +// +// As a result, if it matters, we should see the correct value for `failed` in +// all cases. impl Flag { pub const fn new() -> Flag { - Flag { failed: Cell::new(false) } + Flag { failed: AtomicBool::new(false) } } #[inline] @@ -39,13 +45,13 @@ impl Flag { #[inline] pub fn done(&self, guard: &Guard) { if !guard.panicking && thread::panicking() { - self.failed.set(true); + self.failed.store(true, Ordering::Relaxed); } } #[inline] pub fn get(&self) -> bool { - self.failed.get() + self.failed.load(Ordering::Relaxed) } } diff --git a/src/libstd/sys/common/unwind/gcc.rs b/src/libstd/sys/common/unwind/gcc.rs deleted file mode 100644 index da7a340af35..00000000000 --- a/src/libstd/sys/common/unwind/gcc.rs +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2015 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(private_no_mangle_fns)] - -use prelude::v1::*; - -use any::Any; -use sys_common::libunwind as uw; - -struct Exception { - uwe: uw::_Unwind_Exception, - cause: Option<Box<Any + Send + 'static>>, -} - -pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! { - let exception: Box<_> = box Exception { - uwe: uw::_Unwind_Exception { - exception_class: rust_exception_class(), - exception_cleanup: exception_cleanup, - private: [0; uw::unwinder_private_data_size], - }, - cause: Some(data), - }; - let exception_param = Box::into_raw(exception) as *mut uw::_Unwind_Exception; - let error = uw::_Unwind_RaiseException(exception_param); - rtabort!("Could not unwind stack, error = {}", error as isize); - - extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code, - exception: *mut uw::_Unwind_Exception) { - unsafe { - let _: Box<Exception> = Box::from_raw(exception as *mut Exception); - } - } -} - -pub fn payload() -> *mut u8 { - 0 as *mut u8 -} - -pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> { - let my_ep = ptr as *mut Exception; - let cause = (*my_ep).cause.take(); - uw::_Unwind_DeleteException(ptr as *mut _); - cause.unwrap() -} - -// 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 thread'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. -// -// See also: rustc_trans::trans::intrinsic::trans_gnu_try - -#[cfg(all(not(target_arch = "arm"), - not(all(windows, target_arch = "x86_64")), - not(test)))] -pub mod eabi { - use sys_common::libunwind as uw; - use libc::c_int; - - extern { - 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] - 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) - } - } - - #[lang = "eh_personality_catch"] - #[no_mangle] - pub extern 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_v0(version, actions, exception_class, ue_header, - 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)))] -pub mod eabi { - use sys_common::libunwind as uw; - use libc::c_int; - - extern { - 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] - pub 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_sj0(version, actions, exception_class, ue_header, - context) - } - } - - #[lang = "eh_personality_catch"] - #[no_mangle] - pub extern 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)))] -pub mod eabi { - use sys_common::libunwind as uw; - use libc::c_int; - - extern { - 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] - extern 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) - } - } - - #[lang = "eh_personality_catch"] - #[no_mangle] - pub extern fn rust_eh_personality_catch( - state: uw::_Unwind_State, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - // Backtraces on ARM will call the personality routine with - // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases - // we want to continue unwinding the stack, otherwise all our backtraces - // would end at __rust_try. - if (state as c_int & uw::_US_ACTION_MASK as c_int) - == uw::_US_VIRTUAL_UNWIND_FRAME as c_int - && (state as c_int & uw::_US_FORCE_UNWIND as c_int) == 0 { // search phase - uw::_URC_HANDLER_FOUND // catch! - } - else { // cleanup phase - unsafe { - __gcc_personality_v0(state, ue_header, context) - } - } - } -} - -// See docs in the `unwind` module. -#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu", not(test)))] -#[lang = "eh_unwind_resume"] -#[unwind] -unsafe extern fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! { - uw::_Unwind_Resume(panic_ctx as *mut uw::_Unwind_Exception); -} - -#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] -pub mod eh_frame_registry { - // The implementation of stack unwinding is (for now) deferred to libgcc_eh, however Rust - // crates use these Rust-specific entry points to avoid potential clashes with GCC runtime. - // See also: rtbegin.rs, `unwind` module. - - #[link(name = "gcc_eh")] - #[cfg(not(cargobuild))] - extern {} - - extern { - fn __register_frame_info(eh_frame_begin: *const u8, object: *mut u8); - fn __deregister_frame_info(eh_frame_begin: *const u8, object: *mut u8); - } - #[cfg(not(test))] - #[no_mangle] - #[unstable(feature = "libstd_sys_internals", issue = "0")] - pub unsafe extern fn rust_eh_register_frames(eh_frame_begin: *const u8, - object: *mut u8) { - __register_frame_info(eh_frame_begin, object); - } - #[cfg(not(test))] - #[no_mangle] - #[unstable(feature = "libstd_sys_internals", issue = "0")] - pub unsafe extern fn rust_eh_unregister_frames(eh_frame_begin: *const u8, - object: *mut u8) { - __deregister_frame_info(eh_frame_begin, object); - } -} diff --git a/src/libstd/sys/common/unwind/mod.rs b/src/libstd/sys/common/unwind/mod.rs deleted file mode 100644 index 527c2e63030..00000000000 --- a/src/libstd/sys/common/unwind/mod.rs +++ /dev/null @@ -1,241 +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. - -//! 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://mentorembedded.github.io/cxx-abi/abi-eh.html -//! 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, the unwinder invokes each personality routine again. -//! This time it decides which (if any) cleanup code needs to be run for -//! the current stack frame. If so, the control is transferred to a special branch -//! in the function body, the "landing pad", which invokes destructors, frees memory, -//! etc. At the end of the landing pad, control is transferred back to the unwinder -//! and unwinding resumes. -//! -//! Once stack has been unwound down to the handler frame level, unwinding stops -//! and the last personality routine transfers control to the catch block. -//! -//! ## `eh_personality` and `eh_unwind_resume` -//! -//! These language items are used by the compiler when generating unwind info. -//! The first one is the personality routine described above. The second one -//! allows compilation target to customize the process of resuming unwind at the -//! end of the landing pads. `eh_unwind_resume` is used only if `custom_unwind_resume` -//! flag in the target options is set. -//! -//! ## Frame unwind info registration -//! -//! Each module's image contains a frame unwind info section (usually ".eh_frame"). -//! When a module is loaded/unloaded into the process, the unwinder must be informed -//! about the location of this section in memory. The methods of achieving that vary -//! by the platform. -//! On some (e.g. Linux), the unwinder can discover unwind info sections on its own -//! (by dynamically enumerating currently loaded modules via the dl_iterate_phdr() API -//! and finding their ".eh_frame" sections); -//! Others, like Windows, require modules to actively register their unwind info -//! sections via unwinder API (see `rust_eh_register_frames`/`rust_eh_unregister_frames`). - -#![allow(dead_code)] -#![allow(unused_imports)] - -use prelude::v1::*; - -use any::Any; -use boxed; -use cmp; -use panicking::{self,PANIC_COUNT}; -use fmt; -use intrinsics; -use mem; -use sync::atomic::{self, Ordering}; -use sys_common::mutex::Mutex; - -// The actual unwinding implementation is cfg'd here, and we've got two current -// implementations. One goes through SEH on Windows and the other goes through -// libgcc via the libunwind-like API. - -// *-pc-windows-msvc -#[cfg(target_env = "msvc")] -#[path = "seh.rs"] #[doc(hidden)] -pub mod imp; - -// x86_64-pc-windows-gnu -#[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] -#[path = "seh64_gnu.rs"] #[doc(hidden)] -pub mod imp; - -// i686-pc-windows-gnu and all others -#[cfg(any(unix, all(windows, target_arch = "x86", target_env = "gnu")))] -#[path = "gcc.rs"] #[doc(hidden)] -pub mod imp; - -/// Invoke a closure, capturing the cause of panic if one occurs. -/// -/// This function will return `Ok(())` if the closure did not panic, and will -/// return `Err(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 -/// thread, 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 thread itself. -/// -/// * It is not sound to trigger unwinding while already unwinding. Rust threads -/// have runtime checks in place to ensure this invariant, but it is not -/// guaranteed that a rust thread 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); - return inner_try(try_fn::<F>, &mut f as *mut _ as *mut u8); - - fn try_fn<F: FnOnce()>(opt_closure: *mut u8) { - let opt_closure = opt_closure as *mut Option<F>; - unsafe { (*opt_closure).take().unwrap()(); } - } -} - -unsafe fn inner_try(f: fn(*mut u8), data: *mut u8) - -> Result<(), Box<Any + Send>> { - PANIC_COUNT.with(|s| { - let prev = s.get(); - s.set(0); - - // The "payload" here is a platform-specific region of memory which is - // used to transmit information about the exception being thrown from - // the point-of-throw back to this location. - // - // A pointer to this data is passed to the `try` intrinsic itself, - // allowing this function, the `try` intrinsic, imp::payload(), and - // imp::cleanup() to all work in concert to transmit this information. - // - // More information about what this pointer actually is can be found in - // each implementation as well as browsing the compiler source itself. - let mut payload = imp::payload(); - let r = intrinsics::try(f, data, &mut payload as *mut _ as *mut _); - s.set(prev); - if r == 0 { - Ok(()) - } else { - Err(imp::cleanup(payload)) - } - }) -} - -/// Determines whether the current thread is unwinding because of panic. -pub fn panicking() -> bool { - PANIC_COUNT.with(|s| s.get() != 0) -} - -// An uninlined, unmangled function upon which to slap yer breakpoints -#[inline(never)] -#[no_mangle] -#[allow(private_no_mangle_fns)] -pub fn rust_panic(cause: Box<Any + Send + 'static>) -> ! { - unsafe { - imp::panic(cause) - } -} - -#[cfg(not(test))] -/// Entry point of panic from the libcore crate. -#[lang = "panic_fmt"] -#[unwind] -pub extern fn rust_begin_unwind(msg: fmt::Arguments, - file: &'static str, line: u32) -> ! { - 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. -#[unstable(feature = "libstd_sys_internals", - reason = "used by the panic! macro", - issue = "0")] -#[inline(never)] #[cold] -pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, u32)) -> ! { - use fmt::Write; - - // 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). - - let mut s = String::new(); - let _ = s.write_fmt(msg); - begin_unwind_inner(Box::new(s), file_line) -} - -/// This is the entry point of unwinding for panic!() and assert!(). -#[unstable(feature = "libstd_sys_internals", - reason = "used by the panic! macro", - issue = "0")] -#[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, u32)) -> ! { - // 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 thread instead of the thread that's - // panicking. - - // see below for why we do the `Any` coercion here. - begin_unwind_inner(Box::new(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, u32)) -> ! { - let (file, line) = *file_line; - - // First, invoke the default panic handler. - panicking::on_panic(&*msg, file, line); - - // Finally, perform the unwinding. - rust_panic(msg); -} diff --git a/src/libstd/sys/common/unwind/seh.rs b/src/libstd/sys/common/unwind/seh.rs deleted file mode 100644 index 94da42f0092..00000000000 --- a/src/libstd/sys/common/unwind/seh.rs +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2015 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. - -//! Windows SEH -//! -//! On Windows (currently only on MSVC), the default exception handling -//! mechanism is Structured Exception Handling (SEH). This is quite different -//! than Dwarf-based exception handling (e.g. what other unix platforms use) in -//! terms of compiler internals, so LLVM is required to have a good deal of -//! extra support for SEH. -//! -//! In a nutshell, what happens here is: -//! -//! 1. The `panic` function calls the standard Windows function `RaiseException` -//! with a Rust-specific code, triggering the unwinding process. -//! 2. All landing pads generated by the compiler use the personality function -//! `__C_specific_handler` on 64-bit and `__except_handler3` on 32-bit, -//! functions in the CRT, and the unwinding code in Windows will use this -//! personality function to execute all cleanup code on the stack. -//! 3. All compiler-generated calls to `invoke` have a landing pad set as a -//! `cleanuppad` LLVM instruction, which indicates the start of the cleanup -//! routine. The personality (in step 2, defined in the CRT) is responsible -//! for running the cleanup routines. -//! 4. Eventually the "catch" code in the `try` intrinsic (generated by the -//! compiler) is executed, which will ensure that the exception being caught -//! is indeed a Rust exception, indicating that control should come back to -//! Rust. This is done via a `catchswitch` plus a `catchpad` instruction in -//! LLVM IR terms, finally returning normal control to the program with a -//! `catchret` instruction. The `try` intrinsic uses a filter function to -//! detect what kind of exception is being thrown, and this detection is -//! implemented as the msvc_try_filter language item below. -//! -//! Some specific differences from the gcc-based exception handling are: -//! -//! * Rust has no custom personality function, it is instead *always* -//! __C_specific_handler or __except_handler3, so the filtering is done in a -//! C++-like manner instead of in the personality function itself. Note that -//! the precise codegen for this was lifted from an LLVM test case for SEH -//! (this is the `__rust_try_filter` function below). -//! * We've got some data to transmit across the unwinding boundary, -//! specifically a `Box<Any + Send + 'static>`. Like with Dwarf exceptions -//! these two pointers are stored as a payload in the exception itself. On -//! MSVC, however, there's no need for an extra allocation because the call -//! stack is preserved while filter functions are being executed. This means -//! that the pointers are passed directly to `RaiseException` which are then -//! recovered in the filter function to be written to the stack frame of the -//! `try` intrinsic. -//! -//! [win64]: http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx -//! [llvm]: http://llvm.org/docs/ExceptionHandling.html#background-on-windows-exceptions - -use sys::c; - -// A code which indicates panics that originate from Rust. Note that some of the -// upper bits are used by the system so we just set them to 0 and ignore them. -// 0x 0 R S T -const RUST_PANIC: c::DWORD = 0x00525354; - -pub use self::imp::*; - -mod imp { - use prelude::v1::*; - - use any::Any; - use mem; - use raw; - use super::RUST_PANIC; - use sys::c; - - pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! { - // As mentioned above, the call stack here is preserved while the filter - // functions are running, so it's ok to pass stack-local arrays into - // `RaiseException`. - // - // The two pointers of the `data` trait object are written to the stack, - // passed to `RaiseException`, and they're later extracted by the filter - // function below in the "custom exception information" section of the - // `EXCEPTION_RECORD` type. - let ptrs = mem::transmute::<_, raw::TraitObject>(data); - let ptrs = [ptrs.data, ptrs.vtable]; - c::RaiseException(RUST_PANIC, 0, 2, ptrs.as_ptr() as *mut _); - rtabort!("could not unwind stack"); - } - - pub fn payload() -> [usize; 2] { - [0; 2] - } - - pub unsafe fn cleanup(payload: [usize; 2]) -> Box<Any + Send + 'static> { - mem::transmute(raw::TraitObject { - data: payload[0] as *mut _, - vtable: payload[1] as *mut _, - }) - } - - // This is quite a special function, and it's not literally passed in as the - // filter function for the `catchpad` of the `try` intrinsic. The compiler - // actually generates its own filter function wrapper which will delegate to - // this for the actual execution logic for whether the exception should be - // caught. The reasons for this are: - // - // * Each architecture has a slightly different ABI for the filter function - // here. For example on x86 there are no arguments but on x86_64 there are - // two. - // * This function needs access to the stack frame of the `try` intrinsic - // which is using this filter as a catch pad. This is because the payload - // of this exception, `Box<Any>`, needs to be transmitted to that - // location. - // - // Both of these differences end up using a ton of weird llvm-specific - // intrinsics, so it's actually pretty difficult to express the entire - // filter function in Rust itself. As a compromise, the compiler takes care - // of all the weird LLVM-specific and platform-specific stuff, getting to - // the point where this function makes the actual decision about what to - // catch given two parameters. - // - // The first parameter is `*mut EXCEPTION_POINTERS` which is some contextual - // information about the exception being filtered, and the second pointer is - // `*mut *mut [usize; 2]` (the payload here). This value points directly - // into the stack frame of the `try` intrinsic itself, and we use it to copy - // information from the exception onto the stack. - #[lang = "msvc_try_filter"] - #[cfg(not(test))] - unsafe extern fn __rust_try_filter(eh_ptrs: *mut u8, - payload: *mut u8) -> i32 { - let eh_ptrs = eh_ptrs as *mut c::EXCEPTION_POINTERS; - let payload = payload as *mut *mut [usize; 2]; - let record = &*(*eh_ptrs).ExceptionRecord; - if record.ExceptionCode != RUST_PANIC { - return 0 - } - (**payload)[0] = record.ExceptionInformation[0] as usize; - (**payload)[1] = record.ExceptionInformation[1] as usize; - return 1 - } -} - -// This is required by the compiler to exist (e.g. it's a lang item), but -// it's never actually called by the compiler because __C_specific_handler -// or _except_handler3 is the personality function that is always used. -// Hence this is just an aborting stub. -#[lang = "eh_personality"] -#[cfg(not(test))] -fn rust_eh_personality() { - unsafe { ::intrinsics::abort() } -} diff --git a/src/libstd/sys/common/unwind/seh64_gnu.rs b/src/libstd/sys/common/unwind/seh64_gnu.rs deleted file mode 100644 index 57281d67ebb..00000000000 --- a/src/libstd/sys/common/unwind/seh64_gnu.rs +++ /dev/null @@ -1,145 +0,0 @@ -// Copyright 2015 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. - -//! Unwinding implementation of top of native Win64 SEH, -//! however the unwind handler data (aka LSDA) uses GCC-compatible encoding. - -#![allow(bad_style)] -#![allow(private_no_mangle_fns)] - -use prelude::v1::*; - -use any::Any; -use sys_common::dwarf::eh; -use core::mem; -use core::ptr; -use sys::c; - -// Define our exception codes: -// according to http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx, -// [31:30] = 3 (error), 2 (warning), 1 (info), 0 (success) -// [29] = 1 (user-defined) -// [28] = 0 (reserved) -// we define bits: -// [24:27] = type -// [0:23] = magic -const ETYPE: c::DWORD = 0b1110_u32 << 28; -const MAGIC: c::DWORD = 0x525354; // "RST" - -const RUST_PANIC: c::DWORD = ETYPE | (1 << 24) | MAGIC; - -#[repr(C)] -struct PanicData { - data: Box<Any + Send + 'static> -} - -pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! { - let panic_ctx = Box::new(PanicData { data: data }); - let params = [Box::into_raw(panic_ctx) as c::ULONG_PTR]; - c::RaiseException(RUST_PANIC, - c::EXCEPTION_NONCONTINUABLE, - params.len() as c::DWORD, - ¶ms as *const c::ULONG_PTR); - rtabort!("could not unwind stack"); -} - -pub fn payload() -> *mut u8 { - 0 as *mut u8 -} - -pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> { - let panic_ctx = Box::from_raw(ptr as *mut PanicData); - return panic_ctx.data; -} - -// SEH doesn't support resuming unwinds after calling a landing pad like -// libunwind does. For this reason, MSVC compiler outlines landing pads into -// separate functions that can be called directly from the personality function -// but are nevertheless able to find and modify stack frame of the "parent" -// function. -// -// Since this cannot be done with libdwarf-style landing pads, -// rust_eh_personality instead catches RUST_PANICs, runs the landing pad, then -// reraises the exception. -// -// Note that it makes certain assumptions about the exception: -// -// 1. That RUST_PANIC is non-continuable, so no lower stack frame may choose to -// resume execution. -// 2. That the first parameter of the exception is a pointer to an extra data -// area (PanicData). -// Since these assumptions do not generally hold true for foreign exceptions -// (system faults, C++ exceptions, etc), we make no attempt to invoke our -// landing pads (and, thus, destructors!) for anything other than RUST_PANICs. -// This is considered acceptable, because the behavior of throwing exceptions -// through a C ABI boundary is undefined. - -#[lang = "eh_personality_catch"] -#[cfg(not(test))] -unsafe extern fn rust_eh_personality_catch( - exceptionRecord: *mut c::EXCEPTION_RECORD, - establisherFrame: c::LPVOID, - contextRecord: *mut c::CONTEXT, - dispatcherContext: *mut c::DISPATCHER_CONTEXT -) -> c::EXCEPTION_DISPOSITION -{ - rust_eh_personality(exceptionRecord, establisherFrame, - contextRecord, dispatcherContext) -} - -#[lang = "eh_personality"] -#[cfg(not(test))] -unsafe extern fn rust_eh_personality( - exceptionRecord: *mut c::EXCEPTION_RECORD, - establisherFrame: c::LPVOID, - contextRecord: *mut c::CONTEXT, - dispatcherContext: *mut c::DISPATCHER_CONTEXT -) -> c::EXCEPTION_DISPOSITION -{ - let er = &*exceptionRecord; - let dc = &*dispatcherContext; - - if er.ExceptionFlags & c::EXCEPTION_UNWIND == 0 { // we are in the dispatch phase - if er.ExceptionCode == RUST_PANIC { - if let Some(lpad) = find_landing_pad(dc) { - c::RtlUnwindEx(establisherFrame, - lpad as c::LPVOID, - exceptionRecord, - er.ExceptionInformation[0] as c::LPVOID, // pointer to PanicData - contextRecord, - dc.HistoryTable); - rtabort!("could not unwind"); - } - } - } - c::ExceptionContinueSearch -} - -#[cfg(not(test))] -#[lang = "eh_unwind_resume"] -#[unwind] -unsafe extern fn rust_eh_unwind_resume(panic_ctx: c::LPVOID) -> ! { - let params = [panic_ctx as c::ULONG_PTR]; - c::RaiseException(RUST_PANIC, - c::EXCEPTION_NONCONTINUABLE, - params.len() as c::DWORD, - ¶ms as *const c::ULONG_PTR); - rtabort!("could not resume unwind"); -} - -unsafe fn find_landing_pad(dc: &c::DISPATCHER_CONTEXT) -> Option<usize> { - let eh_ctx = eh::EHContext { - ip: dc.ControlPc as usize, - func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize, - text_start: dc.ImageBase as usize, - data_start: 0 - }; - eh::find_landing_pad(dc.HandlerData, &eh_ctx) -} diff --git a/src/libstd/sys/common/util.rs b/src/libstd/sys/common/util.rs index b7a6b7650d5..b5d03576338 100644 --- a/src/libstd/sys/common/util.rs +++ b/src/libstd/sys/common/util.rs @@ -10,7 +10,6 @@ use env; use fmt; -use intrinsics; use io::prelude::*; use sync::atomic::{self, Ordering}; use sys::stdio::Stderr; @@ -34,9 +33,40 @@ pub fn dumb_print(args: fmt::Arguments) { let _ = Stderr::new().map(|mut stderr| stderr.write_fmt(args)); } +// On Unix-like platforms, libc::abort will unregister signal handlers +// including the SIGABRT handler, preventing the abort from being blocked, and +// fclose streams, with the side effect of flushing them so libc bufferred +// output will be printed. Additionally the shell will generally print a more +// understandable error message like "Abort trap" rather than "Illegal +// instruction" that intrinsics::abort would cause, as intrinsics::abort is +// implemented as an illegal instruction. +#[cfg(unix)] +unsafe fn abort_internal() -> ! { + ::libc::abort() +} + +// On Windows, use the processor-specific __fastfail mechanism. In Windows 8 +// and later, this will terminate the process immediately without running any +// in-process exception handlers. In earlier versions of Windows, this +// sequence of instructions will be treated as an access violation, +// terminating the process but without necessarily bypassing all exception +// handlers. +// +// https://msdn.microsoft.com/en-us/library/dn774154.aspx +#[cfg(all(windows, any(target_arch = "x86", target_arch = "x86_64")))] +unsafe fn abort_internal() -> ! { + asm!("int $$0x29" :: "{ecx}"(7) ::: volatile); // 7 is FAST_FAIL_FATAL_APP_EXIT + ::intrinsics::unreachable(); +} + +// Other platforms should use the appropriate platform-specific mechanism for +// aborting the process. If no platform-specific mechanism is available, +// ::intrinsics::abort() may be used instead. The above implementations cover +// all targets currently supported by libstd. + pub fn abort(args: fmt::Arguments) -> ! { dumb_print(format_args!("fatal runtime error: {}\n", args)); - unsafe { intrinsics::abort(); } + unsafe { abort_internal(); } } #[allow(dead_code)] // stack overflow detection not enabled on all platforms diff --git a/src/libstd/sys/common/wtf8.rs b/src/libstd/sys/common/wtf8.rs index 55e485e5811..2c1a656290f 100644 --- a/src/libstd/sys/common/wtf8.rs +++ b/src/libstd/sys/common/wtf8.rs @@ -567,7 +567,7 @@ impl Wtf8 { return None } match &self.bytes[(len - 3)..] { - [0xED, b2 @ 0xA0...0xAF, b3] => Some(decode_surrogate(b2, b3)), + &[0xED, b2 @ 0xA0...0xAF, b3] => Some(decode_surrogate(b2, b3)), _ => None } } @@ -579,7 +579,7 @@ impl Wtf8 { return None } match &self.bytes[..3] { - [0xED, b2 @ 0xB0...0xBF, b3] => Some(decode_surrogate(b2, b3)), + &[0xED, b2 @ 0xB0...0xBF, b3] => Some(decode_surrogate(b2, b3)), _ => None } } @@ -715,7 +715,7 @@ impl<'a> Iterator for Wtf8CodePoints<'a> { #[inline] fn size_hint(&self) -> (usize, Option<usize>) { - let (len, _) = self.bytes.size_hint(); + let len = self.bytes.len(); (len.saturating_add(3) / 4, Some(len)) } } diff --git a/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs b/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs index de93d3d4e50..ca2e70b5003 100644 --- a/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs +++ b/src/libstd/sys/unix/backtrace/tracing/backtrace_fn.rs @@ -18,12 +18,11 @@ /// simple to use it should be used only on iOS devices as the only viable /// option. -use io; use io::prelude::*; +use io; use libc; use mem; -use result::Result::Ok; -use sync::StaticMutex; +use sys::mutex::Mutex; use super::super::printing::print; @@ -37,18 +36,21 @@ pub fn write(w: &mut Write) -> io::Result<()> { // while it doesn't requires lock for work as everything is // local, it still displays much nicer backtraces when a // couple of threads panic simultaneously - static LOCK: StaticMutex = StaticMutex::new(); - let _g = LOCK.lock(); + static LOCK: Mutex = Mutex::new(); + unsafe { + LOCK.lock(); - writeln!(w, "stack backtrace:")?; - // 100 lines should be enough - const SIZE: usize = 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 usize}; + writeln!(w, "stack backtrace:")?; + // 100 lines should be enough + const SIZE: usize = 100; + let mut buf: [*mut libc::c_void; SIZE] = mem::zeroed(); + let cnt = backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as usize; - // skipping the first one as it is write itself - for i in 1..cnt { - print(w, i as isize, buf[i], buf[i])? + // skipping the first one as it is write itself + for i in 1..cnt { + print(w, i as isize, buf[i], buf[i])? + } + LOCK.unlock(); } Ok(()) } diff --git a/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs b/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs index 8d880917166..c1b45620ab0 100644 --- a/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs +++ b/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs @@ -12,9 +12,10 @@ use io; use io::prelude::*; use libc; use mem; -use sync::StaticMutex; +use sys_common::mutex::Mutex; use super::super::printing::print; +use unwind as uw; #[inline(never)] // if we know this is a function call, we can skip it when // tracing @@ -30,24 +31,28 @@ pub fn write(w: &mut Write) -> io::Result<()> { // 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 = StaticMutex::new(); - let _g = LOCK.lock(); - - 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(()) + static LOCK: Mutex = Mutex::new(); + unsafe { + LOCK.lock(); + + writeln!(w, "stack backtrace:")?; + + let mut cx = Context { writer: w, last_error: None, idx: 0 }; + let ret = match { + 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(()), - }; + _ => Ok(()), + }; + LOCK.unlock(); + return ret + } extern fn trace_fn(ctx: *mut uw::_Unwind_Context, arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { @@ -102,126 +107,3 @@ pub fn write(w: &mut Write) -> io::Result<()> { uw::_URC_NO_REASON } } - -/// 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)] -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; - - // available since GCC 4.2.0, should be fine for our purpose - #[cfg(all(not(all(target_os = "android", target_arch = "arm")), - not(all(target_os = "linux", target_arch = "arm"))))] - pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, - ip_before_insn: *mut libc::c_int) - -> 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(all(target_os = "android", target_arch = "arm"), - 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 doesn't exist on Android or ARM/Linux, so make it same - // to _Unwind_GetIP - #[cfg(any(all(target_os = "android", target_arch = "arm"), - all(target_os = "linux", target_arch = "arm")))] - pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, - ip_before_insn: *mut libc::c_int) - -> libc::uintptr_t - { - *ip_before_insn = 0; - _Unwind_GetIP(ctx) - } - - // 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/ext/fs.rs b/src/libstd/sys/unix/ext/fs.rs index a1528458860..54340773a42 100644 --- a/src/libstd/sys/unix/ext/fs.rs +++ b/src/libstd/sys/unix/ext/fs.rs @@ -88,9 +88,7 @@ pub trait OpenOptionsExt { /// } /// let file = options.open("foo.txt"); /// ``` - #[unstable(feature = "expand_open_options", - reason = "recently added", - issue = "30014")] + #[stable(feature = "open_options_ext", since = "1.10.0")] fn custom_flags(&mut self, flags: i32) -> &mut Self; } @@ -198,6 +196,22 @@ impl FileTypeExt for fs::FileType { pub trait DirEntryExt { /// Returns the underlying `d_ino` field in the contained `dirent` /// structure. + /// + /// # Examples + /// + /// ``` + /// use std::fs; + /// use std::os::unix::fs::DirEntryExt; + /// + /// if let Ok(entries) = fs::read_dir(".") { + /// for entry in entries { + /// if let Ok(entry) = entry { + /// // Here, `entry` is a `DirEntry`. + /// println!("{:?}: {}", entry.file_name(), entry.ino()); + /// } + /// } + /// } + /// ``` #[stable(feature = "dir_entry_ext", since = "1.1.0")] fn ino(&self) -> u64; } @@ -241,6 +255,16 @@ pub fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Result<()> pub trait DirBuilderExt { /// Sets the mode to create new directories with. This option defaults to /// 0o777. + /// + /// # Examples + /// + /// ```ignore + /// use std::fs::DirBuilder; + /// use std::os::unix::fs::DirBuilderExt; + /// + /// let mut builder = DirBuilder::new(); + /// builder.mode(0o755); + /// ``` #[stable(feature = "dir_builder", since = "1.6.0")] fn mode(&mut self, mode: u32) -> &mut Self; } diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs index a74f7ea13b4..b5287cce484 100644 --- a/src/libstd/sys/unix/ext/net.rs +++ b/src/libstd/sys/unix/ext/net.rs @@ -7,7 +7,8 @@ // <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. -#![unstable(feature = "unix_socket", reason = "newly added", issue = "32312")] + +#![stable(feature = "unix_socket", since = "1.10.0")] //! Unix-specific networking functionality @@ -75,6 +76,7 @@ enum AddressKind<'a> { /// An address associated with a Unix socket. #[derive(Clone)] +#[stable(feature = "unix_socket", since = "1.10.0")] pub struct SocketAddr { addr: libc::sockaddr_un, len: libc::socklen_t, @@ -109,6 +111,7 @@ impl SocketAddr { } /// Returns true iff the address is unnamed. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn is_unnamed(&self) -> bool { if let AddressKind::Unnamed = self.address() { true @@ -118,6 +121,7 @@ impl SocketAddr { } /// Returns the contents of this address if it is a `pathname` address. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn as_pathname(&self) -> Option<&Path> { if let AddressKind::Pathname(path) = self.address() { Some(path) @@ -141,6 +145,7 @@ impl SocketAddr { } } +#[stable(feature = "unix_socket", since = "1.10.0")] impl fmt::Debug for SocketAddr { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { match self.address() { @@ -168,8 +173,6 @@ impl<'a> fmt::Display for AsciiEscaped<'a> { /// # Examples /// /// ```rust,no_run -/// #![feature(unix_socket)] -/// /// use std::os::unix::net::UnixStream; /// use std::io::prelude::*; /// @@ -179,8 +182,10 @@ impl<'a> fmt::Display for AsciiEscaped<'a> { /// stream.read_to_string(&mut response).unwrap(); /// println!("{}", response); /// ``` +#[stable(feature = "unix_socket", since = "1.10.0")] pub struct UnixStream(Socket); +#[stable(feature = "unix_socket", since = "1.10.0")] impl fmt::Debug for UnixStream { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let mut builder = fmt.debug_struct("UnixStream"); @@ -197,6 +202,7 @@ impl fmt::Debug for UnixStream { impl UnixStream { /// Connects to the socket named by `path`. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn connect<P: AsRef<Path>>(path: P) -> io::Result<UnixStream> { fn inner(path: &Path) -> io::Result<UnixStream> { unsafe { @@ -213,6 +219,7 @@ impl UnixStream { /// Creates an unnamed pair of connected sockets. /// /// Returns two `UnixStream`s which are connected to each other. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn pair() -> io::Result<(UnixStream, UnixStream)> { let (i1, i2) = Socket::new_pair(libc::AF_UNIX, libc::SOCK_STREAM)?; Ok((UnixStream(i1), UnixStream(i2))) @@ -224,16 +231,19 @@ impl UnixStream { /// object references. Both handles will read and write the same stream of /// data, and options set on one stream will be propogated to the other /// stream. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn try_clone(&self) -> io::Result<UnixStream> { self.0.duplicate().map(UnixStream) } /// Returns the socket address of the local half of this connection. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn local_addr(&self) -> io::Result<SocketAddr> { SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) } /// Returns the socket address of the remote half of this connection. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn peer_addr(&self) -> io::Result<SocketAddr> { SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) }) } @@ -243,6 +253,7 @@ impl UnixStream { /// If the provided value is `None`, then `read` calls will block /// indefinitely. It is an error to pass the zero `Duration` to this /// method. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { self.0.set_timeout(timeout, libc::SO_RCVTIMEO) } @@ -252,26 +263,31 @@ impl UnixStream { /// If the provided value is `None`, then `write` calls will block /// indefinitely. It is an error to pass the zero `Duration` to this /// method. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { self.0.set_timeout(timeout, libc::SO_SNDTIMEO) } /// Returns the read timeout of this socket. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn read_timeout(&self) -> io::Result<Option<Duration>> { self.0.timeout(libc::SO_RCVTIMEO) } /// Returns the write timeout of this socket. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn write_timeout(&self) -> io::Result<Option<Duration>> { self.0.timeout(libc::SO_SNDTIMEO) } /// Moves the socket into or out of nonblocking mode. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) } /// Returns the value of the `SO_ERROR` option. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn take_error(&self) -> io::Result<Option<io::Error>> { self.0.take_error() } @@ -281,11 +297,13 @@ impl UnixStream { /// This function will cause all pending and future I/O calls on the /// specified portions to immediately return with an appropriate value /// (see the documentation of `Shutdown`). + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { self.0.shutdown(how) } } +#[stable(feature = "unix_socket", since = "1.10.0")] impl io::Read for UnixStream { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { io::Read::read(&mut &*self, buf) @@ -296,6 +314,7 @@ impl io::Read for UnixStream { } } +#[stable(feature = "unix_socket", since = "1.10.0")] impl<'a> io::Read for &'a UnixStream { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) @@ -306,6 +325,7 @@ impl<'a> io::Read for &'a UnixStream { } } +#[stable(feature = "unix_socket", since = "1.10.0")] impl io::Write for UnixStream { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { io::Write::write(&mut &*self, buf) @@ -316,6 +336,7 @@ impl io::Write for UnixStream { } } +#[stable(feature = "unix_socket", since = "1.10.0")] impl<'a> io::Write for &'a UnixStream { fn write(&mut self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) @@ -326,18 +347,21 @@ impl<'a> io::Write for &'a UnixStream { } } +#[stable(feature = "unix_socket", since = "1.10.0")] impl AsRawFd for UnixStream { fn as_raw_fd(&self) -> RawFd { *self.0.as_inner() } } +#[stable(feature = "unix_socket", since = "1.10.0")] impl FromRawFd for UnixStream { unsafe fn from_raw_fd(fd: RawFd) -> UnixStream { UnixStream(Socket::from_inner(fd)) } } +#[stable(feature = "unix_socket", since = "1.10.0")] impl IntoRawFd for UnixStream { fn into_raw_fd(self) -> RawFd { self.0.into_inner() @@ -349,8 +373,6 @@ impl IntoRawFd for UnixStream { /// # Examples /// /// ```rust,no_run -/// #![feature(unix_socket)] -/// /// use std::thread; /// use std::os::unix::net::{UnixStream, UnixListener}; /// @@ -377,8 +399,10 @@ impl IntoRawFd for UnixStream { /// // close the listener socket /// drop(listener); /// ``` +#[stable(feature = "unix_socket", since = "1.10.0")] pub struct UnixListener(Socket); +#[stable(feature = "unix_socket", since = "1.10.0")] impl fmt::Debug for UnixListener { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let mut builder = fmt.debug_struct("UnixListener"); @@ -392,6 +416,7 @@ impl fmt::Debug for UnixListener { impl UnixListener { /// Creates a new `UnixListener` bound to the specified socket. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> { fn inner(path: &Path) -> io::Result<UnixListener> { unsafe { @@ -412,6 +437,7 @@ impl UnixListener { /// This function will block the calling thread until a new Unix connection /// is established. When established, the corersponding `UnixStream` and /// the remote peer's address will be returned. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { let mut storage: libc::sockaddr_un = unsafe { mem::zeroed() }; let mut len = mem::size_of_val(&storage) as libc::socklen_t; @@ -425,21 +451,25 @@ impl UnixListener { /// The returned `UnixListener` is a reference to the same socket that this /// object references. Both handles can be used to accept incoming /// connections and options set on one listener will affect the other. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn try_clone(&self) -> io::Result<UnixListener> { self.0.duplicate().map(UnixListener) } /// Returns the local socket address of this listener. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn local_addr(&self) -> io::Result<SocketAddr> { SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) } /// Moves the socket into or out of nonblocking mode. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) } /// Returns the value of the `SO_ERROR` option. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn take_error(&self) -> io::Result<Option<io::Error>> { self.0.take_error() } @@ -448,29 +478,34 @@ impl UnixListener { /// /// The iterator will never return `None` and will also not yield the /// peer's `SocketAddr` structure. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn incoming<'a>(&'a self) -> Incoming<'a> { Incoming { listener: self } } } +#[stable(feature = "unix_socket", since = "1.10.0")] impl AsRawFd for UnixListener { fn as_raw_fd(&self) -> RawFd { *self.0.as_inner() } } +#[stable(feature = "unix_socket", since = "1.10.0")] impl FromRawFd for UnixListener { unsafe fn from_raw_fd(fd: RawFd) -> UnixListener { UnixListener(Socket::from_inner(fd)) } } +#[stable(feature = "unix_socket", since = "1.10.0")] impl IntoRawFd for UnixListener { fn into_raw_fd(self) -> RawFd { self.0.into_inner() } } +#[stable(feature = "unix_socket", since = "1.10.0")] impl<'a> IntoIterator for &'a UnixListener { type Item = io::Result<UnixStream>; type IntoIter = Incoming<'a>; @@ -484,10 +519,12 @@ impl<'a> IntoIterator for &'a UnixListener { /// /// It will never return `None`. #[derive(Debug)] +#[stable(feature = "unix_socket", since = "1.10.0")] pub struct Incoming<'a> { listener: &'a UnixListener, } +#[stable(feature = "unix_socket", since = "1.10.0")] impl<'a> Iterator for Incoming<'a> { type Item = io::Result<UnixStream>; @@ -505,8 +542,6 @@ impl<'a> Iterator for Incoming<'a> { /// # Examples /// /// ```rust,no_run -/// #![feature(unix_socket)] -/// /// use std::os::unix::net::UnixDatagram; /// /// let socket = UnixDatagram::bind("/path/to/my/socket").unwrap(); @@ -515,8 +550,10 @@ impl<'a> Iterator for Incoming<'a> { /// let (count, address) = socket.recv_from(&mut buf).unwrap(); /// println!("socket {:?} sent {:?}", address, &buf[..count]); /// ``` +#[stable(feature = "unix_socket", since = "1.10.0")] pub struct UnixDatagram(Socket); +#[stable(feature = "unix_socket", since = "1.10.0")] impl fmt::Debug for UnixDatagram { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let mut builder = fmt.debug_struct("UnixDatagram"); @@ -533,6 +570,7 @@ impl fmt::Debug for UnixDatagram { impl UnixDatagram { /// Creates a Unix datagram socket bound to the given path. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixDatagram> { fn inner(path: &Path) -> io::Result<UnixDatagram> { unsafe { @@ -548,6 +586,7 @@ impl UnixDatagram { } /// Creates a Unix Datagram socket which is not bound to any address. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn unbound() -> io::Result<UnixDatagram> { let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_DGRAM)?; Ok(UnixDatagram(inner)) @@ -556,6 +595,7 @@ impl UnixDatagram { /// Create an unnamed pair of connected sockets. /// /// Returns two `UnixDatagrams`s which are connected to each other. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> { let (i1, i2) = Socket::new_pair(libc::AF_UNIX, libc::SOCK_DGRAM)?; Ok((UnixDatagram(i1), UnixDatagram(i2))) @@ -565,6 +605,7 @@ impl UnixDatagram { /// /// The `send` method may be used to send data to the specified address. /// `recv` and `recv_from` will only receive data from that address. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn connect<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { fn inner(d: &UnixDatagram, path: &Path) -> io::Result<()> { unsafe { @@ -583,11 +624,13 @@ impl UnixDatagram { /// The returned `UnixListener` is a reference to the same socket that this /// object references. Both handles can be used to accept incoming /// connections and options set on one listener will affect the other. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn try_clone(&self) -> io::Result<UnixDatagram> { self.0.duplicate().map(UnixDatagram) } /// Returns the address of this socket. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn local_addr(&self) -> io::Result<SocketAddr> { SocketAddr::new(|addr, len| unsafe { libc::getsockname(*self.0.as_inner(), addr, len) }) } @@ -595,6 +638,7 @@ impl UnixDatagram { /// Returns the address of this socket's peer. /// /// The `connect` method will connect the socket to a peer. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn peer_addr(&self) -> io::Result<SocketAddr> { SocketAddr::new(|addr, len| unsafe { libc::getpeername(*self.0.as_inner(), addr, len) }) } @@ -603,6 +647,7 @@ impl UnixDatagram { /// /// On success, returns the number of bytes read and the address from /// whence the data came. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { let mut count = 0; let addr = SocketAddr::new(|addr, len| { @@ -629,6 +674,7 @@ impl UnixDatagram { /// Receives data from the socket. /// /// On success, returns the number of bytes read. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) } @@ -636,6 +682,7 @@ impl UnixDatagram { /// Sends data on the socket to the specified address. /// /// On success, returns the number of bytes written. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn send_to<P: AsRef<Path>>(&self, buf: &[u8], path: P) -> io::Result<usize> { fn inner(d: &UnixDatagram, buf: &[u8], path: &Path) -> io::Result<usize> { unsafe { @@ -659,6 +706,7 @@ impl UnixDatagram { /// will return an error if the socket has not already been connected. /// /// On success, returns the number of bytes written. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn send(&self, buf: &[u8]) -> io::Result<usize> { self.0.write(buf) } @@ -668,6 +716,7 @@ impl UnixDatagram { /// If the provided value is `None`, then `recv` and `recv_from` calls will /// block indefinitely. It is an error to pass the zero `Duration` to this /// method. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_read_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { self.0.set_timeout(timeout, libc::SO_RCVTIMEO) } @@ -677,26 +726,31 @@ impl UnixDatagram { /// If the provided value is `None`, then `send` and `send_to` calls will /// block indefinitely. It is an error to pass the zero `Duration` to this /// method. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_write_timeout(&self, timeout: Option<Duration>) -> io::Result<()> { self.0.set_timeout(timeout, libc::SO_SNDTIMEO) } /// Returns the read timeout of this socket. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn read_timeout(&self) -> io::Result<Option<Duration>> { self.0.timeout(libc::SO_RCVTIMEO) } /// Returns the write timeout of this socket. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn write_timeout(&self) -> io::Result<Option<Duration>> { self.0.timeout(libc::SO_SNDTIMEO) } /// Moves the socket into or out of nonblocking mode. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) } /// Returns the value of the `SO_ERROR` option. + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn take_error(&self) -> io::Result<Option<io::Error>> { self.0.take_error() } @@ -706,23 +760,27 @@ impl UnixDatagram { /// This function will cause all pending and future I/O calls on the /// specified portions to immediately return with an appropriate value /// (see the documentation of `Shutdown`). + #[stable(feature = "unix_socket", since = "1.10.0")] pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { self.0.shutdown(how) } } +#[stable(feature = "unix_socket", since = "1.10.0")] impl AsRawFd for UnixDatagram { fn as_raw_fd(&self) -> RawFd { *self.0.as_inner() } } +#[stable(feature = "unix_socket", since = "1.10.0")] impl FromRawFd for UnixDatagram { unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram { UnixDatagram(Socket::from_inner(fd)) } } +#[stable(feature = "unix_socket", since = "1.10.0")] impl IntoRawFd for UnixDatagram { fn into_raw_fd(self) -> RawFd { self.0.into_inner() diff --git a/src/libstd/sys/unix/ext/process.rs b/src/libstd/sys/unix/ext/process.rs index 7f31cf9f3bf..430ec5f94a6 100644 --- a/src/libstd/sys/unix/ext/process.rs +++ b/src/libstd/sys/unix/ext/process.rs @@ -34,21 +34,6 @@ pub trait CommandExt { #[stable(feature = "rust1", since = "1.0.0")] fn gid(&mut self, id: u32) -> &mut process::Command; - /// Create a new session (cf. `setsid(2)`) for the child process. This means - /// that the child is the leader of a new process group. The parent process - /// remains the child reaper of the new process. - /// - /// This is not enough to create a daemon process. The *init* process should - /// be the child reaper of a daemon. This can be achieved if the parent - /// process exit. Moreover, a daemon should not have a controlling terminal. - /// To achieve this, a session leader (the child) must spawn another process - /// (the daemon) in the same session. - #[unstable(feature = "process_session_leader", reason = "recently added", - issue = "27811")] - #[rustc_deprecated(reason = "use `before_exec` instead", - since = "1.9.0")] - fn session_leader(&mut self, on: bool) -> &mut process::Command; - /// Schedules a closure to be run just before the `exec` function is /// invoked. /// @@ -112,11 +97,6 @@ impl CommandExt for process::Command { self } - fn session_leader(&mut self, on: bool) -> &mut process::Command { - self.as_inner_mut().session_leader(on); - self - } - fn before_exec<F>(&mut self, f: F) -> &mut process::Command where F: FnMut() -> io::Result<()> + Send + Sync + 'static { @@ -132,6 +112,11 @@ impl CommandExt for process::Command { /// Unix-specific extensions to `std::process::ExitStatus` #[stable(feature = "rust1", since = "1.0.0")] pub trait ExitStatusExt { + /// Creates a new `ExitStatus` from the raw underlying `i32` return value of + /// a process. + #[unstable(feature = "exit_status_from", issue = "32713")] + fn from_raw(raw: i32) -> Self; + /// If the process was terminated by a signal, returns that signal. #[stable(feature = "rust1", since = "1.0.0")] fn signal(&self) -> Option<i32>; @@ -139,6 +124,10 @@ pub trait ExitStatusExt { #[stable(feature = "rust1", since = "1.0.0")] impl ExitStatusExt for process::ExitStatus { + fn from_raw(raw: i32) -> Self { + process::ExitStatus::from_inner(From::from(raw)) + } + fn signal(&self) -> Option<i32> { self.as_inner().signal() } diff --git a/src/libstd/sys/unix/ext/raw.rs b/src/libstd/sys/unix/ext/raw.rs index 96535e88604..7972990e67d 100644 --- a/src/libstd/sys/unix/ext/raw.rs +++ b/src/libstd/sys/unix/ext/raw.rs @@ -23,7 +23,7 @@ #[stable(feature = "raw_ext", since = "1.1.0")] pub type pid_t = i32; #[doc(inline)] -#[unstable(feature = "pthread_t", issue = "29791")] +#[stable(feature = "pthread_t", since = "1.8.0")] pub use sys::platform::raw::pthread_t; #[doc(inline)] #[stable(feature = "raw_ext", since = "1.1.0")] diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index 94c48be02ff..b99f4a2eacd 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -62,32 +62,31 @@ impl FileDesc { } #[cfg(not(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten")))] - pub fn set_cloexec(&self) { + pub fn set_cloexec(&self) -> io::Result<()> { unsafe { - let ret = libc::ioctl(self.fd, libc::FIOCLEX); - debug_assert_eq!(ret, 0); + cvt(libc::ioctl(self.fd, libc::FIOCLEX))?; + Ok(()) } } #[cfg(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten"))] - pub fn set_cloexec(&self) { + pub fn set_cloexec(&self) -> io::Result<()> { unsafe { - let previous = libc::fcntl(self.fd, libc::F_GETFD); - let ret = libc::fcntl(self.fd, libc::F_SETFD, previous | libc::FD_CLOEXEC); - debug_assert_eq!(ret, 0); + let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?; + cvt(libc::fcntl(self.fd, libc::F_SETFD, previous | libc::FD_CLOEXEC))?; + Ok(()) } } - pub fn set_nonblocking(&self, nonblocking: bool) { + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { unsafe { - let previous = libc::fcntl(self.fd, libc::F_GETFL); - debug_assert!(previous != -1); + let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?; let new = if nonblocking { previous | libc::O_NONBLOCK } else { previous & !libc::O_NONBLOCK }; - let ret = libc::fcntl(self.fd, libc::F_SETFL, new); - debug_assert!(ret != -1); + cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?; + Ok(()) } } @@ -114,8 +113,8 @@ impl FileDesc { let make_filedesc = |fd| { let fd = FileDesc::new(fd); - fd.set_cloexec(); - fd + fd.set_cloexec()?; + Ok(fd) }; static TRY_CLOEXEC: AtomicBool = AtomicBool::new(!cfg!(target_os = "android")); @@ -127,7 +126,7 @@ impl FileDesc { // though it reported doing so on F_DUPFD_CLOEXEC. Ok(fd) => { return Ok(if cfg!(target_os = "linux") { - make_filedesc(fd) + make_filedesc(fd)? } else { FileDesc::new(fd) }) @@ -138,7 +137,7 @@ impl FileDesc { Err(e) => return Err(e), } } - cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).map(make_filedesc) + cvt(unsafe { libc::fcntl(fd, libc::F_DUPFD, 0) }).and_then(make_filedesc) } } diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index 0969a59ea43..b315e676263 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -84,7 +84,7 @@ pub struct OpenOptions { #[derive(Clone, PartialEq, Eq, Debug)] pub struct FilePermissions { mode: mode_t } -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub struct FileType { mode: mode_t } pub struct DirBuilder { mode: mode_t } @@ -100,31 +100,6 @@ impl FileAttr { } } -#[cfg(any(target_os = "ios", target_os = "macos"))] -// FIXME: update SystemTime to store a timespec and don't lose precision -impl FileAttr { - pub fn modified(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(libc::timeval { - tv_sec: self.stat.st_mtime, - tv_usec: (self.stat.st_mtime_nsec / 1000) as libc::suseconds_t, - })) - } - - pub fn accessed(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(libc::timeval { - tv_sec: self.stat.st_atime, - tv_usec: (self.stat.st_atime_nsec / 1000) as libc::suseconds_t, - })) - } - - pub fn created(&self) -> io::Result<SystemTime> { - Ok(SystemTime::from(libc::timeval { - tv_sec: self.stat.st_birthtime, - tv_usec: (self.stat.st_birthtime_nsec / 1000) as libc::suseconds_t, - })) - } -} - #[cfg(target_os = "netbsd")] impl FileAttr { pub fn modified(&self) -> io::Result<SystemTime> { @@ -149,7 +124,7 @@ impl FileAttr { } } -#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "netbsd")))] +#[cfg(not(target_os = "netbsd"))] impl FileAttr { pub fn modified(&self) -> io::Result<SystemTime> { Ok(SystemTime::from(libc::timespec { @@ -167,7 +142,9 @@ impl FileAttr { #[cfg(any(target_os = "bitrig", target_os = "freebsd", - target_os = "openbsd"))] + target_os = "openbsd", + target_os = "macos", + target_os = "ios"))] pub fn created(&self) -> io::Result<SystemTime> { Ok(SystemTime::from(libc::timespec { tv_sec: self.stat.st_birthtime as libc::time_t, @@ -177,7 +154,9 @@ impl FileAttr { #[cfg(not(any(target_os = "bitrig", target_os = "freebsd", - target_os = "openbsd")))] + target_os = "openbsd", + target_os = "macos", + target_os = "ios")))] pub fn created(&self) -> io::Result<SystemTime> { Err(io::Error::new(io::ErrorKind::Other, "creation time is not available on this platform \ @@ -226,9 +205,15 @@ impl Iterator for ReadDir { // of the thread safety, on Illumos the readdir(3C) function is safe to use // in threaded applications and it is generally preferred over the // readdir_r(3C) function. + super::os::set_errno(0); let entry_ptr = libc::readdir(self.dirp.0); if entry_ptr.is_null() { - return None + // NULL can mean either the end is reached or an error occurred. + // So we had to clear errno beforehand to check for an error now. + return match super::os::errno() { + 0 => None, + e => Some(Err(Error::from_raw_os_error(e))), + } } let name = (*entry_ptr).d_name.as_ptr(); @@ -439,7 +424,7 @@ impl File { // The CLOEXEC flag, however, is supported on versions of OSX/BSD/etc // that we support, so we only do this on Linux currently. if cfg!(target_os = "linux") { - fd.set_cloexec(); + fd.set_cloexec()?; } Ok(File(fd)) diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs index 12a877f7478..f0fd42fc99b 100644 --- a/src/libstd/sys/unix/mod.rs +++ b/src/libstd/sys/unix/mod.rs @@ -12,8 +12,6 @@ use io::{self, ErrorKind}; use libc; -use num::One; -use ops::Neg; #[cfg(target_os = "android")] pub use os::android as platform; #[cfg(target_os = "bitrig")] pub use os::bitrig as platform; @@ -123,9 +121,23 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { } } -pub fn cvt<T: One + PartialEq + Neg<Output=T>>(t: T) -> io::Result<T> { - let one: T = T::one(); - if t == -one { +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + +pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> { + if t.is_minus_one() { Err(io::Error::last_os_error()) } else { Ok(t) @@ -133,7 +145,8 @@ pub fn cvt<T: One + PartialEq + Neg<Output=T>>(t: T) -> io::Result<T> { } pub fn cvt_r<T, F>(mut f: F) -> io::Result<T> - where T: One + PartialEq + Neg<Output=T>, F: FnMut() -> T + where T: IsMinusOne, + F: FnMut() -> T { loop { match cvt(f()) { diff --git a/src/libstd/sys/unix/mutex.rs b/src/libstd/sys/unix/mutex.rs index 4e4abcfbeee..52cf3f97c5c 100644 --- a/src/libstd/sys/unix/mutex.rs +++ b/src/libstd/sys/unix/mutex.rs @@ -30,6 +30,39 @@ impl Mutex { Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) } } #[inline] + pub unsafe fn init(&mut self) { + // Issue #33770 + // + // A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have + // a type of PTHREAD_MUTEX_DEFAULT, which has undefined behavior if you + // try to re-lock it from the same thread when you already hold a lock. + // + // In practice, glibc takes advantage of this undefined behavior to + // implement hardware lock elision, which uses hardware transactional + // memory to avoid acquiring the lock. While a transaction is in + // progress, the lock appears to be unlocked. This isn't a problem for + // other threads since the transactional memory will abort if a conflict + // is detected, however no abort is generated if re-locking from the + // same thread. + // + // Since locking the same mutex twice will result in two aliasing &mut + // references, we instead create the mutex with type + // PTHREAD_MUTEX_NORMAL which is guaranteed to deadlock if we try to + // re-lock it from the same thread, thus avoiding undefined behavior. + // + // We can't do anything for StaticMutex, but that type is deprecated + // anyways. + let mut attr: libc::pthread_mutexattr_t = mem::uninitialized(); + let r = libc::pthread_mutexattr_init(&mut attr); + debug_assert_eq!(r, 0); + let r = libc::pthread_mutexattr_settype(&mut attr, libc::PTHREAD_MUTEX_NORMAL); + debug_assert_eq!(r, 0); + let r = libc::pthread_mutex_init(self.inner.get(), &attr); + debug_assert_eq!(r, 0); + let r = libc::pthread_mutexattr_destroy(&mut attr); + debug_assert_eq!(r, 0); + } + #[inline] pub unsafe fn lock(&self) { let r = libc::pthread_mutex_lock(self.inner.get()); debug_assert_eq!(r, 0); diff --git a/src/libstd/sys/unix/net.rs b/src/libstd/sys/unix/net.rs index 830957a7e59..a784741c88c 100644 --- a/src/libstd/sys/unix/net.rs +++ b/src/libstd/sys/unix/net.rs @@ -77,7 +77,7 @@ impl Socket { let fd = cvt(libc::socket(fam, ty, 0))?; let fd = FileDesc::new(fd); - fd.set_cloexec(); + fd.set_cloexec()?; Ok(Socket(fd)) } } @@ -99,9 +99,9 @@ impl Socket { cvt(libc::socketpair(fam, ty, 0, fds.as_mut_ptr()))?; let a = FileDesc::new(fds[0]); - a.set_cloexec(); let b = FileDesc::new(fds[1]); - b.set_cloexec(); + a.set_cloexec()?; + b.set_cloexec()?; Ok((Socket(a), Socket(b))) } } @@ -132,7 +132,7 @@ impl Socket { libc::accept(self.0.raw(), storage, len) })?; let fd = FileDesc::new(fd); - fd.set_cloexec(); + fd.set_cloexec()?; Ok(Socket(fd)) } diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index 94ebbd70ae8..4c3558f91f5 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -27,36 +27,45 @@ use path::{self, PathBuf}; use ptr; use slice; use str; -use sync::StaticMutex; +use sys_common::mutex::Mutex; use sys::cvt; use sys::fd; use vec; const TMPBUF_SZ: usize = 128; -static ENV_LOCK: StaticMutex = StaticMutex::new(); +static ENV_LOCK: Mutex = Mutex::new(); + + +extern { + #[cfg_attr(any(target_os = "linux", target_os = "emscripten"), + link_name = "__errno_location")] + #[cfg_attr(any(target_os = "bitrig", + target_os = "netbsd", + target_os = "openbsd", + target_os = "android", + target_env = "newlib"), + link_name = "__errno")] + #[cfg_attr(target_os = "solaris", link_name = "___errno")] + #[cfg_attr(any(target_os = "macos", + target_os = "ios", + target_os = "freebsd"), + link_name = "__error")] + fn errno_location() -> *mut c_int; +} /// Returns the platform-specific value of errno #[cfg(not(target_os = "dragonfly"))] pub fn errno() -> i32 { - extern { - #[cfg_attr(any(target_os = "linux", target_os = "emscripten"), - link_name = "__errno_location")] - #[cfg_attr(any(target_os = "bitrig", - target_os = "netbsd", - target_os = "openbsd", - target_os = "android", - target_env = "newlib"), - link_name = "__errno")] - #[cfg_attr(target_os = "solaris", link_name = "___errno")] - #[cfg_attr(any(target_os = "macos", - target_os = "ios", - target_os = "freebsd"), - link_name = "__error")] - fn errno_location() -> *const c_int; + unsafe { + (*errno_location()) as i32 } +} +/// Sets the platform-specific value of errno +#[cfg(target_os = "solaris")] // only needed for readdir so far +pub fn set_errno(e: i32) { unsafe { - (*errno_location()) as i32 + *errno_location() = e as c_int } } @@ -227,11 +236,11 @@ pub fn current_exe() -> io::Result<PathBuf> { libc::KERN_PROC_ARGV]; let mib = mib.as_mut_ptr(); let mut argv_len = 0; - cvt(libc::sysctl(mib, 4, 0 as *mut _, &mut argv_len, - 0 as *mut _, 0))?; + cvt(libc::sysctl(mib, 4, ptr::null_mut(), &mut argv_len, + ptr::null_mut(), 0))?; let mut argv = Vec::<*const libc::c_char>::with_capacity(argv_len as usize); cvt(libc::sysctl(mib, 4, argv.as_mut_ptr() as *mut _, - &mut argv_len, 0 as *mut _, 0))?; + &mut argv_len, ptr::null_mut(), 0))?; argv.set_len(argv_len as usize); if argv[0].is_null() { return Err(io::Error::new(io::ErrorKind::Other, @@ -308,6 +317,10 @@ impl ExactSizeIterator for Args { fn len(&self) -> usize { self.iter.len() } } +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option<OsString> { self.iter.next_back() } +} + /// Returns the command line arguments /// /// Returns a list of the command line arguments. @@ -434,10 +447,11 @@ pub unsafe fn environ() -> *mut *const *const c_char { /// Returns a vector of (variable, value) byte-vector pairs for all the /// environment variables of the current process. pub fn env() -> Env { - let _g = ENV_LOCK.lock(); - return unsafe { + unsafe { + ENV_LOCK.lock(); let mut environ = *environ(); if environ == ptr::null() { + ENV_LOCK.unlock(); panic!("os::env() failure getting env string from OS: {}", io::Error::last_os_error()); } @@ -448,8 +462,13 @@ pub fn env() -> Env { } environ = environ.offset(1); } - Env { iter: result.into_iter(), _dont_send_or_sync_me: ptr::null_mut() } - }; + let ret = Env { + iter: result.into_iter(), + _dont_send_or_sync_me: ptr::null_mut(), + }; + ENV_LOCK.unlock(); + return ret + } fn parse(input: &[u8]) -> Option<(OsString, OsString)> { // Strategy (copied from glibc): Variable name and value are separated @@ -471,32 +490,40 @@ pub fn getenv(k: &OsStr) -> io::Result<Option<OsString>> { // environment variables with a nul byte can't be set, so their value is // always None as well let k = CString::new(k.as_bytes())?; - let _g = ENV_LOCK.lock(); - Ok(unsafe { + unsafe { + ENV_LOCK.lock(); let s = libc::getenv(k.as_ptr()) as *const _; - if s.is_null() { + let ret = if s.is_null() { None } else { Some(OsStringExt::from_vec(CStr::from_ptr(s).to_bytes().to_vec())) - } - }) + }; + ENV_LOCK.unlock(); + return Ok(ret) + } } pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> { let k = CString::new(k.as_bytes())?; let v = CString::new(v.as_bytes())?; - let _g = ENV_LOCK.lock(); - cvt(unsafe { - libc::setenv(k.as_ptr(), v.as_ptr(), 1) - }).map(|_| ()) + + unsafe { + ENV_LOCK.lock(); + let ret = cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(|_| ()); + ENV_LOCK.unlock(); + return ret + } } pub fn unsetenv(n: &OsStr) -> io::Result<()> { let nbuf = CString::new(n.as_bytes())?; - let _g = ENV_LOCK.lock(); - cvt(unsafe { - libc::unsetenv(nbuf.as_ptr()) - }).map(|_| ()) + + unsafe { + ENV_LOCK.lock(); + let ret = cvt(libc::unsetenv(nbuf.as_ptr())).map(|_| ()); + ENV_LOCK.unlock(); + return ret + } } pub fn page_size() -> usize { diff --git a/src/libstd/sys/unix/pipe.rs b/src/libstd/sys/unix/pipe.rs index beca2d46753..01059413338 100644 --- a/src/libstd/sys/unix/pipe.rs +++ b/src/libstd/sys/unix/pipe.rs @@ -14,6 +14,7 @@ use cmp; use io; use libc::{self, c_int}; use mem; +use ptr; use sys::cvt_r; use sys::fd::FileDesc; @@ -44,17 +45,18 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { } } if unsafe { libc::pipe(fds.as_mut_ptr()) == 0 } { - Ok((AnonPipe::from_fd(fds[0]), AnonPipe::from_fd(fds[1]))) + let fd0 = FileDesc::new(fds[0]); + let fd1 = FileDesc::new(fds[1]); + Ok((AnonPipe::from_fd(fd0)?, AnonPipe::from_fd(fd1)?)) } else { Err(io::Error::last_os_error()) } } impl AnonPipe { - pub fn from_fd(fd: libc::c_int) -> AnonPipe { - let fd = FileDesc::new(fd); - fd.set_cloexec(); - AnonPipe(fd) + pub fn from_fd(fd: FileDesc) -> io::Result<AnonPipe> { + fd.set_cloexec()?; + Ok(AnonPipe(fd)) } pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> { @@ -81,8 +83,8 @@ pub fn read2(p1: AnonPipe, // in the `select` loop below, and we wouldn't want one to block the other! let p1 = p1.into_fd(); let p2 = p2.into_fd(); - p1.set_nonblocking(true); - p2.set_nonblocking(true); + p1.set_nonblocking(true)?; + p2.set_nonblocking(true)?; let max = cmp::max(p1.raw(), p2.raw()); loop { @@ -91,8 +93,8 @@ pub fn read2(p1: AnonPipe, let mut read: libc::fd_set = mem::zeroed(); libc::FD_SET(p1.raw(), &mut read); libc::FD_SET(p2.raw(), &mut read); - libc::select(max + 1, &mut read, 0 as *mut _, 0 as *mut _, - 0 as *mut _) + libc::select(max + 1, &mut read, ptr::null_mut(), ptr::null_mut(), + ptr::null_mut()) })?; // Read as much as we can from each pipe, ignoring EWOULDBLOCK or @@ -114,11 +116,11 @@ pub fn read2(p1: AnonPipe, } }; if read(&p1, v1)? { - p2.set_nonblocking(false); + p2.set_nonblocking(false)?; return p2.read_to_end(v2).map(|_| ()); } if read(&p2, v2)? { - p1.set_nonblocking(false); + p1.set_nonblocking(false)?; return p1.read_to_end(v1).map(|_| ()); } } diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index 270c2096b2c..d68867fb3d2 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -55,7 +55,6 @@ pub struct Command { cwd: Option<CString>, uid: Option<uid_t>, gid: Option<gid_t>, - session_leader: bool, saw_nul: bool, closures: Vec<Box<FnMut() -> io::Result<()> + Send + Sync>>, stdin: Option<Stdio>, @@ -97,7 +96,7 @@ impl Command { let mut saw_nul = false; let program = os2c(program, &mut saw_nul); Command { - argv: vec![program.as_ptr(), 0 as *const _], + argv: vec![program.as_ptr(), ptr::null()], program: program, args: Vec::new(), env: None, @@ -105,7 +104,6 @@ impl Command { cwd: None, uid: None, gid: None, - session_leader: false, saw_nul: saw_nul, closures: Vec::new(), stdin: None, @@ -119,7 +117,7 @@ impl Command { // pointer. let arg = os2c(arg, &mut self.saw_nul); self.argv[self.args.len() + 1] = arg.as_ptr(); - self.argv.push(0 as *const _); + self.argv.push(ptr::null()); // Also make sure we keep track of the owned value to schedule a // destructor for this memory. @@ -136,7 +134,7 @@ impl Command { envp.push(s.as_ptr()); map.insert(k, (envp.len() - 1, s)); } - envp.push(0 as *const _); + envp.push(ptr::null()); self.env = Some(map); self.envp = Some(envp); } @@ -147,7 +145,7 @@ impl Command { let new_key = pair_to_key(key, val, &mut self.saw_nul); let (map, envp) = self.init_env_map(); - // If `key` is already present then we we just update `envp` in place + // If `key` is already present then we just update `envp` in place // (and store the owned value), but if it's not there we override the // trailing NULL pointer, add a new NULL pointer, and store where we // were located. @@ -160,7 +158,7 @@ impl Command { Entry::Vacant(e) => { let len = envp.len(); envp[len - 1] = new_key.as_ptr(); - envp.push(0 as *const _); + envp.push(ptr::null()); e.insert((len - 1, new_key)); } } @@ -185,7 +183,7 @@ impl Command { pub fn env_clear(&mut self) { self.env = Some(HashMap::new()); - self.envp = Some(vec![0 as *const _]); + self.envp = Some(vec![ptr::null()]); } pub fn cwd(&mut self, dir: &OsStr) { @@ -197,9 +195,6 @@ impl Command { pub fn gid(&mut self, id: gid_t) { self.gid = Some(id); } - pub fn session_leader(&mut self, session_leader: bool) { - self.session_leader = session_leader; - } pub fn before_exec(&mut self, f: Box<FnMut() -> io::Result<()> + Send + Sync>) { @@ -367,12 +362,6 @@ impl Command { t!(cvt(libc::setuid(u as uid_t))); } - if self.session_leader { - // Don't check the error of setsid because it fails if we're the - // process leader already. We just forked so it shouldn't return - // error, but ignore it anyway. - let _ = libc::setsid(); - } if let Some(ref cwd) = self.cwd { t!(cvt(libc::chdir(cwd.as_ptr()))); } @@ -550,6 +539,12 @@ impl ExitStatus { } } +impl From<c_int> for ExitStatus { + fn from(a: c_int) -> ExitStatus { + ExitStatus(a) + } +} + impl fmt::Display for ExitStatus { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(code) = self.code() { diff --git a/src/libstd/sys/unix/rand.rs b/src/libstd/sys/unix/rand.rs index 92c3bf8829a..25a7a3ce50d 100644 --- a/src/libstd/sys/unix/rand.rs +++ b/src/libstd/sys/unix/rand.rs @@ -41,8 +41,10 @@ mod imp { #[cfg(target_arch = "aarch64")] const NR_GETRANDOM: libc::c_long = 278; + const GRND_NONBLOCK: libc::c_uint = 0x0001; + unsafe { - libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), 0) + libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK) } } @@ -63,6 +65,19 @@ mod imp { let err = errno() as libc::c_int; if err == libc::EINTR { continue; + } else if err == libc::EAGAIN { + // if getrandom() returns EAGAIN it would have blocked + // because the non-blocking pool (urandom) has not + // initialized in the kernel yet due to a lack of entropy + // the fallback we do here is to avoid blocking applications + // which could depend on this call without ever knowing + // they do and don't have a work around. The PRNG of + // /dev/urandom will still be used but not over a completely + // full entropy pool + let reader = File::open("/dev/urandom").expect("Unable to open /dev/urandom"); + let mut reader_rng = ReaderRng::new(reader); + reader_rng.fill_bytes(& mut v[read..]); + read += v.len() as usize; } else { panic!("unexpected getrandom error: {}", err); } diff --git a/src/libstd/sys/unix/rwlock.rs b/src/libstd/sys/unix/rwlock.rs index 44bd5d895f2..fbd4e1d1208 100644 --- a/src/libstd/sys/unix/rwlock.rs +++ b/src/libstd/sys/unix/rwlock.rs @@ -10,15 +10,24 @@ use libc; use cell::UnsafeCell; +use sync::atomic::{AtomicUsize, Ordering}; -pub struct RWLock { inner: UnsafeCell<libc::pthread_rwlock_t> } +pub struct RWLock { + inner: UnsafeCell<libc::pthread_rwlock_t>, + write_locked: UnsafeCell<bool>, + num_readers: AtomicUsize, +} unsafe impl Send for RWLock {} unsafe impl Sync for RWLock {} impl RWLock { pub const fn new() -> RWLock { - RWLock { inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER) } + RWLock { + inner: UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER), + write_locked: UnsafeCell::new(false), + num_readers: AtomicUsize::new(0), + } } #[inline] pub unsafe fn read(&self) { @@ -35,37 +44,86 @@ impl RWLock { // // We roughly maintain the deadlocking behavior by panicking to ensure // that this lock acquisition does not succeed. - if r == libc::EDEADLK { + // + // We also check whether there this lock is already write locked. This + // is only possible if it was write locked by the current thread and + // the implementation allows recursive locking. The POSIX standard + // doesn't require recursivly locking a rwlock to deadlock, but we can't + // allow that because it could lead to aliasing issues. + if r == libc::EDEADLK || *self.write_locked.get() { + if r == 0 { + self.raw_unlock(); + } panic!("rwlock read lock would result in deadlock"); } else { debug_assert_eq!(r, 0); + self.num_readers.fetch_add(1, Ordering::Relaxed); } } #[inline] pub unsafe fn try_read(&self) -> bool { - libc::pthread_rwlock_tryrdlock(self.inner.get()) == 0 + let r = libc::pthread_rwlock_tryrdlock(self.inner.get()); + if r == 0 { + if *self.write_locked.get() { + self.raw_unlock(); + false + } else { + self.num_readers.fetch_add(1, Ordering::Relaxed); + true + } + } else { + false + } } #[inline] pub unsafe fn write(&self) { let r = libc::pthread_rwlock_wrlock(self.inner.get()); - // see comments above for why we check for EDEADLK - if r == libc::EDEADLK { + // See comments above for why we check for EDEADLK and write_locked. We + // also need to check that num_readers is 0. + if r == libc::EDEADLK || *self.write_locked.get() || + self.num_readers.load(Ordering::Relaxed) != 0 { + if r == 0 { + self.raw_unlock(); + } panic!("rwlock write lock would result in deadlock"); } else { debug_assert_eq!(r, 0); } + *self.write_locked.get() = true; } #[inline] pub unsafe fn try_write(&self) -> bool { - libc::pthread_rwlock_trywrlock(self.inner.get()) == 0 + let r = libc::pthread_rwlock_trywrlock(self.inner.get()); + if r == 0 { + if *self.write_locked.get() || self.num_readers.load(Ordering::Relaxed) != 0 { + self.raw_unlock(); + false + } else { + *self.write_locked.get() = true; + true + } + } else { + false + } } #[inline] - pub unsafe fn read_unlock(&self) { + unsafe fn raw_unlock(&self) { let r = libc::pthread_rwlock_unlock(self.inner.get()); debug_assert_eq!(r, 0); } #[inline] - pub unsafe fn write_unlock(&self) { self.read_unlock() } + pub unsafe fn read_unlock(&self) { + debug_assert!(!*self.write_locked.get()); + self.num_readers.fetch_sub(1, Ordering::Relaxed); + self.raw_unlock(); + } + #[inline] + pub unsafe fn write_unlock(&self) { + debug_assert_eq!(self.num_readers.load(Ordering::Relaxed), 0); + debug_assert!(*self.write_locked.get()); + *self.write_locked.get() = false; + self.raw_unlock(); + } #[inline] pub unsafe fn destroy(&self) { let r = libc::pthread_rwlock_destroy(self.inner.get()); diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index cb34d1a5fbc..1061ca87f64 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -12,7 +12,6 @@ use prelude::v1::*; use alloc::boxed::FnBox; use cmp; -#[cfg(not(any(target_env = "newlib", target_os = "solaris")))] use ffi::CStr; use io; use libc; @@ -125,16 +124,25 @@ impl Thread { } pub fn sleep(dur: Duration) { - let mut ts = libc::timespec { - tv_sec: dur.as_secs() as libc::time_t, - tv_nsec: dur.subsec_nanos() as libc::c_long, - }; + let mut secs = dur.as_secs(); + let mut nsecs = dur.subsec_nanos() as libc::c_long; // If we're awoken with a signal then the return value will be -1 and // nanosleep will fill in `ts` with the remaining time. unsafe { - while libc::nanosleep(&ts, &mut ts) == -1 { - assert_eq!(os::errno(), libc::EINTR); + while secs > 0 || nsecs > 0 { + let mut ts = libc::timespec { + tv_sec: cmp::min(libc::time_t::max_value() as u64, secs) as libc::time_t, + tv_nsec: nsecs, + }; + secs -= ts.tv_sec as u64; + if libc::nanosleep(&ts, &mut ts) == -1 { + assert_eq!(os::errno(), libc::EINTR); + secs += ts.tv_sec as u64; + nsecs = ts.tv_nsec; + } else { + nsecs = 0; + } } } } diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs index cc7abe25e35..a08cec38f73 100644 --- a/src/libstd/sys/unix/time.rs +++ b/src/libstd/sys/unix/time.rs @@ -8,37 +8,129 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use cmp::Ordering; +use libc; +use time::Duration; + pub use self::inner::{Instant, SystemTime, UNIX_EPOCH}; const NSEC_PER_SEC: u64 = 1_000_000_000; +#[derive(Copy, Clone)] +struct Timespec { + t: libc::timespec, +} + +impl Timespec { + fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { + if self >= other { + Ok(if self.t.tv_nsec >= other.t.tv_nsec { + Duration::new((self.t.tv_sec - other.t.tv_sec) as u64, + (self.t.tv_nsec - other.t.tv_nsec) as u32) + } else { + Duration::new((self.t.tv_sec - 1 - other.t.tv_sec) as u64, + self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - + other.t.tv_nsec as u32) + }) + } else { + match other.sub_timespec(self) { + Ok(d) => Err(d), + Err(d) => Ok(d), + } + } + } + + fn add_duration(&self, other: &Duration) -> Timespec { + let secs = (self.t.tv_sec as i64).checked_add(other.as_secs() as i64); + let mut secs = secs.expect("overflow when adding duration to time"); + + // Nano calculations can't overflow because nanos are <1B which fit + // in a u32. + let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; + if nsec >= NSEC_PER_SEC as u32 { + nsec -= NSEC_PER_SEC as u32; + secs = secs.checked_add(1).expect("overflow when adding \ + duration to time"); + } + Timespec { + t: libc::timespec { + tv_sec: secs as libc::time_t, + tv_nsec: nsec as libc::c_long, + }, + } + } + + fn sub_duration(&self, other: &Duration) -> Timespec { + let secs = (self.t.tv_sec as i64).checked_sub(other.as_secs() as i64); + let mut secs = secs.expect("overflow when subtracting duration \ + from time"); + + // Similar to above, nanos can't overflow. + let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; + if nsec < 0 { + nsec += NSEC_PER_SEC as i32; + secs = secs.checked_sub(1).expect("overflow when subtracting \ + duration from time"); + } + Timespec { + t: libc::timespec { + tv_sec: secs as libc::time_t, + tv_nsec: nsec as libc::c_long, + }, + } + } +} + +impl PartialEq for Timespec { + fn eq(&self, other: &Timespec) -> bool { + self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec + } +} + +impl Eq for Timespec {} + +impl PartialOrd for Timespec { + fn partial_cmp(&self, other: &Timespec) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for Timespec { + fn cmp(&self, other: &Timespec) -> Ordering { + let me = (self.t.tv_sec, self.t.tv_nsec); + let other = (other.t.tv_sec, other.t.tv_nsec); + me.cmp(&other) + } +} + #[cfg(any(target_os = "macos", target_os = "ios"))] mod inner { - use cmp::Ordering; use fmt; use libc; - use super::NSEC_PER_SEC; use sync::Once; use sys::cvt; use sys_common::mul_div_u64; use time::Duration; - const USEC_PER_SEC: u64 = NSEC_PER_SEC / 1000; + use super::NSEC_PER_SEC; + use super::Timespec; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct Instant { t: u64 } - #[derive(Copy, Clone)] + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct SystemTime { - t: libc::timeval, + t: Timespec, } pub const UNIX_EPOCH: SystemTime = SystemTime { - t: libc::timeval { - tv_sec: 0, - tv_usec: 0, + t: Timespec { + t: libc::timespec { + tv_sec: 0, + tv_nsec: 0, + }, }, }; @@ -72,113 +164,52 @@ mod inner { impl SystemTime { pub fn now() -> SystemTime { - let mut s = SystemTime { - t: libc::timeval { - tv_sec: 0, - tv_usec: 0, - }, + use ptr; + + let mut s = libc::timeval { + tv_sec: 0, + tv_usec: 0, }; cvt(unsafe { - libc::gettimeofday(&mut s.t, 0 as *mut _) + libc::gettimeofday(&mut s, ptr::null_mut()) }).unwrap(); - return s + return SystemTime::from(s) } pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> { - if self >= other { - Ok(if self.t.tv_usec >= other.t.tv_usec { - Duration::new((self.t.tv_sec - other.t.tv_sec) as u64, - ((self.t.tv_usec - - other.t.tv_usec) as u32) * 1000) - } else { - Duration::new((self.t.tv_sec - 1 - other.t.tv_sec) as u64, - (self.t.tv_usec as u32 + (USEC_PER_SEC as u32) - - other.t.tv_usec as u32) * 1000) - }) - } else { - match other.sub_time(self) { - Ok(d) => Err(d), - Err(d) => Ok(d), - } - } + self.t.sub_timespec(&other.t) } pub fn add_duration(&self, other: &Duration) -> SystemTime { - let secs = (self.t.tv_sec as i64).checked_add(other.as_secs() as i64); - let mut secs = secs.expect("overflow when adding duration to time"); - - // Nano calculations can't overflow because nanos are <1B which fit - // in a u32. - let mut usec = (other.subsec_nanos() / 1000) + self.t.tv_usec as u32; - if usec >= USEC_PER_SEC as u32 { - usec -= USEC_PER_SEC as u32; - secs = secs.checked_add(1).expect("overflow when adding \ - duration to time"); - } - SystemTime { - t: libc::timeval { - tv_sec: secs as libc::time_t, - tv_usec: usec as libc::suseconds_t, - }, - } + SystemTime { t: self.t.add_duration(other) } } pub fn sub_duration(&self, other: &Duration) -> SystemTime { - let secs = (self.t.tv_sec as i64).checked_sub(other.as_secs() as i64); - let mut secs = secs.expect("overflow when subtracting duration \ - from time"); - - // Similar to above, nanos can't overflow. - let mut usec = self.t.tv_usec as i32 - - (other.subsec_nanos() / 1000) as i32; - if usec < 0 { - usec += USEC_PER_SEC as i32; - secs = secs.checked_sub(1).expect("overflow when subtracting \ - duration from time"); - } - SystemTime { - t: libc::timeval { - tv_sec: secs as libc::time_t, - tv_usec: usec as libc::suseconds_t, - }, - } + SystemTime { t: self.t.sub_duration(other) } } } impl From<libc::timeval> for SystemTime { fn from(t: libc::timeval) -> SystemTime { - SystemTime { t: t } - } - } - - impl PartialEq for SystemTime { - fn eq(&self, other: &SystemTime) -> bool { - self.t.tv_sec == other.t.tv_sec && self.t.tv_usec == other.t.tv_usec - } - } - - impl Eq for SystemTime {} - - impl PartialOrd for SystemTime { - fn partial_cmp(&self, other: &SystemTime) -> Option<Ordering> { - Some(self.cmp(other)) + SystemTime::from(libc::timespec { + tv_sec: t.tv_sec, + tv_nsec: (t.tv_usec * 1000) as libc::c_long, + }) } } - impl Ord for SystemTime { - fn cmp(&self, other: &SystemTime) -> Ordering { - let me = (self.t.tv_sec, self.t.tv_usec); - let other = (other.t.tv_sec, other.t.tv_usec); - me.cmp(&other) + impl From<libc::timespec> for SystemTime { + fn from(t: libc::timespec) -> SystemTime { + SystemTime { t: Timespec { t: t } } } } impl fmt::Debug for SystemTime { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("SystemTime") - .field("tv_sec", &self.t.tv_sec) - .field("tv_usec", &self.t.tv_usec) + .field("tv_sec", &self.t.t.tv_sec) + .field("tv_nsec", &self.t.t.tv_nsec) .finish() } } @@ -209,17 +240,12 @@ mod inner { #[cfg(not(any(target_os = "macos", target_os = "ios")))] mod inner { - use cmp::Ordering; use fmt; use libc; - use super::NSEC_PER_SEC; use sys::cvt; use time::Duration; - #[derive(Copy, Clone)] - struct Timespec { - t: libc::timespec, - } + use super::Timespec; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Instant { @@ -242,7 +268,7 @@ mod inner { impl Instant { pub fn now() -> Instant { - Instant { t: Timespec::now(libc::CLOCK_MONOTONIC) } + Instant { t: now(libc::CLOCK_MONOTONIC) } } pub fn sub_instant(&self, other: &Instant) -> Duration { @@ -271,7 +297,7 @@ mod inner { impl SystemTime { pub fn now() -> SystemTime { - SystemTime { t: Timespec::now(libc::CLOCK_REALTIME) } + SystemTime { t: now(libc::CLOCK_REALTIME) } } pub fn sub_time(&self, other: &SystemTime) @@ -308,98 +334,16 @@ mod inner { #[cfg(target_os = "dragonfly")] pub type clock_t = libc::c_ulong; - impl Timespec { - pub fn now(clock: clock_t) -> Timespec { - let mut t = Timespec { - t: libc::timespec { - tv_sec: 0, - tv_nsec: 0, - } - }; - cvt(unsafe { - libc::clock_gettime(clock, &mut t.t) - }).unwrap(); - t - } - - fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { - if self >= other { - Ok(if self.t.tv_nsec >= other.t.tv_nsec { - Duration::new((self.t.tv_sec - other.t.tv_sec) as u64, - (self.t.tv_nsec - other.t.tv_nsec) as u32) - } else { - Duration::new((self.t.tv_sec - 1 - other.t.tv_sec) as u64, - self.t.tv_nsec as u32 + (NSEC_PER_SEC as u32) - - other.t.tv_nsec as u32) - }) - } else { - match other.sub_timespec(self) { - Ok(d) => Err(d), - Err(d) => Ok(d), - } - } - } - - fn add_duration(&self, other: &Duration) -> Timespec { - let secs = (self.t.tv_sec as i64).checked_add(other.as_secs() as i64); - let mut secs = secs.expect("overflow when adding duration to time"); - - // Nano calculations can't overflow because nanos are <1B which fit - // in a u32. - let mut nsec = other.subsec_nanos() + self.t.tv_nsec as u32; - if nsec >= NSEC_PER_SEC as u32 { - nsec -= NSEC_PER_SEC as u32; - secs = secs.checked_add(1).expect("overflow when adding \ - duration to time"); - } - Timespec { - t: libc::timespec { - tv_sec: secs as libc::time_t, - tv_nsec: nsec as libc::c_long, - }, - } - } - - fn sub_duration(&self, other: &Duration) -> Timespec { - let secs = (self.t.tv_sec as i64).checked_sub(other.as_secs() as i64); - let mut secs = secs.expect("overflow when subtracting duration \ - from time"); - - // Similar to above, nanos can't overflow. - let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; - if nsec < 0 { - nsec += NSEC_PER_SEC as i32; - secs = secs.checked_sub(1).expect("overflow when subtracting \ - duration from time"); - } - Timespec { - t: libc::timespec { - tv_sec: secs as libc::time_t, - tv_nsec: nsec as libc::c_long, - }, + fn now(clock: clock_t) -> Timespec { + let mut t = Timespec { + t: libc::timespec { + tv_sec: 0, + tv_nsec: 0, } - } - } - - impl PartialEq for Timespec { - fn eq(&self, other: &Timespec) -> bool { - self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec - } - } - - impl Eq for Timespec {} - - impl PartialOrd for Timespec { - fn partial_cmp(&self, other: &Timespec) -> Option<Ordering> { - Some(self.cmp(other)) - } - } - - impl Ord for Timespec { - fn cmp(&self, other: &Timespec) -> Ordering { - let me = (self.t.tv_sec, self.t.tv_nsec); - let other = (other.t.tv_sec, other.t.tv_nsec); - me.cmp(&other) - } + }; + cvt(unsafe { + libc::clock_gettime(clock, &mut t.t) + }).unwrap(); + t } } diff --git a/src/libstd/sys/windows/backtrace.rs b/src/libstd/sys/windows/backtrace.rs index 0e10a8d8e8d..82a44c1c110 100644 --- a/src/libstd/sys/windows/backtrace.rs +++ b/src/libstd/sys/windows/backtrace.rs @@ -30,9 +30,9 @@ use io; use libc::c_void; use mem; use ptr; -use sync::StaticMutex; use sys::c; use sys::dynamic_lib::DynamicLibrary; +use sys::mutex::Mutex; macro_rules! sym { ($lib:expr, $e:expr, $t:ident) => ( @@ -101,53 +101,59 @@ impl Drop for Cleanup { pub fn write(w: &mut Write) -> io::Result<()> { // According to windows documentation, all dbghelp functions are // single-threaded. - static LOCK: StaticMutex = StaticMutex::new(); - let _g = LOCK.lock(); + static LOCK: Mutex = Mutex::new(); + unsafe { + LOCK.lock(); + let res = _write(w); + LOCK.unlock(); + return res + } +} +unsafe fn _write(w: &mut Write) -> io::Result<()> { let dbghelp = match DynamicLibrary::open("dbghelp.dll") { Ok(lib) => lib, Err(..) => return Ok(()), }; - unsafe { - // Fetch the symbols necessary from dbghelp.dll - let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn); - let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn); - let StackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn); - - // Allocate necessary structures for doing the stack walk - let process = c::GetCurrentProcess(); - let thread = c::GetCurrentThread(); - let mut context: c::CONTEXT = mem::zeroed(); - c::RtlCaptureContext(&mut context); - let mut frame: c::STACKFRAME64 = mem::zeroed(); - let image = init_frame(&mut frame, &context); - - // Initialize this process's symbols - let ret = SymInitialize(process, ptr::null_mut(), c::TRUE); - if ret != c::TRUE { return Ok(()) } - let _c = Cleanup { handle: process, SymCleanup: SymCleanup }; - - // And now that we're done with all the setup, do the stack walking! - // Start from -1 to avoid printing this stack frame, which will - // always be exactly the same. - let mut i = -1; - write!(w, "stack backtrace:\n")?; - while StackWalk64(image, process, thread, &mut frame, &mut context, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut()) == c::TRUE { - let addr = frame.AddrPC.Offset; - if addr == frame.AddrReturn.Offset || addr == 0 || - frame.AddrReturn.Offset == 0 { break } - - i += 1; - - if i >= 0 { - printing::print(w, i, addr - 1, process, &dbghelp)?; - } - } - Ok(()) + // Fetch the symbols necessary from dbghelp.dll + let SymInitialize = sym!(dbghelp, "SymInitialize", SymInitializeFn); + let SymCleanup = sym!(dbghelp, "SymCleanup", SymCleanupFn); + let StackWalk64 = sym!(dbghelp, "StackWalk64", StackWalk64Fn); + + // Allocate necessary structures for doing the stack walk + let process = c::GetCurrentProcess(); + let thread = c::GetCurrentThread(); + let mut context: c::CONTEXT = mem::zeroed(); + c::RtlCaptureContext(&mut context); + let mut frame: c::STACKFRAME64 = mem::zeroed(); + let image = init_frame(&mut frame, &context); + + // Initialize this process's symbols + let ret = SymInitialize(process, ptr::null_mut(), c::TRUE); + if ret != c::TRUE { return Ok(()) } + let _c = Cleanup { handle: process, SymCleanup: SymCleanup }; + + // And now that we're done with all the setup, do the stack walking! + // Start from -1 to avoid printing this stack frame, which will + // always be exactly the same. + let mut i = -1; + write!(w, "stack backtrace:\n")?; + while StackWalk64(image, process, thread, &mut frame, &mut context, + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut(), + ptr::null_mut()) == c::TRUE { + let addr = frame.AddrPC.Offset; + if addr == frame.AddrReturn.Offset || addr == 0 || + frame.AddrReturn.Offset == 0 { break } + + i += 1; + + if i >= 0 { + printing::print(w, i, addr - 1, process, &dbghelp)?; + } } + + Ok(()) } diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index ab24b9e6fd6..ce563dc7b16 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -181,6 +181,7 @@ pub const ERROR_ACCESS_DENIED: DWORD = 5; pub const ERROR_INVALID_HANDLE: DWORD = 6; pub const ERROR_NO_MORE_FILES: DWORD = 18; pub const ERROR_HANDLE_EOF: DWORD = 38; +pub const ERROR_FILE_EXISTS: DWORD = 80; pub const ERROR_BROKEN_PIPE: DWORD = 109; pub const ERROR_CALL_NOT_IMPLEMENTED: DWORD = 120; pub const ERROR_INSUFFICIENT_BUFFER: DWORD = 122; @@ -277,21 +278,6 @@ pub const CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000; pub const EXCEPTION_CONTINUE_SEARCH: LONG = 0; pub const EXCEPTION_STACK_OVERFLOW: DWORD = 0xc00000fd; pub const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub const EXCEPTION_NONCONTINUABLE: DWORD = 0x1; // Noncontinuable exception -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub const EXCEPTION_UNWINDING: DWORD = 0x2; // Unwind is in progress -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub const EXCEPTION_EXIT_UNWIND: DWORD = 0x4; // Exit unwind is in progress -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub const EXCEPTION_TARGET_UNWIND: DWORD = 0x20; // Target unwind in progress -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler call -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING | - EXCEPTION_EXIT_UNWIND | - EXCEPTION_TARGET_UNWIND | - EXCEPTION_COLLIDED_UNWIND; pub const PIPE_ACCESS_INBOUND: DWORD = 0x00000001; pub const FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD = 0x00080000; @@ -813,31 +799,6 @@ pub struct in6_addr { pub s6_addr: [u8; 16], } -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub enum UNWIND_HISTORY_TABLE {} - -#[repr(C)] -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub struct RUNTIME_FUNCTION { - pub BeginAddress: DWORD, - pub EndAddress: DWORD, - pub UnwindData: DWORD, -} - -#[repr(C)] -#[cfg(all(target_arch = "x86_64", target_env = "gnu"))] -pub struct DISPATCHER_CONTEXT { - pub ControlPc: LPVOID, - pub ImageBase: LPVOID, - pub FunctionEntry: *const RUNTIME_FUNCTION, - pub EstablisherFrame: LPVOID, - pub TargetIp: LPVOID, - pub ContextRecord: *const CONTEXT, - pub LanguageHandler: LPVOID, - pub HandlerData: *const u8, - pub HistoryTable: *const UNWIND_HISTORY_TABLE, -} - #[repr(C)] #[derive(Copy, Clone)] #[allow(dead_code)] // we only use some variants @@ -1113,19 +1074,6 @@ extern "system" { pbBuffer: *mut BYTE) -> BOOL; pub fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> BOOL; - #[unwind] - #[cfg(any(target_arch = "x86_64", target_env = "msvc"))] - pub fn RaiseException(dwExceptionCode: DWORD, - dwExceptionFlags: DWORD, - nNumberOfArguments: DWORD, - lpArguments: *const ULONG_PTR); - #[cfg(all(target_arch = "x86_64", target_env = "gnu"))] - pub fn RtlUnwindEx(TargetFrame: LPVOID, - TargetIp: LPVOID, - ExceptionRecord: *const EXCEPTION_RECORD, - ReturnValue: LPVOID, - OriginalContext: *const CONTEXT, - HistoryTable: *const UNWIND_HISTORY_TABLE); pub fn GetSystemTimeAsFileTime(lpSystemTimeAsFileTime: LPFILETIME); pub fn CreateEventW(lpEventAttributes: LPSECURITY_ATTRIBUTES, diff --git a/src/libstd/sys/windows/ext/fs.rs b/src/libstd/sys/windows/ext/fs.rs index d378a6853f3..4388a0bdff2 100644 --- a/src/libstd/sys/windows/ext/fs.rs +++ b/src/libstd/sys/windows/ext/fs.rs @@ -19,9 +19,7 @@ use sys; use sys_common::{AsInnerMut, AsInner}; /// Windows-specific extensions to `OpenOptions` -#[unstable(feature = "open_options_ext", - reason = "may require more thought/methods", - issue = "27720")] +#[stable(feature = "open_options_ext", since = "1.10.0")] pub trait OpenOptionsExt { /// Overrides the `dwDesiredAccess` argument to the call to `CreateFile` /// with the specified value. @@ -34,7 +32,6 @@ pub trait OpenOptionsExt { /// # Examples /// /// ```no_run - /// #![feature(open_options_ext)] /// use std::fs::OpenOptions; /// use std::os::windows::fs::OpenOptionsExt; /// @@ -42,6 +39,7 @@ pub trait OpenOptionsExt { /// // to call `stat()` on the file /// let file = OpenOptions::new().access_mode(0).open("foo.txt"); /// ``` + #[stable(feature = "open_options_ext", since = "1.10.0")] fn access_mode(&mut self, access: u32) -> &mut Self; /// Overrides the `dwShareMode` argument to the call to `CreateFile` with @@ -55,7 +53,6 @@ pub trait OpenOptionsExt { /// # Examples /// /// ```no_run - /// #![feature(open_options_ext)] /// use std::fs::OpenOptions; /// use std::os::windows::fs::OpenOptionsExt; /// @@ -65,6 +62,7 @@ pub trait OpenOptionsExt { /// .share_mode(0) /// .open("foo.txt"); /// ``` + #[stable(feature = "open_options_ext", since = "1.10.0")] fn share_mode(&mut self, val: u32) -> &mut Self; /// Sets extra flags for the `dwFileFlags` argument to the call to @@ -88,9 +86,7 @@ pub trait OpenOptionsExt { /// } /// let file = options.open("foo.txt"); /// ``` - #[unstable(feature = "expand_open_options", - reason = "recently added", - issue = "30014")] + #[stable(feature = "open_options_ext", since = "1.10.0")] fn custom_flags(&mut self, flags: u32) -> &mut Self; /// Sets the `dwFileAttributes` argument to the call to `CreateFile2` to @@ -111,7 +107,6 @@ pub trait OpenOptionsExt { /// # Examples /// /// ```rust,ignore - /// #![feature(open_options_ext)] /// extern crate winapi; /// use std::fs::OpenOptions; /// use std::os::windows::fs::OpenOptionsExt; @@ -120,17 +115,17 @@ pub trait OpenOptionsExt { /// .attributes(winapi::FILE_ATTRIBUTE_HIDDEN) /// .open("foo.txt"); /// ``` + #[stable(feature = "open_options_ext", since = "1.10.0")] fn attributes(&mut self, val: u32) -> &mut Self; /// Sets the `dwSecurityQosFlags` argument to the call to `CreateFile2` to /// the specified value (or combines it with `custom_flags` and `attributes` /// to set the `dwFlagsAndAttributes` for `CreateFile`). + #[stable(feature = "open_options_ext", since = "1.10.0")] fn security_qos_flags(&mut self, flags: u32) -> &mut OpenOptions; } -#[unstable(feature = "open_options_ext", - reason = "may require more thought/methods", - issue = "27720")] +#[stable(feature = "open_options_ext", since = "1.10.0")] impl OpenOptionsExt for OpenOptions { fn access_mode(&mut self, access: u32) -> &mut OpenOptions { self.as_inner_mut().access_mode(access); self diff --git a/src/libstd/sys/windows/ext/process.rs b/src/libstd/sys/windows/ext/process.rs index f6ee59eec32..56c6a73d4f8 100644 --- a/src/libstd/sys/windows/ext/process.rs +++ b/src/libstd/sys/windows/ext/process.rs @@ -81,3 +81,18 @@ impl IntoRawHandle for process::ChildStderr { self.into_inner().into_handle().into_raw() as *mut _ } } + +/// Windows-specific extensions to `std::process::ExitStatus` +#[unstable(feature = "exit_status_from", issue = "32713")] +pub trait ExitStatusExt { + /// Creates a new `ExitStatus` from the raw underlying `u32` return value of + /// a process. + fn from_raw(raw: u32) -> Self; +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExitStatusExt for process::ExitStatus { + fn from_raw(raw: u32) -> Self { + process::ExitStatus::from_inner(From::from(raw)) + } +} diff --git a/src/libstd/sys/windows/fs.rs b/src/libstd/sys/windows/fs.rs index 529e42248f6..2683e57256d 100644 --- a/src/libstd/sys/windows/fs.rs +++ b/src/libstd/sys/windows/fs.rs @@ -9,7 +9,6 @@ // except according to those terms. use prelude::v1::*; -use io::prelude::*; use os::windows::prelude::*; use ffi::OsString; @@ -39,7 +38,7 @@ pub struct FileAttr { reparse_tag: c::DWORD, } -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum FileType { Dir, File, SymlinkFile, SymlinkDir, ReparsePoint, MountPoint, } @@ -120,8 +119,8 @@ impl DirEntry { fn new(root: &Arc<PathBuf>, wfd: &c::WIN32_FIND_DATAW) -> Option<DirEntry> { match &wfd.cFileName[0..3] { // check for '.' and '..' - [46, 0, ..] | - [46, 46, 0, ..] => return None, + &[46, 0, ..] | + &[46, 46, 0, ..] => return None, _ => {} } diff --git a/src/libstd/sys/windows/handle.rs b/src/libstd/sys/windows/handle.rs index 74546bb893b..d10abae2865 100644 --- a/src/libstd/sys/windows/handle.rs +++ b/src/libstd/sys/windows/handle.rs @@ -46,10 +46,10 @@ impl Handle { pub fn new_event(manual: bool, init: bool) -> io::Result<Handle> { unsafe { - let event = c::CreateEventW(0 as *mut _, + let event = c::CreateEventW(ptr::null_mut(), manual as c::BOOL, init as c::BOOL, - 0 as *const _); + ptr::null()); if event.is_null() { Err(io::Error::last_os_error()) } else { diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs index 384940e4dc4..12219c1e9d4 100644 --- a/src/libstd/sys/windows/mod.rs +++ b/src/libstd/sys/windows/mod.rs @@ -14,7 +14,6 @@ use prelude::v1::*; use ffi::{OsStr, OsString}; use io::{self, ErrorKind}; -use num::Zero; use os::windows::ffi::{OsStrExt, OsStringExt}; use path::PathBuf; use time::Duration; @@ -68,6 +67,7 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind { match errno as c::DWORD { c::ERROR_ACCESS_DENIED => return ErrorKind::PermissionDenied, c::ERROR_ALREADY_EXISTS => return ErrorKind::AlreadyExists, + c::ERROR_FILE_EXISTS => return ErrorKind::AlreadyExists, c::ERROR_BROKEN_PIPE => return ErrorKind::BrokenPipe, c::ERROR_FILE_NOT_FOUND => return ErrorKind::NotFound, c::ERROR_PATH_NOT_FOUND => return ErrorKind::NotFound, @@ -177,8 +177,22 @@ pub fn truncate_utf16_at_nul<'a>(v: &'a [u16]) -> &'a [u16] { } } -fn cvt<I: PartialEq + Zero>(i: I) -> io::Result<I> { - if i == I::zero() { +trait IsZero { + fn is_zero(&self) -> bool; +} + +macro_rules! impl_is_zero { + ($($t:ident)*) => ($(impl IsZero for $t { + fn is_zero(&self) -> bool { + *self == 0 + } + })*) +} + +impl_is_zero! { i8 i16 i32 i64 isize u8 u16 u32 u64 usize } + +fn cvt<I: IsZero>(i: I) -> io::Result<I> { + if i.is_zero() { Err(io::Error::last_os_error()) } else { Ok(i) diff --git a/src/libstd/sys/windows/mutex.rs b/src/libstd/sys/windows/mutex.rs index b770156582d..8762b34e3da 100644 --- a/src/libstd/sys/windows/mutex.rs +++ b/src/libstd/sys/windows/mutex.rs @@ -64,6 +64,8 @@ impl Mutex { held: UnsafeCell::new(false), } } + #[inline] + pub unsafe fn init(&mut self) {} pub unsafe fn lock(&self) { match kind() { Kind::SRWLock => c::AcquireSRWLockExclusive(raw(self)), diff --git a/src/libstd/sys/windows/net.rs b/src/libstd/sys/windows/net.rs index b05dcf42a33..71e164f012f 100644 --- a/src/libstd/sys/windows/net.rs +++ b/src/libstd/sys/windows/net.rs @@ -17,8 +17,6 @@ use io::{self, Read}; use libc::{c_int, c_void, c_ulong}; use mem; use net::{SocketAddr, Shutdown}; -use num::One; -use ops::Neg; use ptr; use sync::Once; use sys::c; @@ -60,11 +58,26 @@ fn last_error() -> io::Error { io::Error::from_raw_os_error(unsafe { c::WSAGetLastError() }) } +#[doc(hidden)] +pub trait IsMinusOne { + fn is_minus_one(&self) -> bool; +} + +macro_rules! impl_is_minus_one { + ($($t:ident)*) => ($(impl IsMinusOne for $t { + fn is_minus_one(&self) -> bool { + *self == -1 + } + })*) +} + +impl_is_minus_one! { i8 i16 i32 i64 isize } + /// Checks if the signed integer is the Windows constant `SOCKET_ERROR` (-1) /// and if so, returns the last error from the Windows socket interface. This /// function must be called before another call to the socket API is made. -pub fn cvt<T: One + PartialEq + Neg<Output=T>>(t: T) -> io::Result<T> { - if t == -T::one() { +pub fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> { + if t.is_minus_one() { Err(last_error()) } else { Ok(t) @@ -82,7 +95,8 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> { /// Just to provide the same interface as sys/unix/net.rs pub fn cvt_r<T, F>(mut f: F) -> io::Result<T> - where T: One + PartialEq + Neg<Output=T>, F: FnMut() -> T + where T: IsMinusOne, + F: FnMut() -> T { cvt(f()) } diff --git a/src/libstd/sys/windows/os.rs b/src/libstd/sys/windows/os.rs index 32ca32e76cb..0cea7f81e36 100644 --- a/src/libstd/sys/windows/os.rs +++ b/src/libstd/sys/windows/os.rs @@ -278,23 +278,30 @@ pub struct Args { cur: *mut *mut u16, } +unsafe fn os_string_from_ptr(ptr: *mut u16) -> OsString { + let mut len = 0; + while *ptr.offset(len) != 0 { len += 1; } + + // Push it onto the list. + let ptr = ptr as *const u16; + let buf = slice::from_raw_parts(ptr, len as usize); + OsStringExt::from_wide(buf) +} + impl Iterator for Args { type Item = OsString; fn next(&mut self) -> Option<OsString> { - self.range.next().map(|i| unsafe { - let ptr = *self.cur.offset(i); - let mut len = 0; - while *ptr.offset(len) != 0 { len += 1; } - - // Push it onto the list. - let ptr = ptr as *const u16; - let buf = slice::from_raw_parts(ptr, len as usize); - OsStringExt::from_wide(buf) - }) + self.range.next().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } ) } fn size_hint(&self) -> (usize, Option<usize>) { self.range.size_hint() } } +impl DoubleEndedIterator for Args { + fn next_back(&mut self) -> Option<OsString> { + self.range.next_back().map(|i| unsafe { os_string_from_ptr(*self.cur.offset(i)) } ) + } +} + impl ExactSizeIterator for Args { fn len(&self) -> usize { self.range.len() } } diff --git a/src/libstd/sys/windows/pipe.rs b/src/libstd/sys/windows/pipe.rs index 8631a63d653..6e9c67051a6 100644 --- a/src/libstd/sys/windows/pipe.rs +++ b/src/libstd/sys/windows/pipe.rs @@ -12,9 +12,10 @@ use prelude::v1::*; use os::windows::prelude::*; use ffi::OsStr; -use path::Path; use io; use mem; +use path::Path; +use ptr; use rand::{self, Rng}; use slice; use sys::c; @@ -66,7 +67,7 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { 4096, 4096, 0, - 0 as *mut _); + ptr::null_mut()); // We pass the FILE_FLAG_FIRST_PIPE_INSTANCE flag above, and we're // also just doing a best effort at selecting a unique name. If diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index f4957297581..3ca75cf3643 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -24,7 +24,7 @@ use mem; use os::windows::ffi::OsStrExt; use path::Path; use ptr; -use sync::StaticMutex; +use sys::mutex::Mutex; use sys::c; use sys::fs::{OpenOptions, File}; use sys::handle::Handle; @@ -75,6 +75,10 @@ pub struct StdioPipes { pub stderr: Option<AnonPipe>, } +struct DropGuard<'a> { + lock: &'a Mutex, +} + impl Command { pub fn new(program: &OsStr) -> Command { Command { @@ -173,8 +177,8 @@ impl Command { // // For more information, msdn also has an article about this race: // http://support.microsoft.com/kb/315939 - static CREATE_PROCESS_LOCK: StaticMutex = StaticMutex::new(); - let _lock = CREATE_PROCESS_LOCK.lock(); + static CREATE_PROCESS_LOCK: Mutex = Mutex::new(); + let _guard = DropGuard::new(&CREATE_PROCESS_LOCK); let mut pipes = StdioPipes { stdin: None, @@ -224,6 +228,23 @@ impl fmt::Debug for Command { } } +impl<'a> DropGuard<'a> { + fn new(lock: &'a Mutex) -> DropGuard<'a> { + unsafe { + lock.lock(); + DropGuard { lock: lock } + } + } +} + +impl<'a> Drop for DropGuard<'a> { + fn drop(&mut self) { + unsafe { + self.lock.unlock(); + } + } +} + impl Stdio { fn to_handle(&self, stdio_id: c::DWORD, pipe: &mut Option<AnonPipe>) -> io::Result<Handle> { @@ -337,6 +358,12 @@ impl ExitStatus { } } +impl From<c::DWORD> for ExitStatus { + fn from(u: c::DWORD) -> ExitStatus { + ExitStatus(u) + } +} + impl fmt::Display for ExitStatus { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "exit code: {}", self.0) diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index 6b54ec8afca..152b9771086 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -100,18 +100,52 @@ pub struct LocalKey<T: 'static> { /// Declare a new thread local storage key of type `std::thread::LocalKey`. /// +/// # Syntax +/// +/// The macro wraps any number of static declarations and makes them thread local. +/// Each static may be public or private, and attributes are allowed. Example: +/// +/// ``` +/// use std::cell::RefCell; +/// thread_local! { +/// pub static FOO: RefCell<u32> = RefCell::new(1); +/// +/// #[allow(unused)] +/// static BAR: RefCell<f32> = RefCell::new(1.0); +/// } +/// # fn main() {} +/// ``` +/// /// See [LocalKey documentation](thread/struct.LocalKey.html) for more /// information. #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable] macro_rules! thread_local { - (static $name:ident: $t:ty = $init:expr) => ( - static $name: $crate::thread::LocalKey<$t> = + // rule 0: empty (base case for the recursion) + () => {}; + + // rule 1: process multiple declarations where the first one is private + ($(#[$attr:meta])* static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( + thread_local!($(#[$attr])* static $name: $t = $init); // go to rule 2 + thread_local!($($rest)*); + ); + + // rule 2: handle a single private declaration + ($(#[$attr:meta])* static $name:ident: $t:ty = $init:expr) => ( + $(#[$attr])* static $name: $crate::thread::LocalKey<$t> = __thread_local_inner!($t, $init); ); - (pub static $name:ident: $t:ty = $init:expr) => ( - pub static $name: $crate::thread::LocalKey<$t> = + + // rule 3: handle multiple declarations where the first one is public + ($(#[$attr:meta])* pub static $name:ident: $t:ty = $init:expr; $($rest:tt)*) => ( + thread_local!($(#[$attr])* pub static $name: $t = $init); // go to rule 4 + thread_local!($($rest)*); + ); + + // rule 4: handle a single public declaration + ($(#[$attr:meta])* pub static $name:ident: $t:ty = $init:expr) => ( + $(#[$attr])* pub static $name: $crate::thread::LocalKey<$t> = __thread_local_inner!($t, $init); ); } diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index b3549dc1264..e9736fea7b3 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -13,7 +13,8 @@ //! ## The threading model //! //! An executing Rust program consists of a collection of native OS threads, -//! each with their own stack and local state. +//! each with their own stack and local state. Threads can be named, and +//! provide some built-in support for low-level synchronization. //! //! Communication between threads can be done through //! [channels](../../std/sync/mpsc/index.html), Rust's message-passing @@ -37,20 +38,6 @@ //! convenient facilities for automatically waiting for the termination of a //! child thread (i.e., join). //! -//! ## The `Thread` type -//! -//! 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` function. -//! * 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: @@ -99,10 +86,24 @@ //! }); //! ``` //! +//! ## The `Thread` type +//! +//! 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` function, and +//! calling `thread()` on the `JoinHandle`. +//! * By requesting the current thread, using the `thread::current` function. +//! +//! The `thread::current()` function is available even for threads not spawned +//! by the APIs of this module. +//! //! ## Blocking support: park and unpark //! //! Every thread is equipped with some basic low-level blocking support, via the -//! `park` and `unpark` functions. +//! `thread::park()` function and `thread::Thread::unpark()` method. `park()` +//! blocks the current thread, which can then be resumed from another thread by +//! calling the `unpark()` method on the blocked thread's handle. //! //! Conceptually, each `Thread` handle has an associated token, which is //! initially not present: @@ -164,14 +165,15 @@ use prelude::v1::*; use any::Any; use cell::UnsafeCell; +use ffi::{CStr, CString}; use fmt; use io; +use panic; +use panicking; use str; -use ffi::{CStr, CString}; use sync::{Mutex, Condvar, Arc}; use sys::thread as imp; use sys_common::thread_info; -use sys_common::unwind; use sys_common::util; use sys_common::{AsInner, IntoInner}; use time::Duration; @@ -181,25 +183,15 @@ use time::Duration; //////////////////////////////////////////////////////////////////////////////// #[macro_use] mod local; -#[macro_use] mod scoped_tls; #[stable(feature = "rust1", since = "1.0.0")] pub use self::local::{LocalKey, LocalKeyState}; -#[unstable(feature = "scoped_tls", - reason = "scoped TLS has yet to have wide enough use to fully \ - consider stabilizing its interface", - issue = "27715")] -#[allow(deprecated)] -pub use self::scoped_tls::ScopedKey; - #[unstable(feature = "libstd_thread_internals", issue = "0")] #[cfg(target_thread_local)] #[doc(hidden)] pub use self::local::elf::Key as __ElfLocalKeyInner; #[unstable(feature = "libstd_thread_internals", issue = "0")] #[doc(hidden)] pub use self::local::os::Key as __OsLocalKeyInner; -#[unstable(feature = "libstd_thread_internals", issue = "0")] -#[doc(hidden)] pub use self::scoped_tls::__KeyInner as __ScopedKeyInner; //////////////////////////////////////////////////////////////////////////////// // Builder @@ -228,6 +220,21 @@ impl Builder { /// Names the thread-to-be. Currently the name is used for identification /// only in panic messages. + /// + /// # Examples + /// + /// ```rust + /// use std::thread; + /// + /// let builder = thread::Builder::new() + /// .name("foo".into()); + /// + /// let handler = builder.spawn(|| { + /// assert_eq!(thread::current().name(), Some("foo")) + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn name(mut self, name: String) -> Builder { self.name = Some(name); @@ -274,14 +281,8 @@ impl Builder { } unsafe { thread_info::set(imp::guard::current(), their_thread); - let mut output = None; - let try_result = { - let ptr = &mut output; - unwind::try(move || *ptr = Some(f())) - }; - *their_packet.get() = Some(try_result.map(|()| { - output.unwrap() - })); + let try_result = panic::catch_unwind(panic::AssertUnwindSafe(f)); + *their_packet.get() = Some(try_result); } }; @@ -335,10 +336,39 @@ pub fn yield_now() { } /// Determines whether the current thread is unwinding because of panic. +/// +/// # Examples +/// +/// ```rust,should_panic +/// use std::thread; +/// +/// struct SomeStruct; +/// +/// impl Drop for SomeStruct { +/// fn drop(&mut self) { +/// if thread::panicking() { +/// println!("dropped while unwinding"); +/// } else { +/// println!("dropped while not unwinding"); +/// } +/// } +/// } +/// +/// { +/// print!("a: "); +/// let a = SomeStruct; +/// } +/// +/// { +/// print!("b: "); +/// let b = SomeStruct; +/// panic!() +/// } +/// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn panicking() -> bool { - unwind::panicking() + panicking::panicking() } /// Puts the current thread to sleep for the specified amount of time. @@ -364,6 +394,19 @@ pub fn sleep_ms(ms: u32) { /// signal being received or a spurious wakeup. Platforms which do not support /// nanosecond precision for sleeping will have `dur` rounded up to the nearest /// granularity of time they can sleep for. +/// +/// # Examples +/// +/// ```rust,no_run +/// use std::{thread, time}; +/// +/// let ten_millis = time::Duration::from_millis(10); +/// let now = time::Instant::now(); +/// +/// thread::sleep(ten_millis); +/// +/// assert!(now.elapsed() >= ten_millis); +/// ``` #[stable(feature = "thread_sleep", since = "1.4.0")] pub fn sleep(dur: Duration) { imp::Thread::sleep(dur) @@ -408,10 +451,10 @@ pub fn park() { /// the specified duration has been reached (may wake spuriously). /// /// The semantics of this function are equivalent to `park()` except that the -/// thread will be blocked for roughly no longer than *ms*. This method +/// thread will be blocked for roughly no longer than `ms`. 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 *ms* long. +/// amount of time waited to be precisely `ms` long. /// /// See the module doc for more detail. #[stable(feature = "rust1", since = "1.0.0")] @@ -424,10 +467,10 @@ pub fn park_timeout_ms(ms: u32) { /// the specified duration has been reached (may wake spuriously). /// /// The semantics of this function are equivalent to `park()` except that the -/// thread will be blocked for roughly no longer than *dur*. This method +/// 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* long. +/// amount of time waited to be precisely `dur` long. /// /// See the module doc for more detail. /// @@ -467,9 +510,9 @@ pub struct Thread { impl Thread { // Used only internally to construct a thread object without spawning fn new(name: Option<String>) -> Thread { - let cname = name.map(|n| CString::new(n).unwrap_or_else(|_| { - panic!("thread name may not contain interior null bytes") - })); + let cname = name.map(|n| { + CString::new(n).expect("thread name may not contain interior null bytes") + }); Thread { inner: Arc::new(Inner { name: cname, @@ -492,6 +535,37 @@ impl Thread { } /// Gets the thread's name. + /// + /// # Examples + /// + /// Threads by default have no name specified: + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new(); + /// + /// let handler = builder.spawn(|| { + /// assert!(thread::current().name().is_none()); + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` + /// + /// Thread with a specified name: + /// + /// ``` + /// use std::thread; + /// + /// let builder = thread::Builder::new() + /// .name("foo".into()); + /// + /// let handler = builder.spawn(|| { + /// assert_eq!(thread::current().name(), Some("foo")) + /// }).unwrap(); + /// + /// handler.join().unwrap(); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn name(&self) -> Option<&str> { self.cname().map(|s| unsafe { str::from_utf8_unchecked(s.to_bytes()) } ) @@ -564,6 +638,36 @@ impl<T> JoinInner<T> { /// Due to platform restrictions, it is not possible to `Clone` this /// handle: the ability to join a child thread is a uniquely-owned /// permission. +/// +/// This `struct` is created by the [`thread::spawn`] function and the +/// [`thread::Builder::spawn`] method. +/// +/// # Examples +/// +/// Creation from [`thread::spawn`]: +/// +/// ```rust +/// use std::thread; +/// +/// let join_handle: thread::JoinHandle<_> = thread::spawn(|| { +/// // some work here +/// }); +/// ``` +/// +/// Creation from [`thread::Builder::spawn`]: +/// +/// ```rust +/// use std::thread; +/// +/// let builder = thread::Builder::new(); +/// +/// let join_handle: thread::JoinHandle<_> = builder.spawn(|| { +/// // some work here +/// }).unwrap(); +/// ``` +/// +/// [`thread::spawn`]: fn.spawn.html +/// [`thread::Builder::spawn`]: struct.Builder.html#method.spawn #[stable(feature = "rust1", since = "1.0.0")] pub struct JoinHandle<T>(JoinInner<T>); diff --git a/src/libstd/thread/scoped_tls.rs b/src/libstd/thread/scoped_tls.rs deleted file mode 100644 index dea58d016e4..00000000000 --- a/src/libstd/thread/scoped_tls.rs +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright 2014-2015 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. -//! -//! # Examples -//! -//! ``` -//! #![feature(scoped_tls)] -//! -//! scoped_thread_local!(static FOO: u32); -//! -//! // 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); -//! }); -//! }); -//! ``` - -#![unstable(feature = "thread_local_internals", issue = "0")] -#![allow(deprecated)] - -#[doc(hidden)] -pub use self::imp::KeyInner as __KeyInner; - -/// 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. -#[unstable(feature = "scoped_tls", - reason = "scoped TLS has yet to have wide enough use to fully consider \ - stabilizing its interface", - issue = "27715")] -#[rustc_deprecated(since = "1.8.0", - reason = "hasn't proven itself over LocalKey")] -pub struct ScopedKey<T:'static> { inner: fn() -> &'static imp::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. -/// -/// See [ScopedKey documentation](thread/struct.ScopedKey.html) for more -/// information. -#[unstable(feature = "thread_local_internals", - reason = "should not be necessary", - issue = "0")] -#[rustc_deprecated(since = "1.8.0", - reason = "hasn't proven itself over LocalKey")] -#[macro_export] -#[allow_internal_unstable] -macro_rules! scoped_thread_local { - (static $name:ident: $t:ty) => ( - static $name: $crate::thread::ScopedKey<$t> = - __scoped_thread_local_inner!($t); - ); - (pub static $name:ident: $t:ty) => ( - pub static $name: $crate::thread::ScopedKey<$t> = - __scoped_thread_local_inner!($t); - ); -} - -#[doc(hidden)] -#[unstable(feature = "thread_local_internals", - reason = "should not be necessary", - issue = "0")] -#[rustc_deprecated(since = "1.8.0", - reason = "hasn't proven itself over LocalKey")] -#[macro_export] -#[allow_internal_unstable] -macro_rules! __scoped_thread_local_inner { - ($t:ty) => {{ - #[cfg_attr(target_thread_local, thread_local)] - static _KEY: $crate::thread::__ScopedKeyInner<$t> = - $crate::thread::__ScopedKeyInner::new(); - fn _getit() -> &'static $crate::thread::__ScopedKeyInner<$t> { &_KEY } - $crate::thread::ScopedKey::new(_getit) - }} -} - -#[unstable(feature = "scoped_tls", - reason = "scoped TLS has yet to have wide enough use to fully consider \ - stabilizing its interface", - issue = "27715")] -#[rustc_deprecated(since = "1.8.0", - reason = "hasn't proven itself over LocalKey")] -impl<T> ScopedKey<T> { - #[doc(hidden)] - pub const fn new(inner: fn() -> &'static imp::KeyInner<T>) -> ScopedKey<T> { - ScopedKey { inner: inner } - } - - /// Inserts 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. - /// - /// # Examples - /// - /// ``` - /// #![feature(scoped_tls)] - /// - /// scoped_thread_local!(static FOO: u32); - /// - /// 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 imp::KeyInner<T>, - val: *mut T, - } - impl<'a, T> Drop for Reset<'a, T> { - fn drop(&mut self) { - unsafe { self.key.set(self.val) } - } - } - - let inner = (self.inner)(); - let prev = unsafe { - let prev = inner.get(); - inner.set(t as *const T as *mut T); - prev - }; - - let _reset = Reset { key: inner, val: prev }; - cb() - } - - /// Gets 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. - /// - /// # Examples - /// - /// ```no_run - /// #![feature(scoped_tls)] - /// - /// scoped_thread_local!(static FOO: u32); - /// - /// 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(target_thread_local)] -#[doc(hidden)] -mod imp { - use cell::Cell; - use ptr; - - pub struct KeyInner<T> { inner: Cell<*mut T> } - - unsafe impl<T> ::marker::Sync for KeyInner<T> { } - - impl<T> KeyInner<T> { - pub const fn new() -> KeyInner<T> { - KeyInner { inner: Cell::new(ptr::null_mut()) } - } - pub unsafe fn set(&self, ptr: *mut T) { self.inner.set(ptr); } - pub unsafe fn get(&self) -> *mut T { self.inner.get() } - } -} - -#[cfg(not(target_thread_local))] -#[doc(hidden)] -mod imp { - use cell::Cell; - use marker; - use sys_common::thread_local::StaticKey as OsStaticKey; - - pub struct KeyInner<T> { - pub inner: OsStaticKey, - pub marker: marker::PhantomData<Cell<T>>, - } - - unsafe impl<T> marker::Sync for KeyInner<T> { } - - impl<T> KeyInner<T> { - pub const fn new() -> KeyInner<T> { - KeyInner { - inner: OsStaticKey::new(None), - marker: marker::PhantomData - } - } - pub unsafe fn set(&self, ptr: *mut T) { self.inner.set(ptr as *mut _) } - pub unsafe fn get(&self) -> *mut T { self.inner.get() as *mut _ } - } -} - - -#[cfg(test)] -mod tests { - use cell::Cell; - - scoped_thread_local!(static FOO: u32); - - #[test] - fn smoke() { - scoped_thread_local!(static BAR: u32); - - 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<u32>); - - BAR.set(&Cell::new(1), || { - BAR.with(|slot| { - assert_eq!(slot.get(), 1); - }); - }); - } - - #[test] - fn scope_item_allowed() { - assert!(!FOO.is_set()); - FOO.set(&1, || { - assert!(FOO.is_set()); - FOO.with(|slot| { - assert_eq!(*slot, 1); - }); - }); - assert!(!FOO.is_set()); - } -} diff --git a/src/libstd/time/duration.rs b/src/libstd/time/duration.rs index 8a50f07e6d8..79bbe5e7daa 100644 --- a/src/libstd/time/duration.rs +++ b/src/libstd/time/duration.rs @@ -51,10 +51,16 @@ impl Duration { /// /// If the nanoseconds is greater than 1 billion (the number of nanoseconds /// in a second), then it will carry over into the seconds provided. + /// + /// # Panics + /// + /// This constructor will panic if the carry from the nanoseconds overflows + /// the seconds counter. #[stable(feature = "duration", since = "1.3.0")] #[inline] pub fn new(secs: u64, nanos: u32) -> Duration { - let secs = secs + (nanos / NANOS_PER_SEC) as u64; + let secs = secs.checked_add((nanos / NANOS_PER_SEC) as u64) + .expect("overflow in Duration::new"); let nanos = nanos % NANOS_PER_SEC; Duration { secs: secs, nanos: nanos } } diff --git a/src/libstd/time/mod.rs b/src/libstd/time/mod.rs index bc50b0d3a70..0e1508a1c4c 100644 --- a/src/libstd/time/mod.rs +++ b/src/libstd/time/mod.rs @@ -76,7 +76,7 @@ pub struct Instant(time::Instant); /// Distinct from the `Instant` type, this time measurement **is not /// monotonic**. This means that you can save a file to the file system, then /// save another file to the file system, **and the second file has a -/// `SystemTime` measurement earlier than the second**. In other words, an +/// `SystemTime` measurement earlier than the first**. In other words, an /// operation that happens after another operation in real time may have an /// earlier `SystemTime`! /// @@ -143,13 +143,6 @@ impl Instant { self.0.sub_instant(&earlier.0) } - /// Deprecated, renamed to `duration_since` - #[unstable(feature = "time2_old", issue = "29866")] - #[rustc_deprecated(since = "1.8.0", reason = "renamed to duration_since")] - pub fn duration_from_earlier(&self, earlier: Instant) -> Duration { - self.0.sub_instant(&earlier.0) - } - /// Returns the amount of time elapsed since this instant was created. /// /// # Panics @@ -235,14 +228,6 @@ impl SystemTime { self.0.sub_time(&earlier.0).map_err(SystemTimeError) } - /// Deprecated, renamed to `duration_since` - #[unstable(feature = "time2_old", issue = "29866")] - #[rustc_deprecated(since = "1.8.0", reason = "renamed to duration_since")] - pub fn duration_from_earlier(&self, earlier: SystemTime) - -> Result<Duration, SystemTimeError> { - self.0.sub_time(&earlier.0).map_err(SystemTimeError) - } - /// Returns the amount of time elapsed since this system time was created. /// /// This function may fail as the underlying system clock is susceptible to |
