44mod tests;
55
66use crate :: cell:: { Cell , RefCell } ;
7- use crate :: fmt ;
7+ use crate :: ffi :: OsStr ;
88use crate :: fs:: File ;
99use crate :: io:: prelude:: * ;
1010use crate :: io:: {
1111 self , BorrowedCursor , BufReader , BufWriter , IoSlice , IoSliceMut , LineWriter , Lines ,
1212 SpecReadByte ,
1313} ;
1414use crate :: panic:: { RefUnwindSafe , UnwindSafe } ;
15+ use crate :: str:: FromStr as _;
1516use crate :: sync:: atomic:: { Atomic , AtomicBool , Ordering } ;
1617use crate :: sync:: { Arc , Mutex , MutexGuard , OnceLock , ReentrantLock , ReentrantLockGuard } ;
1718use crate :: sys:: stdio;
1819use crate :: thread:: AccessError ;
20+ use crate :: { env, fmt} ;
1921
2022type LocalStream = Arc < Mutex < Vec < u8 > > > ;
2123
@@ -579,32 +581,38 @@ impl fmt::Debug for StdinLock<'_> {
579581
580582/// A buffered writer for stdout and stderr.
581583///
582- /// This writer may be either [line-buffered](LineWriter) or [block-buffered](BufWriter), depending
583- /// on whether the underlying file is a terminal or not .
584+ /// This writer may be either [line-buffered](LineWriter),
585+ /// [block-buffered](BufWriter), or unbuffered .
584586#[ derive( Debug ) ]
585587enum StdioBufWriter < W : Write > {
586588 LineBuffered ( LineWriter < W > ) ,
587589 BlockBuffered ( BufWriter < W > ) ,
590+ Unbuffered ( W ) ,
588591}
589592
590- impl < W : Write + IsTerminal > StdioBufWriter < W > {
593+ impl < W : Write > StdioBufWriter < W > {
591594 /// Wraps a writer using the most appropriate buffering method.
592- ///
593- /// If `w` is a terminal, then the resulting `StdioBufWriter` will be line-buffered, otherwise
594- /// it will be block-buffered.
595- fn new ( w : W ) -> Self {
596- if w. is_terminal ( ) {
597- Self :: LineBuffered ( LineWriter :: new ( w) )
598- } else {
599- Self :: BlockBuffered ( BufWriter :: new ( w) )
595+ fn from_env_value ( stderr : bool , w : W , value : Option < & OsStr > ) -> Self {
596+ if let Some ( value) = value {
597+ if value == "L" {
598+ return StdioBufWriter :: LineBuffered ( LineWriter :: new ( w) ) ;
599+ }
600+ if let Some ( size) = value. to_str ( ) . and_then ( |v| usize:: from_str ( v) . ok ( ) ) {
601+ if size == 0 {
602+ return StdioBufWriter :: Unbuffered ( w) ;
603+ } else {
604+ return StdioBufWriter :: BlockBuffered ( BufWriter :: with_capacity ( size, w) ) ;
605+ }
606+ }
600607 }
608+ Self :: default_buffering ( stderr, w)
601609 }
602- }
603-
604- impl < W : Write > StdioBufWriter < W > {
605- /// Wraps a writer using a block-buffer with the given capacity.
606- fn with_capacity ( cap : usize , w : W ) -> Self {
607- Self :: BlockBuffered ( BufWriter :: with_capacity ( cap , w ) )
610+ fn default_buffering ( stderr : bool , w : W ) -> Self {
611+ if stderr {
612+ StdioBufWriter :: Unbuffered ( w )
613+ } else {
614+ StdioBufWriter :: LineBuffered ( LineWriter :: new ( w ) )
615+ }
608616 }
609617}
610618
@@ -613,36 +621,42 @@ impl<W: Write> Write for StdioBufWriter<W> {
613621 match self {
614622 Self :: LineBuffered ( w) => w. write ( buf) ,
615623 Self :: BlockBuffered ( w) => w. write ( buf) ,
624+ Self :: Unbuffered ( w) => w. write ( buf) ,
616625 }
617626 }
618627 fn write_vectored ( & mut self , bufs : & [ IoSlice < ' _ > ] ) -> io:: Result < usize > {
619628 match self {
620629 Self :: LineBuffered ( w) => w. write_vectored ( bufs) ,
621630 Self :: BlockBuffered ( w) => w. write_vectored ( bufs) ,
631+ Self :: Unbuffered ( w) => w. write_vectored ( bufs) ,
622632 }
623633 }
624634 fn is_write_vectored ( & self ) -> bool {
625635 match self {
626636 Self :: LineBuffered ( w) => w. is_write_vectored ( ) ,
627637 Self :: BlockBuffered ( w) => w. is_write_vectored ( ) ,
638+ Self :: Unbuffered ( w) => w. is_write_vectored ( ) ,
628639 }
629640 }
630641 fn flush ( & mut self ) -> io:: Result < ( ) > {
631642 match self {
632643 Self :: LineBuffered ( w) => w. flush ( ) ,
633644 Self :: BlockBuffered ( w) => w. flush ( ) ,
645+ Self :: Unbuffered ( w) => w. flush ( ) ,
634646 }
635647 }
636648 fn write_all ( & mut self , buf : & [ u8 ] ) -> io:: Result < ( ) > {
637649 match self {
638650 Self :: LineBuffered ( w) => w. write_all ( buf) ,
639651 Self :: BlockBuffered ( w) => w. write_all ( buf) ,
652+ Self :: Unbuffered ( w) => w. write_all ( buf) ,
640653 }
641654 }
642655 fn write_all_vectored ( & mut self , bufs : & mut [ IoSlice < ' _ > ] ) -> io:: Result < ( ) > {
643656 match self {
644657 Self :: LineBuffered ( w) => w. write_all_vectored ( bufs) ,
645658 Self :: BlockBuffered ( w) => w. write_all_vectored ( bufs) ,
659+ Self :: Unbuffered ( w) => w. write_all_vectored ( bufs) ,
646660 }
647661 }
648662}
@@ -785,30 +799,46 @@ static STDOUT: OnceLock<ReentrantLock<RefCell<StdioBufWriter<StdoutRaw>>>> = Onc
785799#[ cfg_attr( not( test) , rustc_diagnostic_item = "io_stdout" ) ]
786800pub fn stdout ( ) -> Stdout {
787801 Stdout {
788- inner : STDOUT
789- . get_or_init ( || ReentrantLock :: new ( RefCell :: new ( StdioBufWriter :: new ( stdout_raw ( ) ) ) ) ) ,
802+ inner : STDOUT . get_or_init ( || {
803+ ReentrantLock :: new ( RefCell :: new ( StdioBufWriter :: from_env_value (
804+ false ,
805+ stdout_raw ( ) ,
806+ env:: var_os ( "_STDBUF_O" ) . as_deref ( ) ,
807+ ) ) )
808+ } ) ,
790809 }
791810}
792811
793812// Flush the data and disable buffering during shutdown
794813// by replacing the line writer by one with zero
795814// buffering capacity.
796815pub fn cleanup ( ) {
797- let mut initialized = false ;
798- let stdout = STDOUT . get_or_init ( || {
799- initialized = true ;
800- ReentrantLock :: new ( RefCell :: new ( StdioBufWriter :: with_capacity ( 0 , stdout_raw ( ) ) ) )
801- } ) ;
802-
803- if !initialized {
804- // The buffer was previously initialized, overwrite it here.
805- // We use try_lock() instead of lock(), because someone
806- // might have leaked a StdoutLock, which would
807- // otherwise cause a deadlock here.
808- if let Some ( lock) = stdout. try_lock ( ) {
809- * lock. borrow_mut ( ) = StdioBufWriter :: with_capacity ( 0 , stdout_raw ( ) ) ;
816+ fn cleanup < W : Write , F : Fn ( ) -> W > (
817+ global : & ' static OnceLock < ReentrantLock < RefCell < StdioBufWriter < W > > > > ,
818+ init_writer : F ,
819+ ) {
820+ let mut initialized = false ;
821+ let global = global. get_or_init ( || {
822+ initialized = true ;
823+ ReentrantLock :: new ( RefCell :: new ( StdioBufWriter :: Unbuffered ( init_writer ( ) ) ) )
824+ } ) ;
825+
826+ if !initialized {
827+ // The buffer was previously initialized, overwrite it here.
828+ // We use try_lock() instead of lock(), because someone
829+ // might have leaked a StdoutLock, which would
830+ // otherwise cause a deadlock here.
831+ if let Some ( lock) = global. try_lock ( ) {
832+ let mut lock = lock. borrow_mut ( ) ;
833+ if !matches ! ( * lock, StdioBufWriter :: Unbuffered ( _) ) {
834+ * lock = StdioBufWriter :: Unbuffered ( init_writer ( ) ) ;
835+ }
836+ }
810837 }
811838 }
839+
840+ cleanup ( & STDERR , stderr_raw) ;
841+ cleanup ( & STDOUT , stdout_raw) ;
812842}
813843
814844impl Stdout {
@@ -960,7 +990,9 @@ impl fmt::Debug for StdoutLock<'_> {
960990/// standard library or via raw Windows API calls, will fail.
961991#[ stable( feature = "rust1" , since = "1.0.0" ) ]
962992pub struct Stderr {
963- inner : & ' static ReentrantLock < RefCell < StderrRaw > > ,
993+ // FIXME: if this is not line buffered it should flush-on-panic or some
994+ // form of flush-on-abort.
995+ inner : & ' static ReentrantLock < RefCell < StdioBufWriter < StderrRaw > > > ,
964996}
965997
966998/// A locked reference to the [`Stderr`] handle.
@@ -982,9 +1014,11 @@ pub struct Stderr {
9821014#[ must_use = "if unused stderr will immediately unlock" ]
9831015#[ stable( feature = "rust1" , since = "1.0.0" ) ]
9841016pub struct StderrLock < ' a > {
985- inner : ReentrantLockGuard < ' a , RefCell < StderrRaw > > ,
1017+ inner : ReentrantLockGuard < ' a , RefCell < StdioBufWriter < StderrRaw > > > ,
9861018}
9871019
1020+ static STDERR : OnceLock < ReentrantLock < RefCell < StdioBufWriter < StderrRaw > > > > = OnceLock :: new ( ) ;
1021+
9881022/// Constructs a new handle to the standard error of the current process.
9891023///
9901024/// This handle is not buffered.
@@ -1033,13 +1067,15 @@ pub struct StderrLock<'a> {
10331067#[ stable( feature = "rust1" , since = "1.0.0" ) ]
10341068#[ cfg_attr( not( test) , rustc_diagnostic_item = "io_stderr" ) ]
10351069pub fn stderr ( ) -> Stderr {
1036- // Note that unlike `stdout()` we don't use `at_exit` here to register a
1037- // destructor. Stderr is not buffered, so there's no need to run a
1038- // destructor for flushing the buffer
1039- static INSTANCE : ReentrantLock < RefCell < StderrRaw > > =
1040- ReentrantLock :: new ( RefCell :: new ( stderr_raw ( ) ) ) ;
1041-
1042- Stderr { inner : & INSTANCE }
1070+ Stderr {
1071+ inner : STDERR . get_or_init ( || {
1072+ ReentrantLock :: new ( RefCell :: new ( StdioBufWriter :: from_env_value (
1073+ true ,
1074+ stderr_raw ( ) ,
1075+ env:: var_os ( "_STDBUF_E" ) . as_deref ( ) ,
1076+ ) ) )
1077+ } ) ,
1078+ }
10431079}
10441080
10451081impl Stderr {
0 commit comments