about summary refs log tree commit diff
path: root/src/libstd/io/buffered.rs
diff options
context:
space:
mode:
authorSteven Fackler <sfackler@gmail.com>2016-01-20 20:32:14 -0800
committerSteven Fackler <sfackler@gmail.com>2016-01-20 22:30:46 -0800
commit334bee348940dad95dd7f390ddca5ed5db8213f6 (patch)
treeecae47cac2c401a7956cf21e55886d488d8d5432 /src/libstd/io/buffered.rs
parent51108b64ca3c84d9973736e6b9e094e79c12dc60 (diff)
downloadrust-334bee348940dad95dd7f390ddca5ed5db8213f6.tar.gz
rust-334bee348940dad95dd7f390ddca5ed5db8213f6.zip
Don't flush in BufWriter destructor after a panic in write
We don't want to write the same data twice.

Closes #30888
Diffstat (limited to 'src/libstd/io/buffered.rs')
-rw-r--r--src/libstd/io/buffered.rs43
1 files changed, 40 insertions, 3 deletions
diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs
index a9a79fe2c77..625818a295a 100644
--- a/src/libstd/io/buffered.rs
+++ b/src/libstd/io/buffered.rs
@@ -300,6 +300,10 @@ impl<R: Seek> Seek for BufReader<R> {
 pub struct BufWriter<W: Write> {
     inner: Option<W>,
     buf: Vec<u8>,
+    // #30888: If the inner writer panics in a call to write, we don't want to
+    // write the buffered data a second time in BufWriter's destructor. This
+    // flag tells the Drop impl if it should skip the flush.
+    panicked: bool,
 }
 
 /// An error returned by `into_inner` which combines an error that
@@ -364,6 +368,7 @@ impl<W: Write> BufWriter<W> {
         BufWriter {
             inner: Some(inner),
             buf: Vec::with_capacity(cap),
+            panicked: false,
         }
     }
 
@@ -372,7 +377,11 @@ impl<W: Write> BufWriter<W> {
         let len = self.buf.len();
         let mut ret = Ok(());
         while written < len {
-            match self.inner.as_mut().unwrap().write(&self.buf[written..]) {
+            self.panicked = true;
+            let r = self.inner.as_mut().unwrap().write(&self.buf[written..]);
+            self.panicked = false;
+
+            match r {
                 Ok(0) => {
                     ret = Err(Error::new(ErrorKind::WriteZero,
                                          "failed to write the buffered data"));
@@ -455,7 +464,10 @@ impl<W: Write> Write for BufWriter<W> {
             try!(self.flush_buf());
         }
         if buf.len() >= self.buf.capacity() {
-            self.inner.as_mut().unwrap().write(buf)
+            self.panicked = true;
+            let r = self.inner.as_mut().unwrap().write(buf);
+            self.panicked = false;
+            r
         } else {
             let amt = cmp::min(buf.len(), self.buf.capacity());
             Write::write(&mut self.buf, &buf[..amt])
@@ -489,7 +501,7 @@ impl<W: Write + Seek> Seek for BufWriter<W> {
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<W: Write> Drop for BufWriter<W> {
     fn drop(&mut self) {
-        if self.inner.is_some() {
+        if self.inner.is_some() && !self.panicked {
             // dtors should not panic, so we ignore a failed flush
             let _r = self.flush_buf();
         }
@@ -777,6 +789,8 @@ mod tests {
     use prelude::v1::*;
     use io::prelude::*;
     use io::{self, BufReader, BufWriter, LineWriter, SeekFrom};
+    use sync::atomic::{AtomicUsize, Ordering};
+    use thread;
     use test;
 
     /// A dummy reader intended at testing short-reads propagation.
@@ -1065,6 +1079,29 @@ mod tests {
         panic!();
     }
 
+    #[test]
+    fn panic_in_write_doesnt_flush_in_drop() {
+        static WRITES: AtomicUsize = AtomicUsize::new(0);
+
+        struct PanicWriter;
+
+        impl Write for PanicWriter {
+            fn write(&mut self, _: &[u8]) -> io::Result<usize> {
+                WRITES.fetch_add(1, Ordering::SeqCst);
+                panic!();
+            }
+            fn flush(&mut self) -> io::Result<()> { Ok(()) }
+        }
+
+        thread::spawn(|| {
+            let mut writer = BufWriter::new(PanicWriter);
+            writer.write(b"hello world");
+            writer.flush();
+        }).join().err().unwrap();
+
+        assert_eq!(WRITES.load(Ordering::SeqCst), 1);
+    }
+
     #[bench]
     fn bench_buffered_reader(b: &mut test::Bencher) {
         b.iter(|| {