-
Notifications
You must be signed in to change notification settings - Fork 13.3k
BufWriter remove Option #59759
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BufWriter remove Option #59759
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -410,7 +410,7 @@ impl<R: Seek> Seek for BufReader<R> { | |
/// [`flush`]: #method.flush | ||
#[stable(feature = "rust1", since = "1.0.0")] | ||
pub struct BufWriter<W: Write> { | ||
inner: Option<W>, | ||
inner: 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 | ||
|
@@ -479,7 +479,7 @@ impl<W: Write> BufWriter<W> { | |
#[stable(feature = "rust1", since = "1.0.0")] | ||
pub fn with_capacity(cap: usize, inner: W) -> BufWriter<W> { | ||
BufWriter { | ||
inner: Some(inner), | ||
inner, | ||
buf: Vec::with_capacity(cap), | ||
panicked: false, | ||
} | ||
|
@@ -491,7 +491,7 @@ impl<W: Write> BufWriter<W> { | |
let mut ret = Ok(()); | ||
while written < len { | ||
self.panicked = true; | ||
let r = self.inner.as_mut().unwrap().write(&self.buf[written..]); | ||
let r = self.inner.write(&self.buf[written..]); | ||
self.panicked = false; | ||
|
||
match r { | ||
|
@@ -526,7 +526,7 @@ impl<W: Write> BufWriter<W> { | |
/// let reference = buffer.get_ref(); | ||
/// ``` | ||
#[stable(feature = "rust1", since = "1.0.0")] | ||
pub fn get_ref(&self) -> &W { self.inner.as_ref().unwrap() } | ||
pub fn get_ref(&self) -> &W { &self.inner } | ||
|
||
/// Gets a mutable reference to the underlying writer. | ||
/// | ||
|
@@ -544,7 +544,7 @@ impl<W: Write> BufWriter<W> { | |
/// let reference = buffer.get_mut(); | ||
/// ``` | ||
#[stable(feature = "rust1", since = "1.0.0")] | ||
pub fn get_mut(&mut self) -> &mut W { self.inner.as_mut().unwrap() } | ||
pub fn get_mut(&mut self) -> &mut W { &mut self.inner } | ||
|
||
/// Returns a reference to the internally buffered data. | ||
/// | ||
|
@@ -588,7 +588,14 @@ impl<W: Write> BufWriter<W> { | |
pub fn into_inner(mut self) -> Result<W, IntoInnerError<BufWriter<W>>> { | ||
match self.flush_buf() { | ||
Err(e) => Err(IntoInnerError(self, e)), | ||
Ok(()) => Ok(self.inner.take().unwrap()) | ||
Ok(()) => { | ||
use crate::mem::forget; | ||
use crate::ptr::{drop_in_place, read}; | ||
let inner = unsafe { read(&mut self.inner) }; | ||
unsafe { drop_in_place(&mut self.buf) }; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I notice that if you have a global memory allocator that can panic in dealloc, a panic here would turn into a double-free of
so this seems fine for now but needs at least a comment why the code is safe despite appearances. |
||
forget(self); | ||
Ok(inner) | ||
} | ||
} | ||
} | ||
} | ||
|
@@ -601,7 +608,7 @@ impl<W: Write> Write for BufWriter<W> { | |
} | ||
if buf.len() >= self.buf.capacity() { | ||
self.panicked = true; | ||
let r = self.inner.as_mut().unwrap().write(buf); | ||
let r = self.inner.write(buf); | ||
self.panicked = false; | ||
r | ||
} else { | ||
|
@@ -616,7 +623,7 @@ impl<W: Write> Write for BufWriter<W> { | |
} | ||
if total_len >= self.buf.capacity() { | ||
self.panicked = true; | ||
let r = self.inner.as_mut().unwrap().write_vectored(bufs); | ||
let r = self.inner.write_vectored(bufs); | ||
self.panicked = false; | ||
r | ||
} else { | ||
|
@@ -633,7 +640,7 @@ impl<W: Write> Write for BufWriter<W> { | |
impl<W: Write> fmt::Debug for BufWriter<W> where W: fmt::Debug { | ||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
fmt.debug_struct("BufWriter") | ||
.field("writer", &self.inner.as_ref().unwrap()) | ||
.field("writer", &self.inner) | ||
.field("buffer", &format_args!("{}/{}", self.buf.len(), self.buf.capacity())) | ||
.finish() | ||
} | ||
|
@@ -652,7 +659,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() && !self.panicked { | ||
if !self.panicked { | ||
// dtors should not panic, so we ignore a failed flush | ||
let _r = self.flush_buf(); | ||
} | ||
|
@@ -1392,6 +1399,32 @@ mod tests { | |
}); | ||
} | ||
|
||
#[bench] | ||
fn bench_buffered_writer_large_write_causing_flush(b: &mut test::Bencher) { | ||
b.iter(|| { | ||
let mut buf_writer = BufWriter::with_capacity(10, io::sink()); | ||
buf_writer.write(b"abcdefghijklmnopqrstuvwxyz").unwrap(); | ||
}); | ||
} | ||
|
||
#[bench] | ||
fn bench_buffered_writer_small_write(b: &mut test::Bencher) { | ||
b.iter(|| { | ||
let mut buf_writer = BufWriter::with_capacity(10, io::sink()); | ||
buf_writer.write(b"abcdef").unwrap(); | ||
}); | ||
} | ||
|
||
#[bench] | ||
fn bench_buffered_writer_multiple_small_writes(b: &mut test::Bencher) { | ||
b.iter(|| { | ||
let mut buf_writer = BufWriter::with_capacity(10, io::sink()); | ||
buf_writer.write(b"abcdef").unwrap(); | ||
buf_writer.write(b"abcdef").unwrap(); | ||
buf_writer.write(b"abcdef").unwrap(); | ||
}); | ||
} | ||
|
||
struct AcceptOneThenFail { | ||
written: bool, | ||
flushed: bool, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach of ~manually running the drop glue for all of the other parts of the struct seems a bit dangerous and error prone. Using
ManuallyDrop::take
seems like it'd be cleaner: https://doc.rust-lang.org/std/mem/struct.ManuallyDrop.html?search=#method.takeThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are you suggesting applying
ManuallyDrop::take
in this context?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you would need the struct to contain:
where you call
ManuallyDrop::drop(&mut self.buf); ManuallyDrop::drop(&mut self.inner)
inside Drop andManuallyDrop::drop(&mut self.buf); ManuallyDrop::take(&mut self.inner)
inside into_inner.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like that implementation at all. The entire point of this change is to isolate this edge case into one function rather than spreading it throughout the entire struct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps he meant putting
self
into aManuallyDrop
to prevent these double free issues? That seems reasonable: