Skip to content

Back io::stdin with a global singleton BufferedReader #19416

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 5, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/libstd/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
//! ```rust
//! use std::io;
//!
//! for line in io::stdin().lines() {
//! for line in io::stdin().lock().lines() {
//! print!("{}", line.unwrap());
//! }
//! ```
Expand Down Expand Up @@ -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
Expand Down
152 changes: 132 additions & 20 deletions src/libstd/io/stdio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
///
/// 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()
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/test/bench/shootout-k-nucleotide.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
4 changes: 2 additions & 2 deletions src/test/bench/sudoku.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
2 changes: 1 addition & 1 deletion src/test/run-pass/issue-13304.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn parent() {
}

fn child() {
for line in io::stdin().lines() {
for line in io::stdin().lock().lines() {
println!("{}", line.unwrap());
}
}
2 changes: 1 addition & 1 deletion src/test/run-pass/issue-14456.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
2 changes: 1 addition & 1 deletion src/test/run-pass/issue-16671.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
pub fn main() {
let mut stdin = std::io::stdin();
spawn(proc() {
let _ = stdin.lines();
let _ = stdin.read_to_end();
});
}