From eb44672f9bd837ca38c2cdd1109e6088c845b4e6 Mon Sep 17 00:00:00 2001
From: Usama Arif <usama.arif@bytedance.com>
Date: Mon, 15 Aug 2022 18:16:30 +0100
Subject: [PATCH] std::io: retry write operation on ErrorKind::WouldBlock

If writing to tty when O_NONBLOCK bit is set and the lock to
tty is held (e.g. multiple threads writing to console), the Linux kernel
returns -EAGAIN.
(https://elixir.bootlin.com/linux/v5.19/source/drivers/tty/tty_io.c#L952)

Not accounting for -EAGAIN (i.e. ErrorKind::WouldBlock), results in
a panic. Its better to retry the write as the other thread could have
release the lock by then.

Signed-off-by: Usama Arif <usama.arif@bytedance.com>
Reviewed-by: Fam Zheng <fam.zheng@bytedance.com>
---
 library/std/src/io/buffered/bufwriter.rs |  4 ++-
 library/std/src/io/mod.rs                | 34 ++++++++++++++----------
 2 files changed, 23 insertions(+), 15 deletions(-)

diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs
index 6acb937e78479..fbce96c764957 100644
--- a/library/std/src/io/buffered/bufwriter.rs
+++ b/library/std/src/io/buffered/bufwriter.rs
@@ -174,7 +174,9 @@ impl<W: Write> BufWriter<W> {
                     ));
                 }
                 Ok(n) => guard.consume(n),
-                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
+                Err(ref e)
+                    if e.kind() == io::ErrorKind::Interrupted
+                        || e.kind() == ErrorKind::WouldBlock => {}
                 Err(e) => return Err(e),
             }
         }
diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs
index 96addbd1a0558..efae16ea9ba86 100644
--- a/library/std/src/io/mod.rs
+++ b/library/std/src/io/mod.rs
@@ -1396,8 +1396,9 @@ pub trait Write {
     /// It is **not** considered an error if the entire buffer could not be
     /// written to this writer.
     ///
-    /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the
-    /// write operation should be retried if there is nothing else to do.
+    /// An error of kind [`ErrorKind::Interrupted`] or [`ErrorKind::WouldBlock`]
+    /// is non-fatal and the write operation should be retried if there is
+    /// nothing else to do.
     ///
     /// # Examples
     ///
@@ -1498,18 +1499,19 @@ pub trait Write {
     /// Attempts to write an entire buffer into this writer.
     ///
     /// This method will continuously call [`write`] until there is no more data
-    /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is
-    /// returned. This method will not return until the entire buffer has been
-    /// successfully written or such an error occurs. The first error that is
-    /// not of [`ErrorKind::Interrupted`] kind generated from this method will be
-    /// returned.
+    /// to be written or an error is returned that is not of kind [`ErrorKind::Interrupted`]
+    /// or [`ErrorKind::WouldBlock`]. This method will not return until the
+    /// entire buffer has been successfully written or such an error occurs. The
+    /// first error that is not of kind [`ErrorKind::Interrupted`] or
+    /// [`ErrorKind::WouldBlock`] generated from this method will be returned.
     ///
     /// If the buffer contains no data, this will never call [`write`].
     ///
     /// # Errors
     ///
     /// This function will return the first error of
-    /// non-[`ErrorKind::Interrupted`] kind that [`write`] returns.
+    /// that [`write`] returns which is not of kind [`ErrorKind::Interrupted`]
+    /// or [`ErrorKind::WouldBlock`].
     ///
     /// [`write`]: Write::write
     ///
@@ -1537,20 +1539,23 @@ pub trait Write {
                     ));
                 }
                 Ok(n) => buf = &buf[n..],
-                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
+                Err(ref e)
+                    if e.kind() == ErrorKind::Interrupted || e.kind() == ErrorKind::WouldBlock => {}
                 Err(e) => return Err(e),
             }
         }
+
         Ok(())
     }
 
     /// Attempts to write multiple buffers into this writer.
     ///
     /// This method will continuously call [`write_vectored`] until there is no
-    /// more data to be written or an error of non-[`ErrorKind::Interrupted`]
-    /// kind is returned. This method will not return until all buffers have
-    /// been successfully written or such an error occurs. The first error that
-    /// is not of [`ErrorKind::Interrupted`] kind generated from this method
+    /// more data to be written or an error is returned that is not of kind
+    /// [`ErrorKind::Interrupted`] or [`ErrorKind::WouldBlock`]. This method will
+    /// not return until all buffers have been successfully written or such an
+    /// error occurs. The first error that is not of kind [`ErrorKind::Interrupted`]
+    /// or [`ErrorKind::WouldBlock`] generated from this method
     /// will be returned.
     ///
     /// If the buffer contains no data, this will never call [`write_vectored`].
@@ -1605,7 +1610,8 @@ pub trait Write {
                     ));
                 }
                 Ok(n) => IoSlice::advance_slices(&mut bufs, n),
-                Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
+                Err(ref e)
+                    if e.kind() == ErrorKind::Interrupted || e.kind() == ErrorKind::WouldBlock => {}
                 Err(e) => return Err(e),
             }
         }