]> git.lizzy.rs Git - rust.git/commitdiff
Back io::stdin with a global singleton BufferedReader
authorSteven Fackler <sfackler@gmail.com>
Sun, 30 Nov 2014 07:07:43 +0000 (23:07 -0800)
committerSteven Fackler <sfackler@gmail.com>
Thu, 4 Dec 2014 07:18:52 +0000 (23:18 -0800)
io::stdin returns a new `BufferedReader` each time it's called, which
results in some very confusing behavior with disappearing output. It now
returns a `StdinReader`, which wraps a global singleton
`Arc<Mutex<BufferedReader<StdReader>>`. `Reader` is implemented directly
on `StdinReader`. However, `Buffer` is not, as the `fill_buf` method is
fundamentaly un-thread safe. A `lock` method is defined on `StdinReader`
which returns a smart pointer wrapping the underlying `BufferedReader`
while guaranteeing mutual exclusion.

Code that treats the return value of io::stdin as implementing `Buffer`
will break. Add a call to `lock`:

```rust
io::stdin().lines()
// =>
io::stdin().lock().lines()
```

Closes #14434

[breaking-change]

src/libstd/io/mod.rs
src/libstd/io/stdio.rs
src/test/bench/shootout-k-nucleotide.rs
src/test/bench/sudoku.rs
src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs
src/test/run-pass/issue-13304.rs
src/test/run-pass/issue-14456.rs
src/test/run-pass/issue-16671.rs

index 5ed10eab15b808c901b2c577a51419ff83cdad51..b2e4fc75cf2387bd7068589bd49bf487cdd6a0aa 100644 (file)
@@ -32,7 +32,7 @@
 //!     ```rust
 //!     use std::io;
 //!
-//!     for line in io::stdin().lines() {
+//!     for line in io::stdin().lock().lines() {
 //!         print!("{}", line.unwrap());
 //!     }
 //!     ```
@@ -1413,10 +1413,10 @@ pub trait Buffer: Reader {
     /// # Example
     ///
     /// ```rust
-    /// use std::io;
+    /// use std::io::BufReader;
     ///
-    /// let mut reader = io::stdin();
-    /// let input = reader.read_line().ok().unwrap_or("nothing".to_string());
+    /// let mut reader = BufReader::new(b"hello\nworld");
+    /// assert_eq!("hello\n", &*reader.read_line().unwrap());
     /// ```
     ///
     /// # Error
index 665000eae883773ccd302a23b724502455593a06..ad5dcf71df737e304ed1a3ef276d030324fbdb24 100644 (file)
 
 use boxed::Box;
 use cell::RefCell;
+use clone::Clone;
 use failure::LOCAL_STDERR;
 use fmt;
-use io::{Reader, Writer, IoResult, IoError, OtherIoError,
+use io::{Reader, Writer, IoResult, IoError, OtherIoError, Buffer,
          standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
 use kinds::Send;
 use libc;
 use mem;
 use option::{Option, Some, None};
+use ops::{Deref, DerefMut};
 use result::{Ok, Err};
 use rustrt;
 use rustrt::local::Local;
 use rustrt::task::Task;
 use slice::SlicePrelude;
 use str::StrPrelude;
+use string::String;
 use sys::{fs, tty};
+use sync::{Arc, Mutex, MutexGuard, Once, ONCE_INIT};
 use uint;
+use vec::Vec;
 
 // And so begins the tale of acquiring a uv handle to a stdio stream on all
 // platforms in all situations. Our story begins by splitting the world into two
@@ -90,28 +95,135 @@ fn src<T>(fd: libc::c_int, _readable: bool, f: |StdSource| -> T) -> T {
     RefCell::new(None)
 })
 
-/// Creates a new non-blocking handle to the stdin of the current process.
-///
-/// The returned handled is buffered by default with a `BufferedReader`. If
-/// buffered access is not desired, the `stdin_raw` function is provided to
-/// provided unbuffered access to stdin.
+/// A synchronized wrapper around a buffered reader from stdin
+#[deriving(Clone)]
+pub struct StdinReader {
+    inner: Arc<Mutex<BufferedReader<StdReader>>>,
+}
+
+/// A guard for exlusive access to `StdinReader`'s internal `BufferedReader`.
+pub struct StdinReaderGuard<'a> {
+    inner: MutexGuard<'a, BufferedReader<StdReader>>,
+}
+
+impl<'a> Deref<BufferedReader<StdReader>> for StdinReaderGuard<'a> {
+    fn deref(&self) -> &BufferedReader<StdReader> {
+        &*self.inner
+    }
+}
+
+impl<'a> DerefMut<BufferedReader<StdReader>> for StdinReaderGuard<'a> {
+    fn deref_mut(&mut self) -> &mut BufferedReader<StdReader> {
+        &mut *self.inner
+    }
+}
+
+impl StdinReader {
+    /// Locks the `StdinReader`, granting the calling thread exclusive access
+    /// to the underlying `BufferedReader`.
+    ///
+    /// This provides access to methods like `chars` and `lines`.
+    ///
+    /// ## Example
+    ///
+    /// ```rust
+    /// use std::io;
+    ///
+    /// for line in io::stdin().lock().lines() {
+    ///     println!("{}", line.unwrap());
+    /// }
+    /// ```
+    pub fn lock<'a>(&'a mut self) -> StdinReaderGuard<'a> {
+        StdinReaderGuard {
+            inner: self.inner.lock()
+        }
+    }
+
+    /// Like `Buffer::read_line`.
+    ///
+    /// The read is performed atomically - concurrent read calls in other
+    /// threads will not interleave with this one.
+    pub fn read_line(&mut self) -> IoResult<String> {
+        self.inner.lock().read_line()
+    }
+
+    /// Like `Buffer::read_until`.
+    ///
+    /// The read is performed atomically - concurrent read calls in other
+    /// threads will not interleave with this one.
+    pub fn read_until(&mut self, byte: u8) -> IoResult<Vec<u8>> {
+        self.inner.lock().read_until(byte)
+    }
+
+    /// Like `Buffer::read_char`.
+    ///
+    /// The read is performed atomically - concurrent read calls in other
+    /// threads will not interleave with this one.
+    pub fn read_char(&mut self) -> IoResult<char> {
+        self.inner.lock().read_char()
+    }
+}
+
+impl Reader for StdinReader {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        self.inner.lock().read(buf)
+    }
+
+    // We have to manually delegate all of these because the default impls call
+    // read more than once and we don't want those calls to interleave (or
+    // incur the costs of repeated locking).
+
+    fn read_at_least(&mut self, min: uint, buf: &mut [u8]) -> IoResult<uint> {
+        self.inner.lock().read_at_least(min, buf)
+    }
+
+    fn push_at_least(&mut self, min: uint, len: uint, buf: &mut Vec<u8>) -> IoResult<uint> {
+        self.inner.lock().push_at_least(min, len, buf)
+    }
+
+    fn read_to_end(&mut self) -> IoResult<Vec<u8>> {
+        self.inner.lock().read_to_end()
+    }
+
+    fn read_le_uint_n(&mut self, nbytes: uint) -> IoResult<u64> {
+        self.inner.lock().read_le_uint_n(nbytes)
+    }
+
+    fn read_be_uint_n(&mut self, nbytes: uint) -> IoResult<u64> {
+        self.inner.lock().read_be_uint_n(nbytes)
+    }
+}
+
+/// Creates a new handle to the stdin of the current process.
 ///
-/// Care should be taken when creating multiple handles to the stdin of a
-/// process. Because this is a buffered reader by default, it's possible for
-/// pending input to be unconsumed in one reader and unavailable to other
-/// readers. It is recommended that only one handle at a time is created for the
-/// stdin of a process.
+/// The returned handle is a wrapper around a global `BufferedReader` shared
+/// by all threads. If buffered access is not desired, the `stdin_raw` function
+/// is provided to provided unbuffered access to stdin.
 ///
 /// See `stdout()` for more notes about this function.
-pub fn stdin() -> BufferedReader<StdReader> {
-    // The default buffer capacity is 64k, but apparently windows doesn't like
-    // 64k reads on stdin. See #13304 for details, but the idea is that on
-    // windows we use a slightly smaller buffer that's been seen to be
-    // acceptable.
-    if cfg!(windows) {
-        BufferedReader::with_capacity(8 * 1024, stdin_raw())
-    } else {
-        BufferedReader::new(stdin_raw())
+pub fn stdin() -> StdinReader {
+    // We're following the same strategy as kimundi's lazy_static library
+    static mut STDIN: *const StdinReader = 0 as *const StdinReader;
+    static ONCE: Once = ONCE_INIT;
+
+    unsafe {
+        ONCE.doit(|| {
+            // The default buffer capacity is 64k, but apparently windows doesn't like
+            // 64k reads on stdin. See #13304 for details, but the idea is that on
+            // windows we use a slightly smaller buffer that's been seen to be
+            // acceptable.
+            let stdin = if cfg!(windows) {
+                BufferedReader::with_capacity(8 * 1024, stdin_raw())
+            } else {
+                BufferedReader::new(stdin_raw())
+            };
+            let stdin = StdinReader {
+                inner: Arc::new(Mutex::new(stdin))
+            };
+            STDIN = mem::transmute(box stdin);
+        });
+
+        (*STDIN).clone()
     }
 }
 
index b030e7bb93e87d71e5a929c4b59b2f3a0b50d046..8ed041513c47c45a2ef7006660ecba830c17832f 100644 (file)
@@ -295,7 +295,7 @@ fn main() {
         let fd = std::io::File::open(&Path::new("shootout-k-nucleotide.data"));
         get_sequence(&mut std::io::BufferedReader::new(fd), ">THREE")
     } else {
-        get_sequence(&mut std::io::stdin(), ">THREE")
+        get_sequence(&mut *std::io::stdin().lock(), ">THREE")
     };
     let input = Arc::new(input);
 
index 6664eeecd5d85d5b7bc3d3cf06d2fa2e2aa89a50..c55f85f40e8b628b180c101af342108698e8214c 100644 (file)
@@ -65,7 +65,7 @@ pub fn equal(&self, other: &Sudoku) -> bool {
         return true;
     }
 
-    pub fn read(mut reader: BufferedReader<StdReader>) -> Sudoku {
+    pub fn read(mut reader: &mut BufferedReader<StdReader>) -> Sudoku {
         /* assert first line is exactly "9,9" */
         assert!(reader.read_line().unwrap() == "9,9".to_string());
 
@@ -284,7 +284,7 @@ fn main() {
     let mut sudoku = if use_default {
         Sudoku::from_vec(&DEFAULT_SUDOKU)
     } else {
-        Sudoku::read(io::stdin())
+        Sudoku::read(&mut *io::stdin().lock())
     };
     sudoku.solve();
     sudoku.write(&mut io::stdout());
index fcb09c200000b046abe62affbb9511ca54c7fe30..daad1afedaaeae10308c5e52b1c4c24956d7c9d3 100644 (file)
@@ -14,6 +14,6 @@ fn main() {
     //~^ ERROR: cannot assign to immutable captured outer variable in a proc `x`
 
     let s = std::io::stdin();
-    proc() { s.lines(); };
+    proc() { s.read_to_end(); };
     //~^ ERROR: cannot borrow immutable captured outer variable in a proc `s` as mutable
 }
index 047ff74035b0f3cffa60bc6f561010a48f61878a..11003c6fc524de50a71f2e39331bef3168328261 100644 (file)
@@ -37,7 +37,7 @@ fn parent() {
 }
 
 fn child() {
-    for line in io::stdin().lines() {
+    for line in io::stdin().lock().lines() {
         println!("{}", line.unwrap());
     }
 }
index 2339e3f63029a3035348e240898366f1aafd88fd..f5fdf8704ed25ca888d624599e6379802c4228f6 100644 (file)
@@ -27,7 +27,7 @@ fn main() {
 fn child() {
     io::stdout().write_line("foo").unwrap();
     io::stderr().write_line("bar").unwrap();
-    assert_eq!(io::stdin().read_line().err().unwrap().kind, io::EndOfFile);
+    assert_eq!(io::stdin().lock().read_line().err().unwrap().kind, io::EndOfFile);
 }
 
 fn test() {
index a0d384418f9728e4d0508b4ddf25f28589bb71c9..27a97e1f172e36bd561713266b997edb5e4733dc 100644 (file)
@@ -19,6 +19,6 @@
 pub fn main() {
     let mut stdin = std::io::stdin();
     spawn(proc() {
-        let _ = stdin.lines();
+        let _ = stdin.read_to_end();
     });
 }