Skip to content

Commit 7517ecf

Browse files
committed
Auto merge of #26168 - sfackler:stdout-panic, r=alexcrichton
Closes #25977 The various `stdfoo_raw` methods in std::io now return `io::Result`s, since they may not exist on Windows. They will always return `Ok` on Unix-like platforms. [breaking-change]
2 parents a54a809 + a7bbd7d commit 7517ecf

File tree

7 files changed

+161
-33
lines changed

7 files changed

+161
-33
lines changed

src/libstd/io/stdio.rs

+77-19
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use io::{self, BufReader, LineWriter};
1919
use sync::{Arc, Mutex, MutexGuard};
2020
use sys::stdio;
2121
use sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
22+
use libc;
2223

2324
/// Stdout used by print! and println! macros
2425
thread_local! {
@@ -52,7 +53,7 @@ struct StderrRaw(stdio::Stderr);
5253
/// handles is **not** available to raw handles returned from this function.
5354
///
5455
/// The returned handle has no external synchronization or buffering.
55-
fn stdin_raw() -> StdinRaw { StdinRaw(stdio::Stdin::new()) }
56+
fn stdin_raw() -> io::Result<StdinRaw> { stdio::Stdin::new().map(StdinRaw) }
5657

5758
/// Constructs a new raw handle to the standard input stream of this process.
5859
///
@@ -63,7 +64,7 @@ fn stdin_raw() -> StdinRaw { StdinRaw(stdio::Stdin::new()) }
6364
///
6465
/// The returned handle has no external synchronization or buffering layered on
6566
/// top.
66-
fn stdout_raw() -> StdoutRaw { StdoutRaw(stdio::Stdout::new()) }
67+
fn stdout_raw() -> io::Result<StdoutRaw> { stdio::Stdout::new().map(StdoutRaw) }
6768

6869
/// Constructs a new raw handle to the standard input stream of this process.
6970
///
@@ -72,7 +73,7 @@ fn stdout_raw() -> StdoutRaw { StdoutRaw(stdio::Stdout::new()) }
7273
///
7374
/// The returned handle has no external synchronization or buffering layered on
7475
/// top.
75-
fn stderr_raw() -> StderrRaw { StderrRaw(stdio::Stderr::new()) }
76+
fn stderr_raw() -> io::Result<StderrRaw> { stdio::Stderr::new().map(StderrRaw) }
7677

7778
impl Read for StdinRaw {
7879
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { self.0.read(buf) }
@@ -86,6 +87,48 @@ impl Write for StderrRaw {
8687
fn flush(&mut self) -> io::Result<()> { Ok(()) }
8788
}
8889

90+
enum Maybe<T> {
91+
Real(T),
92+
Fake,
93+
}
94+
95+
impl<W: io::Write> io::Write for Maybe<W> {
96+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
97+
match *self {
98+
Maybe::Real(ref mut w) => handle_ebadf(w.write(buf), buf.len()),
99+
Maybe::Fake => Ok(buf.len())
100+
}
101+
}
102+
103+
fn flush(&mut self) -> io::Result<()> {
104+
match *self {
105+
Maybe::Real(ref mut w) => handle_ebadf(w.flush(), ()),
106+
Maybe::Fake => Ok(())
107+
}
108+
}
109+
}
110+
111+
impl<R: io::Read> io::Read for Maybe<R> {
112+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
113+
match *self {
114+
Maybe::Real(ref mut r) => handle_ebadf(r.read(buf), buf.len()),
115+
Maybe::Fake => Ok(0)
116+
}
117+
}
118+
}
119+
120+
fn handle_ebadf<T>(r: io::Result<T>, default: T) -> io::Result<T> {
121+
#[cfg(windows)]
122+
const ERR: libc::c_int = libc::ERROR_INVALID_HANDLE;
123+
#[cfg(not(windows))]
124+
const ERR: libc::c_int = libc::EBADF;
125+
126+
match r {
127+
Err(ref e) if e.raw_os_error() == Some(ERR) => Ok(default),
128+
r => r
129+
}
130+
}
131+
89132
/// A handle to the standard input stream of a process.
90133
///
91134
/// Each handle is a shared reference to a global buffer of input data to this
@@ -99,7 +142,7 @@ impl Write for StderrRaw {
99142
/// Created by the function `io::stdin()`.
100143
#[stable(feature = "rust1", since = "1.0.0")]
101144
pub struct Stdin {
102-
inner: Arc<Mutex<BufReader<StdinRaw>>>,
145+
inner: Arc<Mutex<BufReader<Maybe<StdinRaw>>>>,
103146
}
104147

105148
/// A locked reference to the a `Stdin` handle.
@@ -108,7 +151,7 @@ pub struct Stdin {
108151
/// constructed via the `lock` method on `Stdin`.
109152
#[stable(feature = "rust1", since = "1.0.0")]
110153
pub struct StdinLock<'a> {
111-
inner: MutexGuard<'a, BufReader<StdinRaw>>,
154+
inner: MutexGuard<'a, BufReader<Maybe<StdinRaw>>>,
112155
}
113156

114157
/// Creates a new handle to the global standard input stream of this process.
@@ -122,20 +165,25 @@ pub struct StdinLock<'a> {
122165
/// locked version, `StdinLock`, implements both `Read` and `BufRead`, however.
123166
#[stable(feature = "rust1", since = "1.0.0")]
124167
pub fn stdin() -> Stdin {
125-
static INSTANCE: Lazy<Mutex<BufReader<StdinRaw>>> = Lazy::new(stdin_init);
168+
static INSTANCE: Lazy<Mutex<BufReader<Maybe<StdinRaw>>>> = Lazy::new(stdin_init);
126169
return Stdin {
127170
inner: INSTANCE.get().expect("cannot access stdin during shutdown"),
128171
};
129172

130-
fn stdin_init() -> Arc<Mutex<BufReader<StdinRaw>>> {
173+
fn stdin_init() -> Arc<Mutex<BufReader<Maybe<StdinRaw>>>> {
174+
let stdin = match stdin_raw() {
175+
Ok(stdin) => Maybe::Real(stdin),
176+
_ => Maybe::Fake
177+
};
178+
131179
// The default buffer capacity is 64k, but apparently windows
132180
// doesn't like 64k reads on stdin. See #13304 for details, but the
133181
// idea is that on windows we use a slightly smaller buffer that's
134182
// been seen to be acceptable.
135183
Arc::new(Mutex::new(if cfg!(windows) {
136-
BufReader::with_capacity(8 * 1024, stdin_raw())
184+
BufReader::with_capacity(8 * 1024, stdin)
137185
} else {
138-
BufReader::new(stdin_raw())
186+
BufReader::new(stdin)
139187
}))
140188
}
141189
}
@@ -181,6 +229,7 @@ impl<'a> Read for StdinLock<'a> {
181229
self.inner.read(buf)
182230
}
183231
}
232+
184233
#[stable(feature = "rust1", since = "1.0.0")]
185234
impl<'a> BufRead for StdinLock<'a> {
186235
fn fill_buf(&mut self) -> io::Result<&[u8]> { self.inner.fill_buf() }
@@ -215,7 +264,7 @@ pub struct Stdout {
215264
// FIXME: this should be LineWriter or BufWriter depending on the state of
216265
// stdout (tty or not). Note that if this is not line buffered it
217266
// should also flush-on-panic or some form of flush-on-abort.
218-
inner: Arc<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>>,
267+
inner: Arc<ReentrantMutex<RefCell<LineWriter<Maybe<StdoutRaw>>>>>,
219268
}
220269

221270
/// A locked reference to the a `Stdout` handle.
@@ -224,7 +273,7 @@ pub struct Stdout {
224273
/// method on `Stdout`.
225274
#[stable(feature = "rust1", since = "1.0.0")]
226275
pub struct StdoutLock<'a> {
227-
inner: ReentrantMutexGuard<'a, RefCell<LineWriter<StdoutRaw>>>,
276+
inner: ReentrantMutexGuard<'a, RefCell<LineWriter<Maybe<StdoutRaw>>>>,
228277
}
229278

230279
/// Constructs a new reference to the standard output of the current process.
@@ -236,13 +285,18 @@ pub struct StdoutLock<'a> {
236285
/// The returned handle implements the `Write` trait.
237286
#[stable(feature = "rust1", since = "1.0.0")]
238287
pub fn stdout() -> Stdout {
239-
static INSTANCE: Lazy<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> = Lazy::new(stdout_init);
288+
static INSTANCE: Lazy<ReentrantMutex<RefCell<LineWriter<Maybe<StdoutRaw>>>>>
289+
= Lazy::new(stdout_init);
240290
return Stdout {
241291
inner: INSTANCE.get().expect("cannot access stdout during shutdown"),
242292
};
243293

244-
fn stdout_init() -> Arc<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> {
245-
Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))))
294+
fn stdout_init() -> Arc<ReentrantMutex<RefCell<LineWriter<Maybe<StdoutRaw>>>>> {
295+
let stdout = match stdout_raw() {
296+
Ok(stdout) => Maybe::Real(stdout),
297+
_ => Maybe::Fake,
298+
};
299+
Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout))))
246300
}
247301
}
248302

@@ -288,7 +342,7 @@ impl<'a> Write for StdoutLock<'a> {
288342
/// For more information, see `stderr`
289343
#[stable(feature = "rust1", since = "1.0.0")]
290344
pub struct Stderr {
291-
inner: Arc<ReentrantMutex<RefCell<StderrRaw>>>,
345+
inner: Arc<ReentrantMutex<RefCell<Maybe<StderrRaw>>>>,
292346
}
293347

294348
/// A locked reference to the a `Stderr` handle.
@@ -297,7 +351,7 @@ pub struct Stderr {
297351
/// method on `Stderr`.
298352
#[stable(feature = "rust1", since = "1.0.0")]
299353
pub struct StderrLock<'a> {
300-
inner: ReentrantMutexGuard<'a, RefCell<StderrRaw>>,
354+
inner: ReentrantMutexGuard<'a, RefCell<Maybe<StderrRaw>>>,
301355
}
302356

303357
/// Constructs a new reference to the standard error stream of a process.
@@ -308,13 +362,17 @@ pub struct StderrLock<'a> {
308362
/// The returned handle implements the `Write` trait.
309363
#[stable(feature = "rust1", since = "1.0.0")]
310364
pub fn stderr() -> Stderr {
311-
static INSTANCE: Lazy<ReentrantMutex<RefCell<StderrRaw>>> = Lazy::new(stderr_init);
365+
static INSTANCE: Lazy<ReentrantMutex<RefCell<Maybe<StderrRaw>>>> = Lazy::new(stderr_init);
312366
return Stderr {
313367
inner: INSTANCE.get().expect("cannot access stderr during shutdown"),
314368
};
315369

316-
fn stderr_init() -> Arc<ReentrantMutex<RefCell<StderrRaw>>> {
317-
Arc::new(ReentrantMutex::new(RefCell::new(stderr_raw())))
370+
fn stderr_init() -> Arc<ReentrantMutex<RefCell<Maybe<StderrRaw>>>> {
371+
let stderr = match stderr_raw() {
372+
Ok(stderr) => Maybe::Real(stderr),
373+
_ => Maybe::Fake,
374+
};
375+
Arc::new(ReentrantMutex::new(RefCell::new(stderr)))
318376
}
319377
}
320378

src/libstd/panicking.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) {
3333
None => "Box<Any>",
3434
}
3535
};
36-
let mut err = Stderr::new();
36+
let mut err = match Stderr::new() {
37+
Ok(err) => err,
38+
_ => return,
39+
};
3740
let thread = thread_info::current_thread();
3841
let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");
3942
let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take());

src/libstd/rt/util.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ pub const ENFORCE_SANITY: bool = true || !cfg!(rtopt) || cfg!(rtdebug) ||
6363
cfg!(rtassert);
6464

6565
pub fn dumb_print(args: fmt::Arguments) {
66-
let _ = Stderr::new().write_fmt(args);
66+
let _ = Stderr::new().map(|mut stderr| stderr.write_fmt(args));
6767
}
6868

6969
pub fn abort(args: fmt::Arguments) -> ! {

src/libstd/sys/unix/stdio.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ pub struct Stdout(());
1919
pub struct Stderr(());
2020

2121
impl Stdin {
22-
pub fn new() -> Stdin { Stdin(()) }
22+
pub fn new() -> io::Result<Stdin> { Ok(Stdin(())) }
2323

2424
pub fn read(&self, data: &mut [u8]) -> io::Result<usize> {
2525
let fd = FileDesc::new(libc::STDIN_FILENO);
@@ -30,7 +30,7 @@ impl Stdin {
3030
}
3131

3232
impl Stdout {
33-
pub fn new() -> Stdout { Stdout(()) }
33+
pub fn new() -> io::Result<Stdout> { Ok(Stdout(())) }
3434

3535
pub fn write(&self, data: &[u8]) -> io::Result<usize> {
3636
let fd = FileDesc::new(libc::STDOUT_FILENO);
@@ -41,7 +41,7 @@ impl Stdout {
4141
}
4242

4343
impl Stderr {
44-
pub fn new() -> Stderr { Stderr(()) }
44+
pub fn new() -> io::Result<Stderr> { Ok(Stderr(())) }
4545

4646
pub fn write(&self, data: &[u8]) -> io::Result<usize> {
4747
let fd = FileDesc::new(libc::STDERR_FILENO);

src/libstd/sys/windows/stdio.rs

+11-9
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,13 @@ fn write(out: &Output, data: &[u8]) -> io::Result<usize> {
7777
}
7878

7979
impl Stdin {
80-
pub fn new() -> Stdin {
81-
Stdin {
82-
handle: get(c::STD_INPUT_HANDLE).unwrap(),
83-
utf8: Mutex::new(Cursor::new(Vec::new())),
84-
}
80+
pub fn new() -> io::Result<Stdin> {
81+
get(c::STD_INPUT_HANDLE).map(|handle| {
82+
Stdin {
83+
handle: handle,
84+
utf8: Mutex::new(Cursor::new(Vec::new())),
85+
}
86+
})
8587
}
8688

8789
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
@@ -116,8 +118,8 @@ impl Stdin {
116118
}
117119

118120
impl Stdout {
119-
pub fn new() -> Stdout {
120-
Stdout(get(c::STD_OUTPUT_HANDLE).unwrap())
121+
pub fn new() -> io::Result<Stdout> {
122+
get(c::STD_OUTPUT_HANDLE).map(Stdout)
121123
}
122124

123125
pub fn write(&self, data: &[u8]) -> io::Result<usize> {
@@ -126,8 +128,8 @@ impl Stdout {
126128
}
127129

128130
impl Stderr {
129-
pub fn new() -> Stderr {
130-
Stderr(get(c::STD_ERROR_HANDLE).unwrap())
131+
pub fn new() -> io::Result<Stderr> {
132+
get(c::STD_ERROR_HANDLE).map(Stderr)
131133
}
132134

133135
pub fn write(&self, data: &[u8]) -> io::Result<usize> {

src/test/run-pass/rfc-1014-2.rs

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
#![feature(libc)]
11+
12+
extern crate libc;
13+
14+
#[cfg(windows)]
15+
extern "system" {
16+
fn SetStdHandle(nStdHandle: libc::DWORD, nHandle: libc::HANDLE) -> libc::BOOL;
17+
}
18+
19+
#[cfg(windows)]
20+
fn close_stdout() {
21+
const STD_OUTPUT_HANDLE: libc::DWORD = -11i32 as libc::DWORD;
22+
unsafe { SetStdHandle(STD_OUTPUT_HANDLE, 0 as libc::HANDLE); }
23+
}
24+
25+
#[cfg(windows)]
26+
fn main() {
27+
close_stdout();
28+
println!("hello world");
29+
}
30+
31+
#[cfg(not(windows))]
32+
fn main() {}

src/test/run-pass/rfc-1014.rs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
#![feature(libc)]
11+
12+
extern crate libc;
13+
14+
#[cfg(windows)]
15+
extern "system" {
16+
pub fn GetStdHandle(which: libc::DWORD) -> libc::HANDLE;
17+
}
18+
19+
#[cfg(windows)]
20+
fn close_stdout() {
21+
const STD_OUTPUT_HANDLE: libc::DWORD = -11i32 as libc::DWORD;
22+
unsafe { libc::CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE)); }
23+
}
24+
25+
#[cfg(not(windows))]
26+
fn close_stdout() {
27+
unsafe { libc::close(libc::STDOUT_FILENO); }
28+
}
29+
30+
fn main() {
31+
close_stdout();
32+
println!("hello world");
33+
}

0 commit comments

Comments
 (0)