rollup merge of #19416: sfackler/global-stdin
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().read_line(); // => io::stdin().lock().read_line(); ``` Closes #14434 [breaking-change]
This commit is contained in:
commit
a6ce402401
@ -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
|
||||
|
@ -29,22 +29,27 @@ use self::StdSource::*;
|
||||
|
||||
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 @@ thread_local!(static LOCAL_STDOUT: RefCell<Option<Box<Writer + Send>>> = {
|
||||
RefCell::new(None)
|
||||
})
|
||||
|
||||
/// Creates a new non-blocking handle to the stdin of the current process.
|
||||
/// 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.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -65,7 +65,7 @@ impl Sudoku {
|
||||
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());
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ fn parent() {
|
||||
}
|
||||
|
||||
fn child() {
|
||||
for line in io::stdin().lines() {
|
||||
for line in io::stdin().lock().lines() {
|
||||
println!("{}", line.unwrap());
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -19,6 +19,6 @@
|
||||
pub fn main() {
|
||||
let mut stdin = std::io::stdin();
|
||||
spawn(proc() {
|
||||
let _ = stdin.lines();
|
||||
let _ = stdin.read_to_end();
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user