about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-02-25 15:04:40 +0000
committerbors <bors@rust-lang.org>2018-02-25 15:04:40 +0000
commit322d7f7b97f1672bade31e1c43d5753e0e45a1a8 (patch)
tree9da99f11e21caf2cb8e83bfa84cff6a2f0c376ee /src/libstd
parent026339e42ba11a559767029d933d1197aefb877a (diff)
parent1aa103511b6c52943e54f62992551babef9d4526 (diff)
downloadrust-322d7f7b97f1672bade31e1c43d5753e0e45a1a8.tar.gz
rust-322d7f7b97f1672bade31e1c43d5753e0e45a1a8.zip
Auto merge of #48531 - kennytm:rollup, r=kennytm
Rollup of 17 pull requests

- Successful merges: #47964, #47970, #48076, #48115, #48166, #48281, #48297, #48302, #48362, #48369, #48489, #48491, #48494, #48517, #48529, #48235, #48330
- Failed merges:
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/ascii.rs2
-rw-r--r--src/libstd/collections/hash/map.rs3
-rw-r--r--src/libstd/net/tcp.rs20
-rw-r--r--src/libstd/net/udp.rs17
-rw-r--r--src/libstd/sync/condvar.rs216
-rw-r--r--src/libstd/sys/redox/net/tcp.rs10
-rw-r--r--src/libstd/sys/redox/net/udp.rs10
-rw-r--r--src/libstd/sys/unix/ext/net.rs41
8 files changed, 311 insertions, 8 deletions
diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs
index 82e1a3447dc..430c9df396a 100644
--- a/src/libstd/ascii.rs
+++ b/src/libstd/ascii.rs
@@ -245,7 +245,7 @@ pub trait AsciiExt {
     fn is_ascii_punctuation(&self) -> bool { unimplemented!(); }
 
     /// Checks if the value is an ASCII graphic character:
-    /// U+0021 '@' ... U+007E '~'.
+    /// U+0021 '!' ... U+007E '~'.
     /// For strings, true if all characters in the string are
     /// ASCII graphic characters.
     ///
diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs
index a82ff915093..4dfdc23ebee 100644
--- a/src/libstd/collections/hash/map.rs
+++ b/src/libstd/collections/hash/map.rs
@@ -2082,7 +2082,6 @@ impl<'a, K, V> Entry<'a, K, V> {
     /// # Examples
     ///
     /// ```
-    /// #![feature(entry_and_modify)]
     /// use std::collections::HashMap;
     ///
     /// let mut map: HashMap<&str, u32> = HashMap::new();
@@ -2097,7 +2096,7 @@ impl<'a, K, V> Entry<'a, K, V> {
     ///    .or_insert(42);
     /// assert_eq!(map["poneyland"], 43);
     /// ```
-    #[unstable(feature = "entry_and_modify", issue = "44733")]
+    #[stable(feature = "entry_and_modify", since = "1.26.0")]
     pub fn and_modify<F>(self, mut f: F) -> Self
         where F: FnMut(&mut V)
     {
diff --git a/src/libstd/net/tcp.rs b/src/libstd/net/tcp.rs
index 263a2c13249..7931d14ed11 100644
--- a/src/libstd/net/tcp.rs
+++ b/src/libstd/net/tcp.rs
@@ -1545,6 +1545,26 @@ mod tests {
         drop(listener);
     }
 
+    // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
+    // when passed zero Durations
+    #[test]
+    fn test_timeout_zero_duration() {
+        let addr = next_test_ip4();
+
+        let listener = t!(TcpListener::bind(&addr));
+        let stream = t!(TcpStream::connect(&addr));
+
+        let result = stream.set_write_timeout(Some(Duration::new(0, 0)));
+        let err = result.unwrap_err();
+        assert_eq!(err.kind(), ErrorKind::InvalidInput);
+
+        let result = stream.set_read_timeout(Some(Duration::new(0, 0)));
+        let err = result.unwrap_err();
+        assert_eq!(err.kind(), ErrorKind::InvalidInput);
+
+        drop(listener);
+    }
+
     #[test]
     fn nodelay() {
         let addr = next_test_ip4();
diff --git a/src/libstd/net/udp.rs b/src/libstd/net/udp.rs
index 5e19519b88f..b49cc78ac40 100644
--- a/src/libstd/net/udp.rs
+++ b/src/libstd/net/udp.rs
@@ -1024,6 +1024,23 @@ mod tests {
         assert!(start.elapsed() > Duration::from_millis(400));
     }
 
+    // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
+    // when passed zero Durations
+    #[test]
+    fn test_timeout_zero_duration() {
+        let addr = next_test_ip4();
+
+        let socket = t!(UdpSocket::bind(&addr));
+
+        let result = socket.set_write_timeout(Some(Duration::new(0, 0)));
+        let err = result.unwrap_err();
+        assert_eq!(err.kind(), ErrorKind::InvalidInput);
+
+        let result = socket.set_read_timeout(Some(Duration::new(0, 0)));
+        let err = result.unwrap_err();
+        assert_eq!(err.kind(), ErrorKind::InvalidInput);
+    }
+
     #[test]
     fn connect_send_recv() {
         let addr = next_test_ip4();
diff --git a/src/libstd/sync/condvar.rs b/src/libstd/sync/condvar.rs
index 54bb6513650..3014283da5b 100644
--- a/src/libstd/sync/condvar.rs
+++ b/src/libstd/sync/condvar.rs
@@ -14,7 +14,7 @@ use sync::{mutex, MutexGuard, PoisonError};
 use sys_common::condvar as sys;
 use sys_common::mutex as sys_mutex;
 use sys_common::poison::{self, LockResult};
-use time::Duration;
+use time::{Duration, Instant};
 
 /// A type indicating whether a timed wait on a condition variable returned
 /// due to a time out or not.
@@ -221,6 +221,64 @@ impl Condvar {
         }
     }
 
+    /// Blocks the current thread until this condition variable receives a
+    /// notification and the required condition is met. Spurious wakeups are
+    /// ignored and this function will only return once the condition has been
+    /// met.
+    ///
+    /// This function will atomically unlock the mutex specified (represented by
+    /// `guard`) and block the current thread. This means that any calls
+    /// to [`notify_one`] or [`notify_all`] which happen logically after the
+    /// mutex is unlocked are candidates to wake this thread up. When this
+    /// function call returns, the lock specified will have been re-acquired.
+    ///
+    /// # Errors
+    ///
+    /// This function will return an error if the mutex being waited on is
+    /// poisoned when this thread re-acquires the lock. For more information,
+    /// see information about [poisoning] on the [`Mutex`] type.
+    ///
+    /// [`notify_one`]: #method.notify_one
+    /// [`notify_all`]: #method.notify_all
+    /// [poisoning]: ../sync/struct.Mutex.html#poisoning
+    /// [`Mutex`]: ../sync/struct.Mutex.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(wait_until)]
+    ///
+    /// use std::sync::{Arc, Mutex, Condvar};
+    /// use std::thread;
+    ///
+    /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
+    /// let pair2 = pair.clone();
+    ///
+    /// thread::spawn(move|| {
+    ///     let &(ref lock, ref cvar) = &*pair2;
+    ///     let mut started = lock.lock().unwrap();
+    ///     *started = true;
+    ///     // We notify the condvar that the value has changed.
+    ///     cvar.notify_one();
+    /// });
+    ///
+    /// // Wait for the thread to start up.
+    /// let &(ref lock, ref cvar) = &*pair;
+    /// // As long as the value inside the `Mutex` is false, we wait.
+    /// let _guard = cvar.wait_until(lock.lock().unwrap(), |started| { *started }).unwrap();
+    /// ```
+    #[unstable(feature = "wait_until", issue = "47960")]
+    pub fn wait_until<'a, T, F>(&self, mut guard: MutexGuard<'a, T>,
+                                mut condition: F)
+                                -> LockResult<MutexGuard<'a, T>>
+                                where F: FnMut(&mut T) -> bool {
+        while !condition(&mut *guard) {
+            guard = self.wait(guard)?;
+        }
+        Ok(guard)
+    }
+
+
     /// Waits on this condition variable for a notification, timing out after a
     /// specified duration.
     ///
@@ -295,7 +353,15 @@ impl Condvar {
     ///
     /// Note that the best effort is made to ensure that the time waited is
     /// measured with a monotonic clock, and not affected by the changes made to
-    /// the system time.
+    /// the system time.  This function is susceptible to spurious wakeups.
+    /// Condition variables normally have a boolean predicate associated with
+    /// them, and the predicate must always be checked each time this function
+    /// returns to protect against spurious wakeups.  Additionally, it is
+    /// typically desirable for the time-out to not exceed some duration in
+    /// spite of spurious wakes, thus the sleep-duration is decremented by the
+    /// amount slept.  Alternatively, use the `wait_timeout_until` method
+    /// to wait until a condition is met with a total time-out regardless
+    /// of spurious wakes.
     ///
     /// The returned [`WaitTimeoutResult`] value indicates if the timeout is
     /// known to have elapsed.
@@ -304,6 +370,7 @@ impl Condvar {
     /// returns, regardless of whether the timeout elapsed or not.
     ///
     /// [`wait`]: #method.wait
+    /// [`wait_timeout_until`]: #method.wait_timeout_until
     /// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html
     ///
     /// # Examples
@@ -355,6 +422,80 @@ impl Condvar {
         }
     }
 
+    /// Waits on this condition variable for a notification, timing out after a
+    /// specified duration.  Spurious wakes will not cause this function to
+    /// return.
+    ///
+    /// The semantics of this function are equivalent to [`wait_until`] except
+    /// that the thread will be blocked for roughly no longer than `dur`. This
+    /// method should not be used for precise timing due to anomalies such as
+    /// preemption or platform differences that may not cause the maximum
+    /// amount of time waited to be precisely `dur`.
+    ///
+    /// Note that the best effort is made to ensure that the time waited is
+    /// measured with a monotonic clock, and not affected by the changes made to
+    /// the system time.
+    ///
+    /// The returned [`WaitTimeoutResult`] value indicates if the timeout is
+    /// known to have elapsed without the condition being met.
+    ///
+    /// Like [`wait_until`], the lock specified will be re-acquired when this
+    /// function returns, regardless of whether the timeout elapsed or not.
+    ///
+    /// [`wait_until`]: #method.wait_until
+    /// [`wait_timeout`]: #method.wait_timeout
+    /// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(wait_timeout_until)]
+    ///
+    /// use std::sync::{Arc, Mutex, Condvar};
+    /// use std::thread;
+    /// use std::time::Duration;
+    ///
+    /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
+    /// let pair2 = pair.clone();
+    ///
+    /// thread::spawn(move|| {
+    ///     let &(ref lock, ref cvar) = &*pair2;
+    ///     let mut started = lock.lock().unwrap();
+    ///     *started = true;
+    ///     // We notify the condvar that the value has changed.
+    ///     cvar.notify_one();
+    /// });
+    ///
+    /// // wait for the thread to start up
+    /// let &(ref lock, ref cvar) = &*pair;
+    /// let result = cvar.wait_timeout_until(
+    ///     lock.lock().unwrap(),
+    ///     Duration::from_millis(100),
+    ///     |&mut started| started,
+    /// ).unwrap();
+    /// if result.1.timed_out() {
+    ///     // timed-out without the condition ever evaluating to true.
+    /// }
+    /// // access the locked mutex via result.0
+    /// ```
+    #[unstable(feature = "wait_timeout_until", issue = "47960")]
+    pub fn wait_timeout_until<'a, T, F>(&self, mut guard: MutexGuard<'a, T>,
+                                        dur: Duration, mut condition: F)
+                                        -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)>
+                                        where F: FnMut(&mut T) -> bool {
+        let start = Instant::now();
+        loop {
+            if condition(&mut *guard) {
+                return Ok((guard, WaitTimeoutResult(false)));
+            }
+            let timeout = match dur.checked_sub(start.elapsed()) {
+                Some(timeout) => timeout,
+                None => return Ok((guard, WaitTimeoutResult(true))),
+            };
+            guard = self.wait_timeout(guard, timeout)?.0;
+        }
+    }
+
     /// Wakes up one blocked thread on this condvar.
     ///
     /// If there is a blocked thread on this condition variable, then it will
@@ -480,6 +621,7 @@ impl Drop for Condvar {
 
 #[cfg(test)]
 mod tests {
+    /// #![feature(wait_until)]
     use sync::mpsc::channel;
     use sync::{Condvar, Mutex, Arc};
     use sync::atomic::{AtomicBool, Ordering};
@@ -550,6 +692,29 @@ mod tests {
 
     #[test]
     #[cfg_attr(target_os = "emscripten", ignore)]
+    fn wait_until() {
+        let pair = Arc::new((Mutex::new(false), Condvar::new()));
+        let pair2 = pair.clone();
+
+        // Inside of our lock, spawn a new thread, and then wait for it to start.
+        thread::spawn(move|| {
+            let &(ref lock, ref cvar) = &*pair2;
+            let mut started = lock.lock().unwrap();
+            *started = true;
+            // We notify the condvar that the value has changed.
+            cvar.notify_one();
+        });
+
+        // Wait for the thread to start up.
+        let &(ref lock, ref cvar) = &*pair;
+        let guard = cvar.wait_until(lock.lock().unwrap(), |started| {
+            *started
+        });
+        assert!(*guard.unwrap());
+    }
+
+    #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn wait_timeout_wait() {
         let m = Arc::new(Mutex::new(()));
         let c = Arc::new(Condvar::new());
@@ -569,6 +734,53 @@ mod tests {
 
     #[test]
     #[cfg_attr(target_os = "emscripten", ignore)]
+    fn wait_timeout_until_wait() {
+        let m = Arc::new(Mutex::new(()));
+        let c = Arc::new(Condvar::new());
+
+        let g = m.lock().unwrap();
+        let (_g, wait) = c.wait_timeout_until(g, Duration::from_millis(1), |_| { false }).unwrap();
+        // no spurious wakeups. ensure it timed-out
+        assert!(wait.timed_out());
+    }
+
+    #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
+    fn wait_timeout_until_instant_satisfy() {
+        let m = Arc::new(Mutex::new(()));
+        let c = Arc::new(Condvar::new());
+
+        let g = m.lock().unwrap();
+        let (_g, wait) = c.wait_timeout_until(g, Duration::from_millis(0), |_| { true }).unwrap();
+        // ensure it didn't time-out even if we were not given any time.
+        assert!(!wait.timed_out());
+    }
+
+    #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
+    fn wait_timeout_until_wake() {
+        let pair = Arc::new((Mutex::new(false), Condvar::new()));
+        let pair_copy = pair.clone();
+
+        let &(ref m, ref c) = &*pair;
+        let g = m.lock().unwrap();
+        let _t = thread::spawn(move || {
+            let &(ref lock, ref cvar) = &*pair_copy;
+            let mut started = lock.lock().unwrap();
+            thread::sleep(Duration::from_millis(1));
+            *started = true;
+            cvar.notify_one();
+        });
+        let (g2, wait) = c.wait_timeout_until(g, Duration::from_millis(u64::MAX), |&mut notified| {
+            notified
+        }).unwrap();
+        // ensure it didn't time-out even if we were not given any time.
+        assert!(!wait.timed_out());
+        assert!(*g2);
+    }
+
+    #[test]
+    #[cfg_attr(target_os = "emscripten", ignore)]
     fn wait_timeout_wake() {
         let m = Arc::new(Mutex::new(()));
         let c = Arc::new(Condvar::new());
diff --git a/src/libstd/sys/redox/net/tcp.rs b/src/libstd/sys/redox/net/tcp.rs
index 319965ab396..b5664908479 100644
--- a/src/libstd/sys/redox/net/tcp.rs
+++ b/src/libstd/sys/redox/net/tcp.rs
@@ -9,7 +9,7 @@
 // except according to those terms.
 
 use cmp;
-use io::{Error, ErrorKind, Result};
+use io::{self, Error, ErrorKind, Result};
 use mem;
 use net::{SocketAddr, Shutdown};
 use path::Path;
@@ -130,6 +130,10 @@ impl TcpStream {
     pub fn set_read_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
         let file = self.0.dup(b"read_timeout")?;
         if let Some(duration) = duration_option {
+            if duration.as_secs() == 0 && duration.subsec_nanos() == 0 {
+                return Err(io::Error::new(io::ErrorKind::InvalidInput,
+                                          "cannot set a 0 duration timeout"));
+            }
             file.write(&TimeSpec {
                 tv_sec: duration.as_secs() as i64,
                 tv_nsec: duration.subsec_nanos() as i32
@@ -143,6 +147,10 @@ impl TcpStream {
     pub fn set_write_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
         let file = self.0.dup(b"write_timeout")?;
         if let Some(duration) = duration_option {
+            if duration.as_secs() == 0 && duration.subsec_nanos() == 0 {
+                return Err(io::Error::new(io::ErrorKind::InvalidInput,
+                                          "cannot set a 0 duration timeout"));
+            }
             file.write(&TimeSpec {
                 tv_sec: duration.as_secs() as i64,
                 tv_nsec: duration.subsec_nanos() as i32
diff --git a/src/libstd/sys/redox/net/udp.rs b/src/libstd/sys/redox/net/udp.rs
index 7e7666e7ef3..2ed67bd2836 100644
--- a/src/libstd/sys/redox/net/udp.rs
+++ b/src/libstd/sys/redox/net/udp.rs
@@ -10,7 +10,7 @@
 
 use cell::UnsafeCell;
 use cmp;
-use io::{Error, ErrorKind, Result};
+use io::{self, Error, ErrorKind, Result};
 use mem;
 use net::{SocketAddr, Ipv4Addr, Ipv6Addr};
 use path::Path;
@@ -179,6 +179,10 @@ impl UdpSocket {
     pub fn set_read_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
         let file = self.0.dup(b"read_timeout")?;
         if let Some(duration) = duration_option {
+            if duration.as_secs() == 0 && duration.subsec_nanos() == 0 {
+                return Err(io::Error::new(io::ErrorKind::InvalidInput,
+                                          "cannot set a 0 duration timeout"));
+            }
             file.write(&TimeSpec {
                 tv_sec: duration.as_secs() as i64,
                 tv_nsec: duration.subsec_nanos() as i32
@@ -192,6 +196,10 @@ impl UdpSocket {
     pub fn set_write_timeout(&self, duration_option: Option<Duration>) -> Result<()> {
         let file = self.0.dup(b"write_timeout")?;
         if let Some(duration) = duration_option {
+            if duration.as_secs() == 0 && duration.subsec_nanos() == 0 {
+                return Err(io::Error::new(io::ErrorKind::InvalidInput,
+                                          "cannot set a 0 duration timeout"));
+            }
             file.write(&TimeSpec {
                 tv_sec: duration.as_secs() as i64,
                 tv_nsec: duration.subsec_nanos() as i32
diff --git a/src/libstd/sys/unix/ext/net.rs b/src/libstd/sys/unix/ext/net.rs
index 31bdc5ea1f5..ab4aef1a582 100644
--- a/src/libstd/sys/unix/ext/net.rs
+++ b/src/libstd/sys/unix/ext/net.rs
@@ -1410,7 +1410,7 @@ impl IntoRawFd for UnixDatagram {
 #[cfg(all(test, not(target_os = "emscripten")))]
 mod test {
     use thread;
-    use io;
+    use io::{self, ErrorKind};
     use io::prelude::*;
     use time::Duration;
     use sys_common::io::test::tmpdir;
@@ -1613,6 +1613,27 @@ mod test {
         assert!(kind == io::ErrorKind::WouldBlock || kind == io::ErrorKind::TimedOut);
     }
 
+    // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
+    // when passed zero Durations
+    #[test]
+    fn test_unix_stream_timeout_zero_duration() {
+        let dir = tmpdir();
+        let socket_path = dir.path().join("sock");
+
+        let listener = or_panic!(UnixListener::bind(&socket_path));
+        let stream = or_panic!(UnixStream::connect(&socket_path));
+
+        let result = stream.set_write_timeout(Some(Duration::new(0, 0)));
+        let err = result.unwrap_err();
+        assert_eq!(err.kind(), ErrorKind::InvalidInput);
+
+        let result = stream.set_read_timeout(Some(Duration::new(0, 0)));
+        let err = result.unwrap_err();
+        assert_eq!(err.kind(), ErrorKind::InvalidInput);
+
+        drop(listener);
+    }
+
     #[test]
     fn test_unix_datagram() {
         let dir = tmpdir();
@@ -1712,6 +1733,24 @@ mod test {
         thread.join().unwrap();
     }
 
+    // Ensure the `set_read_timeout` and `set_write_timeout` calls return errors
+    // when passed zero Durations
+    #[test]
+    fn test_unix_datagram_timeout_zero_duration() {
+        let dir = tmpdir();
+        let path = dir.path().join("sock");
+
+        let datagram = or_panic!(UnixDatagram::bind(&path));
+
+        let result = datagram.set_write_timeout(Some(Duration::new(0, 0)));
+        let err = result.unwrap_err();
+        assert_eq!(err.kind(), ErrorKind::InvalidInput);
+
+        let result = datagram.set_read_timeout(Some(Duration::new(0, 0)));
+        let err = result.unwrap_err();
+        assert_eq!(err.kind(), ErrorKind::InvalidInput);
+    }
+
     #[test]
     fn abstract_namespace_not_allowed() {
         assert!(UnixStream::connect("\0asdf").is_err());