Skip to content

Commit a6ce402

Browse files
committed
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]
2 parents 26f2867 + e7c1f57 commit a6ce402

File tree

8 files changed

+143
-31
lines changed

8 files changed

+143
-31
lines changed

src/libstd/io/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
//! ```rust
3333
//! use std::io;
3434
//!
35-
//! for line in io::stdin().lines() {
35+
//! for line in io::stdin().lock().lines() {
3636
//! print!("{}", line.unwrap());
3737
//! }
3838
//! ```
@@ -1413,10 +1413,10 @@ pub trait Buffer: Reader {
14131413
/// # Example
14141414
///
14151415
/// ```rust
1416-
/// use std::io;
1416+
/// use std::io::BufReader;
14171417
///
1418-
/// let mut reader = io::stdin();
1419-
/// let input = reader.read_line().ok().unwrap_or("nothing".to_string());
1418+
/// let mut reader = BufReader::new(b"hello\nworld");
1419+
/// assert_eq!("hello\n", &*reader.read_line().unwrap());
14201420
/// ```
14211421
///
14221422
/// # Error

src/libstd/io/stdio.rs

Lines changed: 132 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,22 +29,27 @@ use self::StdSource::*;
2929

3030
use boxed::Box;
3131
use cell::RefCell;
32+
use clone::Clone;
3233
use failure::LOCAL_STDERR;
3334
use fmt;
34-
use io::{Reader, Writer, IoResult, IoError, OtherIoError,
35+
use io::{Reader, Writer, IoResult, IoError, OtherIoError, Buffer,
3536
standard_error, EndOfFile, LineBufferedWriter, BufferedReader};
3637
use kinds::Send;
3738
use libc;
3839
use mem;
3940
use option::{Option, Some, None};
41+
use ops::{Deref, DerefMut};
4042
use result::{Ok, Err};
4143
use rustrt;
4244
use rustrt::local::Local;
4345
use rustrt::task::Task;
4446
use slice::SlicePrelude;
4547
use str::StrPrelude;
48+
use string::String;
4649
use sys::{fs, tty};
50+
use sync::{Arc, Mutex, MutexGuard, Once, ONCE_INIT};
4751
use uint;
52+
use vec::Vec;
4853

4954
// And so begins the tale of acquiring a uv handle to a stdio stream on all
5055
// 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>>> = {
9095
RefCell::new(None)
9196
})
9297

93-
/// Creates a new non-blocking handle to the stdin of the current process.
94-
///
95-
/// The returned handled is buffered by default with a `BufferedReader`. If
96-
/// buffered access is not desired, the `stdin_raw` function is provided to
97-
/// provided unbuffered access to stdin.
98+
/// A synchronized wrapper around a buffered reader from stdin
99+
#[deriving(Clone)]
100+
pub struct StdinReader {
101+
inner: Arc<Mutex<BufferedReader<StdReader>>>,
102+
}
103+
104+
/// A guard for exlusive access to `StdinReader`'s internal `BufferedReader`.
105+
pub struct StdinReaderGuard<'a> {
106+
inner: MutexGuard<'a, BufferedReader<StdReader>>,
107+
}
108+
109+
impl<'a> Deref<BufferedReader<StdReader>> for StdinReaderGuard<'a> {
110+
fn deref(&self) -> &BufferedReader<StdReader> {
111+
&*self.inner
112+
}
113+
}
114+
115+
impl<'a> DerefMut<BufferedReader<StdReader>> for StdinReaderGuard<'a> {
116+
fn deref_mut(&mut self) -> &mut BufferedReader<StdReader> {
117+
&mut *self.inner
118+
}
119+
}
120+
121+
impl StdinReader {
122+
/// Locks the `StdinReader`, granting the calling thread exclusive access
123+
/// to the underlying `BufferedReader`.
124+
///
125+
/// This provides access to methods like `chars` and `lines`.
126+
///
127+
/// ## Example
128+
///
129+
/// ```rust
130+
/// use std::io;
131+
///
132+
/// for line in io::stdin().lock().lines() {
133+
/// println!("{}", line.unwrap());
134+
/// }
135+
/// ```
136+
pub fn lock<'a>(&'a mut self) -> StdinReaderGuard<'a> {
137+
StdinReaderGuard {
138+
inner: self.inner.lock()
139+
}
140+
}
141+
142+
/// Like `Buffer::read_line`.
143+
///
144+
/// The read is performed atomically - concurrent read calls in other
145+
/// threads will not interleave with this one.
146+
pub fn read_line(&mut self) -> IoResult<String> {
147+
self.inner.lock().read_line()
148+
}
149+
150+
/// Like `Buffer::read_until`.
151+
///
152+
/// The read is performed atomically - concurrent read calls in other
153+
/// threads will not interleave with this one.
154+
pub fn read_until(&mut self, byte: u8) -> IoResult<Vec<u8>> {
155+
self.inner.lock().read_until(byte)
156+
}
157+
158+
/// Like `Buffer::read_char`.
159+
///
160+
/// The read is performed atomically - concurrent read calls in other
161+
/// threads will not interleave with this one.
162+
pub fn read_char(&mut self) -> IoResult<char> {
163+
self.inner.lock().read_char()
164+
}
165+
}
166+
167+
impl Reader for StdinReader {
168+
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
169+
self.inner.lock().read(buf)
170+
}
171+
172+
// We have to manually delegate all of these because the default impls call
173+
// read more than once and we don't want those calls to interleave (or
174+
// incur the costs of repeated locking).
175+
176+
fn read_at_least(&mut self, min: uint, buf: &mut [u8]) -> IoResult<uint> {
177+
self.inner.lock().read_at_least(min, buf)
178+
}
179+
180+
fn push_at_least(&mut self, min: uint, len: uint, buf: &mut Vec<u8>) -> IoResult<uint> {
181+
self.inner.lock().push_at_least(min, len, buf)
182+
}
183+
184+
fn read_to_end(&mut self) -> IoResult<Vec<u8>> {
185+
self.inner.lock().read_to_end()
186+
}
187+
188+
fn read_le_uint_n(&mut self, nbytes: uint) -> IoResult<u64> {
189+
self.inner.lock().read_le_uint_n(nbytes)
190+
}
191+
192+
fn read_be_uint_n(&mut self, nbytes: uint) -> IoResult<u64> {
193+
self.inner.lock().read_be_uint_n(nbytes)
194+
}
195+
}
196+
197+
/// Creates a new handle to the stdin of the current process.
98198
///
99-
/// Care should be taken when creating multiple handles to the stdin of a
100-
/// process. Because this is a buffered reader by default, it's possible for
101-
/// pending input to be unconsumed in one reader and unavailable to other
102-
/// readers. It is recommended that only one handle at a time is created for the
103-
/// stdin of a process.
199+
/// The returned handle is a wrapper around a global `BufferedReader` shared
200+
/// by all threads. If buffered access is not desired, the `stdin_raw` function
201+
/// is provided to provided unbuffered access to stdin.
104202
///
105203
/// See `stdout()` for more notes about this function.
106-
pub fn stdin() -> BufferedReader<StdReader> {
107-
// The default buffer capacity is 64k, but apparently windows doesn't like
108-
// 64k reads on stdin. See #13304 for details, but the idea is that on
109-
// windows we use a slightly smaller buffer that's been seen to be
110-
// acceptable.
111-
if cfg!(windows) {
112-
BufferedReader::with_capacity(8 * 1024, stdin_raw())
113-
} else {
114-
BufferedReader::new(stdin_raw())
204+
pub fn stdin() -> StdinReader {
205+
// We're following the same strategy as kimundi's lazy_static library
206+
static mut STDIN: *const StdinReader = 0 as *const StdinReader;
207+
static ONCE: Once = ONCE_INIT;
208+
209+
unsafe {
210+
ONCE.doit(|| {
211+
// The default buffer capacity is 64k, but apparently windows doesn't like
212+
// 64k reads on stdin. See #13304 for details, but the idea is that on
213+
// windows we use a slightly smaller buffer that's been seen to be
214+
// acceptable.
215+
let stdin = if cfg!(windows) {
216+
BufferedReader::with_capacity(8 * 1024, stdin_raw())
217+
} else {
218+
BufferedReader::new(stdin_raw())
219+
};
220+
let stdin = StdinReader {
221+
inner: Arc::new(Mutex::new(stdin))
222+
};
223+
STDIN = mem::transmute(box stdin);
224+
});
225+
226+
(*STDIN).clone()
115227
}
116228
}
117229

src/test/bench/shootout-k-nucleotide.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ fn main() {
295295
let fd = std::io::File::open(&Path::new("shootout-k-nucleotide.data"));
296296
get_sequence(&mut std::io::BufferedReader::new(fd), ">THREE")
297297
} else {
298-
get_sequence(&mut std::io::stdin(), ">THREE")
298+
get_sequence(&mut *std::io::stdin().lock(), ">THREE")
299299
};
300300
let input = Arc::new(input);
301301

src/test/bench/sudoku.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ impl Sudoku {
6565
return true;
6666
}
6767

68-
pub fn read(mut reader: BufferedReader<StdReader>) -> Sudoku {
68+
pub fn read(mut reader: &mut BufferedReader<StdReader>) -> Sudoku {
6969
/* assert first line is exactly "9,9" */
7070
assert!(reader.read_line().unwrap() == "9,9".to_string());
7171

@@ -284,7 +284,7 @@ fn main() {
284284
let mut sudoku = if use_default {
285285
Sudoku::from_vec(&DEFAULT_SUDOKU)
286286
} else {
287-
Sudoku::read(io::stdin())
287+
Sudoku::read(&mut *io::stdin().lock())
288288
};
289289
sudoku.solve();
290290
sudoku.write(&mut io::stdout());

src/test/compile-fail/cannot-mutate-captured-non-mut-var.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ fn main() {
1414
//~^ ERROR: cannot assign to immutable captured outer variable in a proc `x`
1515

1616
let s = std::io::stdin();
17-
proc() { s.lines(); };
17+
proc() { s.read_to_end(); };
1818
//~^ ERROR: cannot borrow immutable captured outer variable in a proc `s` as mutable
1919
}

src/test/run-pass/issue-13304.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ fn parent() {
3737
}
3838

3939
fn child() {
40-
for line in io::stdin().lines() {
40+
for line in io::stdin().lock().lines() {
4141
println!("{}", line.unwrap());
4242
}
4343
}

src/test/run-pass/issue-14456.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ fn main() {
2727
fn child() {
2828
io::stdout().write_line("foo").unwrap();
2929
io::stderr().write_line("bar").unwrap();
30-
assert_eq!(io::stdin().read_line().err().unwrap().kind, io::EndOfFile);
30+
assert_eq!(io::stdin().lock().read_line().err().unwrap().kind, io::EndOfFile);
3131
}
3232

3333
fn test() {

src/test/run-pass/issue-16671.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@
1919
pub fn main() {
2020
let mut stdin = std::io::stdin();
2121
spawn(proc() {
22-
let _ = stdin.lines();
22+
let _ = stdin.read_to_end();
2323
});
2424
}

0 commit comments

Comments
 (0)