Skip to content
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

Implement RFC 1014 #26168

Merged
merged 1 commit into from
Jun 15, 2015
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
96 changes: 77 additions & 19 deletions src/libstd/io/stdio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use io::{self, BufReader, LineWriter};
use sync::{Arc, Mutex, MutexGuard};
use sys::stdio;
use sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
use libc;

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

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

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

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

enum Maybe<T> {
Real(T),
Fake,
}

impl<W: io::Write> io::Write for Maybe<W> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match *self {
Maybe::Real(ref mut w) => handle_ebadf(w.write(buf), buf.len()),
Maybe::Fake => Ok(buf.len())
}
}

fn flush(&mut self) -> io::Result<()> {
match *self {
Maybe::Real(ref mut w) => handle_ebadf(w.flush(), ()),
Maybe::Fake => Ok(())
}
}
}

impl<R: io::Read> io::Read for Maybe<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match *self {
Maybe::Real(ref mut r) => handle_ebadf(r.read(buf), buf.len()),
Maybe::Fake => Ok(0)
}
}
}

fn handle_ebadf<T>(r: io::Result<T>, default: T) -> io::Result<T> {
#[cfg(windows)]
const ERR: libc::c_int = libc::ERROR_INVALID_HANDLE;
#[cfg(not(windows))]
const ERR: libc::c_int = libc::EBADF;

match r {
Err(ref e) if e.raw_os_error() == Some(ERR) => Ok(default),
r => r
}
}

/// A handle to the standard input stream of a process.
///
/// Each handle is a shared reference to a global buffer of input data to this
Expand All @@ -99,7 +142,7 @@ impl Write for StderrRaw {
/// Created by the function `io::stdin()`.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Stdin {
inner: Arc<Mutex<BufReader<StdinRaw>>>,
inner: Arc<Mutex<BufReader<Maybe<StdinRaw>>>>,
}

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

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

fn stdin_init() -> Arc<Mutex<BufReader<StdinRaw>>> {
fn stdin_init() -> Arc<Mutex<BufReader<Maybe<StdinRaw>>>> {
let stdin = match stdin_raw() {
Ok(stdin) => Maybe::Real(stdin),
_ => Maybe::Fake
};

// 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.
Arc::new(Mutex::new(if cfg!(windows) {
BufReader::with_capacity(8 * 1024, stdin_raw())
BufReader::with_capacity(8 * 1024, stdin)
} else {
BufReader::new(stdin_raw())
BufReader::new(stdin)
}))
}
}
Expand Down Expand Up @@ -181,6 +229,7 @@ impl<'a> Read for StdinLock<'a> {
self.inner.read(buf)
}
}

#[stable(feature = "rust1", since = "1.0.0")]
impl<'a> BufRead for StdinLock<'a> {
fn fill_buf(&mut self) -> io::Result<&[u8]> { self.inner.fill_buf() }
Expand Down Expand Up @@ -215,7 +264,7 @@ pub struct Stdout {
// FIXME: this should be LineWriter or BufWriter depending on the state of
// stdout (tty or not). Note that if this is not line buffered it
// should also flush-on-panic or some form of flush-on-abort.
inner: Arc<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>>,
inner: Arc<ReentrantMutex<RefCell<LineWriter<Maybe<StdoutRaw>>>>>,
}

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

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

fn stdout_init() -> Arc<ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>> {
Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))))
fn stdout_init() -> Arc<ReentrantMutex<RefCell<LineWriter<Maybe<StdoutRaw>>>>> {
let stdout = match stdout_raw() {
Ok(stdout) => Maybe::Real(stdout),
_ => Maybe::Fake,
};
Arc::new(ReentrantMutex::new(RefCell::new(LineWriter::new(stdout))))
}
}

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

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

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

fn stderr_init() -> Arc<ReentrantMutex<RefCell<StderrRaw>>> {
Arc::new(ReentrantMutex::new(RefCell::new(stderr_raw())))
fn stderr_init() -> Arc<ReentrantMutex<RefCell<Maybe<StderrRaw>>>> {
let stderr = match stderr_raw() {
Ok(stderr) => Maybe::Real(stderr),
_ => Maybe::Fake,
};
Arc::new(ReentrantMutex::new(RefCell::new(stderr)))
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/libstd/panicking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ pub fn on_panic(obj: &(Any+Send), file: &'static str, line: u32) {
None => "Box<Any>",
}
};
let mut err = Stderr::new();
let mut err = match Stderr::new() {
Ok(err) => err,
_ => return,
};
let thread = thread_info::current_thread();
let name = thread.as_ref().and_then(|t| t.name()).unwrap_or("<unnamed>");
let prev = LOCAL_STDERR.with(|s| s.borrow_mut().take());
Expand Down
2 changes: 1 addition & 1 deletion src/libstd/rt/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pub const ENFORCE_SANITY: bool = true || !cfg!(rtopt) || cfg!(rtdebug) ||
cfg!(rtassert);

pub fn dumb_print(args: fmt::Arguments) {
let _ = Stderr::new().write_fmt(args);
let _ = Stderr::new().map(|mut stderr| stderr.write_fmt(args));
}

pub fn abort(args: fmt::Arguments) -> ! {
Expand Down
6 changes: 3 additions & 3 deletions src/libstd/sys/unix/stdio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub struct Stdout(());
pub struct Stderr(());

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

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

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

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

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

pub fn write(&self, data: &[u8]) -> io::Result<usize> {
let fd = FileDesc::new(libc::STDERR_FILENO);
Expand Down
20 changes: 11 additions & 9 deletions src/libstd/sys/windows/stdio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,13 @@ fn write(out: &Output, data: &[u8]) -> io::Result<usize> {
}

impl Stdin {
pub fn new() -> Stdin {
Stdin {
handle: get(c::STD_INPUT_HANDLE).unwrap(),
utf8: Mutex::new(Cursor::new(Vec::new())),
}
pub fn new() -> io::Result<Stdin> {
get(c::STD_INPUT_HANDLE).map(|handle| {
Stdin {
handle: handle,
utf8: Mutex::new(Cursor::new(Vec::new())),
}
})
}

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

impl Stdout {
pub fn new() -> Stdout {
Stdout(get(c::STD_OUTPUT_HANDLE).unwrap())
pub fn new() -> io::Result<Stdout> {
get(c::STD_OUTPUT_HANDLE).map(Stdout)
}

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

impl Stderr {
pub fn new() -> Stderr {
Stderr(get(c::STD_ERROR_HANDLE).unwrap())
pub fn new() -> io::Result<Stderr> {
get(c::STD_ERROR_HANDLE).map(Stderr)
}

pub fn write(&self, data: &[u8]) -> io::Result<usize> {
Expand Down
32 changes: 32 additions & 0 deletions src/test/run-pass/rfc-1014-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(libc)]

extern crate libc;

#[cfg(windows)]
extern "system" {
fn SetStdHandle(nStdHandle: libc::DWORD, nHandle: libc::HANDLE) -> libc::BOOL;
}

#[cfg(windows)]
fn close_stdout() {
const STD_OUTPUT_HANDLE: libc::DWORD = -11i32 as libc::DWORD;
unsafe { SetStdHandle(STD_OUTPUT_HANDLE, 0 as libc::HANDLE); }
}

#[cfg(windows)]
fn main() {
close_stdout();
println!("hello world");
}

#[cfg(not(windows))]
fn main() {}
33 changes: 33 additions & 0 deletions src/test/run-pass/rfc-1014.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(libc)]

extern crate libc;

#[cfg(windows)]
extern "system" {
pub fn GetStdHandle(which: libc::DWORD) -> libc::HANDLE;
}

#[cfg(windows)]
fn close_stdout() {
const STD_OUTPUT_HANDLE: libc::DWORD = -11i32 as libc::DWORD;
unsafe { libc::CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE)); }
}

#[cfg(not(windows))]
fn close_stdout() {
unsafe { libc::close(libc::STDOUT_FILENO); }
}

fn main() {
close_stdout();
println!("hello world");
}