@@ -9,26 +9,41 @@ use crate::cell::RefCell;
9
9
use crate :: fmt;
10
10
use crate :: io:: { self , BufReader , Initializer , IoSlice , IoSliceMut , LineWriter } ;
11
11
use crate :: lazy:: SyncOnceCell ;
12
+ use crate :: sync:: atomic:: { AtomicBool , Ordering } ;
12
13
use crate :: sync:: { Mutex , MutexGuard } ;
13
14
use crate :: sys:: stdio;
14
15
use crate :: sys_common;
15
16
use crate :: sys_common:: remutex:: { ReentrantMutex , ReentrantMutexGuard } ;
16
17
use crate :: thread:: LocalKey ;
17
18
18
19
thread_local ! {
19
- /// Stdout used by print! and println! macros
20
+ /// Used by the test crate to capture the output of the print! and println! macros.
20
21
static LOCAL_STDOUT : RefCell <Option <Box <dyn Write + Send >>> = {
21
22
RefCell :: new( None )
22
23
}
23
24
}
24
25
25
26
thread_local ! {
26
- /// Stderr used by eprint! and eprintln! macros, and panics
27
+ /// Used by the test crate to capture the output of the eprint! and eprintln! macros, and panics.
27
28
static LOCAL_STDERR : RefCell <Option <Box <dyn Write + Send >>> = {
28
29
RefCell :: new( None )
29
30
}
30
31
}
31
32
33
+ /// Flag to indicate LOCAL_STDOUT and/or LOCAL_STDERR is used.
34
+ ///
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.
38
+ ///
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
41
+ /// 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
43
+ /// the same thread*. Within the same thread, things always have a perfectly
44
+ /// consistent order. So Ordering::Relaxed is fine.
45
+ static LOCAL_STREAMS : AtomicBool = AtomicBool :: new ( false ) ;
46
+
32
47
/// A handle to a raw instance of the standard input stream of this process.
33
48
///
34
49
/// This handle is not synchronized or buffered in any fashion. Constructed via
@@ -890,10 +905,18 @@ impl fmt::Debug for StderrLock<'_> {
890
905
#[ doc( hidden) ]
891
906
pub fn set_panic ( sink : Option < Box < dyn Write + Send > > ) -> Option < Box < dyn Write + Send > > {
892
907
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
- } )
908
+ if sink. is_none ( ) && !LOCAL_STREAMS . load ( Ordering :: Relaxed ) {
909
+ // LOCAL_STDERR is definitely None since LOCAL_STREAMS is false.
910
+ return None ;
911
+ }
912
+ let s = LOCAL_STDERR . with ( move |slot| mem:: replace ( & mut * slot. borrow_mut ( ) , sink) ) . and_then (
913
+ |mut s| {
914
+ let _ = s. flush ( ) ;
915
+ Some ( s)
916
+ } ,
917
+ ) ;
918
+ LOCAL_STREAMS . store ( true , Ordering :: Relaxed ) ;
919
+ s
897
920
}
898
921
899
922
/// Resets the thread-local stdout handle to the specified writer
@@ -913,10 +936,18 @@ pub fn set_panic(sink: Option<Box<dyn Write + Send>>) -> Option<Box<dyn Write +
913
936
#[ doc( hidden) ]
914
937
pub fn set_print ( sink : Option < Box < dyn Write + Send > > ) -> Option < Box < dyn Write + Send > > {
915
938
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
- } )
939
+ if sink. is_none ( ) && !LOCAL_STREAMS . load ( Ordering :: Relaxed ) {
940
+ // LOCAL_STDOUT is definitely None since LOCAL_STREAMS is false.
941
+ return None ;
942
+ }
943
+ let s = LOCAL_STDOUT . with ( move |slot| mem:: replace ( & mut * slot. borrow_mut ( ) , sink) ) . and_then (
944
+ |mut s| {
945
+ let _ = s. flush ( ) ;
946
+ Some ( s)
947
+ } ,
948
+ ) ;
949
+ LOCAL_STREAMS . store ( true , Ordering :: Relaxed ) ;
950
+ s
920
951
}
921
952
922
953
/// Write `args` to output stream `local_s` if possible, `global_s`
@@ -937,20 +968,26 @@ fn print_to<T>(
937
968
) where
938
969
T : Write ,
939
970
{
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)
971
+ let result = LOCAL_STREAMS
972
+ . load ( Ordering :: Relaxed )
973
+ . then ( || {
974
+ local_s
975
+ . try_with ( |s| {
976
+ // Note that we completely remove a local sink to write to in case
977
+ // our printing recursively panics/prints, so the recursive
978
+ // panic/print goes to the global sink instead of our local sink.
979
+ let prev = s. borrow_mut ( ) . take ( ) ;
980
+ if let Some ( mut w) = prev {
981
+ let result = w. write_fmt ( args) ;
982
+ * s. borrow_mut ( ) = Some ( w) ;
983
+ return result;
984
+ }
985
+ global_s ( ) . write_fmt ( args)
986
+ } )
987
+ . ok ( )
952
988
} )
953
- . unwrap_or_else ( |_| global_s ( ) . write_fmt ( args) ) ;
989
+ . flatten ( )
990
+ . unwrap_or_else ( || global_s ( ) . write_fmt ( args) ) ;
954
991
955
992
if let Err ( e) = result {
956
993
panic ! ( "failed printing to {}: {}" , label, e) ;
0 commit comments