diff options
Diffstat (limited to 'src/libstd')
31 files changed, 1933 insertions, 329 deletions
diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index 21e6acc37f3..fcf84cb7169 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -13,14 +13,14 @@ crate-type = ["dylib", "rlib"] alloc = { path = "../liballoc" } alloc_jemalloc = { path = "../liballoc_jemalloc", optional = true } alloc_system = { path = "../liballoc_system" } -panic_unwind = { path = "../libpanic_unwind" } +panic_unwind = { path = "../libpanic_unwind", optional = true } panic_abort = { path = "../libpanic_abort" } collections = { path = "../libcollections" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } rand = { path = "../librand" } compiler_builtins = { path = "../libcompiler_builtins" } -rustc_unicode = { path = "../librustc_unicode" } +std_unicode = { path = "../libstd_unicode" } unwind = { path = "../libunwind" } [build-dependencies] @@ -29,5 +29,6 @@ gcc = "0.3.27" [features] backtrace = [] -jemalloc = ["alloc_jemalloc"] debug-jemalloc = ["alloc_jemalloc/debug"] +jemalloc = ["alloc_jemalloc"] +panic-unwind = ["panic_unwind"] diff --git a/src/libstd/build.rs b/src/libstd/build.rs index 72cd6e4830b..1087d1f2447 100644 --- a/src/libstd/build.rs +++ b/src/libstd/build.rs @@ -60,6 +60,8 @@ fn main() { println!("cargo:rustc-link-lib=shell32"); } else if target.contains("fuchsia") { println!("cargo:rustc-link-lib=magenta"); + println!("cargo:rustc-link-lib=mxio"); + println!("cargo:rustc-link-lib=launchpad"); // for std::process } } diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index ece51d6d826..0b310eb2585 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -371,9 +371,9 @@ fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) -> Inter return InternalEntry::TableIsEmpty; } - let size = table.size() as isize; + let size = table.size(); let mut probe = Bucket::new(table, hash); - let ib = probe.index() as isize; + let mut displacement = 0; loop { let full = match probe.peek() { @@ -387,15 +387,15 @@ fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) -> Inter Full(bucket) => bucket, }; - let robin_ib = full.index() as isize - full.displacement() as isize; + let probe_displacement = full.displacement(); - if ib < robin_ib { + if probe_displacement < displacement { // Found a luckier bucket than me. // We can finish the search early if we hit any bucket // with a lower distance to initial bucket than we've probed. return InternalEntry::Vacant { hash: hash, - elem: NeqElem(full, robin_ib as usize), + elem: NeqElem(full, probe_displacement), }; } @@ -406,9 +406,9 @@ fn search_hashed<K, V, M, F>(table: M, hash: SafeHash, mut is_match: F) -> Inter return InternalEntry::Occupied { elem: full }; } } - + displacement += 1; probe = full.next(); - debug_assert!(probe.index() as isize != ib + size + 1); + debug_assert!(displacement <= size); } } @@ -431,12 +431,11 @@ fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>) -> (K, V) { } /// Perform robin hood bucket stealing at the given `bucket`. You must -/// also pass the position of that bucket's initial bucket so we don't have -/// to recalculate it. +/// also pass that bucket's displacement so we don't have to recalculate it. /// /// `hash`, `k`, and `v` are the elements to "robin hood" into the hashtable. fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>, - mut ib: usize, + mut displacement: usize, mut hash: SafeHash, mut key: K, mut val: V) @@ -457,6 +456,7 @@ fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>, val = old_val; loop { + displacement += 1; let probe = bucket.next(); debug_assert!(probe.index() != idx_end); @@ -476,13 +476,13 @@ fn robin_hood<'a, K: 'a, V: 'a>(bucket: FullBucketMut<'a, K, V>, Full(bucket) => bucket, }; - let probe_ib = full_bucket.index() - full_bucket.displacement(); + let probe_displacement = full_bucket.displacement(); bucket = full_bucket; // Robin hood! Steal the spot. - if ib < probe_ib { - ib = probe_ib; + if probe_displacement < displacement { + displacement = probe_displacement; break; } } @@ -520,13 +520,16 @@ impl<K, V, S> HashMap<K, V, S> search_hashed(&mut self.table, hash, |k| q.eq(k.borrow())) } - // The caller should ensure that invariants by Robin Hood Hashing hold. + // The caller should ensure that invariants by Robin Hood Hashing hold + // and that there's space in the underlying table. fn insert_hashed_ordered(&mut self, hash: SafeHash, k: K, v: V) { let raw_cap = self.raw_capacity(); let mut buckets = Bucket::new(&mut self.table, hash); - let ib = buckets.index(); + // note that buckets.index() keeps increasing + // even if the pointer wraps back to the first bucket. + let limit_bucket = buckets.index() + raw_cap; - while buckets.index() != ib + raw_cap { + loop { // We don't need to compare hashes for value swap. // Not even DIBs for Robin Hood. buckets = match buckets.peek() { @@ -537,8 +540,8 @@ impl<K, V, S> HashMap<K, V, S> Full(b) => b.into_bucket(), }; buckets.next(); + debug_assert!(buckets.index() < limit_bucket); } - panic!("Internal HashMap error: Out of space."); } } @@ -1959,7 +1962,7 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { #[stable(feature = "rust1", since = "1.0.0")] pub fn insert(self, value: V) -> &'a mut V { match self.elem { - NeqElem(bucket, ib) => robin_hood(bucket, ib, self.hash, self.key, value), + NeqElem(bucket, disp) => robin_hood(bucket, disp, self.hash, self.key, value), NoElem(bucket) => bucket.put(self.hash, self.key, value).into_mut_refs().1, } } @@ -1971,10 +1974,8 @@ impl<K, V, S> FromIterator<(K, V)> for HashMap<K, V, S> S: BuildHasher + Default { fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> HashMap<K, V, S> { - let iterator = iter.into_iter(); - let lower = iterator.size_hint().0; - let mut map = HashMap::with_capacity_and_hasher(lower, Default::default()); - map.extend(iterator); + let mut map = HashMap::with_hasher(Default::default()); + map.extend(iter); map } } @@ -1985,6 +1986,17 @@ impl<K, V, S> Extend<(K, V)> for HashMap<K, V, S> S: BuildHasher { fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) { + // Keys may be already present or show multiple times in the iterator. + // Reserve the entire hint lower bound if the map is empty. + // Otherwise reserve half the hint (rounded up), so the map + // will only resize twice in the worst case. + let iter = iter.into_iter(); + let reserve = if self.is_empty() { + iter.size_hint().0 + } else { + (iter.size_hint().0 + 1) / 2 + }; + self.reserve(reserve); for (k, v) in iter { self.insert(k, v); } @@ -2105,6 +2117,10 @@ impl DefaultHasher { #[stable(feature = "hashmap_default_hasher", since = "1.13.0")] impl Default for DefaultHasher { + /// Creates a new `DefaultHasher` using [`DefaultHasher::new`]. See + /// [`DefaultHasher::new`] documentation for more information. + /// + /// [`DefaultHasher::new`]: #method.new fn default() -> DefaultHasher { DefaultHasher::new() } diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs index 1ec7a4a7b63..72af612f569 100644 --- a/src/libstd/collections/hash/set.rs +++ b/src/libstd/collections/hash/set.rs @@ -663,10 +663,8 @@ impl<T, S> FromIterator<T> for HashSet<T, S> S: BuildHasher + Default { fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> HashSet<T, S> { - let iterator = iter.into_iter(); - let lower = iterator.size_hint().0; - let mut set = HashSet::with_capacity_and_hasher(lower, Default::default()); - set.extend(iterator); + let mut set = HashSet::with_hasher(Default::default()); + set.extend(iter); set } } @@ -677,9 +675,7 @@ impl<T, S> Extend<T> for HashSet<T, S> S: BuildHasher { fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) { - for k in iter { - self.insert(k); - } + self.map.extend(iter.into_iter().map(|k| (k, ()))); } } diff --git a/src/libstd/env.rs b/src/libstd/env.rs index e29dbe35c5a..ee6a907f616 100644 --- a/src/libstd/env.rs +++ b/src/libstd/env.rs @@ -546,17 +546,23 @@ pub fn current_exe() -> io::Result<PathBuf> { os_imp::current_exe() } -/// An iterator over the arguments of a process, yielding a `String` value +/// An iterator over the arguments of a process, yielding a [`String`] value /// for each argument. /// -/// This structure is created through the `std::env::args` method. +/// This structure is created through the [`std::env::args`] method. +/// +/// [`String`]: ../string/struct.String.html +/// [`std::env::args`]: ./fn.args.html #[stable(feature = "env", since = "1.0.0")] pub struct Args { inner: ArgsOs } -/// An iterator over the arguments of a process, yielding an `OsString` value +/// An iterator over the arguments of a process, yielding an [`OsString`] value /// for each argument. /// -/// This structure is created through the `std::env::args_os` method. +/// This structure is created through the [`std::env::args_os`] method. +/// +/// [`OsString`]: ../ffi/struct.OsString.html +/// [`std::env::args_os`]: ./fn.args_os.html #[stable(feature = "env", since = "1.0.0")] pub struct ArgsOs { inner: sys::args::Args } @@ -571,7 +577,7 @@ pub struct ArgsOs { inner: sys::args::Args } /// /// The returned iterator will panic during iteration if any argument to the /// process is not valid unicode. If this is not desired, -/// use the `args_os` function instead. +/// use the [`args_os`] function instead. /// /// # Examples /// @@ -583,6 +589,8 @@ pub struct ArgsOs { inner: sys::args::Args } /// println!("{}", argument); /// } /// ``` +/// +/// [`args_os`]: ./fn.args_os.html #[stable(feature = "env", since = "1.0.0")] pub fn args() -> Args { Args { inner: args_os() } @@ -622,6 +630,7 @@ impl Iterator for Args { #[stable(feature = "env", since = "1.0.0")] impl ExactSizeIterator for Args { fn len(&self) -> usize { self.inner.len() } + fn is_empty(&self) -> bool { self.inner.is_empty() } } #[stable(feature = "env_iterators", since = "1.11.0")] @@ -641,6 +650,7 @@ impl Iterator for ArgsOs { #[stable(feature = "env", since = "1.0.0")] impl ExactSizeIterator for ArgsOs { fn len(&self) -> usize { self.inner.len() } + fn is_empty(&self) -> bool { self.inner.is_empty() } } #[stable(feature = "env_iterators", since = "1.11.0")] diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 454fa47cfbc..e115263d2eb 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -109,7 +109,7 @@ pub trait Error: Debug + Display { /// /// impl Error for SuperError { /// fn description(&self) -> &str { - /// "I'm the superhero of errors!" + /// "I'm the superhero of errors" /// } /// /// fn cause(&self) -> Option<&Error> { @@ -128,7 +128,7 @@ pub trait Error: Debug + Display { /// /// impl Error for SuperErrorSideKick { /// fn description(&self) -> &str { - /// "I'm SuperError side kick!" + /// "I'm SuperError side kick" /// } /// } /// diff --git a/src/libstd/io/impls.rs b/src/libstd/io/impls.rs index 6b26c016638..f691289811b 100644 --- a/src/libstd/io/impls.rs +++ b/src/libstd/io/impls.rs @@ -157,7 +157,16 @@ impl<'a> Read for &'a [u8] { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { let amt = cmp::min(buf.len(), self.len()); let (a, b) = self.split_at(amt); - buf[..amt].copy_from_slice(a); + + // First check if the amount of bytes we want to read is small: + // `copy_from_slice` will generally expand to a call to `memcpy`, and + // for a single byte the overhead is significant. + if amt == 1 { + buf[0] = a[0]; + } else { + buf[..amt].copy_from_slice(a); + } + *self = b; Ok(amt) } @@ -169,7 +178,16 @@ impl<'a> Read for &'a [u8] { "failed to fill whole buffer")); } let (a, b) = self.split_at(buf.len()); - buf.copy_from_slice(a); + + // First check if the amount of bytes we want to read is small: + // `copy_from_slice` will generally expand to a call to `memcpy`, and + // for a single byte the overhead is significant. + if buf.len() == 1 { + buf[0] = a[0]; + } else { + buf.copy_from_slice(a); + } + *self = b; Ok(()) } diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index ad9ae5638b6..b3b89213df1 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -256,7 +256,7 @@ #![stable(feature = "rust1", since = "1.0.0")] use cmp; -use rustc_unicode::str as core_str; +use std_unicode::str as core_str; use error as std_error; use fmt; use result; diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 12dbbe3c469..414f25fa5eb 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -249,8 +249,8 @@ #![feature(const_fn)] #![feature(core_float)] #![feature(core_intrinsics)] -#![cfg_attr(stage0, feature(dotdot_in_tuple_patterns))] #![feature(dropck_parametricity)] +#![feature(exact_size_is_empty)] #![feature(float_extras)] #![feature(float_from_str_radix)] #![feature(fn_traits)] @@ -276,7 +276,6 @@ #![feature(panic_unwind)] #![feature(placement_in_syntax)] #![feature(prelude_import)] -#![cfg_attr(stage0, feature(question_mark))] #![feature(rand)] #![feature(raw)] #![feature(repr_simd)] @@ -324,7 +323,7 @@ extern crate collections as core_collections; #[allow(deprecated)] extern crate rand as core_rand; extern crate alloc; -extern crate rustc_unicode; +extern crate std_unicode; extern crate libc; // We always need an unwinder currently for backtraces @@ -421,7 +420,7 @@ pub use core_collections::string; #[stable(feature = "rust1", since = "1.0.0")] pub use core_collections::vec; #[stable(feature = "rust1", since = "1.0.0")] -pub use rustc_unicode::char; +pub use std_unicode::char; pub mod f32; pub mod f64; diff --git a/src/libstd/net/addr.rs b/src/libstd/net/addr.rs index 1c016015b79..1ce37f6cc0a 100644 --- a/src/libstd/net/addr.rs +++ b/src/libstd/net/addr.rs @@ -194,6 +194,14 @@ impl SocketAddr { impl SocketAddrV4 { /// Creates a new socket address from the (ip, port) pair. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV4, Ipv4Addr}; + /// + /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn new(ip: Ipv4Addr, port: u16) -> SocketAddrV4 { SocketAddrV4 { @@ -207,6 +215,15 @@ impl SocketAddrV4 { } /// Returns the IP address associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV4, Ipv4Addr}; + /// + /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); + /// assert_eq!(socket.ip(), &Ipv4Addr::new(127, 0, 0, 1)); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn ip(&self) -> &Ipv4Addr { unsafe { @@ -215,18 +232,47 @@ impl SocketAddrV4 { } /// Change the IP address associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV4, Ipv4Addr}; + /// + /// let mut socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); + /// socket.set_ip(Ipv4Addr::new(192, 168, 0, 1)); + /// assert_eq!(socket.ip(), &Ipv4Addr::new(192, 168, 0, 1)); + /// ``` #[stable(feature = "sockaddr_setters", since = "1.9.0")] pub fn set_ip(&mut self, new_ip: Ipv4Addr) { self.inner.sin_addr = *new_ip.as_inner() } /// Returns the port number associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV4, Ipv4Addr}; + /// + /// let socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); + /// assert_eq!(socket.port(), 8080); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn port(&self) -> u16 { ntoh(self.inner.sin_port) } /// Change the port number associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV4, Ipv4Addr}; + /// + /// let mut socket = SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080); + /// socket.set_port(4242); + /// assert_eq!(socket.port(), 4242); + /// ``` #[stable(feature = "sockaddr_setters", since = "1.9.0")] pub fn set_port(&mut self, new_port: u16) { self.inner.sin_port = hton(new_port); @@ -236,6 +282,14 @@ impl SocketAddrV4 { impl SocketAddrV6 { /// Creates a new socket address from the ip/port/flowinfo/scope_id /// components. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn new(ip: Ipv6Addr, port: u16, flowinfo: u32, scope_id: u32) -> SocketAddrV6 { @@ -252,6 +306,15 @@ impl SocketAddrV6 { } /// Returns the IP address associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); + /// assert_eq!(socket.ip(), &Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn ip(&self) -> &Ipv6Addr { unsafe { @@ -260,18 +323,47 @@ impl SocketAddrV6 { } /// Change the IP address associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); + /// socket.set_ip(Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0)); + /// assert_eq!(socket.ip(), &Ipv6Addr::new(76, 45, 0, 0, 0, 0, 0, 0)); + /// ``` #[stable(feature = "sockaddr_setters", since = "1.9.0")] pub fn set_ip(&mut self, new_ip: Ipv6Addr) { self.inner.sin6_addr = *new_ip.as_inner() } /// Returns the port number associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); + /// assert_eq!(socket.port(), 8080); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn port(&self) -> u16 { ntoh(self.inner.sin6_port) } /// Change the port number associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 0); + /// socket.set_port(4242); + /// assert_eq!(socket.port(), 4242); + /// ``` #[stable(feature = "sockaddr_setters", since = "1.9.0")] pub fn set_port(&mut self, new_port: u16) { self.inner.sin6_port = hton(new_port); @@ -279,12 +371,31 @@ impl SocketAddrV6 { /// Returns the flow information associated with this address, /// corresponding to the `sin6_flowinfo` field in C. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 10, 0); + /// assert_eq!(socket.flowinfo(), 10); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn flowinfo(&self) -> u32 { self.inner.sin6_flowinfo } /// Change the flow information associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 10, 0); + /// socket.set_flowinfo(56); + /// assert_eq!(socket.flowinfo(), 56); + /// ``` #[stable(feature = "sockaddr_setters", since = "1.9.0")] pub fn set_flowinfo(&mut self, new_flowinfo: u32) { self.inner.sin6_flowinfo = new_flowinfo; @@ -292,12 +403,31 @@ impl SocketAddrV6 { /// Returns the scope ID associated with this address, /// corresponding to the `sin6_scope_id` field in C. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 78); + /// assert_eq!(socket.scope_id(), 78); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn scope_id(&self) -> u32 { self.inner.sin6_scope_id } /// Change the scope ID associated with this socket address. + /// + /// # Examples + /// + /// ``` + /// use std::net::{SocketAddrV6, Ipv6Addr}; + /// + /// let mut socket = SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 8080, 0, 78); + /// socket.set_scope_id(42); + /// assert_eq!(socket.scope_id(), 42); + /// ``` #[stable(feature = "sockaddr_setters", since = "1.9.0")] pub fn set_scope_id(&mut self, new_scope_id: u32) { self.inner.sin6_scope_id = new_scope_id; diff --git a/src/libstd/net/ip.rs b/src/libstd/net/ip.rs index 7b7be6e2eee..c1e610f33fb 100644 --- a/src/libstd/net/ip.rs +++ b/src/libstd/net/ip.rs @@ -79,8 +79,18 @@ pub enum Ipv6MulticastScope { 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 + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)).is_unspecified(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)).is_unspecified(), true); + /// ``` #[stable(feature = "ip_shared", since = "1.12.0")] pub fn is_unspecified(&self) -> bool { match *self { @@ -90,8 +100,18 @@ impl IpAddr { } /// 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 + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)).is_loopback(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1)).is_loopback(), true); + /// ``` #[stable(feature = "ip_shared", since = "1.12.0")] pub fn is_loopback(&self) -> bool { match *self { @@ -101,8 +121,23 @@ impl IpAddr { } /// 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 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// fn main() { + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(80, 9, 12, 3)).is_global(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1)).is_global(), + /// true); + /// } + /// ``` pub fn is_global(&self) -> bool { match *self { IpAddr::V4(ref a) => a.is_global(), @@ -111,8 +146,18 @@ impl IpAddr { } /// 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 + /// + /// # Examples + /// + /// ``` + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(224, 254, 0, 0)).is_multicast(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0)).is_multicast(), true); + /// ``` #[stable(feature = "ip_shared", since = "1.12.0")] pub fn is_multicast(&self) -> bool { match *self { @@ -122,8 +167,23 @@ impl IpAddr { } /// 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 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// fn main() { + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_documentation(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)) + /// .is_documentation(), true); + /// } + /// ``` pub fn is_documentation(&self) -> bool { match *self { IpAddr::V4(ref a) => a.is_documentation(), @@ -132,6 +192,20 @@ impl IpAddr { } /// Returns true if this address is a valid IPv4 address, false if it's a valid IPv6 address. + /// + /// # Examples + /// + /// ``` + /// #![feature(ipaddr_checker)] + /// + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// fn main() { + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv4(), true); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv4(), + /// false); + /// } + /// ``` #[unstable(feature = "ipaddr_checker", issue = "36949")] pub fn is_ipv4(&self) -> bool { match *self { @@ -141,6 +215,20 @@ impl IpAddr { } /// Returns true if this address is a valid IPv6 address, false if it's a valid IPv4 address. + /// + /// # Examples + /// + /// ``` + /// #![feature(ipaddr_checker)] + /// + /// use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + /// + /// fn main() { + /// assert_eq!(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)).is_ipv6(), false); + /// assert_eq!(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0)).is_ipv6(), + /// true); + /// } + /// ``` #[unstable(feature = "ipaddr_checker", issue = "36949")] pub fn is_ipv6(&self) -> bool { match *self { @@ -522,6 +610,14 @@ impl Ipv6Addr { /// Creates a new IPv6 address from eight 16-bit segments. /// /// The result will represent the IP address a:b:c:d:e:f:g:h. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// let addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr { @@ -538,6 +634,15 @@ impl Ipv6Addr { } /// Returns the eight 16-bit segments that make up this address. + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).segments(), + /// [0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff]); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn segments(&self) -> [u16; 8] { let arr = &self.inner.s6_addr; @@ -558,6 +663,15 @@ impl Ipv6Addr { /// This property is defined in [RFC 4291]. /// /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unspecified(), false); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0).is_unspecified(), true); + /// ``` #[stable(since = "1.7.0", feature = "ip_17")] pub fn is_unspecified(&self) -> bool { self.segments() == [0, 0, 0, 0, 0, 0, 0, 0] @@ -568,6 +682,15 @@ impl Ipv6Addr { /// This property is defined in [RFC 4291]. /// /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_loopback(), false); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_loopback(), true); + /// ``` #[stable(since = "1.7.0", feature = "ip_17")] pub fn is_loopback(&self) -> bool { self.segments() == [0, 0, 0, 0, 0, 0, 0, 1] @@ -580,6 +703,20 @@ impl Ipv6Addr { /// - the loopback address /// - link-local, site-local, and unique local unicast addresses /// - interface-, link-, realm-, admin- and site-local multicast addresses + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// fn main() { + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_global(), true); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0x1).is_global(), false); + /// assert_eq!(Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1).is_global(), true); + /// } + /// ``` pub fn is_global(&self) -> bool { match self.multicast_scope() { Some(Ipv6MulticastScope::Global) => true, @@ -593,6 +730,20 @@ impl Ipv6Addr { /// This property is defined in [RFC 4193]. /// /// [RFC 4193]: https://tools.ietf.org/html/rfc4193 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// fn main() { + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unique_local(), + /// false); + /// assert_eq!(Ipv6Addr::new(0xfc02, 0, 0, 0, 0, 0, 0, 0).is_unique_local(), true); + /// } + /// ``` pub fn is_unique_local(&self) -> bool { (self.segments()[0] & 0xfe00) == 0xfc00 } @@ -602,12 +753,40 @@ impl Ipv6Addr { /// This property is defined in [RFC 4291]. /// /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// fn main() { + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unicast_link_local(), + /// false); + /// assert_eq!(Ipv6Addr::new(0xfe8a, 0, 0, 0, 0, 0, 0, 0).is_unicast_link_local(), true); + /// } + /// ``` pub fn is_unicast_link_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfe80 } /// Returns true if this is a deprecated unicast site-local address /// (fec0::/10). + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// fn main() { + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unicast_site_local(), + /// false); + /// assert_eq!(Ipv6Addr::new(0xfec2, 0, 0, 0, 0, 0, 0, 0).is_unicast_site_local(), true); + /// } + /// ``` pub fn is_unicast_site_local(&self) -> bool { (self.segments()[0] & 0xffc0) == 0xfec0 } @@ -618,6 +797,20 @@ impl Ipv6Addr { /// This property is defined in [RFC 3849]. /// /// [RFC 3849]: https://tools.ietf.org/html/rfc3849 + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// fn main() { + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_documentation(), + /// false); + /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_documentation(), true); + /// } + /// ``` pub fn is_documentation(&self) -> bool { (self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8) } @@ -632,6 +825,20 @@ impl Ipv6Addr { /// - unique local addresses /// - the unspecified address /// - the address range reserved for documentation + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::Ipv6Addr; + /// + /// fn main() { + /// assert_eq!(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0).is_unicast_global(), false); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_unicast_global(), + /// true); + /// } + /// ``` pub fn is_unicast_global(&self) -> bool { !self.is_multicast() && !self.is_loopback() && !self.is_unicast_link_local() @@ -640,6 +847,20 @@ impl Ipv6Addr { } /// Returns the address's multicast scope if the address is multicast. + /// + /// # Examples + /// + /// ``` + /// #![feature(ip)] + /// + /// use std::net::{Ipv6Addr, Ipv6MulticastScope}; + /// + /// fn main() { + /// assert_eq!(Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0).multicast_scope(), + /// Some(Ipv6MulticastScope::Global)); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).multicast_scope(), None); + /// } + /// ``` pub fn multicast_scope(&self) -> Option<Ipv6MulticastScope> { if self.is_multicast() { match self.segments()[0] & 0x000f { @@ -662,6 +883,14 @@ impl Ipv6Addr { /// This property is defined by [RFC 4291]. /// /// [RFC 4291]: https://tools.ietf.org/html/rfc4291 + /// # Examples + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).is_multicast(), true); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).is_multicast(), false); + /// ``` #[stable(since = "1.7.0", feature = "ip_17")] pub fn is_multicast(&self) -> bool { (self.segments()[0] & 0xff00) == 0xff00 @@ -671,6 +900,16 @@ impl Ipv6Addr { /// neither IPv4-compatible or IPv4-mapped. /// /// ::a.b.c.d and ::ffff:a.b.c.d become a.b.c.d + /// + /// ``` + /// use std::net::{Ipv4Addr, Ipv6Addr}; + /// + /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).to_ipv4(), None); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff).to_ipv4(), + /// Some(Ipv4Addr::new(192, 10, 2, 255))); + /// assert_eq!(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).to_ipv4(), + /// Some(Ipv4Addr::new(0, 0, 0, 1))); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn to_ipv4(&self) -> Option<Ipv4Addr> { match self.segments() { @@ -683,6 +922,13 @@ impl Ipv6Addr { } /// Returns the sixteen eight-bit integers the IPv6 address consists of. + /// + /// ``` + /// use std::net::Ipv6Addr; + /// + /// assert_eq!(Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0).octets(), + /// [255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); + /// ``` #[stable(feature = "ipv6_to_octets", since = "1.12.0")] pub fn octets(&self) -> [u8; 16] { self.inner.s6_addr diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs index 159aa997b27..be9636a0a19 100644 --- a/src/libstd/net/tcp.rs +++ b/src/libstd/net/tcp.rs @@ -86,18 +86,52 @@ impl TcpStream { /// documentation for concrete examples. /// In case `ToSocketAddrs::to_socket_addrs()` returns more than one entry, /// then the first valid and reachable address is used. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// if let Ok(stream) = TcpStream::connect("127.0.0.1:8080") { + /// println!("Connected to the server!"); + /// } else { + /// println!("Couldn't connect to server..."); + /// } + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> { super::each_addr(addr, net_imp::TcpStream::connect).map(TcpStream) } /// Returns the socket address of the remote peer of this TCP connection. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpStream}; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// assert_eq!(stream.peer_addr().unwrap(), + /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080))); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn peer_addr(&self) -> io::Result<SocketAddr> { self.0.peer_addr() } /// Returns the socket address of the local half of this TCP connection. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpStream}; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// assert_eq!(stream.local_addr().unwrap(), + /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080))); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn local_addr(&self) -> io::Result<SocketAddr> { self.0.socket_addr() @@ -107,7 +141,19 @@ impl TcpStream { /// /// This function will cause all pending and future I/O on the specified /// portions to return immediately with an appropriate value (see the - /// documentation of `Shutdown`). + /// documentation of [`Shutdown`]). + /// + /// [`Shutdown`]: ../../std/net/enum.Shutdown.html + /// + /// # Examples + /// + /// ```no_run + /// use std::net::{Shutdown, TcpStream}; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.shutdown(Shutdown::Both).expect("shutdown call failed"); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { self.0.shutdown(how) @@ -119,6 +165,16 @@ impl TcpStream { /// object references. Both handles will read and write the same stream of /// data, and options set on one stream will be propagated to the other /// stream. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// let stream_clone = stream.try_clone().expect("clone failed..."); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn try_clone(&self) -> io::Result<TcpStream> { self.0.duplicate().map(TcpStream) @@ -126,7 +182,7 @@ impl TcpStream { /// Sets the read timeout to the timeout specified. /// - /// If the value specified is `None`, then `read` calls will block + /// If the value specified is [`None`], then [`read()`] calls will block /// indefinitely. It is an error to pass the zero `Duration` to this /// method. /// @@ -134,7 +190,22 @@ impl TcpStream { /// /// Platforms may return a different error code whenever a read times out as /// a result of setting this option. For example Unix typically returns an - /// error of the kind `WouldBlock`, but Windows may return `TimedOut`. + /// error of the kind [`WouldBlock`], but Windows may return [`TimedOut`]. + /// + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// [`read()`]: ../../std/io/trait.Read.html#tymethod.read + /// [`WouldBlock`]: ../../std/io/enum.ErrorKind.html#variant.WouldBlock + /// [`TimedOut`]: ../../std/io/enum.ErrorKind.html#variant.TimedOut + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_read_timeout(None).expect("set_read_timeout call failed"); + /// ``` #[stable(feature = "socket_timeout", since = "1.4.0")] pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> { self.0.set_read_timeout(dur) @@ -142,15 +213,31 @@ impl TcpStream { /// Sets the write timeout to the timeout specified. /// - /// If the value specified is `None`, then `write` calls will block - /// indefinitely. It is an error to pass the zero `Duration` to this + /// If the value specified is [`None`], then [`write()`] calls will block + /// indefinitely. It is an error to pass the zero [`Duration`] to this /// method. /// /// # Note /// /// Platforms may return a different error code whenever a write times out /// as a result of setting this option. For example Unix typically returns - /// an error of the kind `WouldBlock`, but Windows may return `TimedOut`. + /// an error of the kind [`WouldBlock`], but Windows may return [`TimedOut`]. + /// + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// [`write()`]: ../../std/io/trait.Write.html#tymethod.write + /// [`Duration`]: ../../std/time/struct.Duration.html + /// [`WouldBlock`]: ../../std/io/enum.ErrorKind.html#variant.WouldBlock + /// [`TimedOut`]: ../../std/io/enum.ErrorKind.html#variant.TimedOut + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_write_timeout(None).expect("set_write_timeout call failed"); + /// ``` #[stable(feature = "socket_timeout", since = "1.4.0")] pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> { self.0.set_write_timeout(dur) @@ -158,11 +245,25 @@ impl TcpStream { /// Returns the read timeout of this socket. /// - /// If the timeout is `None`, then `read` calls will block indefinitely. + /// If the timeout is [`None`], then [`read()`] calls will block indefinitely. /// /// # Note /// /// Some platforms do not provide access to the current timeout. + /// + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// [`read()`]: ../../std/io/trait.Read.html#tymethod.read + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_read_timeout(None).expect("set_read_timeout call failed"); + /// assert_eq!(stream.read_timeout().unwrap(), None); + /// ``` #[stable(feature = "socket_timeout", since = "1.4.0")] pub fn read_timeout(&self) -> io::Result<Option<Duration>> { self.0.read_timeout() @@ -170,11 +271,25 @@ impl TcpStream { /// Returns the write timeout of this socket. /// - /// If the timeout is `None`, then `write` calls will block indefinitely. + /// If the timeout is [`None`], then [`write()`] calls will block indefinitely. /// /// # Note /// /// Some platforms do not provide access to the current timeout. + /// + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// [`write()`]: ../../std/io/trait.Write.html#tymethod.write + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_write_timeout(None).expect("set_write_timeout call failed"); + /// assert_eq!(stream.write_timeout().unwrap(), None); + /// ``` #[stable(feature = "socket_timeout", since = "1.4.0")] pub fn write_timeout(&self) -> io::Result<Option<Duration>> { self.0.write_timeout() @@ -187,6 +302,16 @@ impl TcpStream { /// small amount of data. When not set, data is buffered until there is a /// sufficient amount to send out, thereby avoiding the frequent sending of /// small packets. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_nodelay(true).expect("set_nodelay call failed"); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { self.0.set_nodelay(nodelay) @@ -197,6 +322,17 @@ impl TcpStream { /// For more information about this option, see [`set_nodelay`][link]. /// /// [link]: #method.set_nodelay + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_nodelay(true).expect("set_nodelay call failed"); + /// assert_eq!(stream.nodelay().unwrap_or(false), true); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn nodelay(&self) -> io::Result<bool> { self.0.nodelay() @@ -206,6 +342,16 @@ impl TcpStream { /// /// This value sets the time-to-live field that is used in every packet sent /// from this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_ttl(100).expect("set_ttl call failed"); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { self.0.set_ttl(ttl) @@ -216,6 +362,17 @@ impl TcpStream { /// For more information about this option, see [`set_ttl`][link]. /// /// [link]: #method.set_ttl + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_ttl(100).expect("set_ttl call failed"); + /// assert_eq!(stream.ttl().unwrap_or(0), 100); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn ttl(&self) -> io::Result<u32> { self.0.ttl() @@ -226,6 +383,16 @@ impl TcpStream { /// This will retrieve the stored error in the underlying socket, clearing /// the field in the process. This can be useful for checking errors between /// calls. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.take_error().expect("No error was expected..."); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn take_error(&self) -> io::Result<Option<io::Error>> { self.0.take_error() @@ -235,6 +402,16 @@ impl TcpStream { /// /// On Unix this corresponds to calling fcntl, and on Windows this /// corresponds to calling ioctlsocket. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpStream; + /// + /// let stream = TcpStream::connect("127.0.0.1:8080") + /// .expect("Couldn't connect to the server..."); + /// stream.set_nonblocking(true).expect("set_nonblocking call failed"); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) @@ -297,12 +474,30 @@ impl TcpListener { /// /// The address type can be any implementor of `ToSocketAddrs` trait. See /// its documentation for concrete examples. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> { super::each_addr(addr, net_imp::TcpListener::bind).map(TcpListener) } /// Returns the local socket address of this listener. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpListener}; + /// + /// let listener = TcpListener::bind("127.0.0.1:8080").unwrap(); + /// assert_eq!(listener.local_addr().unwrap(), + /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080))); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn local_addr(&self) -> io::Result<SocketAddr> { self.0.socket_addr() @@ -313,6 +508,15 @@ impl TcpListener { /// The returned `TcpListener` 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. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:8080").unwrap(); + /// let listener_clone = listener.try_clone().unwrap(); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn try_clone(&self) -> io::Result<TcpListener> { self.0.duplicate().map(TcpListener) @@ -323,6 +527,18 @@ impl TcpListener { /// This function will block the calling thread until a new TCP connection /// is established. When established, the corresponding `TcpStream` and the /// remote peer's address will be returned. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:8080").unwrap(); + /// match listener.accept() { + /// Ok((_socket, addr)) => println!("new client: {:?}", addr), + /// Err(e) => println!("couldn't get client: {:?}", e), + /// } + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { self.0.accept().map(|(a, b)| (TcpStream(a), b)) @@ -331,8 +547,28 @@ impl TcpListener { /// Returns an iterator over the connections being received on this /// listener. /// - /// The returned iterator will never return `None` and will also not yield - /// the peer's `SocketAddr` structure. + /// The returned iterator will never return [`None`] and will also not yield + /// the peer's [`SocketAddr`] structure. + /// + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// [`SocketAddr`]: ../../std/net/enum.SocketAddr.html + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// + /// for stream in listener.incoming() { + /// match stream { + /// Ok(stream) => { + /// println!("new client!"); + /// } + /// Err(e) => { /* connection failed */ } + /// } + /// } + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn incoming(&self) -> Incoming { Incoming { listener: self } @@ -342,6 +578,15 @@ impl TcpListener { /// /// This value sets the time-to-live field that is used in every packet sent /// from this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// listener.set_ttl(100).expect("could not set TTL"); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { self.0.set_ttl(ttl) @@ -349,9 +594,19 @@ impl TcpListener { /// Gets the value of the `IP_TTL` option for this socket. /// - /// For more information about this option, see [`set_ttl`][link]. + /// For more information about this option, see [`set_ttl()`][link]. /// /// [link]: #method.set_ttl + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// listener.set_ttl(100).expect("could not set TTL"); + /// assert_eq!(listener.ttl().unwrap_or(0), 100); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn ttl(&self) -> io::Result<u32> { self.0.ttl() @@ -365,6 +620,15 @@ impl TcpListener { /// /// If this is set to `false` then the socket can be used to send and /// receive packets from an IPv4-mapped IPv6 address. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// listener.set_only_v6(true).expect("Cannot set to IPv6"); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> { self.0.set_only_v6(only_v6) @@ -375,6 +639,16 @@ impl TcpListener { /// For more information about this option, see [`set_only_v6`][link]. /// /// [link]: #method.set_only_v6 + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// listener.set_only_v6(true).expect("Cannot set to IPv6"); + /// assert_eq!(listener.only_v6().unwrap_or(false), true); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn only_v6(&self) -> io::Result<bool> { self.0.only_v6() @@ -385,6 +659,15 @@ impl TcpListener { /// This will retrieve the stored error in the underlying socket, clearing /// the field in the process. This can be useful for checking errors between /// calls. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// listener.take_error().expect("No error was expected"); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn take_error(&self) -> io::Result<Option<io::Error>> { self.0.take_error() @@ -394,6 +677,15 @@ impl TcpListener { /// /// On Unix this corresponds to calling fcntl, and on Windows this /// corresponds to calling ioctlsocket. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::TcpListener; + /// + /// let listener = TcpListener::bind("127.0.0.1:80").unwrap(); + /// listener.set_nonblocking(true).expect("Cannot set non-blocking"); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { self.0.set_nonblocking(nonblocking) diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs index 559250adac5..b280f466dd4 100644 --- a/src/libstd/net/udp.rs +++ b/src/libstd/net/udp.rs @@ -48,8 +48,18 @@ pub struct UdpSocket(net_imp::UdpSocket); impl UdpSocket { /// Creates a UDP socket from the given address. /// - /// The address type can be any implementor of `ToSocketAddr` trait. See + /// The address type can be any implementor of [`ToSocketAddrs`] trait. See /// its documentation for concrete examples. + /// + /// [`ToSocketAddrs`]: ../../std/net/trait.ToSocketAddrs.html + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<UdpSocket> { super::each_addr(addr, net_imp::UdpSocket::bind).map(UdpSocket) @@ -57,6 +67,17 @@ impl UdpSocket { /// Receives data from the socket. On success, returns the number of bytes /// read and the address from whence the data came. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// let mut buf = [0; 10]; + /// let (number_of_bytes, src_addr) = socket.recv_from(&mut buf) + /// .expect("Didn't receive data"); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { self.0.recv_from(buf) @@ -65,11 +86,24 @@ impl UdpSocket { /// Sends data on the socket to the given address. On success, returns the /// number of bytes written. /// - /// Address type can be any implementor of `ToSocketAddrs` trait. See its + /// Address type can be any implementor of [`ToSocketAddrs`] trait. See its /// documentation for concrete examples. + /// /// This will return an error when the IP version of the local socket - /// does not match that returned from `ToSocketAddrs` + /// does not match that returned from [`ToSocketAddrs`]. + /// /// See https://github.com/rust-lang/rust/issues/34202 for more details. + /// + /// [`ToSocketAddrs`]: ../../std/net/trait.ToSocketAddrs.html + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.send_to(&[0; 10], "127.0.0.1:4242").expect("couldn't send data"); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn send_to<A: ToSocketAddrs>(&self, buf: &[u8], addr: A) -> io::Result<usize> { @@ -81,6 +115,16 @@ impl UdpSocket { } /// Returns the socket address that this socket was created from. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// assert_eq!(socket.local_addr().unwrap(), + /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 34254))); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn local_addr(&self) -> io::Result<SocketAddr> { self.0.socket_addr() @@ -91,6 +135,15 @@ impl UdpSocket { /// The returned `UdpSocket` is a reference to the same socket that this /// object references. Both handles will read and write the same port, and /// options set on one socket will be propagated to the other. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// let socket_clone = socket.try_clone().expect("couldn't clone the socket"); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn try_clone(&self) -> io::Result<UdpSocket> { self.0.duplicate().map(UdpSocket) @@ -98,15 +151,30 @@ impl UdpSocket { /// Sets the read timeout to the timeout specified. /// - /// If the value specified is `None`, then `read` calls will block - /// indefinitely. It is an error to pass the zero `Duration` to this + /// If the value specified is [`None`], then [`read()`] calls will block + /// indefinitely. It is an error to pass the zero [`Duration`] to this /// method. /// /// # Note /// /// Platforms may return a different error code whenever a read times out as /// a result of setting this option. For example Unix typically returns an - /// error of the kind `WouldBlock`, but Windows may return `TimedOut`. + /// error of the kind [`WouldBlock`], but Windows may return [`TimedOut`]. + /// + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// [`read()`]: ../../std/io/trait.Read.html#tymethod.read + /// [`Duration`]: ../../std/time/struct.Duration.html + /// [`WouldBlock`]: ../../std/io/enum.ErrorKind.html#variant.WouldBlock + /// [`TimedOut`]: ../../std/io/enum.ErrorKind.html#variant.TimedOut + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_read_timeout(None).expect("set_read_timeout call failed"); + /// ``` #[stable(feature = "socket_timeout", since = "1.4.0")] pub fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> { self.0.set_read_timeout(dur) @@ -114,15 +182,30 @@ impl UdpSocket { /// Sets the write timeout to the timeout specified. /// - /// If the value specified is `None`, then `write` calls will block - /// indefinitely. It is an error to pass the zero `Duration` to this + /// If the value specified is [`None`], then [`write()`] calls will block + /// indefinitely. It is an error to pass the zero [`Duration`] to this /// method. /// /// # Note /// /// Platforms may return a different error code whenever a write times out /// as a result of setting this option. For example Unix typically returns - /// an error of the kind `WouldBlock`, but Windows may return `TimedOut`. + /// an error of the kind [`WouldBlock`], but Windows may return [`TimedOut`]. + /// + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// [`write()`]: ../../std/io/trait.Write.html#tymethod.write + /// [`Duration`]: ../../std/time/struct.Duration.html + /// [`WouldBlock`]: ../../std/io/enum.ErrorKind.html#variant.WouldBlock + /// [`TimedOut`]: ../../std/io/enum.ErrorKind.html#variant.TimedOut + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_write_timeout(None).expect("set_write_timeout call failed"); + /// ``` #[stable(feature = "socket_timeout", since = "1.4.0")] pub fn set_write_timeout(&self, dur: Option<Duration>) -> io::Result<()> { self.0.set_write_timeout(dur) @@ -130,7 +213,20 @@ impl UdpSocket { /// Returns the read timeout of this socket. /// - /// If the timeout is `None`, then `read` calls will block indefinitely. + /// If the timeout is [`None`], then [`read()`] calls will block indefinitely. + /// + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// [`read()`]: ../../std/io/trait.Read.html#tymethod.read + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_read_timeout(None).expect("set_read_timeout call failed"); + /// assert_eq!(socket.read_timeout().unwrap(), None); + /// ``` #[stable(feature = "socket_timeout", since = "1.4.0")] pub fn read_timeout(&self) -> io::Result<Option<Duration>> { self.0.read_timeout() @@ -138,7 +234,20 @@ impl UdpSocket { /// Returns the write timeout of this socket. /// - /// If the timeout is `None`, then `write` calls will block indefinitely. + /// If the timeout is [`None`], then [`write()`] calls will block indefinitely. + /// + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// [`write()`]: ../../std/io/trait.Write.html#tymethod.write + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_write_timeout(None).expect("set_write_timeout call failed"); + /// assert_eq!(socket.write_timeout().unwrap(), None); + /// ``` #[stable(feature = "socket_timeout", since = "1.4.0")] pub fn write_timeout(&self) -> io::Result<Option<Duration>> { self.0.write_timeout() @@ -148,6 +257,15 @@ impl UdpSocket { /// /// When enabled, this socket is allowed to send packets to a broadcast /// address. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_broadcast(false).expect("set_broadcast call failed"); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_broadcast(&self, broadcast: bool) -> io::Result<()> { self.0.set_broadcast(broadcast) @@ -159,6 +277,16 @@ impl UdpSocket { /// [`set_broadcast`][link]. /// /// [link]: #method.set_broadcast + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_broadcast(false).expect("set_broadcast call failed"); + /// assert_eq!(socket.broadcast().unwrap(), false); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn broadcast(&self) -> io::Result<bool> { self.0.broadcast() @@ -168,6 +296,15 @@ impl UdpSocket { /// /// If enabled, multicast packets will be looped back to the local socket. /// Note that this may not have any affect on IPv6 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_multicast_loop_v4(false).expect("set_multicast_loop_v4 call failed"); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_multicast_loop_v4(&self, multicast_loop_v4: bool) -> io::Result<()> { self.0.set_multicast_loop_v4(multicast_loop_v4) @@ -179,6 +316,16 @@ impl UdpSocket { /// [`set_multicast_loop_v4`][link]. /// /// [link]: #method.set_multicast_loop_v4 + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_multicast_loop_v4(false).expect("set_multicast_loop_v4 call failed"); + /// assert_eq!(socket.multicast_loop_v4().unwrap(), false); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn multicast_loop_v4(&self) -> io::Result<bool> { self.0.multicast_loop_v4() @@ -191,6 +338,15 @@ impl UdpSocket { /// don't leave the local network unless explicitly requested. /// /// Note that this may not have any affect on IPv6 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_multicast_ttl_v4(42).expect("set_multicast_ttl_v4 call failed"); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_multicast_ttl_v4(&self, multicast_ttl_v4: u32) -> io::Result<()> { self.0.set_multicast_ttl_v4(multicast_ttl_v4) @@ -202,6 +358,16 @@ impl UdpSocket { /// [`set_multicast_ttl_v4`][link]. /// /// [link]: #method.set_multicast_ttl_v4 + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_multicast_ttl_v4(42).expect("set_multicast_ttl_v4 call failed"); + /// assert_eq!(socket.multicast_ttl_v4().unwrap(), 42); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn multicast_ttl_v4(&self) -> io::Result<u32> { self.0.multicast_ttl_v4() @@ -211,6 +377,15 @@ impl UdpSocket { /// /// Controls whether this socket sees the multicast packets it sends itself. /// Note that this may not have any affect on IPv4 sockets. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_multicast_loop_v6(false).expect("set_multicast_loop_v6 call failed"); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_multicast_loop_v6(&self, multicast_loop_v6: bool) -> io::Result<()> { self.0.set_multicast_loop_v6(multicast_loop_v6) @@ -222,6 +397,16 @@ impl UdpSocket { /// [`set_multicast_loop_v6`][link]. /// /// [link]: #method.set_multicast_loop_v6 + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_multicast_loop_v6(false).expect("set_multicast_loop_v6 call failed"); + /// assert_eq!(socket.multicast_loop_v6().unwrap(), false); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn multicast_loop_v6(&self) -> io::Result<bool> { self.0.multicast_loop_v6() @@ -231,6 +416,15 @@ impl UdpSocket { /// /// This value sets the time-to-live field that is used in every packet sent /// from this socket. + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_ttl(42).expect("set_ttl call failed"); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { self.0.set_ttl(ttl) @@ -241,6 +435,16 @@ impl UdpSocket { /// For more information about this option, see [`set_ttl`][link]. /// /// [link]: #method.set_ttl + /// + /// # Examples + /// + /// ```no_run + /// use std::net::UdpSocket; + /// + /// let socket = UdpSocket::bind("127.0.0.1:34254").expect("couldn't bind to address"); + /// socket.set_ttl(42).expect("set_ttl call failed"); + /// assert_eq!(socket.ttl().unwrap(), 42); + /// ``` #[stable(feature = "net2_mutators", since = "1.9.0")] pub fn ttl(&self) -> io::Result<u32> { self.0.ttl() diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 1f5b3437b61..45a10d24528 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -153,12 +153,29 @@ pub fn take_hook() -> Box<Fn(&PanicInfo) + 'static + Sync + Send> { match hook { Hook::Default => Box::new(default_hook), - Hook::Custom(ptr) => {Box::from_raw(ptr)} // FIXME #30530 + Hook::Custom(ptr) => Box::from_raw(ptr), } } } /// A struct providing information about a panic. +/// +/// `PanicInfo` structure is passed to a panic hook set by the [`set_hook()`] +/// function. +/// +/// [`set_hook()`]: ../../std/panic/fn.set_hook.html +/// +/// # Examples +/// +/// ```should_panic +/// use std::panic; +/// +/// panic::set_hook(Box::new(|panic_info| { +/// println!("panic occured: {:?}", panic_info.payload().downcast_ref::<&str>().unwrap()); +/// })); +/// +/// panic!("Normal panic"); +/// ``` #[stable(feature = "panic_hooks", since = "1.10.0")] pub struct PanicInfo<'a> { payload: &'a (Any + Send), @@ -168,7 +185,21 @@ pub struct PanicInfo<'a> { impl<'a> PanicInfo<'a> { /// Returns the payload associated with the panic. /// - /// This will commonly, but not always, be a `&'static str` or `String`. + /// This will commonly, but not always, be a `&'static str` or [`String`]. + /// + /// [`String`]: ../../std/string/struct.String.html + /// + /// # Examples + /// + /// ```should_panic + /// use std::panic; + /// + /// panic::set_hook(Box::new(|panic_info| { + /// println!("panic occured: {:?}", panic_info.payload().downcast_ref::<&str>().unwrap()); + /// })); + /// + /// panic!("Normal panic"); + /// ``` #[stable(feature = "panic_hooks", since = "1.10.0")] pub fn payload(&self) -> &(Any + Send) { self.payload @@ -177,8 +208,26 @@ impl<'a> PanicInfo<'a> { /// Returns information about the location from which the panic originated, /// if available. /// - /// This method will currently always return `Some`, but this may change + /// This method will currently always return [`Some`], but this may change /// in future versions. + /// + /// [`Some`]: ../../std/option/enum.Option.html#variant.Some + /// + /// # Examples + /// + /// ```should_panic + /// use std::panic; + /// + /// panic::set_hook(Box::new(|panic_info| { + /// if let Some(location) = panic_info.location() { + /// println!("panic occured in file '{}' at line {}", location.file(), location.line()); + /// } else { + /// println!("panic occured but can't get location information..."); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` #[stable(feature = "panic_hooks", since = "1.10.0")] pub fn location(&self) -> Option<&Location> { Some(&self.location) @@ -186,6 +235,27 @@ impl<'a> PanicInfo<'a> { } /// A struct containing information about the location of a panic. +/// +/// This structure is created by the [`location()`] method of [`PanicInfo`]. +/// +/// [`location()`]: ../../std/panic/struct.PanicInfo.html#method.location +/// [`PanicInfo`]: ../../std/panic/struct.PanicInfo.html +/// +/// # Examples +/// +/// ```should_panic +/// use std::panic; +/// +/// panic::set_hook(Box::new(|panic_info| { +/// if let Some(location) = panic_info.location() { +/// println!("panic occured in file '{}' at line {}", location.file(), location.line()); +/// } else { +/// println!("panic occured but can't get location information..."); +/// } +/// })); +/// +/// panic!("Normal panic"); +/// ``` #[stable(feature = "panic_hooks", since = "1.10.0")] pub struct Location<'a> { file: &'a str, @@ -194,12 +264,44 @@ pub struct Location<'a> { impl<'a> Location<'a> { /// Returns the name of the source file from which the panic originated. + /// + /// # Examples + /// + /// ```should_panic + /// use std::panic; + /// + /// panic::set_hook(Box::new(|panic_info| { + /// if let Some(location) = panic_info.location() { + /// println!("panic occured in file '{}'", location.file()); + /// } else { + /// println!("panic occured but can't get location information..."); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` #[stable(feature = "panic_hooks", since = "1.10.0")] pub fn file(&self) -> &str { self.file } /// Returns the line number from which the panic originated. + /// + /// # Examples + /// + /// ```should_panic + /// use std::panic; + /// + /// panic::set_hook(Box::new(|panic_info| { + /// if let Some(location) = panic_info.location() { + /// println!("panic occured at line {}", location.line()); + /// } else { + /// println!("panic occured but can't get location information..."); + /// } + /// })); + /// + /// panic!("Normal panic"); + /// ``` #[stable(feature = "panic_hooks", since = "1.10.0")] pub fn line(&self) -> u32 { self.line diff --git a/src/libstd/path.rs b/src/libstd/path.rs index 95c8af66425..d13baea40a9 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -254,7 +254,9 @@ pub fn is_separator(c: char) -> bool { c.is_ascii() && is_sep_byte(c as u8) } -/// The primary separator for the current platform +/// The primary separator of path components for the current platform. +/// +/// For example, `/` on Unix and `\` on Windows. #[stable(feature = "rust1", since = "1.0.0")] pub const MAIN_SEPARATOR: char = ::sys::path::MAIN_SEP; @@ -455,7 +457,17 @@ pub enum Component<'a> { } impl<'a> Component<'a> { - /// Extracts the underlying `OsStr` slice + /// Extracts the underlying `OsStr` slice. + /// + /// # Examples + /// + /// ``` + /// use std::path::Path; + /// + /// let path = Path::new("./tmp/foo/bar.txt"); + /// let components: Vec<_> = path.components().map(|comp| comp.as_os_str()).collect(); + /// assert_eq!(&components, &[".", "tmp", "foo", "bar.txt"]); + /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn as_os_str(self) -> &'a OsStr { match self { diff --git a/src/libstd/process.rs b/src/libstd/process.rs index c99fda9febc..858537dd2de 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -253,6 +253,14 @@ impl Command { /// Builder methods are provided to change these defaults and /// otherwise configure the process. /// + /// If `program` is not an absolute path, the `PATH` will be searched in + /// an OS-defined way. + /// + /// The search path to be used may be controlled by setting the + /// `PATH` environment variable on the Command, + /// but this has some implementation limitations on Windows + /// (see https://github.com/rust-lang/rust/issues/37519). + /// /// # Examples /// /// Basic usage: @@ -819,6 +827,14 @@ impl Child { /// will be run. If a clean shutdown is needed it is recommended to only call /// this function at a known point where there are no more destructors left /// to run. +/// +/// # Examples +/// +/// ``` +/// use std::process; +/// +/// process::exit(0); +/// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn exit(code: i32) -> ! { ::sys_common::cleanup(); @@ -1159,4 +1175,62 @@ mod tests { Ok(_) => panic!(), } } + + /// Test that process creation flags work by debugging a process. + /// Other creation flags make it hard or impossible to detect + /// behavioral changes in the process. + #[test] + #[cfg(windows)] + fn test_creation_flags() { + use os::windows::process::CommandExt; + use sys::c::{BOOL, DWORD, INFINITE}; + #[repr(C, packed)] + struct DEBUG_EVENT { + pub event_code: DWORD, + pub process_id: DWORD, + pub thread_id: DWORD, + // This is a union in the real struct, but we don't + // need this data for the purposes of this test. + pub _junk: [u8; 164], + } + + extern "system" { + fn WaitForDebugEvent(lpDebugEvent: *mut DEBUG_EVENT, dwMilliseconds: DWORD) -> BOOL; + fn ContinueDebugEvent(dwProcessId: DWORD, dwThreadId: DWORD, + dwContinueStatus: DWORD) -> BOOL; + } + + const DEBUG_PROCESS: DWORD = 1; + const EXIT_PROCESS_DEBUG_EVENT: DWORD = 5; + const DBG_EXCEPTION_NOT_HANDLED: DWORD = 0x80010001; + + let mut child = Command::new("cmd") + .creation_flags(DEBUG_PROCESS) + .stdin(Stdio::piped()).spawn().unwrap(); + child.stdin.take().unwrap().write_all(b"exit\r\n").unwrap(); + let mut events = 0; + let mut event = DEBUG_EVENT { + event_code: 0, + process_id: 0, + thread_id: 0, + _junk: [0; 164], + }; + loop { + if unsafe { WaitForDebugEvent(&mut event as *mut DEBUG_EVENT, INFINITE) } == 0 { + panic!("WaitForDebugEvent failed!"); + } + events += 1; + + if event.event_code == EXIT_PROCESS_DEBUG_EVENT { + break; + } + + if unsafe { ContinueDebugEvent(event.process_id, + event.thread_id, + DBG_EXCEPTION_NOT_HANDLED) } == 0 { + panic!("ContinueDebugEvent failed!"); + } + } + assert!(events > 0); + } } diff --git a/src/libstd/sync/mpsc/mod.rs b/src/libstd/sync/mpsc/mod.rs index 2773629c7d7..ca6e46eb15a 100644 --- a/src/libstd/sync/mpsc/mod.rs +++ b/src/libstd/sync/mpsc/mod.rs @@ -491,11 +491,11 @@ pub fn channel<T>() -> (Sender<T>, Receiver<T>) { /// becomes available. These channels differ greatly in the semantics of the /// sender from asynchronous channels, however. /// -/// This channel has an internal buffer on which messages will be queued. When -/// the internal buffer becomes full, future sends will *block* waiting for the -/// buffer to open up. Note that a buffer size of 0 is valid, in which case this -/// becomes "rendezvous channel" where each send will not return until a recv -/// is paired with it. +/// This channel has an internal buffer on which messages will be queued. `bound` +/// specifies the buffer size. When the internal buffer becomes full, future sends +/// will *block* waiting for the buffer to open up. Note that a buffer size of 0 +/// is valid, in which case this becomes "rendezvous channel" where each send will +/// not return until a recv is paired with it. /// /// As with asynchronous channels, all senders will panic in `send` if the /// `Receiver` has been destroyed. diff --git a/src/libstd/sync/mutex.rs b/src/libstd/sync/mutex.rs index 812724c7a16..df4a3746a49 100644 --- a/src/libstd/sync/mutex.rs +++ b/src/libstd/sync/mutex.rs @@ -133,7 +133,14 @@ unsafe impl<T: ?Sized + Send> Sync for Mutex<T> { } /// dropped (falls out of scope), the lock will be unlocked. /// /// The data protected by the mutex can be access through this guard via its -/// `Deref` and `DerefMut` implementations +/// `Deref` and `DerefMut` implementations. +/// +/// This structure is created by the [`lock()`] and [`try_lock()`] methods on +/// [`Mutex`]. +/// +/// [`lock()`]: struct.Mutex.html#method.lock +/// [`try_lock()`]: struct.Mutex.html#method.try_lock +/// [`Mutex`]: struct.Mutex.html #[must_use] #[stable(feature = "rust1", since = "1.0.0")] pub struct MutexGuard<'a, T: ?Sized + 'a> { diff --git a/src/libstd/sync/rwlock.rs b/src/libstd/sync/rwlock.rs index f08b7641521..f83cf7ba9c2 100644 --- a/src/libstd/sync/rwlock.rs +++ b/src/libstd/sync/rwlock.rs @@ -77,6 +77,13 @@ unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {} /// RAII structure used to release the shared read access of a lock when /// dropped. +/// +/// This structure is created by the [`read()`] and [`try_read()`] methods on +/// [`RwLock`]. +/// +/// [`read()`]: struct.RwLock.html#method.read +/// [`try_read()`]: struct.RwLock.html#method.try_read +/// [`RwLock`]: struct.RwLock.html #[must_use] #[stable(feature = "rust1", since = "1.0.0")] pub struct RwLockReadGuard<'a, T: ?Sized + 'a> { @@ -88,6 +95,13 @@ impl<'a, T: ?Sized> !marker::Send for RwLockReadGuard<'a, T> {} /// RAII structure used to release the exclusive write access of a lock when /// dropped. +/// +/// This structure is created by the [`write()`] and [`try_write()`] methods +/// on [`RwLock`]. +/// +/// [`write()`]: struct.RwLock.html#method.write +/// [`try_write()`]: struct.RwLock.html#method.try_write +/// [`RwLock`]: struct.RwLock.html #[must_use] #[stable(feature = "rust1", since = "1.0.0")] pub struct RwLockWriteGuard<'a, T: ?Sized + 'a> { diff --git a/src/libstd/sys/unix/args.rs b/src/libstd/sys/unix/args.rs index c04fd863674..0f447ff4ec4 100644 --- a/src/libstd/sys/unix/args.rs +++ b/src/libstd/sys/unix/args.rs @@ -172,10 +172,23 @@ mod imp { extern { fn sel_registerName(name: *const libc::c_uchar) -> Sel; - fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId; fn objc_getClass(class_name: *const libc::c_uchar) -> NsId; } + #[cfg(target_arch="aarch64")] + extern { + fn objc_msgSend(obj: NsId, sel: Sel) -> NsId; + #[link_name="objc_msgSend"] + fn objc_msgSend_ul(obj: NsId, sel: Sel, i: libc::c_ulong) -> NsId; + } + + #[cfg(not(target_arch="aarch64"))] + extern { + fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId; + #[link_name="objc_msgSend"] + fn objc_msgSend_ul(obj: NsId, sel: Sel, ...) -> NsId; + } + #[link(name = "Foundation", kind = "framework")] #[link(name = "objc")] #[cfg(not(cargobuild))] @@ -199,7 +212,7 @@ mod imp { let cnt: usize = mem::transmute(objc_msgSend(args, count_sel)); for i in 0..cnt { - let tmp = objc_msgSend(args, object_at_sel, i); + let tmp = objc_msgSend_ul(args, object_at_sel, i as libc::c_ulong); let utf_c_str: *const libc::c_char = mem::transmute(objc_msgSend(tmp, utf8_sel)); let bytes = CStr::from_ptr(utf_c_str).to_bytes(); diff --git a/src/libstd/sys/unix/ext/io.rs b/src/libstd/sys/unix/ext/io.rs index 4163ede46af..296235e173d 100644 --- a/src/libstd/sys/unix/ext/io.rs +++ b/src/libstd/sys/unix/ext/io.rs @@ -43,7 +43,7 @@ pub trait AsRawFd { /// descriptor. #[stable(feature = "from_raw_os", since = "1.1.0")] pub trait FromRawFd { - /// Constructs a new instances of `Self` from the given raw file + /// Constructs a new instance of `Self` from the given raw file /// descriptor. /// /// This function **consumes ownership** of the specified file diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index 41bb96fed16..61eb60da486 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -110,6 +110,7 @@ impl FileDesc { #[cfg(not(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten", + target_os = "fuchsia", target_os = "haiku")))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { @@ -120,6 +121,7 @@ impl FileDesc { #[cfg(any(target_env = "newlib", target_os = "solaris", target_os = "emscripten", + target_os = "fuchsia", target_os = "haiku"))] pub fn set_cloexec(&self) -> io::Result<()> { unsafe { diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index e591f25cac1..6992a17832e 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -78,7 +78,7 @@ pub fn errno() -> i32 { static errno: c_int; } - errno as i32 + unsafe { errno as i32 } } /// Gets a detailed string description for the given error number. @@ -193,7 +193,7 @@ impl StdError for JoinPathsError { fn description(&self) -> &str { "failed to join paths" } } -#[cfg(target_os = "freebsd")] +#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] pub fn current_exe() -> io::Result<PathBuf> { unsafe { let mut mib = [libc::CTL_KERN as c_int, @@ -218,11 +218,6 @@ pub fn current_exe() -> io::Result<PathBuf> { } } -#[cfg(target_os = "dragonfly")] -pub fn current_exe() -> io::Result<PathBuf> { - ::fs::read_link("/proc/curproc/file") -} - #[cfg(target_os = "netbsd")] pub fn current_exe() -> io::Result<PathBuf> { ::fs::read_link("/proc/curproc/exe") diff --git a/src/libstd/sys/unix/pipe.rs b/src/libstd/sys/unix/pipe.rs index ffe8032e460..a8ed415b7f4 100644 --- a/src/libstd/sys/unix/pipe.rs +++ b/src/libstd/sys/unix/pipe.rs @@ -77,6 +77,7 @@ pub fn read2(p1: AnonPipe, v1: &mut Vec<u8>, p2: AnonPipe, v2: &mut Vec<u8>) -> io::Result<()> { + // Set both pipes into nonblocking mode as we're gonna be reading from both // in the `select` loop below, and we wouldn't want one to block the other! let p1 = p1.into_fd(); diff --git a/src/libstd/sys/unix/process/magenta.rs b/src/libstd/sys/unix/process/magenta.rs new file mode 100644 index 00000000000..319fbce35cd --- /dev/null +++ b/src/libstd/sys/unix/process/magenta.rs @@ -0,0 +1,191 @@ +// Copyright 2016 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(non_camel_case_types)] + +use convert::TryInto; +use io; +use os::raw::c_char; +use u64; + +use libc::{c_int, c_void}; + +pub type mx_handle_t = i32; +pub type mx_vaddr_t = usize; +pub type mx_rights_t = u32; +pub type mx_status_t = i32; + +pub type mx_size_t = usize; +pub type mx_ssize_t = isize; + +pub const MX_HANDLE_INVALID: mx_handle_t = 0; + +pub type mx_time_t = u64; +pub const MX_TIME_INFINITE : mx_time_t = u64::MAX; + +pub type mx_signals_t = u32; + +pub const MX_OBJECT_SIGNAL_3 : mx_signals_t = 1 << 3; + +pub const MX_TASK_TERMINATED : mx_signals_t = MX_OBJECT_SIGNAL_3; + +pub const MX_RIGHT_SAME_RIGHTS : mx_rights_t = 1 << 31; + +pub type mx_object_info_topic_t = u32; + +pub const MX_INFO_PROCESS : mx_object_info_topic_t = 3; + +pub const MX_HND_TYPE_JOB: u32 = 6; + +pub fn mx_cvt<T>(t: T) -> io::Result<T> where T: TryInto<mx_status_t>+Copy { + if let Ok(status) = TryInto::try_into(t) { + if status < 0 { + Err(io::Error::from_raw_os_error(status)) + } else { + Ok(t) + } + } else { + Err(io::Error::last_os_error()) + } +} + +// Safe wrapper around mx_handle_t +pub struct Handle { + raw: mx_handle_t, +} + +impl Handle { + pub fn new(raw: mx_handle_t) -> Handle { + Handle { + raw: raw, + } + } + + pub fn raw(&self) -> mx_handle_t { + self.raw + } +} + +impl Drop for Handle { + fn drop(&mut self) { + unsafe { mx_cvt(mx_handle_close(self.raw)).expect("Failed to close mx_handle_t"); } + } +} + +// Common MX_INFO header +#[derive(Default)] +#[repr(C)] +pub struct mx_info_header_t { + pub topic: u32, // identifies the info struct + pub avail_topic_size: u16, // “native” size of the struct + pub topic_size: u16, // size of the returned struct (<=topic_size) + pub avail_count: u32, // number of records the kernel has + pub count: u32, // number of records returned (limited by buffer size) +} + +#[derive(Default)] +#[repr(C)] +pub struct mx_record_process_t { + pub return_code: c_int, +} + +// Returned for topic MX_INFO_PROCESS +#[derive(Default)] +#[repr(C)] +pub struct mx_info_process_t { + pub hdr: mx_info_header_t, + pub rec: mx_record_process_t, +} + +extern { + pub fn mx_task_kill(handle: mx_handle_t) -> mx_status_t; + + pub fn mx_handle_close(handle: mx_handle_t) -> mx_status_t; + + pub fn mx_handle_duplicate(handle: mx_handle_t, rights: mx_rights_t, + out: *const mx_handle_t) -> mx_handle_t; + + pub fn mx_handle_wait_one(handle: mx_handle_t, signals: mx_signals_t, timeout: mx_time_t, + pending: *mut mx_signals_t) -> mx_status_t; + + pub fn mx_object_get_info(handle: mx_handle_t, topic: u32, buffer: *mut c_void, + buffer_size: mx_size_t, actual_size: *mut mx_size_t, + avail: *mut mx_size_t) -> mx_status_t; +} + +// Handle Info entries associate a type and optional +// argument with each handle included in the process +// arguments message. +pub fn mx_hnd_info(hnd_type: u32, arg: u32) -> u32 { + (hnd_type & 0xFFFF) | ((arg & 0xFFFF) << 16) +} + +extern { + pub fn mxio_get_startup_handle(id: u32) -> mx_handle_t; +} + +// From `enum special_handles` in system/ulib/launchpad/launchpad.c +#[allow(unused)] pub const HND_LOADER_SVC: usize = 0; +// HND_EXEC_VMO = 1 +#[allow(unused)] pub const HND_SPECIAL_COUNT: usize = 2; + +#[repr(C)] +pub struct launchpad_t { + argc: u32, + envc: u32, + args: *const c_char, + args_len: usize, + env: *const c_char, + env_len: usize, + + handles: *mut mx_handle_t, + handles_info: *mut u32, + handle_count: usize, + handle_alloc: usize, + + entry: mx_vaddr_t, + base: mx_vaddr_t, + vdso_base: mx_vaddr_t, + + stack_size: usize, + + special_handles: [mx_handle_t; HND_SPECIAL_COUNT], + loader_message: bool, +} + +extern { + pub fn launchpad_create(job: mx_handle_t, name: *const c_char, + lp: *mut *mut launchpad_t) -> mx_status_t; + + pub fn launchpad_start(lp: *mut launchpad_t) -> mx_status_t; + + pub fn launchpad_destroy(lp: *mut launchpad_t); + + pub fn launchpad_arguments(lp: *mut launchpad_t, argc: c_int, + argv: *const *const c_char) -> mx_status_t; + + pub fn launchpad_environ(lp: *mut launchpad_t, envp: *const *const c_char) -> mx_status_t; + + pub fn launchpad_clone_mxio_root(lp: *mut launchpad_t) -> mx_status_t; + + pub fn launchpad_clone_mxio_cwd(lp: *mut launchpad_t) -> mx_status_t; + + pub fn launchpad_clone_fd(lp: *mut launchpad_t, fd: c_int, target_fd: c_int) -> mx_status_t; + + pub fn launchpad_transfer_fd(lp: *mut launchpad_t, fd: c_int, target_fd: c_int) -> mx_status_t; + + pub fn launchpad_elf_load(lp: *mut launchpad_t, vmo: mx_handle_t) -> mx_status_t; + + pub fn launchpad_add_vdso_vmo(lp: *mut launchpad_t) -> mx_status_t; + + pub fn launchpad_load_vdso(lp: *mut launchpad_t, vmo: mx_handle_t) -> mx_status_t; + + pub fn launchpad_vmo_from_file(filename: *const c_char) -> mx_handle_t; +} diff --git a/src/libstd/sys/unix/process/mod.rs b/src/libstd/sys/unix/process/mod.rs new file mode 100644 index 00000000000..b50384d8eee --- /dev/null +++ b/src/libstd/sys/unix/process/mod.rs @@ -0,0 +1,22 @@ +// Copyright 2016 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. + +pub use self::process_common::{Command, ExitStatus, Stdio, StdioPipes}; +pub use self::process_inner::Process; + +mod process_common; +#[cfg(not(target_os = "fuchsia"))] +#[path = "process_unix.rs"] +mod process_inner; +#[cfg(target_os = "fuchsia")] +#[path = "process_fuchsia.rs"] +mod process_inner; +#[cfg(target_os = "fuchsia")] +mod magenta; diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process/process_common.rs index dafc11d9cc8..3497b266340 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process/process_common.rs @@ -1,4 +1,4 @@ -// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -14,14 +14,12 @@ use collections::hash_map::{HashMap, Entry}; use env; use ffi::{OsString, OsStr, CString, CStr}; use fmt; -use io::{self, Error, ErrorKind}; -use libc::{self, pid_t, c_int, gid_t, uid_t, c_char}; -use mem; +use io; +use libc::{self, c_int, gid_t, uid_t, c_char}; use ptr; use sys::fd::FileDesc; use sys::fs::{File, OpenOptions}; use sys::pipe::{self, AnonPipe}; -use sys::{self, cvt, cvt_r}; //////////////////////////////////////////////////////////////////////////////// // Command @@ -71,13 +69,13 @@ pub struct StdioPipes { // passed to do_exec() with configuration of what the child stdio should look // like -struct ChildPipes { - stdin: ChildStdio, - stdout: ChildStdio, - stderr: ChildStdio, +pub struct ChildPipes { + pub stdin: ChildStdio, + pub stdout: ChildStdio, + pub stderr: ChildStdio, } -enum ChildStdio { +pub enum ChildStdio { Inherit, Explicit(c_int), Owned(FileDesc), @@ -195,6 +193,33 @@ impl Command { self.gid = Some(id); } + pub fn saw_nul(&self) -> bool { + self.saw_nul + } + pub fn get_envp(&self) -> &Option<Vec<*const c_char>> { + &self.envp + } + pub fn get_argv(&self) -> &Vec<*const c_char> { + &self.argv + } + + #[allow(dead_code)] + pub fn get_cwd(&self) -> &Option<CString> { + &self.cwd + } + #[allow(dead_code)] + pub fn get_uid(&self) -> Option<uid_t> { + self.uid + } + #[allow(dead_code)] + pub fn get_gid(&self) -> Option<gid_t> { + self.gid + } + + pub fn get_closures(&mut self) -> &mut Vec<Box<FnMut() -> io::Result<()> + Send + Sync>> { + &mut self.closures + } + pub fn before_exec(&mut self, f: Box<FnMut() -> io::Result<()> + Send + Sync>) { self.closures.push(f); @@ -203,200 +228,16 @@ impl Command { pub fn stdin(&mut self, stdin: Stdio) { self.stdin = Some(stdin); } + pub fn stdout(&mut self, stdout: Stdio) { self.stdout = Some(stdout); } + pub fn stderr(&mut self, stderr: Stdio) { self.stderr = Some(stderr); } - pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) - -> io::Result<(Process, StdioPipes)> { - const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; - - if self.saw_nul { - return Err(io::Error::new(ErrorKind::InvalidInput, - "nul byte found in provided data")); - } - - let (ours, theirs) = self.setup_io(default, needs_stdin)?; - let (input, output) = sys::pipe::anon_pipe()?; - - let pid = unsafe { - match cvt(libc::fork())? { - 0 => { - drop(input); - let err = self.do_exec(theirs); - let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; - let bytes = [ - (errno >> 24) as u8, - (errno >> 16) as u8, - (errno >> 8) as u8, - (errno >> 0) as u8, - CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1], - CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3] - ]; - // pipe I/O up to PIPE_BUF bytes should be atomic, and then - // we want to be sure we *don't* run at_exit destructors as - // we're being torn down regardless - assert!(output.write(&bytes).is_ok()); - libc::_exit(1) - } - n => n, - } - }; - - let mut p = Process { pid: pid, status: None }; - drop(output); - let mut bytes = [0; 8]; - - // loop to handle EINTR - loop { - match input.read(&mut bytes) { - Ok(0) => return Ok((p, ours)), - Ok(8) => { - assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]), - "Validation on the CLOEXEC pipe failed: {:?}", bytes); - let errno = combine(&bytes[0.. 4]); - assert!(p.wait().is_ok(), - "wait() should either return Ok or panic"); - return Err(Error::from_raw_os_error(errno)) - } - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} - Err(e) => { - assert!(p.wait().is_ok(), - "wait() should either return Ok or panic"); - panic!("the CLOEXEC pipe failed: {:?}", e) - }, - Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic - assert!(p.wait().is_ok(), - "wait() should either return Ok or panic"); - panic!("short read on the CLOEXEC pipe") - } - } - } - - fn combine(arr: &[u8]) -> i32 { - let a = arr[0] as u32; - let b = arr[1] as u32; - let c = arr[2] as u32; - let d = arr[3] as u32; - - ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32 - } - } - - pub fn exec(&mut self, default: Stdio) -> io::Error { - if self.saw_nul { - return io::Error::new(ErrorKind::InvalidInput, - "nul byte found in provided data") - } - - match self.setup_io(default, true) { - Ok((_, theirs)) => unsafe { self.do_exec(theirs) }, - Err(e) => e, - } - } - - // And at this point we've reached a special time in the life of the - // child. The child must now be considered hamstrung and unable to - // do anything other than syscalls really. Consider the following - // scenario: - // - // 1. Thread A of process 1 grabs the malloc() mutex - // 2. Thread B of process 1 forks(), creating thread C - // 3. Thread C of process 2 then attempts to malloc() - // 4. The memory of process 2 is the same as the memory of - // process 1, so the mutex is locked. - // - // This situation looks a lot like deadlock, right? It turns out - // that this is what pthread_atfork() takes care of, which is - // presumably implemented across platforms. The first thing that - // threads to *before* forking is to do things like grab the malloc - // mutex, and then after the fork they unlock it. - // - // Despite this information, libnative's spawn has been witnessed to - // deadlock on both OSX and FreeBSD. I'm not entirely sure why, but - // all collected backtraces point at malloc/free traffic in the - // child spawned process. - // - // For this reason, the block of code below should contain 0 - // invocations of either malloc of free (or their related friends). - // - // As an example of not having malloc/free traffic, we don't close - // this file descriptor by dropping the FileDesc (which contains an - // allocation). Instead we just close it manually. This will never - // have the drop glue anyway because this code never returns (the - // child will either exec() or invoke libc::exit) - unsafe fn do_exec(&mut self, stdio: ChildPipes) -> io::Error { - macro_rules! t { - ($e:expr) => (match $e { - Ok(e) => e, - Err(e) => return e, - }) - } - - if let Some(fd) = stdio.stdin.fd() { - t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))); - } - if let Some(fd) = stdio.stdout.fd() { - t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))); - } - if let Some(fd) = stdio.stderr.fd() { - t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))); - } - - if let Some(u) = self.gid { - t!(cvt(libc::setgid(u as gid_t))); - } - if let Some(u) = self.uid { - // When dropping privileges from root, the `setgroups` call - // will remove any extraneous groups. If we don't call this, - // then even though our uid has dropped, we may still have - // groups that enable us to do super-user things. This will - // fail if we aren't root, so don't bother checking the - // return value, this is just done as an optimistic - // privilege dropping function. - let _ = libc::setgroups(0, ptr::null()); - - t!(cvt(libc::setuid(u as uid_t))); - } - if let Some(ref cwd) = self.cwd { - t!(cvt(libc::chdir(cwd.as_ptr()))); - } - if let Some(ref envp) = self.envp { - *sys::os::environ() = envp.as_ptr(); - } - - // NaCl has no signal support. - if cfg!(not(any(target_os = "nacl", target_os = "emscripten"))) { - // Reset signal handling so the child process starts in a - // standardized state. libstd ignores SIGPIPE, and signal-handling - // libraries often set a mask. Child processes inherit ignored - // signals and the signal mask from their parent, but most - // UNIX programs do not reset these things on their own, so we - // need to clean things up now to avoid confusing the program - // we're about to run. - let mut set: libc::sigset_t = mem::uninitialized(); - t!(cvt(libc::sigemptyset(&mut set))); - t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set, - ptr::null_mut()))); - let ret = super::signal(libc::SIGPIPE, libc::SIG_DFL); - if ret == libc::SIG_ERR { - return io::Error::last_os_error() - } - } - - for callback in self.closures.iter_mut() { - t!(callback()); - } - - libc::execvp(self.argv[0], self.argv.as_ptr()); - io::Error::last_os_error() - } - - - fn setup_io(&self, default: Stdio, needs_stdin: bool) + pub fn setup_io(&self, default: Stdio, needs_stdin: bool) -> io::Result<(StdioPipes, ChildPipes)> { let null = Stdio::Null; let default_stdin = if needs_stdin {&default} else {&null}; @@ -428,10 +269,12 @@ fn os2c(s: &OsStr, saw_nul: &mut bool) -> CString { } impl Stdio { - fn to_child_stdio(&self, readable: bool) + pub fn to_child_stdio(&self, readable: bool) -> io::Result<(ChildStdio, Option<AnonPipe>)> { match *self { - Stdio::Inherit => Ok((ChildStdio::Inherit, None)), + Stdio::Inherit => { + Ok((ChildStdio::Inherit, None)) + }, // Make sure that the source descriptors are not an stdio // descriptor, otherwise the order which we set the child's @@ -473,7 +316,7 @@ impl Stdio { } impl ChildStdio { - fn fd(&self) -> Option<c_int> { + pub fn fd(&self) -> Option<c_int> { match *self { ChildStdio::Inherit => None, ChildStdio::Explicit(fd) => Some(fd), @@ -504,15 +347,15 @@ impl fmt::Debug for Command { } } -//////////////////////////////////////////////////////////////////////////////// -// Processes -//////////////////////////////////////////////////////////////////////////////// - /// Unix exit statuses #[derive(PartialEq, Eq, Clone, Copy, Debug)] pub struct ExitStatus(c_int); impl ExitStatus { + pub fn new(status: c_int) -> ExitStatus { + ExitStatus(status) + } + fn exited(&self) -> bool { unsafe { libc::WIFEXITED(self.0) } } @@ -555,40 +398,6 @@ impl fmt::Display for ExitStatus { } } -/// The unique id of the process (this should never be negative). -pub struct Process { - pid: pid_t, - status: Option<ExitStatus>, -} - -impl Process { - pub fn id(&self) -> u32 { - self.pid as u32 - } - - pub fn kill(&mut self) -> io::Result<()> { - // If we've already waited on this process then the pid can be recycled - // and used for another process, and we probably shouldn't be killing - // random processes, so just return an error. - if self.status.is_some() { - Err(Error::new(ErrorKind::InvalidInput, - "invalid argument: can't kill an exited process")) - } else { - cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ()) - } - } - - pub fn wait(&mut self) -> io::Result<ExitStatus> { - if let Some(status) = self.status { - return Ok(status) - } - let mut status = 0 as c_int; - cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?; - self.status = Some(ExitStatus(status)); - Ok(ExitStatus(status)) - } -} - #[cfg(all(test, not(target_os = "emscripten")))] mod tests { use super::*; diff --git a/src/libstd/sys/unix/process/process_fuchsia.rs b/src/libstd/sys/unix/process/process_fuchsia.rs new file mode 100644 index 00000000000..f0a42b12799 --- /dev/null +++ b/src/libstd/sys/unix/process/process_fuchsia.rs @@ -0,0 +1,174 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use io; +use libc; +use mem; +use ptr; + +use sys::process::magenta::{Handle, launchpad_t, mx_handle_t}; +use sys::process::process_common::*; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +impl Command { + pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) + -> io::Result<(Process, StdioPipes)> { + if self.saw_nul() { + return Err(io::Error::new(io::ErrorKind::InvalidInput, + "nul byte found in provided data")); + } + + let (ours, theirs) = self.setup_io(default, needs_stdin)?; + + let (launchpad, process_handle) = unsafe { self.do_exec(theirs)? }; + + Ok((Process { launchpad: launchpad, handle: Handle::new(process_handle) }, ours)) + } + + pub fn exec(&mut self, default: Stdio) -> io::Error { + if self.saw_nul() { + return io::Error::new(io::ErrorKind::InvalidInput, + "nul byte found in provided data") + } + + match self.setup_io(default, true) { + Ok((_, _)) => { + // FIXME: This is tough because we don't support the exec syscalls + unimplemented!(); + }, + Err(e) => e, + } + } + + unsafe fn do_exec(&mut self, stdio: ChildPipes) + -> io::Result<(*mut launchpad_t, mx_handle_t)> { + use sys::process::magenta::*; + + let job_handle = mxio_get_startup_handle(mx_hnd_info(MX_HND_TYPE_JOB, 0)); + let envp = match *self.get_envp() { + Some(ref envp) => envp.as_ptr(), + None => ptr::null(), + }; + + // To make sure launchpad_destroy gets called on the launchpad if this function fails + struct LaunchpadDestructor(*mut launchpad_t); + impl Drop for LaunchpadDestructor { + fn drop(&mut self) { unsafe { launchpad_destroy(self.0); } } + } + + let mut launchpad: *mut launchpad_t = ptr::null_mut(); + let launchpad_destructor = LaunchpadDestructor(launchpad); + + // Duplicate the job handle + let mut job_copy: mx_handle_t = MX_HANDLE_INVALID; + mx_cvt(mx_handle_duplicate(job_handle, MX_RIGHT_SAME_RIGHTS, &mut job_copy))?; + // Create a launchpad + mx_cvt(launchpad_create(job_copy, self.get_argv()[0], &mut launchpad))?; + // Set the process argv + mx_cvt(launchpad_arguments(launchpad, self.get_argv().len() as i32 - 1, + self.get_argv().as_ptr()))?; + // Setup the environment vars + mx_cvt(launchpad_environ(launchpad, envp))?; + mx_cvt(launchpad_add_vdso_vmo(launchpad))?; + mx_cvt(launchpad_clone_mxio_root(launchpad))?; + // Load the executable + mx_cvt(launchpad_elf_load(launchpad, launchpad_vmo_from_file(self.get_argv()[0])))?; + mx_cvt(launchpad_load_vdso(launchpad, MX_HANDLE_INVALID))?; + mx_cvt(launchpad_clone_mxio_cwd(launchpad))?; + + // Clone stdin, stdout, and stderr + if let Some(fd) = stdio.stdin.fd() { + launchpad_transfer_fd(launchpad, fd, 0); + } else { + launchpad_clone_fd(launchpad, 0, 0); + } + if let Some(fd) = stdio.stdout.fd() { + launchpad_transfer_fd(launchpad, fd, 1); + } else { + launchpad_clone_fd(launchpad, 1, 1); + } + if let Some(fd) = stdio.stderr.fd() { + launchpad_transfer_fd(launchpad, fd, 2); + } else { + launchpad_clone_fd(launchpad, 2, 2); + } + + // We don't want FileDesc::drop to be called on any stdio. It would close their fds. The + // fds will be closed once the child process finishes. + mem::forget(stdio); + + for callback in self.get_closures().iter_mut() { + callback()?; + } + + let process_handle = mx_cvt(launchpad_start(launchpad))?; + + // Successfully started the launchpad + mem::forget(launchpad_destructor); + + Ok((launchpad, process_handle)) + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +pub struct Process { + launchpad: *mut launchpad_t, + handle: Handle, +} + +impl Process { + pub fn id(&self) -> u32 { + self.handle.raw() as u32 + } + + pub fn kill(&mut self) -> io::Result<()> { + use sys::process::magenta::*; + + unsafe { mx_cvt(mx_task_kill(self.handle.raw()))?; } + + Ok(()) + } + + pub fn wait(&mut self) -> io::Result<ExitStatus> { + use default::Default; + use sys::process::magenta::*; + + let mut proc_info: mx_info_process_t = Default::default(); + let mut actual: mx_size_t = 0; + let mut avail: mx_size_t = 0; + + unsafe { + mx_cvt(mx_handle_wait_one(self.handle.raw(), MX_TASK_TERMINATED, + MX_TIME_INFINITE, ptr::null_mut()))?; + mx_cvt(mx_object_get_info(self.handle.raw(), MX_INFO_PROCESS, + &mut proc_info as *mut _ as *mut libc::c_void, + mem::size_of::<mx_info_process_t>(), &mut actual, + &mut avail))?; + } + if actual != 1 { + return Err(io::Error::new(io::ErrorKind::InvalidData, + "Failed to get exit status of process")); + } + Ok(ExitStatus::new(proc_info.rec.return_code)) + } +} + +impl Drop for Process { + fn drop(&mut self) { + use sys::process::magenta::launchpad_destroy; + unsafe { launchpad_destroy(self.launchpad); } + } +} diff --git a/src/libstd/sys/unix/process/process_unix.rs b/src/libstd/sys/unix/process/process_unix.rs new file mode 100644 index 00000000000..aa426722025 --- /dev/null +++ b/src/libstd/sys/unix/process/process_unix.rs @@ -0,0 +1,250 @@ +// 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. + +use io::{self, Error, ErrorKind}; +use libc::{self, c_int, gid_t, pid_t, uid_t}; +use mem; +use ptr; + +use sys::cvt; +use sys::process::process_common::*; + +//////////////////////////////////////////////////////////////////////////////// +// Command +//////////////////////////////////////////////////////////////////////////////// + +impl Command { + pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) + -> io::Result<(Process, StdioPipes)> { + use sys; + + const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; + + if self.saw_nul() { + return Err(io::Error::new(ErrorKind::InvalidInput, + "nul byte found in provided data")); + } + + let (ours, theirs) = self.setup_io(default, needs_stdin)?; + let (input, output) = sys::pipe::anon_pipe()?; + + let pid = unsafe { + match cvt(libc::fork())? { + 0 => { + drop(input); + let err = self.do_exec(theirs); + let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; + let bytes = [ + (errno >> 24) as u8, + (errno >> 16) as u8, + (errno >> 8) as u8, + (errno >> 0) as u8, + CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1], + CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3] + ]; + // pipe I/O up to PIPE_BUF bytes should be atomic, and then + // we want to be sure we *don't* run at_exit destructors as + // we're being torn down regardless + assert!(output.write(&bytes).is_ok()); + libc::_exit(1) + } + n => n, + } + }; + + let mut p = Process { pid: pid, status: None }; + drop(output); + let mut bytes = [0; 8]; + + // loop to handle EINTR + loop { + match input.read(&mut bytes) { + Ok(0) => return Ok((p, ours)), + Ok(8) => { + assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]), + "Validation on the CLOEXEC pipe failed: {:?}", bytes); + let errno = combine(&bytes[0.. 4]); + assert!(p.wait().is_ok(), + "wait() should either return Ok or panic"); + return Err(Error::from_raw_os_error(errno)) + } + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(e) => { + assert!(p.wait().is_ok(), + "wait() should either return Ok or panic"); + panic!("the CLOEXEC pipe failed: {:?}", e) + }, + Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic + assert!(p.wait().is_ok(), + "wait() should either return Ok or panic"); + panic!("short read on the CLOEXEC pipe") + } + } + } + + fn combine(arr: &[u8]) -> i32 { + let a = arr[0] as u32; + let b = arr[1] as u32; + let c = arr[2] as u32; + let d = arr[3] as u32; + + ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32 + } + } + + pub fn exec(&mut self, default: Stdio) -> io::Error { + if self.saw_nul() { + return io::Error::new(ErrorKind::InvalidInput, + "nul byte found in provided data") + } + + match self.setup_io(default, true) { + Ok((_, theirs)) => unsafe { self.do_exec(theirs) }, + Err(e) => e, + } + } + + // And at this point we've reached a special time in the life of the + // child. The child must now be considered hamstrung and unable to + // do anything other than syscalls really. Consider the following + // scenario: + // + // 1. Thread A of process 1 grabs the malloc() mutex + // 2. Thread B of process 1 forks(), creating thread C + // 3. Thread C of process 2 then attempts to malloc() + // 4. The memory of process 2 is the same as the memory of + // process 1, so the mutex is locked. + // + // This situation looks a lot like deadlock, right? It turns out + // that this is what pthread_atfork() takes care of, which is + // presumably implemented across platforms. The first thing that + // threads to *before* forking is to do things like grab the malloc + // mutex, and then after the fork they unlock it. + // + // Despite this information, libnative's spawn has been witnessed to + // deadlock on both OSX and FreeBSD. I'm not entirely sure why, but + // all collected backtraces point at malloc/free traffic in the + // child spawned process. + // + // For this reason, the block of code below should contain 0 + // invocations of either malloc of free (or their related friends). + // + // As an example of not having malloc/free traffic, we don't close + // this file descriptor by dropping the FileDesc (which contains an + // allocation). Instead we just close it manually. This will never + // have the drop glue anyway because this code never returns (the + // child will either exec() or invoke libc::exit) + unsafe fn do_exec(&mut self, stdio: ChildPipes) -> io::Error { + use sys::{self, cvt_r}; + + macro_rules! t { + ($e:expr) => (match $e { + Ok(e) => e, + Err(e) => return e, + }) + } + + if let Some(fd) = stdio.stdin.fd() { + t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))); + } + if let Some(fd) = stdio.stdout.fd() { + t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))); + } + if let Some(fd) = stdio.stderr.fd() { + t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))); + } + + if let Some(u) = self.get_gid() { + t!(cvt(libc::setgid(u as gid_t))); + } + if let Some(u) = self.get_uid() { + // When dropping privileges from root, the `setgroups` call + // will remove any extraneous groups. If we don't call this, + // then even though our uid has dropped, we may still have + // groups that enable us to do super-user things. This will + // fail if we aren't root, so don't bother checking the + // return value, this is just done as an optimistic + // privilege dropping function. + let _ = libc::setgroups(0, ptr::null()); + + t!(cvt(libc::setuid(u as uid_t))); + } + if let Some(ref cwd) = *self.get_cwd() { + t!(cvt(libc::chdir(cwd.as_ptr()))); + } + if let Some(ref envp) = *self.get_envp() { + *sys::os::environ() = envp.as_ptr(); + } + + // NaCl has no signal support. + if cfg!(not(any(target_os = "nacl", target_os = "emscripten"))) { + // Reset signal handling so the child process starts in a + // standardized state. libstd ignores SIGPIPE, and signal-handling + // libraries often set a mask. Child processes inherit ignored + // signals and the signal mask from their parent, but most + // UNIX programs do not reset these things on their own, so we + // need to clean things up now to avoid confusing the program + // we're about to run. + let mut set: libc::sigset_t = mem::uninitialized(); + t!(cvt(libc::sigemptyset(&mut set))); + t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set, + ptr::null_mut()))); + let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL); + if ret == libc::SIG_ERR { + return io::Error::last_os_error() + } + } + + for callback in self.get_closures().iter_mut() { + t!(callback()); + } + + libc::execvp(self.get_argv()[0], self.get_argv().as_ptr()); + io::Error::last_os_error() + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Processes +//////////////////////////////////////////////////////////////////////////////// + +/// The unique id of the process (this should never be negative). +pub struct Process { + pid: pid_t, + status: Option<ExitStatus>, +} + +impl Process { + pub fn id(&self) -> u32 { + self.pid as u32 + } + + pub fn kill(&mut self) -> io::Result<()> { + // If we've already waited on this process then the pid can be recycled + // and used for another process, and we probably shouldn't be killing + // random processes, so just return an error. + if self.status.is_some() { + Err(Error::new(ErrorKind::InvalidInput, + "invalid argument: can't kill an exited process")) + } else { + cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ()) + } + } + pub fn wait(&mut self) -> io::Result<ExitStatus> { + use sys::cvt_r; + if let Some(status) = self.status { + return Ok(status) + } + let mut status = 0 as c_int; + cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?; + self.status = Some(ExitStatus::new(status)); + Ok(ExitStatus::new(status)) + } +} diff --git a/src/libstd/sys/windows/ext/process.rs b/src/libstd/sys/windows/ext/process.rs index bce32959a23..0a3221aeae6 100644 --- a/src/libstd/sys/windows/ext/process.rs +++ b/src/libstd/sys/windows/ext/process.rs @@ -15,7 +15,7 @@ use os::windows::io::{FromRawHandle, RawHandle, AsRawHandle, IntoRawHandle}; use process; use sys; -use sys_common::{AsInner, FromInner, IntoInner}; +use sys_common::{AsInnerMut, AsInner, FromInner, IntoInner}; #[stable(feature = "process_extensions", since = "1.2.0")] impl FromRawHandle for process::Stdio { @@ -97,3 +97,22 @@ impl ExitStatusExt for process::ExitStatus { process::ExitStatus::from_inner(From::from(raw)) } } + +/// Windows-specific extensions to the `std::process::Command` builder +#[unstable(feature = "windows_process_extensions", issue = "37827")] +pub trait CommandExt { + /// Sets the [process creation flags][1] to be passed to `CreateProcess`. + /// + /// These will always be ORed with `CREATE_UNICODE_ENVIRONMENT`. + /// [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx + #[unstable(feature = "windows_process_extensions", issue = "37827")] + fn creation_flags(&mut self, flags: u32) -> &mut process::Command; +} + +#[unstable(feature = "windows_process_extensions", issue = "37827")] +impl CommandExt for process::Command { + fn creation_flags(&mut self, flags: u32) -> &mut process::Command { + self.as_inner_mut().creation_flags(flags); + self + } +} diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs index d371714ff0e..969de6b85a6 100644 --- a/src/libstd/sys/windows/process.rs +++ b/src/libstd/sys/windows/process.rs @@ -54,6 +54,7 @@ pub struct Command { args: Vec<OsString>, env: Option<HashMap<OsString, OsString>>, cwd: Option<OsString>, + flags: u32, detach: bool, // not currently exposed in std::process stdin: Option<Stdio>, stdout: Option<Stdio>, @@ -84,6 +85,7 @@ impl Command { args: Vec::new(), env: None, cwd: None, + flags: 0, detach: false, stdin: None, stdout: None, @@ -124,6 +126,9 @@ impl Command { pub fn stderr(&mut self, stderr: Stdio) { self.stderr = Some(stderr); } + pub fn creation_flags(&mut self, flags: u32) { + self.flags = flags; + } pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) -> io::Result<(Process, StdioPipes)> { @@ -157,7 +162,7 @@ impl Command { cmd_str.push(0); // add null terminator // stolen from the libuv code. - let mut flags = c::CREATE_UNICODE_ENVIRONMENT; + let mut flags = self.flags | c::CREATE_UNICODE_ENVIRONMENT; if self.detach { flags |= c::DETACHED_PROCESS | c::CREATE_NEW_PROCESS_GROUP; } |
