@@ -8,8 +8,10 @@ use crate::fmt;
88use crate :: fs:: File ;
99use crate :: io:: prelude:: * ;
1010use crate :: io:: {
11- self , BorrowedCursor , BufReader , IoSlice , IoSliceMut , LineWriter , Lines , SpecReadByte ,
11+ self , BorrowedCursor , BufReader , BufWriter , IoSlice , IoSliceMut , LineWriter , Lines ,
12+ SpecReadByte ,
1213} ;
14+ use crate :: os:: fd:: { AsFd , BorrowedFd } ;
1315use crate :: panic:: { RefUnwindSafe , UnwindSafe } ;
1416use crate :: sync:: atomic:: { Atomic , AtomicBool , Ordering } ;
1517use crate :: sync:: { Arc , Mutex , MutexGuard , OnceLock , ReentrantLock , ReentrantLockGuard } ;
@@ -168,6 +170,13 @@ impl Write for StdoutRaw {
168170 }
169171}
170172
173+ impl AsFd for StdoutRaw {
174+ #[ inline]
175+ fn as_fd ( & self ) -> BorrowedFd < ' _ > {
176+ unsafe { BorrowedFd :: borrow_raw ( 1 ) }
177+ }
178+ }
179+
171180impl Write for StderrRaw {
172181 fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
173182 handle_ebadf ( self . 0 . write ( buf) , || Ok ( buf. len ( ) ) )
@@ -200,6 +209,13 @@ impl Write for StderrRaw {
200209 }
201210}
202211
212+ impl AsFd for StderrRaw {
213+ #[ inline]
214+ fn as_fd ( & self ) -> BorrowedFd < ' _ > {
215+ unsafe { BorrowedFd :: borrow_raw ( 2 ) }
216+ }
217+ }
218+
203219fn handle_ebadf < T > ( r : io:: Result < T > , default : impl FnOnce ( ) -> io:: Result < T > ) -> io:: Result < T > {
204220 match r {
205221 Err ( ref e) if stdio:: is_ebadf ( e) => default ( ) ,
@@ -576,6 +592,76 @@ impl fmt::Debug for StdinLock<'_> {
576592 }
577593}
578594
595+ /// A buffered writer for stdout and stderr.
596+ ///
597+ /// This writer may be either [line-buffered](LineWriter) or [block-buffered](BufWriter), depending
598+ /// on whether the underlying file is a terminal or not.
599+ #[ derive( Debug ) ]
600+ enum StdioBufWriter < W : Write > {
601+ LineBuffered ( LineWriter < W > ) ,
602+ BlockBuffered ( BufWriter < W > ) ,
603+ }
604+
605+ impl < W : Write + IsTerminal > StdioBufWriter < W > {
606+ /// Wraps a writer using the most appropriate buffering method.
607+ ///
608+ /// If `w` is a terminal, then the resulting `StdioBufWriter` will be line-buffered, otherwise
609+ /// it will be block-buffered.
610+ fn new ( w : W ) -> Self {
611+ if w. is_terminal ( ) {
612+ Self :: LineBuffered ( LineWriter :: new ( w) )
613+ } else {
614+ Self :: BlockBuffered ( BufWriter :: new ( w) )
615+ }
616+ }
617+ }
618+
619+ impl < W : Write > StdioBufWriter < W > {
620+ /// Wraps a writer using a block-buffer with the given capacity.
621+ fn with_capacity ( cap : usize , w : W ) -> Self {
622+ Self :: BlockBuffered ( BufWriter :: with_capacity ( cap, w) )
623+ }
624+ }
625+
626+ impl < W : Write > Write for StdioBufWriter < W > {
627+ fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
628+ match self {
629+ Self :: LineBuffered ( w) => w. write ( buf) ,
630+ Self :: BlockBuffered ( w) => w. write ( buf) ,
631+ }
632+ }
633+ fn write_vectored ( & mut self , bufs : & [ IoSlice < ' _ > ] ) -> io:: Result < usize > {
634+ match self {
635+ Self :: LineBuffered ( w) => w. write_vectored ( bufs) ,
636+ Self :: BlockBuffered ( w) => w. write_vectored ( bufs) ,
637+ }
638+ }
639+ fn is_write_vectored ( & self ) -> bool {
640+ match self {
641+ Self :: LineBuffered ( w) => w. is_write_vectored ( ) ,
642+ Self :: BlockBuffered ( w) => w. is_write_vectored ( ) ,
643+ }
644+ }
645+ fn flush ( & mut self ) -> io:: Result < ( ) > {
646+ match self {
647+ Self :: LineBuffered ( w) => w. flush ( ) ,
648+ Self :: BlockBuffered ( w) => w. flush ( ) ,
649+ }
650+ }
651+ fn write_all ( & mut self , buf : & [ u8 ] ) -> io:: Result < ( ) > {
652+ match self {
653+ Self :: LineBuffered ( w) => w. write_all ( buf) ,
654+ Self :: BlockBuffered ( w) => w. write_all ( buf) ,
655+ }
656+ }
657+ fn write_all_vectored ( & mut self , bufs : & mut [ IoSlice < ' _ > ] ) -> io:: Result < ( ) > {
658+ match self {
659+ Self :: LineBuffered ( w) => w. write_all_vectored ( bufs) ,
660+ Self :: BlockBuffered ( w) => w. write_all_vectored ( bufs) ,
661+ }
662+ }
663+ }
664+
579665/// A handle to the global standard output stream of the current process.
580666///
581667/// Each handle shares a global buffer of data to be written to the standard
@@ -606,10 +692,9 @@ impl fmt::Debug for StdinLock<'_> {
606692/// [`io::stdout`]: stdout
607693#[ stable( feature = "rust1" , since = "1.0.0" ) ]
608694pub struct Stdout {
609- // FIXME: this should be LineWriter or BufWriter depending on the state of
610- // stdout (tty or not). Note that if this is not line buffered it
611- // should also flush-on-panic or some form of flush-on-abort.
612- inner : & ' static ReentrantLock < RefCell < LineWriter < StdoutRaw > > > ,
695+ // FIXME: if this is not line buffered it should flush-on-panic or some
696+ // form of flush-on-abort.
697+ inner : & ' static ReentrantLock < RefCell < StdioBufWriter < StdoutRaw > > > ,
613698}
614699
615700/// A locked reference to the [`Stdout`] handle.
@@ -638,10 +723,10 @@ pub struct Stdout {
638723#[ must_use = "if unused stdout will immediately unlock" ]
639724#[ stable( feature = "rust1" , since = "1.0.0" ) ]
640725pub struct StdoutLock < ' a > {
641- inner : ReentrantLockGuard < ' a , RefCell < LineWriter < StdoutRaw > > > ,
726+ inner : ReentrantLockGuard < ' a , RefCell < StdioBufWriter < StdoutRaw > > > ,
642727}
643728
644- static STDOUT : OnceLock < ReentrantLock < RefCell < LineWriter < StdoutRaw > > > > = OnceLock :: new ( ) ;
729+ static STDOUT : OnceLock < ReentrantLock < RefCell < StdioBufWriter < StdoutRaw > > > > = OnceLock :: new ( ) ;
645730
646731/// Constructs a new handle to the standard output of the current process.
647732///
@@ -716,7 +801,7 @@ static STDOUT: OnceLock<ReentrantLock<RefCell<LineWriter<StdoutRaw>>>> = OnceLoc
716801pub fn stdout ( ) -> Stdout {
717802 Stdout {
718803 inner : STDOUT
719- . get_or_init ( || ReentrantLock :: new ( RefCell :: new ( LineWriter :: new ( stdout_raw ( ) ) ) ) ) ,
804+ . get_or_init ( || ReentrantLock :: new ( RefCell :: new ( StdioBufWriter :: new ( stdout_raw ( ) ) ) ) ) ,
720805 }
721806}
722807
@@ -727,7 +812,7 @@ pub fn cleanup() {
727812 let mut initialized = false ;
728813 let stdout = STDOUT . get_or_init ( || {
729814 initialized = true ;
730- ReentrantLock :: new ( RefCell :: new ( LineWriter :: with_capacity ( 0 , stdout_raw ( ) ) ) )
815+ ReentrantLock :: new ( RefCell :: new ( StdioBufWriter :: with_capacity ( 0 , stdout_raw ( ) ) ) )
731816 } ) ;
732817
733818 if !initialized {
@@ -736,7 +821,7 @@ pub fn cleanup() {
736821 // might have leaked a StdoutLock, which would
737822 // otherwise cause a deadlock here.
738823 if let Some ( lock) = stdout. try_lock ( ) {
739- * lock. borrow_mut ( ) = LineWriter :: with_capacity ( 0 , stdout_raw ( ) ) ;
824+ * lock. borrow_mut ( ) = StdioBufWriter :: with_capacity ( 0 , stdout_raw ( ) ) ;
740825 }
741826 }
742827}
@@ -1262,7 +1347,17 @@ macro_rules! impl_is_terminal {
12621347 ) * }
12631348}
12641349
1265- impl_is_terminal ! ( File , Stdin , StdinLock <' _>, Stdout , StdoutLock <' _>, Stderr , StderrLock <' _>) ;
1350+ impl_is_terminal ! (
1351+ File ,
1352+ Stdin ,
1353+ StdinLock <' _>,
1354+ Stdout ,
1355+ StdoutLock <' _>,
1356+ StdoutRaw ,
1357+ Stderr ,
1358+ StderrLock <' _>,
1359+ StderrRaw
1360+ ) ;
12661361
12671362#[ unstable(
12681363 feature = "print_internals" ,
0 commit comments