about summary refs log tree commit diff
path: root/library/std/src
diff options
context:
space:
mode:
authorTyler Mandry <tmandry@gmail.com>2020-08-04 18:42:36 -0700
committerSergio Benitez <sb@sergio.bz>2020-10-22 18:15:44 -0700
commitd0d0e7820806bbb89a2733bae8f9aaa9c78345d0 (patch)
treeb7a7a24124c08c5da4fd25ab60b892d3b35bdacc /library/std/src
parent6b9fbf212a06944ff23325d2d63215805dc3c6c3 (diff)
downloadrust-d0d0e7820806bbb89a2733bae8f9aaa9c78345d0.tar.gz
rust-d0d0e7820806bbb89a2733bae8f9aaa9c78345d0.zip
Capture output from threads spawned in tests
Fixes #42474.
Diffstat (limited to 'library/std/src')
-rw-r--r--library/std/src/io/impls.rs6
-rw-r--r--library/std/src/io/mod.rs4
-rw-r--r--library/std/src/io/stdio.rs33
-rw-r--r--library/std/src/panicking.rs2
-rw-r--r--library/std/src/thread/mod.rs5
5 files changed, 40 insertions, 10 deletions
diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs
index e09e7ba978e..66426101d27 100644
--- a/library/std/src/io/impls.rs
+++ b/library/std/src/io/impls.rs
@@ -213,13 +213,13 @@ impl<B: BufRead + ?Sized> BufRead for Box<B> {
 #[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())
     }
 }
 
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index d9d03807819..e6efe6ec57e 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -277,10 +277,12 @@ pub use self::stdio::{StderrLock, StdinLock, StdoutLock};
 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;
diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs
index 36b49401591..5b50ce4c2ee 100644
--- a/library/std/src/io/stdio.rs
+++ b/library/std/src/io/stdio.rs
@@ -18,14 +18,14 @@ use crate::thread::LocalKey;
 
 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 @@ impl fmt::Debug for StderrLock<'_> {
     }
 }
 
+/// 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 @@ impl fmt::Debug for StderrLock<'_> {
     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
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index 8dceb12de87..17d5398b4ef 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -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() {
diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs
index 45c10266ba2..bdb8fc7807b 100644
--- a/library/std/src/thread/mod.rs
+++ b/library/std/src/thread/mod.rs
@@ -457,11 +457,16 @@ impl Builder {
         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.