Merge set_panic and set_print into set_output_capture.

There were no use cases for setting them separately.
Merging them simplifies some things.
This commit is contained in:
Mara Bos 2020-11-04 00:11:14 +01:00
parent 08b7cb79e0
commit aff7bd66e8
13 changed files with 57 additions and 131 deletions

View File

@ -1,6 +1,6 @@
#![feature(bool_to_option)] #![feature(bool_to_option)]
#![feature(box_syntax)] #![feature(box_syntax)]
#![feature(set_stdio)] #![feature(internal_output_capture)]
#![feature(nll)] #![feature(nll)]
#![feature(generator_trait)] #![feature(generator_trait)]
#![feature(generators)] #![feature(generators)]

View File

@ -148,7 +148,7 @@ pub fn setup_callbacks_and_run_in_thread_pool_with_globals<F: FnOnce() -> R + Se
let main_handler = move || { let main_handler = move || {
rustc_span::with_session_globals(edition, || { rustc_span::with_session_globals(edition, || {
io::set_panic(stderr.clone()); io::set_output_capture(stderr.clone());
f() f()
}) })
}; };
@ -186,7 +186,7 @@ pub fn setup_callbacks_and_run_in_thread_pool_with_globals<F: FnOnce() -> R + Se
// on the new threads. // on the new threads.
let main_handler = move |thread: rayon::ThreadBuilder| { let main_handler = move |thread: rayon::ThreadBuilder| {
rustc_span::SESSION_GLOBALS.set(session_globals, || { rustc_span::SESSION_GLOBALS.set(session_globals, || {
io::set_panic(stderr.clone()); io::set_output_capture(stderr.clone());
thread.run() thread.run()
}) })
}; };

View File

@ -269,20 +269,18 @@ pub use self::buffered::{BufReader, BufWriter, LineWriter};
pub use self::cursor::Cursor; pub use self::cursor::Cursor;
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub use self::error::{Error, ErrorKind, Result}; pub use self::error::{Error, ErrorKind, Result};
#[unstable(feature = "internal_output_capture", issue = "none")]
#[doc(no_inline, hidden)]
pub use self::stdio::set_output_capture;
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub use self::stdio::{stderr, stdin, stdout, Stderr, Stdin, Stdout}; pub use self::stdio::{stderr, stdin, stdout, Stderr, Stdin, Stdout};
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub use self::stdio::{StderrLock, StdinLock, StdoutLock}; pub use self::stdio::{StderrLock, StdinLock, StdoutLock};
#[unstable(feature = "print_internals", issue = "none")] #[unstable(feature = "print_internals", issue = "none")]
pub use self::stdio::{_eprint, _print}; pub use self::stdio::{_eprint, _print};
#[unstable(feature = "libstd_io_internals", issue = "42788")]
#[doc(no_inline, hidden)]
pub use self::stdio::{set_panic, set_print};
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
pub use self::util::{copy, empty, repeat, sink, Empty, Repeat, Sink}; pub use self::util::{copy, empty, repeat, sink, Empty, Repeat, Sink};
pub(crate) use self::stdio::clone_io;
mod buffered; mod buffered;
mod cursor; mod cursor;
mod error; mod error;

View File

@ -14,37 +14,29 @@ use crate::sync::{Arc, Mutex, MutexGuard};
use crate::sys::stdio; use crate::sys::stdio;
use crate::sys_common; use crate::sys_common;
use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard}; use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
use crate::thread::LocalKey;
type LocalStream = Arc<Mutex<Vec<u8>>>; type LocalStream = Arc<Mutex<Vec<u8>>>;
thread_local! { thread_local! {
/// Used by the test crate to capture the output of the print! and println! macros. /// Used by the test crate to capture the output of the print macros and panics.
static LOCAL_STDOUT: Cell<Option<LocalStream>> = { static OUTPUT_CAPTURE: Cell<Option<LocalStream>> = {
Cell::new(None) Cell::new(None)
} }
} }
thread_local! { /// Flag to indicate OUTPUT_CAPTURE is used.
/// Used by the test crate to capture the output of the eprint! and eprintln! macros, and panics.
static LOCAL_STDERR: Cell<Option<LocalStream>> = {
Cell::new(None)
}
}
/// Flag to indicate LOCAL_STDOUT and/or LOCAL_STDERR is used.
/// ///
/// If both are None and were never set on any thread, this flag is set to /// If it is None and was never set on any thread, this flag is set to false,
/// false, and both LOCAL_STDOUT and LOCAL_STDOUT can be safely ignored on all /// and OUTPUT_CAPTURE can be safely ignored on all threads, saving some time
/// threads, saving some time and memory registering an unused thread local. /// and memory registering an unused thread local.
/// ///
/// Note about memory ordering: This contains information about whether two /// Note about memory ordering: This contains information about whether a
/// thread local variables might be in use. Although this is a global flag, the /// thread local variable might be in use. Although this is a global flag, the
/// memory ordering between threads does not matter: we only want this flag to /// memory ordering between threads does not matter: we only want this flag to
/// have a consistent order between set_print/set_panic and print_to *within /// have a consistent order between set_output_capture and print_to *within
/// the same thread*. Within the same thread, things always have a perfectly /// the same thread*. Within the same thread, things always have a perfectly
/// consistent order. So Ordering::Relaxed is fine. /// consistent order. So Ordering::Relaxed is fine.
static LOCAL_STREAMS: AtomicBool = AtomicBool::new(false); static OUTPUT_CAPTURE_USED: AtomicBool = AtomicBool::new(false);
/// A handle to a raw instance of the standard input stream of this process. /// A handle to a raw instance of the standard input stream of this process.
/// ///
@ -890,70 +882,24 @@ impl fmt::Debug for StderrLock<'_> {
} }
} }
/// Resets the thread-local stderr handle to the specified writer /// Sets the thread-local output capture buffer and returns the old one.
///
/// This will replace the current thread's stderr handle, returning the old
/// handle. All future calls to `panic!` and friends will emit their output to
/// this specified handle.
///
/// Note that this does not need to be called for all new threads; the default
/// output handle is to the process's stderr stream.
#[unstable( #[unstable(
feature = "set_stdio", feature = "internal_output_capture",
reason = "this function may disappear completely or be replaced \ reason = "this function is meant for use in the test crate \
with a more general mechanism", and may disappear in the future",
issue = "none" issue = "none"
)] )]
#[doc(hidden)] #[doc(hidden)]
pub fn set_panic(sink: Option<LocalStream>) -> Option<LocalStream> { pub fn set_output_capture(sink: Option<LocalStream>) -> Option<LocalStream> {
if sink.is_none() && !LOCAL_STREAMS.load(Ordering::Relaxed) { if sink.is_none() && !OUTPUT_CAPTURE_USED.load(Ordering::Relaxed) {
// LOCAL_STDERR is definitely None since LOCAL_STREAMS is false. // OUTPUT_CAPTURE is definitely None since OUTPUT_CAPTURE_USED is false.
return None; return None;
} }
LOCAL_STREAMS.store(true, Ordering::Relaxed); OUTPUT_CAPTURE_USED.store(true, Ordering::Relaxed);
LOCAL_STDERR.with(move |slot| slot.replace(sink)) OUTPUT_CAPTURE.with(move |slot| slot.replace(sink))
} }
/// Resets the thread-local stdout handle to the specified writer /// Write `args` to the capture buffer if enabled and possible, or `global_s`
///
/// This will replace the current thread's stdout handle, returning the old
/// handle. All future calls to `print!` and friends will emit their output to
/// this specified handle.
///
/// Note that this does not need to be called for all new threads; the default
/// output handle is to the process's stdout stream.
#[unstable(
feature = "set_stdio",
reason = "this function may disappear completely or be replaced \
with a more general mechanism",
issue = "none"
)]
#[doc(hidden)]
pub fn set_print(sink: Option<LocalStream>) -> Option<LocalStream> {
if sink.is_none() && !LOCAL_STREAMS.load(Ordering::Relaxed) {
// LOCAL_STDOUT is definitely None since LOCAL_STREAMS is false.
return None;
}
LOCAL_STREAMS.store(true, Ordering::Relaxed);
LOCAL_STDOUT.with(move |slot| slot.replace(sink))
}
pub(crate) fn clone_io() -> (Option<LocalStream>, Option<LocalStream>) {
// Don't waste time when LOCAL_{STDOUT,STDERR} are definitely None.
if !LOCAL_STREAMS.load(Ordering::Relaxed) {
return (None, None);
}
let clone = |cell: &Cell<Option<LocalStream>>| {
let s = cell.take();
cell.set(s.clone());
s
};
(LOCAL_STDOUT.with(clone), LOCAL_STDERR.with(clone))
}
/// Write `args` to output stream `local_s` if possible, `global_s`
/// otherwise. `label` identifies the stream in a panic message. /// otherwise. `label` identifies the stream in a panic message.
/// ///
/// This function is used to print error messages, so it takes extra /// This function is used to print error messages, so it takes extra
@ -963,16 +909,12 @@ pub(crate) fn clone_io() -> (Option<LocalStream>, Option<LocalStream>) {
/// thread, it will just fall back to the global stream. /// thread, it will just fall back to the global stream.
/// ///
/// However, if the actual I/O causes an error, this function does panic. /// However, if the actual I/O causes an error, this function does panic.
fn print_to<T>( fn print_to<T>(args: fmt::Arguments<'_>, global_s: fn() -> T, label: &str)
args: fmt::Arguments<'_>, where
local_s: &'static LocalKey<Cell<Option<LocalStream>>>,
global_s: fn() -> T,
label: &str,
) where
T: Write, T: Write,
{ {
if LOCAL_STREAMS.load(Ordering::Relaxed) if OUTPUT_CAPTURE_USED.load(Ordering::Relaxed)
&& local_s.try_with(|s| { && OUTPUT_CAPTURE.try_with(|s| {
// Note that we completely remove a local sink to write to in case // Note that we completely remove a local sink to write to in case
// our printing recursively panics/prints, so the recursive // our printing recursively panics/prints, so the recursive
// panic/print goes to the global sink instead of our local sink. // panic/print goes to the global sink instead of our local sink.
@ -982,7 +924,7 @@ fn print_to<T>(
}) })
}) == Ok(Some(())) }) == Ok(Some(()))
{ {
// Succesfully wrote to local stream. // Succesfully wrote to capture buffer.
return; return;
} }
@ -999,7 +941,7 @@ fn print_to<T>(
#[doc(hidden)] #[doc(hidden)]
#[cfg(not(test))] #[cfg(not(test))]
pub fn _print(args: fmt::Arguments<'_>) { pub fn _print(args: fmt::Arguments<'_>) {
print_to(args, &LOCAL_STDOUT, stdout, "stdout"); print_to(args, stdout, "stdout");
} }
#[unstable( #[unstable(
@ -1010,7 +952,7 @@ pub fn _print(args: fmt::Arguments<'_>) {
#[doc(hidden)] #[doc(hidden)]
#[cfg(not(test))] #[cfg(not(test))]
pub fn _eprint(args: fmt::Arguments<'_>) { pub fn _eprint(args: fmt::Arguments<'_>) {
print_to(args, &LOCAL_STDERR, stderr, "stderr"); print_to(args, stderr, "stderr");
} }
#[cfg(test)] #[cfg(test)]

View File

@ -207,7 +207,7 @@
// std may use features in a platform-specific way // std may use features in a platform-specific way
#![allow(unused_features)] #![allow(unused_features)]
#![cfg_attr(not(bootstrap), feature(rustc_allow_const_fn_unstable))] #![cfg_attr(not(bootstrap), feature(rustc_allow_const_fn_unstable))]
#![cfg_attr(test, feature(print_internals, set_stdio, update_panic_count))] #![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count))]
#![cfg_attr( #![cfg_attr(
all(target_vendor = "fortanix", target_env = "sgx"), all(target_vendor = "fortanix", target_env = "sgx"),
feature(slice_index_methods, coerce_unsized, sgx_platform) feature(slice_index_methods, coerce_unsized, sgx_platform)

View File

@ -24,11 +24,11 @@ use crate::sys_common::{thread_info, util};
use crate::thread; use crate::thread;
#[cfg(not(test))] #[cfg(not(test))]
use crate::io::set_panic; use crate::io::set_output_capture;
// make sure to use the stderr output configured // make sure to use the stderr output configured
// by libtest in the real copy of std // by libtest in the real copy of std
#[cfg(test)] #[cfg(test)]
use realstd::io::set_panic; use realstd::io::set_output_capture;
// Binary interface to the panic runtime that the standard library depends on. // Binary interface to the panic runtime that the standard library depends on.
// //
@ -218,9 +218,9 @@ fn default_hook(info: &PanicInfo<'_>) {
} }
}; };
if let Some(local) = set_panic(None) { if let Some(local) = set_output_capture(None) {
write(&mut *local.lock().unwrap_or_else(|e| e.into_inner())); write(&mut *local.lock().unwrap_or_else(|e| e.into_inner()));
set_panic(Some(local)); set_output_capture(Some(local));
} else if let Some(mut out) = panic_output() { } else if let Some(mut out) = panic_output() {
write(&mut out); write(&mut out);
} }

View File

@ -456,15 +456,15 @@ impl Builder {
let my_packet: Arc<UnsafeCell<Option<Result<T>>>> = Arc::new(UnsafeCell::new(None)); let my_packet: Arc<UnsafeCell<Option<Result<T>>>> = Arc::new(UnsafeCell::new(None));
let their_packet = my_packet.clone(); let their_packet = my_packet.clone();
let (stdout, stderr) = crate::io::clone_io(); let output_capture = crate::io::set_output_capture(None);
crate::io::set_output_capture(output_capture.clone());
let main = move || { let main = move || {
if let Some(name) = their_thread.cname() { if let Some(name) = their_thread.cname() {
imp::Thread::set_name(name); imp::Thread::set_name(name);
} }
crate::io::set_print(stdout); crate::io::set_output_capture(output_capture);
crate::io::set_panic(stderr);
// SAFETY: the stack guard passed is the one for the current thread. // SAFETY: the stack guard passed is the one for the current thread.
// This means the current thread's stack and the new thread's stack // This means the current thread's stack and the new thread's stack

View File

@ -184,18 +184,14 @@ where
let mut bs = Bencher { mode: BenchMode::Auto, summary: None, bytes: 0 }; let mut bs = Bencher { mode: BenchMode::Auto, summary: None, bytes: 0 };
let data = Arc::new(Mutex::new(Vec::new())); let data = Arc::new(Mutex::new(Vec::new()));
let oldio = if !nocapture {
Some((io::set_print(Some(data.clone())), io::set_panic(Some(data.clone())))) if !nocapture {
} else { io::set_output_capture(Some(data.clone()));
None }
};
let result = catch_unwind(AssertUnwindSafe(|| bs.bench(f))); let result = catch_unwind(AssertUnwindSafe(|| bs.bench(f)));
if let Some((printio, panicio)) = oldio { io::set_output_capture(None);
io::set_print(printio);
io::set_panic(panicio);
}
let test_result = match result { let test_result = match result {
//bs.bench(f) { //bs.bench(f) {

View File

@ -25,7 +25,7 @@
#![feature(nll)] #![feature(nll)]
#![feature(bool_to_option)] #![feature(bool_to_option)]
#![feature(available_concurrency)] #![feature(available_concurrency)]
#![feature(set_stdio)] #![feature(internal_output_capture)]
#![feature(panic_unwind)] #![feature(panic_unwind)]
#![feature(staged_api)] #![feature(staged_api)]
#![feature(termination_trait_lib)] #![feature(termination_trait_lib)]
@ -530,11 +530,9 @@ fn run_test_in_process(
// Buffer for capturing standard I/O // Buffer for capturing standard I/O
let data = Arc::new(Mutex::new(Vec::new())); let data = Arc::new(Mutex::new(Vec::new()));
let oldio = if !nocapture { if !nocapture {
Some((io::set_print(Some(data.clone())), io::set_panic(Some(data.clone())))) io::set_output_capture(Some(data.clone()));
} else { }
None
};
let start = report_time.then(Instant::now); let start = report_time.then(Instant::now);
let result = catch_unwind(AssertUnwindSafe(testfn)); let result = catch_unwind(AssertUnwindSafe(testfn));
@ -543,10 +541,7 @@ fn run_test_in_process(
TestExecTime(duration) TestExecTime(duration)
}); });
if let Some((printio, panicio)) = oldio { io::set_output_capture(None);
io::set_print(printio);
io::set_panic(panicio);
}
let test_result = match result { let test_result = match result {
Ok(()) => calc_result(&desc, Ok(()), &time_opts, &exec_time), Ok(()) => calc_result(&desc, Ok(()), &time_opts, &exec_time),

View File

@ -1,4 +1,4 @@
# `libstd_io_internals` # `internal_output_capture`
This feature is internal to the Rust compiler and is not intended for general use. This feature is internal to the Rust compiler and is not intended for general use.

View File

@ -1,5 +0,0 @@
# `set_stdio`
This feature is internal to the Rust compiler and is not intended for general use.
------------------------

View File

@ -1,11 +1,11 @@
// run-pass // run-pass
// ignore-emscripten no subprocess support // ignore-emscripten no subprocess support
#![feature(set_stdio)] #![feature(internal_output_capture)]
use std::fmt; use std::fmt;
use std::fmt::{Display, Formatter}; use std::fmt::{Display, Formatter};
use std::io::set_panic; use std::io::set_output_capture;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
pub struct A; pub struct A;
@ -17,7 +17,7 @@ impl Display for A {
} }
fn main() { fn main() {
set_panic(Some(Arc::new(Mutex::new(Vec::new())))); set_output_capture(Some(Arc::new(Mutex::new(Vec::new()))));
assert!(std::panic::catch_unwind(|| { assert!(std::panic::catch_unwind(|| {
eprintln!("{}", A); eprintln!("{}", A);
}) })

View File

@ -1,7 +1,7 @@
// run-pass // run-pass
// ignore-emscripten no threads support // ignore-emscripten no threads support
#![feature(set_stdio)] #![feature(internal_output_capture)]
use std::io; use std::io;
use std::str; use std::str;
@ -13,7 +13,7 @@ fn main() {
let res = thread::Builder::new().spawn({ let res = thread::Builder::new().spawn({
let data = data.clone(); let data = data.clone();
move || { move || {
io::set_panic(Some(data)); io::set_output_capture(Some(data));
panic!("Hello, world!") panic!("Hello, world!")
} }
}).unwrap().join(); }).unwrap().join();