Skip to content

Commit 07fd17f

Browse files
committed
Only use LOCAL_{STDOUT,STDERR} when set_{print/panic} is used.
The thread local LOCAL_STDOUT and LOCAL_STDERR are only used by the test crate to capture output from tests when running them in the same process in differen threads. However, every program will check these variables on every print, even outside of testing. This involves allocating a thread local key, and registering a thread local destructor. This can be somewhat expensive. This change keeps a global flag (LOCAL_STREAMS) which will be set to true when either of these local streams is used. (So, effectively only in test and benchmark runs.) When this flag is off, these thread locals are not even looked at and therefore will not be initialized on the first output on every thread, which also means no thread local destructors will be registered.
1 parent b836329 commit 07fd17f

File tree

2 files changed

+41
-23
lines changed

2 files changed

+41
-23
lines changed

library/std/src/io/stdio.rs

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,24 @@ use crate::cell::RefCell;
99
use crate::fmt;
1010
use crate::io::{self, BufReader, Initializer, IoSlice, IoSliceMut, LineWriter};
1111
use crate::lazy::SyncOnceCell;
12+
use crate::sync::atomic::{AtomicBool, Ordering};
1213
use crate::sync::{Mutex, MutexGuard};
1314
use crate::sys::stdio;
1415
use crate::sys_common;
1516
use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
1617
use crate::thread::LocalKey;
1718

19+
static LOCAL_STREAMS: AtomicBool = AtomicBool::new(false);
20+
1821
thread_local! {
19-
/// Stdout used by print! and println! macros
22+
/// Used by the test crate to capture the output of the print! and println! macros.
2023
static LOCAL_STDOUT: RefCell<Option<Box<dyn Write + Send>>> = {
2124
RefCell::new(None)
2225
}
2326
}
2427

2528
thread_local! {
26-
/// Stderr used by eprint! and eprintln! macros, and panics
29+
/// Used by the test crate to capture the output of the eprint! and eprintln! macros, and panics.
2730
static LOCAL_STDERR: RefCell<Option<Box<dyn Write + Send>>> = {
2831
RefCell::new(None)
2932
}
@@ -890,10 +893,14 @@ impl fmt::Debug for StderrLock<'_> {
890893
#[doc(hidden)]
891894
pub fn set_panic(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write + Send>> {
892895
use crate::mem;
893-
LOCAL_STDERR.with(move |slot| mem::replace(&mut *slot.borrow_mut(), sink)).and_then(|mut s| {
894-
let _ = s.flush();
895-
Some(s)
896-
})
896+
let s = LOCAL_STDERR.with(move |slot| mem::replace(&mut *slot.borrow_mut(), sink)).and_then(
897+
|mut s| {
898+
let _ = s.flush();
899+
Some(s)
900+
},
901+
);
902+
LOCAL_STREAMS.store(true, Ordering::Release);
903+
s
897904
}
898905

899906
/// Resets the thread-local stdout handle to the specified writer
@@ -913,10 +920,14 @@ pub fn set_panic(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write +
913920
#[doc(hidden)]
914921
pub fn set_print(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write + Send>> {
915922
use crate::mem;
916-
LOCAL_STDOUT.with(move |slot| mem::replace(&mut *slot.borrow_mut(), sink)).and_then(|mut s| {
917-
let _ = s.flush();
918-
Some(s)
919-
})
923+
let s = LOCAL_STDOUT.with(move |slot| mem::replace(&mut *slot.borrow_mut(), sink)).and_then(
924+
|mut s| {
925+
let _ = s.flush();
926+
Some(s)
927+
},
928+
);
929+
LOCAL_STREAMS.store(true, Ordering::Release);
930+
s
920931
}
921932

922933
/// Write `args` to output stream `local_s` if possible, `global_s`
@@ -937,20 +948,26 @@ fn print_to<T>(
937948
) where
938949
T: Write,
939950
{
940-
let result = local_s
941-
.try_with(|s| {
942-
// Note that we completely remove a local sink to write to in case
943-
// our printing recursively panics/prints, so the recursive
944-
// panic/print goes to the global sink instead of our local sink.
945-
let prev = s.borrow_mut().take();
946-
if let Some(mut w) = prev {
947-
let result = w.write_fmt(args);
948-
*s.borrow_mut() = Some(w);
949-
return result;
950-
}
951-
global_s().write_fmt(args)
951+
let result = LOCAL_STREAMS
952+
.load(Ordering::Acquire)
953+
.then(|| {
954+
local_s
955+
.try_with(|s| {
956+
// Note that we completely remove a local sink to write to in case
957+
// our printing recursively panics/prints, so the recursive
958+
// panic/print goes to the global sink instead of our local sink.
959+
let prev = s.borrow_mut().take();
960+
if let Some(mut w) = prev {
961+
let result = w.write_fmt(args);
962+
*s.borrow_mut() = Some(w);
963+
return result;
964+
}
965+
global_s().write_fmt(args)
966+
})
967+
.ok()
952968
})
953-
.unwrap_or_else(|_| global_s().write_fmt(args));
969+
.flatten()
970+
.unwrap_or_else(|| global_s().write_fmt(args));
954971

955972
if let Err(e) = result {
956973
panic!("failed printing to {}: {}", label, e);

library/std/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@
226226
#![feature(asm)]
227227
#![feature(associated_type_bounds)]
228228
#![feature(atomic_mut_ptr)]
229+
#![feature(bool_to_option)]
229230
#![feature(box_syntax)]
230231
#![feature(c_variadic)]
231232
#![feature(cfg_accessible)]

0 commit comments

Comments
 (0)