parent
6b9fbf212a
commit
d0d0e78208
@ -113,6 +113,11 @@ fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl io::LocalOutput for Sink {
|
||||
fn clone_box(&self) -> Box<dyn io::LocalOutput> {
|
||||
Box::new(Self(self.0.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
/// Like a `thread::Builder::spawn` followed by a `join()`, but avoids the need
|
||||
/// for `'static` bounds.
|
||||
|
@ -213,13 +213,13 @@ fn read_line(&mut self, buf: &mut String) -> io::Result<usize> {
|
||||
#[cfg(test)]
|
||||
/// This impl is only used by printing logic, so any error returned is always
|
||||
/// of kind `Other`, and should be ignored.
|
||||
impl Write for Box<dyn (::realstd::io::Write) + Send> {
|
||||
impl Write for dyn ::realstd::io::LocalOutput {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
(**self).write(buf).map_err(|_| ErrorKind::Other.into())
|
||||
(*self).write(buf).map_err(|_| ErrorKind::Other.into())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
(**self).flush().map_err(|_| ErrorKind::Other.into())
|
||||
(*self).flush().map_err(|_| ErrorKind::Other.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -277,10 +277,12 @@
|
||||
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};
|
||||
pub use self::stdio::{set_panic, set_print, LocalOutput};
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub use self::util::{copy, empty, repeat, sink, Empty, Repeat, Sink};
|
||||
|
||||
pub(crate) use self::stdio::clone_io;
|
||||
|
||||
mod buffered;
|
||||
mod cursor;
|
||||
mod error;
|
||||
|
@ -18,14 +18,14 @@
|
||||
|
||||
thread_local! {
|
||||
/// Used by the test crate to capture the output of the print! and println! macros.
|
||||
static LOCAL_STDOUT: RefCell<Option<Box<dyn Write + Send>>> = {
|
||||
static LOCAL_STDOUT: RefCell<Option<Box<dyn LocalOutput>>> = {
|
||||
RefCell::new(None)
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
/// Used by the test crate to capture the output of the eprint! and eprintln! macros, and panics.
|
||||
static LOCAL_STDERR: RefCell<Option<Box<dyn Write + Send>>> = {
|
||||
static LOCAL_STDERR: RefCell<Option<Box<dyn LocalOutput>>> = {
|
||||
RefCell::new(None)
|
||||
}
|
||||
}
|
||||
@ -888,6 +888,18 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
}
|
||||
}
|
||||
|
||||
/// A writer than can be cloned to new threads.
|
||||
#[unstable(
|
||||
feature = "set_stdio",
|
||||
reason = "this trait may disappear completely or be replaced \
|
||||
with a more general mechanism",
|
||||
issue = "none"
|
||||
)]
|
||||
#[doc(hidden)]
|
||||
pub trait LocalOutput: Write + Send {
|
||||
fn clone_box(&self) -> Box<dyn LocalOutput>;
|
||||
}
|
||||
|
||||
/// Resets the thread-local stderr handle to the specified writer
|
||||
///
|
||||
/// This will replace the current thread's stderr handle, returning the old
|
||||
@ -903,7 +915,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
issue = "none"
|
||||
)]
|
||||
#[doc(hidden)]
|
||||
pub fn set_panic(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write + Send>> {
|
||||
pub fn set_panic(sink: Option<Box<dyn LocalOutput>>) -> Option<Box<dyn LocalOutput>> {
|
||||
use crate::mem;
|
||||
if sink.is_none() && !LOCAL_STREAMS.load(Ordering::Relaxed) {
|
||||
// LOCAL_STDERR is definitely None since LOCAL_STREAMS is false.
|
||||
@ -934,7 +946,7 @@ pub fn set_panic(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write +
|
||||
issue = "none"
|
||||
)]
|
||||
#[doc(hidden)]
|
||||
pub fn set_print(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write + Send>> {
|
||||
pub fn set_print(sink: Option<Box<dyn LocalOutput>>) -> Option<Box<dyn LocalOutput>> {
|
||||
use crate::mem;
|
||||
if sink.is_none() && !LOCAL_STREAMS.load(Ordering::Relaxed) {
|
||||
// LOCAL_STDOUT is definitely None since LOCAL_STREAMS is false.
|
||||
@ -950,6 +962,17 @@ pub fn set_print(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write +
|
||||
s
|
||||
}
|
||||
|
||||
pub(crate) fn clone_io() -> (Option<Box<dyn LocalOutput>>, Option<Box<dyn LocalOutput>>) {
|
||||
LOCAL_STDOUT.with(|stdout| {
|
||||
LOCAL_STDERR.with(|stderr| {
|
||||
(
|
||||
stdout.borrow().as_ref().map(|o| o.clone_box()),
|
||||
stderr.borrow().as_ref().map(|o| o.clone_box()),
|
||||
)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Write `args` to output stream `local_s` if possible, `global_s`
|
||||
/// otherwise. `label` identifies the stream in a panic message.
|
||||
///
|
||||
@ -962,7 +985,7 @@ pub fn set_print(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write +
|
||||
/// However, if the actual I/O causes an error, this function does panic.
|
||||
fn print_to<T>(
|
||||
args: fmt::Arguments<'_>,
|
||||
local_s: &'static LocalKey<RefCell<Option<Box<dyn Write + Send>>>>,
|
||||
local_s: &'static LocalKey<RefCell<Option<Box<dyn LocalOutput>>>>,
|
||||
global_s: fn() -> T,
|
||||
label: &str,
|
||||
) where
|
||||
|
@ -220,7 +220,7 @@ fn default_hook(info: &PanicInfo<'_>) {
|
||||
|
||||
if let Some(mut local) = set_panic(None) {
|
||||
// NB. In `cfg(test)` this uses the forwarding impl
|
||||
// for `Box<dyn (::realstd::io::Write) + Send>`.
|
||||
// for `dyn ::realstd::io::LocalOutput`.
|
||||
write(&mut local);
|
||||
set_panic(Some(local));
|
||||
} else if let Some(mut out) = panic_output() {
|
||||
|
@ -457,11 +457,16 @@ pub unsafe fn spawn_unchecked<'a, F, T>(self, f: F) -> io::Result<JoinHandle<T>>
|
||||
let my_packet: Arc<UnsafeCell<Option<Result<T>>>> = Arc::new(UnsafeCell::new(None));
|
||||
let their_packet = my_packet.clone();
|
||||
|
||||
let (stdout, stderr) = crate::io::clone_io();
|
||||
|
||||
let main = move || {
|
||||
if let Some(name) = their_thread.cname() {
|
||||
imp::Thread::set_name(name);
|
||||
}
|
||||
|
||||
crate::io::set_print(stdout);
|
||||
crate::io::set_panic(stderr);
|
||||
|
||||
// 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
|
||||
// are properly set and protected from each other.
|
||||
|
@ -6,6 +6,7 @@
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Sink(Arc<Mutex<Vec<u8>>>);
|
||||
|
||||
impl Sink {
|
||||
@ -14,6 +15,12 @@ pub fn new_boxed(data: &Arc<Mutex<Vec<u8>>>) -> Box<Self> {
|
||||
}
|
||||
}
|
||||
|
||||
impl io::LocalOutput for Sink {
|
||||
fn clone_box(&self) -> Box<dyn io::LocalOutput> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Sink {
|
||||
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||
Write::write(&mut *self.0.lock().unwrap(), data)
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
use std::fmt;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::io::set_panic;
|
||||
use std::io::{self, set_panic, LocalOutput, Write};
|
||||
|
||||
pub struct A;
|
||||
|
||||
@ -15,8 +15,23 @@ fn fmt(&self, _f: &mut Formatter<'_>) -> fmt::Result {
|
||||
}
|
||||
}
|
||||
|
||||
struct Sink;
|
||||
impl Write for Sink {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
Ok(buf.len())
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl LocalOutput for Sink {
|
||||
fn clone_box(&self) -> Box<dyn LocalOutput> {
|
||||
Box::new(Sink)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
set_panic(Some(Box::new(Vec::new())));
|
||||
set_panic(Some(Box::new(Sink)));
|
||||
assert!(std::panic::catch_unwind(|| {
|
||||
eprintln!("{}", A);
|
||||
})
|
||||
|
30
src/test/ui/test-thread-capture.rs
Normal file
30
src/test/ui/test-thread-capture.rs
Normal file
@ -0,0 +1,30 @@
|
||||
// compile-flags: --test
|
||||
// run-fail
|
||||
// run-flags: --test-threads=1
|
||||
// check-run-results
|
||||
// exec-env:RUST_BACKTRACE=0
|
||||
|
||||
#[test]
|
||||
fn thready_pass() {
|
||||
println!("fee");
|
||||
std::thread::spawn(|| {
|
||||
println!("fie");
|
||||
println!("foe");
|
||||
})
|
||||
.join()
|
||||
.unwrap();
|
||||
println!("fum");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn thready_fail() {
|
||||
println!("fee");
|
||||
std::thread::spawn(|| {
|
||||
println!("fie");
|
||||
println!("foe");
|
||||
})
|
||||
.join()
|
||||
.unwrap();
|
||||
println!("fum");
|
||||
panic!();
|
||||
}
|
21
src/test/ui/test-thread-capture.run.stdout
Normal file
21
src/test/ui/test-thread-capture.run.stdout
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
running 2 tests
|
||||
test thready_fail ... FAILED
|
||||
test thready_pass ... ok
|
||||
|
||||
failures:
|
||||
|
||||
---- thready_fail stdout ----
|
||||
fee
|
||||
fie
|
||||
foe
|
||||
fum
|
||||
thread 'main' panicked at 'explicit panic', $DIR/test-thread-capture.rs:29:5
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||
|
||||
|
||||
failures:
|
||||
thready_fail
|
||||
|
||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
|
30
src/test/ui/test-thread-nocapture.rs
Normal file
30
src/test/ui/test-thread-nocapture.rs
Normal file
@ -0,0 +1,30 @@
|
||||
// compile-flags: --test
|
||||
// run-fail
|
||||
// run-flags: --test-threads=1 --nocapture
|
||||
// check-run-results
|
||||
// exec-env:RUST_BACKTRACE=0
|
||||
|
||||
#[test]
|
||||
fn thready_pass() {
|
||||
println!("fee");
|
||||
std::thread::spawn(|| {
|
||||
println!("fie");
|
||||
println!("foe");
|
||||
})
|
||||
.join()
|
||||
.unwrap();
|
||||
println!("fum");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn thready_fail() {
|
||||
println!("fee");
|
||||
std::thread::spawn(|| {
|
||||
println!("fie");
|
||||
println!("foe");
|
||||
})
|
||||
.join()
|
||||
.unwrap();
|
||||
println!("fum");
|
||||
panic!();
|
||||
}
|
2
src/test/ui/test-thread-nocapture.run.stderr
Normal file
2
src/test/ui/test-thread-nocapture.run.stderr
Normal file
@ -0,0 +1,2 @@
|
||||
thread 'main' panicked at 'explicit panic', $DIR/test-thread-nocapture.rs:29:5
|
||||
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
20
src/test/ui/test-thread-nocapture.run.stdout
Normal file
20
src/test/ui/test-thread-nocapture.run.stdout
Normal file
@ -0,0 +1,20 @@
|
||||
|
||||
running 2 tests
|
||||
test thready_fail ... fee
|
||||
fie
|
||||
foe
|
||||
fum
|
||||
FAILED
|
||||
test thready_pass ... fee
|
||||
fie
|
||||
foe
|
||||
fum
|
||||
ok
|
||||
|
||||
failures:
|
||||
|
||||
failures:
|
||||
thready_fail
|
||||
|
||||
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out
|
||||
|
@ -16,6 +16,11 @@ fn write(&mut self, data: &[u8]) -> io::Result<usize> {
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> { Ok(()) }
|
||||
}
|
||||
impl io::LocalOutput for Sink {
|
||||
fn clone_box(&self) -> Box<dyn io::LocalOutput> {
|
||||
Box::new(Sink(self.0.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let data = Arc::new(Mutex::new(Vec::new()));
|
||||
|
Loading…
Reference in New Issue
Block a user