Skip to content

Commit ef788d5

Browse files
brsonalexcrichton
authored andcommitted
std: Modify TempDir to not fail on drop. Closes #12628
After discussion with Alex, we think the proper policy is for dtors to not fail. This is consistent with C++. BufferedWriter already does this, so this patch modifies TempDir to not fail in the dtor, adding a `close` method for handling errors on destruction.
1 parent bfbd732 commit ef788d5

File tree

3 files changed

+106
-10
lines changed

3 files changed

+106
-10
lines changed

Diff for: src/libstd/io/buffered.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ impl<W: Writer> Writer for BufferedWriter<W> {
209209
impl<W: Writer> Drop for BufferedWriter<W> {
210210
fn drop(&mut self) {
211211
if self.inner.is_some() {
212-
// FIXME(#12628): should this error be ignored?
212+
// dtors should not fail, so we ignore a failed flush
213213
let _ = self.flush_buf();
214214
}
215215
}
@@ -370,6 +370,7 @@ mod test {
370370
use io;
371371
use prelude::*;
372372
use super::*;
373+
use super::super::{IoResult, EndOfFile};
373374
use super::super::mem::{MemReader, MemWriter, BufReader};
374375
use self::test::Bencher;
375376
use str::StrSlice;
@@ -584,6 +585,24 @@ mod test {
584585
assert_eq!(it.next(), None);
585586
}
586587

588+
#[test]
589+
#[should_fail]
590+
fn dont_fail_in_drop_on_failed_flush() {
591+
struct FailFlushWriter;
592+
593+
impl Writer for FailFlushWriter {
594+
fn write(&mut self, _buf: &[u8]) -> IoResult<()> { Ok(()) }
595+
fn flush(&mut self) -> IoResult<()> { Err(io::standard_error(EndOfFile)) }
596+
}
597+
598+
let writer = FailFlushWriter;
599+
let _writer = BufferedWriter::new(writer);
600+
601+
// Trigger failure. If writer fails *again* due to the flush
602+
// error then the process will abort.
603+
fail!();
604+
}
605+
587606
#[bench]
588607
fn bench_buffered_reader(b: &mut Bencher) {
589608
b.iter(|| {

Diff for: src/libstd/io/tempfile.rs

+27-9
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,22 @@
1010

1111
//! Temporary files and directories
1212
13-
use io::fs;
13+
use io::{fs, IoResult};
1414
use io;
1515
use iter::{Iterator, range};
1616
use libc;
1717
use ops::Drop;
1818
use option::{Option, None, Some};
1919
use os;
2020
use path::{Path, GenericPath};
21-
use result::{Ok, Err, ResultUnwrap};
21+
use result::{Ok, Err};
2222
use sync::atomics;
2323

2424
/// A wrapper for a path to temporary directory implementing automatic
2525
/// scope-based deletion.
2626
pub struct TempDir {
27-
path: Option<Path>
27+
path: Option<Path>,
28+
disarmed: bool
2829
}
2930

3031
impl TempDir {
@@ -48,7 +49,7 @@ impl TempDir {
4849
let p = tmpdir.join(filename);
4950
match fs::mkdir(&p, io::UserRWX) {
5051
Err(..) => {}
51-
Ok(()) => return Some(TempDir { path: Some(p) })
52+
Ok(()) => return Some(TempDir { path: Some(p), disarmed: false })
5253
}
5354
}
5455
None
@@ -75,15 +76,32 @@ impl TempDir {
7576
pub fn path<'a>(&'a self) -> &'a Path {
7677
self.path.get_ref()
7778
}
79+
80+
/// Close and remove the temporary directory
81+
///
82+
/// Although `TempDir` removes the directory on drop, in the destructor
83+
/// any errors are ignored. To detect errors cleaning up the temporary
84+
/// directory, call `close` instead.
85+
pub fn close(mut self) -> IoResult<()> {
86+
self.cleanup_dir()
87+
}
88+
89+
fn cleanup_dir(&mut self) -> IoResult<()> {
90+
assert!(!self.disarmed);
91+
self.disarmed = true;
92+
match self.path {
93+
Some(ref p) => {
94+
fs::rmdir_recursive(p)
95+
}
96+
None => Ok(())
97+
}
98+
}
7899
}
79100

80101
impl Drop for TempDir {
81102
fn drop(&mut self) {
82-
for path in self.path.iter() {
83-
if path.exists() {
84-
// FIXME: is failing the right thing to do?
85-
fs::rmdir_recursive(path).unwrap();
86-
}
103+
if !self.disarmed {
104+
let _ = self.cleanup_dir();
87105
}
88106
}
89107
}

Diff for: src/test/run-pass/tempfile.rs

+59
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,50 @@ fn test_rm_tempdir() {
7474
assert!(!path.exists());
7575
}
7676

77+
fn test_rm_tempdir_close() {
78+
let (tx, rx) = channel();
79+
let f: proc():Send = proc() {
80+
let tmp = TempDir::new("test_rm_tempdir").unwrap();
81+
tx.send(tmp.path().clone());
82+
tmp.close();
83+
fail!("fail to unwind past `tmp`");
84+
};
85+
task::try(f);
86+
let path = rx.recv();
87+
assert!(!path.exists());
88+
89+
let tmp = TempDir::new("test_rm_tempdir").unwrap();
90+
let path = tmp.path().clone();
91+
let f: proc():Send = proc() {
92+
let tmp = tmp;
93+
tmp.close();
94+
fail!("fail to unwind past `tmp`");
95+
};
96+
task::try(f);
97+
assert!(!path.exists());
98+
99+
let path;
100+
{
101+
let f = proc() {
102+
TempDir::new("test_rm_tempdir").unwrap()
103+
};
104+
let tmp = task::try(f).ok().expect("test_rm_tmdir");
105+
path = tmp.path().clone();
106+
assert!(path.exists());
107+
tmp.close();
108+
}
109+
assert!(!path.exists());
110+
111+
let path;
112+
{
113+
let tmp = TempDir::new("test_rm_tempdir").unwrap();
114+
path = tmp.unwrap();
115+
}
116+
assert!(path.exists());
117+
fs::rmdir_recursive(&path);
118+
assert!(!path.exists());
119+
}
120+
77121
// Ideally these would be in std::os but then core would need
78122
// to depend on std
79123
fn recursive_mkdir_rel() {
@@ -130,6 +174,19 @@ pub fn test_rmdir_recursive_ok() {
130174
assert!(!root.join("bar").join("blat").exists());
131175
}
132176

177+
pub fn dont_double_fail() {
178+
let r: Result<(), _> = task::try(proc() {
179+
let tmpdir = TempDir::new("test").unwrap();
180+
// Remove the temporary directory so that TempDir sees
181+
// an error on drop
182+
fs::rmdir(tmpdir.path());
183+
// Trigger failure. If TempDir fails *again* due to the rmdir
184+
// error then the process will abort.
185+
fail!();
186+
});
187+
assert!(r.is_err());
188+
}
189+
133190
fn in_tmpdir(f: ||) {
134191
let tmpdir = TempDir::new("test").expect("can't make tmpdir");
135192
assert!(os::change_dir(tmpdir.path()));
@@ -140,8 +197,10 @@ fn in_tmpdir(f: ||) {
140197
pub fn main() {
141198
in_tmpdir(test_tempdir);
142199
in_tmpdir(test_rm_tempdir);
200+
in_tmpdir(test_rm_tempdir_close);
143201
in_tmpdir(recursive_mkdir_rel);
144202
in_tmpdir(recursive_mkdir_dot);
145203
in_tmpdir(recursive_mkdir_rel_2);
146204
in_tmpdir(test_rmdir_recursive_ok);
205+
in_tmpdir(dont_double_fail);
147206
}

0 commit comments

Comments
 (0)