Skip to content

Commit 11ce918

Browse files
authored
Rollup merge of #78714 - m-ou-se:simplify-local-streams, r=KodrAus
Simplify output capturing This is a sequence of incremental improvements to the unstable/internal `set_panic` and `set_print` mechanism used by the `test` crate: 1. Remove the `LocalOutput` trait and use `Arc<Mutex<dyn Write>>` instead of `Box<dyn LocalOutput>`. In practice, all implementations of `LocalOutput` were just `Arc<Mutex<..>>`. This simplifies some logic and removes all custom `Sink` implementations such as `library/test/src/helpers/sink.rs`. Also removes a layer of indirection, as the outermost `Box` is now gone. It also means that locking now happens per `write_fmt`, not per individual `write` within. (So `"{} {}\n"` now results in one `lock()`, not four or more.) 2. Since in all cases the `dyn Write`s were just `Vec<u8>`s, replace the type with `Arc<Mutex<Vec<u8>>>`. This simplifies things more, as error handling and flushing can be removed now. This also removes the hack needed in the default panic handler to make this work with `::realstd`, as (unlike `Write`) `Vec<u8>` is from `alloc`, not `std`. 3. Replace the `RefCell`s by regular `Cell`s. The `RefCell`s were mostly used as `mem::replace(&mut *cell.borrow_mut(), something)`, which is just `Cell::replace`. This removes an unecessary bookkeeping and makes the code a bit easier to read. 4. Merge `set_panic` and `set_print` into a single `set_output_capture`. Neither the test crate nor rustc (the only users of this feature) have a use for using these separately. Merging them simplifies things even more. This uses a new function name and feature name, to make it clearer this is internal and not supposed to be used by other crates. Might be easier to review per commit.
2 parents 7a1bd80 + aff7bd6 commit 11ce918

File tree

16 files changed

+85
-290
lines changed

16 files changed

+85
-290
lines changed

compiler/rustc_interface/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![feature(bool_to_option)]
22
#![feature(box_syntax)]
3-
#![feature(set_stdio)]
3+
#![feature(internal_output_capture)]
44
#![feature(nll)]
55
#![feature(generator_trait)]
66
#![feature(generators)]

compiler/rustc_interface/src/util.rs

+3-22
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use rustc_span::symbol::{sym, Symbol};
2525
use smallvec::SmallVec;
2626
use std::env;
2727
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
28-
use std::io::{self, Write};
28+
use std::io;
2929
use std::lazy::SyncOnceCell;
3030
use std::mem;
3131
use std::ops::DerefMut;
@@ -106,21 +106,6 @@ fn get_stack_size() -> Option<usize> {
106106
env::var_os("RUST_MIN_STACK").is_none().then_some(STACK_SIZE)
107107
}
108108

109-
struct Sink(Arc<Mutex<Vec<u8>>>);
110-
impl Write for Sink {
111-
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
112-
Write::write(&mut *self.0.lock().unwrap(), data)
113-
}
114-
fn flush(&mut self) -> io::Result<()> {
115-
Ok(())
116-
}
117-
}
118-
impl io::LocalOutput for Sink {
119-
fn clone_box(&self) -> Box<dyn io::LocalOutput> {
120-
Box::new(Self(self.0.clone()))
121-
}
122-
}
123-
124109
/// Like a `thread::Builder::spawn` followed by a `join()`, but avoids the need
125110
/// for `'static` bounds.
126111
#[cfg(not(parallel_compiler))]
@@ -163,9 +148,7 @@ pub fn setup_callbacks_and_run_in_thread_pool_with_globals<F: FnOnce() -> R + Se
163148

164149
let main_handler = move || {
165150
rustc_span::with_session_globals(edition, || {
166-
if let Some(stderr) = stderr {
167-
io::set_panic(Some(box Sink(stderr.clone())));
168-
}
151+
io::set_output_capture(stderr.clone());
169152
f()
170153
})
171154
};
@@ -203,9 +186,7 @@ pub fn setup_callbacks_and_run_in_thread_pool_with_globals<F: FnOnce() -> R + Se
203186
// on the new threads.
204187
let main_handler = move |thread: rayon::ThreadBuilder| {
205188
rustc_span::SESSION_GLOBALS.set(session_globals, || {
206-
if let Some(stderr) = stderr {
207-
io::set_panic(Some(box Sink(stderr.clone())));
208-
}
189+
io::set_output_capture(stderr.clone());
209190
thread.run()
210191
})
211192
};

library/std/src/io/impls.rs

-14
Original file line numberDiff line numberDiff line change
@@ -209,20 +209,6 @@ impl<B: BufRead + ?Sized> BufRead for Box<B> {
209209
}
210210
}
211211

212-
// Used by panicking::default_hook
213-
#[cfg(test)]
214-
/// This impl is only used by printing logic, so any error returned is always
215-
/// of kind `Other`, and should be ignored.
216-
impl Write for dyn ::realstd::io::LocalOutput {
217-
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
218-
(*self).write(buf).map_err(|_| ErrorKind::Other.into())
219-
}
220-
221-
fn flush(&mut self) -> io::Result<()> {
222-
(*self).flush().map_err(|_| ErrorKind::Other.into())
223-
}
224-
}
225-
226212
// =============================================================================
227213
// In-memory buffer implementations
228214

library/std/src/io/mod.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -271,20 +271,18 @@ pub use self::copy::copy;
271271
pub use self::cursor::Cursor;
272272
#[stable(feature = "rust1", since = "1.0.0")]
273273
pub use self::error::{Error, ErrorKind, Result};
274+
#[unstable(feature = "internal_output_capture", issue = "none")]
275+
#[doc(no_inline, hidden)]
276+
pub use self::stdio::set_output_capture;
274277
#[stable(feature = "rust1", since = "1.0.0")]
275278
pub use self::stdio::{stderr, stdin, stdout, Stderr, Stdin, Stdout};
276279
#[stable(feature = "rust1", since = "1.0.0")]
277280
pub use self::stdio::{StderrLock, StdinLock, StdoutLock};
278281
#[unstable(feature = "print_internals", issue = "none")]
279282
pub use self::stdio::{_eprint, _print};
280-
#[unstable(feature = "libstd_io_internals", issue = "42788")]
281-
#[doc(no_inline, hidden)]
282-
pub use self::stdio::{set_panic, set_print, LocalOutput};
283283
#[stable(feature = "rust1", since = "1.0.0")]
284284
pub use self::util::{empty, repeat, sink, Empty, Repeat, Sink};
285285

286-
pub(crate) use self::stdio::clone_io;
287-
288286
mod buffered;
289287
pub(crate) mod copy;
290288
mod cursor;

library/std/src/io/stdio.rs

+44-133
Original file line numberDiff line numberDiff line change
@@ -5,44 +5,38 @@ mod tests;
55

66
use crate::io::prelude::*;
77

8-
use crate::cell::RefCell;
8+
use crate::cell::{Cell, RefCell};
99
use crate::fmt;
1010
use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter};
1111
use crate::lazy::SyncOnceCell;
1212
use crate::sync::atomic::{AtomicBool, Ordering};
13-
use crate::sync::{Mutex, MutexGuard};
13+
use crate::sync::{Arc, Mutex, MutexGuard};
1414
use crate::sys::stdio;
1515
use crate::sys_common;
1616
use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
17-
use crate::thread::LocalKey;
1817

19-
thread_local! {
20-
/// Used by the test crate to capture the output of the print! and println! macros.
21-
static LOCAL_STDOUT: RefCell<Option<Box<dyn LocalOutput>>> = {
22-
RefCell::new(None)
23-
}
24-
}
18+
type LocalStream = Arc<Mutex<Vec<u8>>>;
2519

2620
thread_local! {
27-
/// Used by the test crate to capture the output of the eprint! and eprintln! macros, and panics.
28-
static LOCAL_STDERR: RefCell<Option<Box<dyn LocalOutput>>> = {
29-
RefCell::new(None)
21+
/// Used by the test crate to capture the output of the print macros and panics.
22+
static OUTPUT_CAPTURE: Cell<Option<LocalStream>> = {
23+
Cell::new(None)
3024
}
3125
}
3226

33-
/// Flag to indicate LOCAL_STDOUT and/or LOCAL_STDERR is used.
27+
/// Flag to indicate OUTPUT_CAPTURE is used.
3428
///
35-
/// If both are None and were never set on any thread, this flag is set to
36-
/// false, and both LOCAL_STDOUT and LOCAL_STDOUT can be safely ignored on all
37-
/// threads, saving some time and memory registering an unused thread local.
29+
/// If it is None and was never set on any thread, this flag is set to false,
30+
/// and OUTPUT_CAPTURE can be safely ignored on all threads, saving some time
31+
/// and memory registering an unused thread local.
3832
///
39-
/// Note about memory ordering: This contains information about whether two
40-
/// thread local variables might be in use. Although this is a global flag, the
33+
/// Note about memory ordering: This contains information about whether a
34+
/// thread local variable might be in use. Although this is a global flag, the
4135
/// memory ordering between threads does not matter: we only want this flag to
42-
/// have a consistent order between set_print/set_panic and print_to *within
36+
/// have a consistent order between set_output_capture and print_to *within
4337
/// the same thread*. Within the same thread, things always have a perfectly
4438
/// consistent order. So Ordering::Relaxed is fine.
45-
static LOCAL_STREAMS: AtomicBool = AtomicBool::new(false);
39+
static OUTPUT_CAPTURE_USED: AtomicBool = AtomicBool::new(false);
4640

4741
/// A handle to a raw instance of the standard input stream of this process.
4842
///
@@ -896,97 +890,24 @@ impl fmt::Debug for StderrLock<'_> {
896890
}
897891
}
898892

899-
/// A writer than can be cloned to new threads.
900-
#[unstable(
901-
feature = "set_stdio",
902-
reason = "this trait may disappear completely or be replaced \
903-
with a more general mechanism",
904-
issue = "none"
905-
)]
906-
#[doc(hidden)]
907-
pub trait LocalOutput: Write + Send {
908-
fn clone_box(&self) -> Box<dyn LocalOutput>;
909-
}
910-
911-
/// Resets the thread-local stderr handle to the specified writer
912-
///
913-
/// This will replace the current thread's stderr handle, returning the old
914-
/// handle. All future calls to `panic!` and friends will emit their output to
915-
/// this specified handle.
916-
///
917-
/// Note that this does not need to be called for all new threads; the default
918-
/// output handle is to the process's stderr stream.
919-
#[unstable(
920-
feature = "set_stdio",
921-
reason = "this function may disappear completely or be replaced \
922-
with a more general mechanism",
923-
issue = "none"
924-
)]
925-
#[doc(hidden)]
926-
pub fn set_panic(sink: Option<Box<dyn LocalOutput>>) -> Option<Box<dyn LocalOutput>> {
927-
use crate::mem;
928-
if sink.is_none() && !LOCAL_STREAMS.load(Ordering::Relaxed) {
929-
// LOCAL_STDERR is definitely None since LOCAL_STREAMS is false.
930-
return None;
931-
}
932-
let s = LOCAL_STDERR.with(move |slot| mem::replace(&mut *slot.borrow_mut(), sink)).and_then(
933-
|mut s| {
934-
let _ = s.flush();
935-
Some(s)
936-
},
937-
);
938-
LOCAL_STREAMS.store(true, Ordering::Relaxed);
939-
s
940-
}
941-
942-
/// Resets the thread-local stdout handle to the specified writer
943-
///
944-
/// This will replace the current thread's stdout handle, returning the old
945-
/// handle. All future calls to `print!` and friends will emit their output to
946-
/// this specified handle.
947-
///
948-
/// Note that this does not need to be called for all new threads; the default
949-
/// output handle is to the process's stdout stream.
893+
/// Sets the thread-local output capture buffer and returns the old one.
950894
#[unstable(
951-
feature = "set_stdio",
952-
reason = "this function may disappear completely or be replaced \
953-
with a more general mechanism",
895+
feature = "internal_output_capture",
896+
reason = "this function is meant for use in the test crate \
897+
and may disappear in the future",
954898
issue = "none"
955899
)]
956900
#[doc(hidden)]
957-
pub fn set_print(sink: Option<Box<dyn LocalOutput>>) -> Option<Box<dyn LocalOutput>> {
958-
use crate::mem;
959-
if sink.is_none() && !LOCAL_STREAMS.load(Ordering::Relaxed) {
960-
// LOCAL_STDOUT is definitely None since LOCAL_STREAMS is false.
901+
pub fn set_output_capture(sink: Option<LocalStream>) -> Option<LocalStream> {
902+
if sink.is_none() && !OUTPUT_CAPTURE_USED.load(Ordering::Relaxed) {
903+
// OUTPUT_CAPTURE is definitely None since OUTPUT_CAPTURE_USED is false.
961904
return None;
962905
}
963-
let s = LOCAL_STDOUT.with(move |slot| mem::replace(&mut *slot.borrow_mut(), sink)).and_then(
964-
|mut s| {
965-
let _ = s.flush();
966-
Some(s)
967-
},
968-
);
969-
LOCAL_STREAMS.store(true, Ordering::Relaxed);
970-
s
971-
}
972-
973-
pub(crate) fn clone_io() -> (Option<Box<dyn LocalOutput>>, Option<Box<dyn LocalOutput>>) {
974-
// Don't waste time when LOCAL_{STDOUT,STDERR} are definitely None.
975-
if !LOCAL_STREAMS.load(Ordering::Relaxed) {
976-
return (None, None);
977-
}
978-
979-
LOCAL_STDOUT.with(|stdout| {
980-
LOCAL_STDERR.with(|stderr| {
981-
(
982-
stdout.borrow().as_ref().map(|o| o.clone_box()),
983-
stderr.borrow().as_ref().map(|o| o.clone_box()),
984-
)
985-
})
986-
})
906+
OUTPUT_CAPTURE_USED.store(true, Ordering::Relaxed);
907+
OUTPUT_CAPTURE.with(move |slot| slot.replace(sink))
987908
}
988909

989-
/// Write `args` to output stream `local_s` if possible, `global_s`
910+
/// Write `args` to the capture buffer if enabled and possible, or `global_s`
990911
/// otherwise. `label` identifies the stream in a panic message.
991912
///
992913
/// This function is used to print error messages, so it takes extra
@@ -996,36 +917,26 @@ pub(crate) fn clone_io() -> (Option<Box<dyn LocalOutput>>, Option<Box<dyn LocalO
996917
/// thread, it will just fall back to the global stream.
997918
///
998919
/// However, if the actual I/O causes an error, this function does panic.
999-
fn print_to<T>(
1000-
args: fmt::Arguments<'_>,
1001-
local_s: &'static LocalKey<RefCell<Option<Box<dyn LocalOutput>>>>,
1002-
global_s: fn() -> T,
1003-
label: &str,
1004-
) where
920+
fn print_to<T>(args: fmt::Arguments<'_>, global_s: fn() -> T, label: &str)
921+
where
1005922
T: Write,
1006923
{
1007-
let result = LOCAL_STREAMS
1008-
.load(Ordering::Relaxed)
1009-
.then(|| {
1010-
local_s
1011-
.try_with(|s| {
1012-
// Note that we completely remove a local sink to write to in case
1013-
// our printing recursively panics/prints, so the recursive
1014-
// panic/print goes to the global sink instead of our local sink.
1015-
let prev = s.borrow_mut().take();
1016-
if let Some(mut w) = prev {
1017-
let result = w.write_fmt(args);
1018-
*s.borrow_mut() = Some(w);
1019-
return result;
1020-
}
1021-
global_s().write_fmt(args)
1022-
})
1023-
.ok()
1024-
})
1025-
.flatten()
1026-
.unwrap_or_else(|| global_s().write_fmt(args));
1027-
1028-
if let Err(e) = result {
924+
if OUTPUT_CAPTURE_USED.load(Ordering::Relaxed)
925+
&& OUTPUT_CAPTURE.try_with(|s| {
926+
// Note that we completely remove a local sink to write to in case
927+
// our printing recursively panics/prints, so the recursive
928+
// panic/print goes to the global sink instead of our local sink.
929+
s.take().map(|w| {
930+
let _ = w.lock().unwrap_or_else(|e| e.into_inner()).write_fmt(args);
931+
s.set(Some(w));
932+
})
933+
}) == Ok(Some(()))
934+
{
935+
// Succesfully wrote to capture buffer.
936+
return;
937+
}
938+
939+
if let Err(e) = global_s().write_fmt(args) {
1029940
panic!("failed printing to {}: {}", label, e);
1030941
}
1031942
}
@@ -1038,7 +949,7 @@ fn print_to<T>(
1038949
#[doc(hidden)]
1039950
#[cfg(not(test))]
1040951
pub fn _print(args: fmt::Arguments<'_>) {
1041-
print_to(args, &LOCAL_STDOUT, stdout, "stdout");
952+
print_to(args, stdout, "stdout");
1042953
}
1043954

1044955
#[unstable(
@@ -1049,7 +960,7 @@ pub fn _print(args: fmt::Arguments<'_>) {
1049960
#[doc(hidden)]
1050961
#[cfg(not(test))]
1051962
pub fn _eprint(args: fmt::Arguments<'_>) {
1052-
print_to(args, &LOCAL_STDERR, stderr, "stderr");
963+
print_to(args, stderr, "stderr");
1053964
}
1054965

1055966
#[cfg(test)]

library/std/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@
207207
// std may use features in a platform-specific way
208208
#![allow(unused_features)]
209209
#![cfg_attr(not(bootstrap), feature(rustc_allow_const_fn_unstable))]
210-
#![cfg_attr(test, feature(print_internals, set_stdio, update_panic_count))]
210+
#![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count))]
211211
#![cfg_attr(
212212
all(target_vendor = "fortanix", target_env = "sgx"),
213213
feature(slice_index_methods, coerce_unsized, sgx_platform)
@@ -298,6 +298,7 @@
298298
#![feature(raw)]
299299
#![feature(raw_ref_macros)]
300300
#![feature(ready_macro)]
301+
#![feature(refcell_take)]
301302
#![feature(rustc_attrs)]
302303
#![feature(rustc_private)]
303304
#![feature(shrink_to)]

library/std/src/panicking.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ use crate::sys_common::{thread_info, util};
2424
use crate::thread;
2525

2626
#[cfg(not(test))]
27-
use crate::io::set_panic;
27+
use crate::io::set_output_capture;
2828
// make sure to use the stderr output configured
2929
// by libtest in the real copy of std
3030
#[cfg(test)]
31-
use realstd::io::set_panic;
31+
use realstd::io::set_output_capture;
3232

3333
// Binary interface to the panic runtime that the standard library depends on.
3434
//
@@ -218,11 +218,9 @@ fn default_hook(info: &PanicInfo<'_>) {
218218
}
219219
};
220220

221-
if let Some(mut local) = set_panic(None) {
222-
// NB. In `cfg(test)` this uses the forwarding impl
223-
// for `dyn ::realstd::io::LocalOutput`.
224-
write(&mut local);
225-
set_panic(Some(local));
221+
if let Some(local) = set_output_capture(None) {
222+
write(&mut *local.lock().unwrap_or_else(|e| e.into_inner()));
223+
set_output_capture(Some(local));
226224
} else if let Some(mut out) = panic_output() {
227225
write(&mut out);
228226
}

0 commit comments

Comments
 (0)