diff options
| author | Steven Fackler <sfackler@gmail.com> | 2016-01-20 20:32:14 -0800 |
|---|---|---|
| committer | Steven Fackler <sfackler@gmail.com> | 2016-01-20 22:30:46 -0800 |
| commit | 334bee348940dad95dd7f390ddca5ed5db8213f6 (patch) | |
| tree | ecae47cac2c401a7956cf21e55886d488d8d5432 /src/libstd/io/buffered.rs | |
| parent | 51108b64ca3c84d9973736e6b9e094e79c12dc60 (diff) | |
| download | rust-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.rs | 43 |
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(|| { |
