From f8869e3483b4758150c2fd117c41dfc9850c0043 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 3 Oct 2025 14:58:55 -0500 Subject: [PATCH 1/2] refactor(process): Centralize Write bookkeeping --- src/process/terminalsource.rs | 46 ++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/process/terminalsource.rs b/src/process/terminalsource.rs index 46b510c661..f145a187ac 100644 --- a/src/process/terminalsource.rs +++ b/src/process/terminalsource.rs @@ -69,6 +69,16 @@ enum TerminalInner { TestWriter(TestWriter, ColorChoice), } +impl TerminalInner { + fn as_write(&mut self) -> &mut dyn io::Write { + match self { + TerminalInner::StandardStream(s, _) => s, + #[cfg(feature = "test")] + TerminalInner::TestWriter(w, _) => w, + } + } +} + pub struct ColorableTerminalLocked { // Must drop the lock before the guard, as the guard borrows from inner. locked: TerminalInnerLocked, @@ -83,6 +93,16 @@ enum TerminalInnerLocked { TestWriter(TestWriterLock<'static>), } +impl TerminalInnerLocked { + fn as_write(&mut self) -> &mut dyn io::Write { + match self { + TerminalInnerLocked::StandardStream(s) => s, + #[cfg(feature = "test")] + TerminalInnerLocked::TestWriter(w) => w, + } + } +} + impl ColorableTerminal { /// A terminal that supports colorisation of a stream. /// If `RUSTUP_TERM_COLOR` is set to `always`, or if the stream is a tty and @@ -202,37 +222,23 @@ pub enum Attr { impl io::Write for ColorableTerminal { fn write(&mut self, buf: &[u8]) -> std::result::Result { - match self.inner.lock().unwrap().deref_mut() { - TerminalInner::StandardStream(s, _) => s.write(buf), - #[cfg(feature = "test")] - TerminalInner::TestWriter(w, _) => w.write(buf), - } + let mut locked = self.inner.lock().unwrap(); + locked.deref_mut().as_write().write(buf) } fn flush(&mut self) -> std::result::Result<(), io::Error> { - match self.inner.lock().unwrap().deref_mut() { - TerminalInner::StandardStream(s, _) => s.flush(), - #[cfg(feature = "test")] - TerminalInner::TestWriter(w, _) => w.flush(), - } + let mut locked = self.inner.lock().unwrap(); + locked.deref_mut().as_write().flush() } } impl io::Write for ColorableTerminalLocked { fn write(&mut self, buf: &[u8]) -> io::Result { - match &mut self.locked { - TerminalInnerLocked::StandardStream(s) => s.write(buf), - #[cfg(feature = "test")] - TerminalInnerLocked::TestWriter(w) => w.write(buf), - } + self.locked.as_write().write(buf) } fn flush(&mut self) -> io::Result<()> { - match &mut self.locked { - TerminalInnerLocked::StandardStream(s) => s.flush(), - #[cfg(feature = "test")] - TerminalInnerLocked::TestWriter(w) => w.flush(), - } + self.locked.as_write().flush() } } From 979e66bf6e18a11d3d8fbe082ffc3709febebb12 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 3 Oct 2025 15:01:25 -0500 Subject: [PATCH 2/2] fix(process): Ensure stdout/stderr lock is held across calls While `std::io::Write` only has a few required methods, while implementing `anstream` I found its better to implement as many as possible when dealing with `stdout` or `stderr` so that the implicitly acquired lock is aquired once for the call, rather than multiple times as the inherent methods break down a single higher level calls into multiple lower level calls. --- src/process/terminalsource.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/process/terminalsource.rs b/src/process/terminalsource.rs index f145a187ac..b0331b4b01 100644 --- a/src/process/terminalsource.rs +++ b/src/process/terminalsource.rs @@ -226,10 +226,25 @@ impl io::Write for ColorableTerminal { locked.deref_mut().as_write().write(buf) } + fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result { + let mut locked = self.inner.lock().unwrap(); + locked.deref_mut().as_write().write_vectored(bufs) + } + fn flush(&mut self) -> std::result::Result<(), io::Error> { let mut locked = self.inner.lock().unwrap(); locked.deref_mut().as_write().flush() } + + fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { + let mut locked = self.inner.lock().unwrap(); + locked.deref_mut().as_write().write_all(buf) + } + + fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> std::io::Result<()> { + let mut locked = self.inner.lock().unwrap(); + locked.deref_mut().as_write().write_fmt(args) + } } impl io::Write for ColorableTerminalLocked { @@ -237,9 +252,21 @@ impl io::Write for ColorableTerminalLocked { self.locked.as_write().write(buf) } + fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result { + self.locked.as_write().write_vectored(bufs) + } + fn flush(&mut self) -> io::Result<()> { self.locked.as_write().flush() } + + fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { + self.locked.as_write().write_all(buf) + } + + fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> std::io::Result<()> { + self.locked.as_write().write_fmt(args) + } } impl TermLike for ColorableTerminal {