-
-
Notifications
You must be signed in to change notification settings - Fork 14.4k
Fix race condition in fs::create_dir_all #39799
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
Changes from 7 commits
db00ba9
0ec28b7
f2adee7
a51c6aa
c3e2eaf
bcae6a3
b5d590b
088696b
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 |
|---|---|---|
|
|
@@ -1534,6 +1534,12 @@ pub fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { | |
| /// error conditions for when a directory is being created (after it is | ||
| /// determined to not exist) are outlined by `fs::create_dir`. | ||
| /// | ||
| /// Notable exception is made for situations where any of the directories | ||
| /// specified in the `path` could not be created as it was created concurrently. | ||
| /// Such cases are considered success. In other words: calling `create_dir_all` | ||
| /// concurrently from multiple threads or processes is guaranteed to not fail | ||
| /// due to race itself. | ||
| /// | ||
| /// # Examples | ||
| /// | ||
| /// ``` | ||
|
|
@@ -1769,11 +1775,25 @@ impl DirBuilder { | |
| } | ||
|
|
||
| fn create_dir_all(&self, path: &Path) -> io::Result<()> { | ||
| if path == Path::new("") || path.is_dir() { return Ok(()) } | ||
| if let Some(p) = path.parent() { | ||
| self.create_dir_all(p)? | ||
| if path == Path::new("") { | ||
| return Ok(()) | ||
| } | ||
|
|
||
| match self.inner.mkdir(path) { | ||
| Ok(()) => return Ok(()), | ||
| Err(ref e) if e.kind() == io::ErrorKind::NotFound => {} | ||
| Err(_) if path.is_dir() => return Ok(()), | ||
| Err(e) => return Err(e), | ||
| } | ||
| match path.parent() { | ||
| Some(p) => try!(self.create_dir_all(p)), | ||
| None => return Err(io::Error::new(io::ErrorKind::Other, "failed to create whole tree")), | ||
| } | ||
| match self.inner.mkdir(path) { | ||
| Ok(()) => Ok(()), | ||
| Err(_) if path.is_dir() => Ok(()), | ||
| Err(e) => Err(e), | ||
| } | ||
| self.inner.mkdir(path) | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -1793,6 +1813,7 @@ mod tests { | |
| use rand::{StdRng, Rng}; | ||
| use str; | ||
| use sys_common::io::test::{TempDir, tmpdir}; | ||
| use thread; | ||
|
|
||
| #[cfg(windows)] | ||
| use os::windows::fs::{symlink_dir, symlink_file}; | ||
|
|
@@ -2260,11 +2281,41 @@ mod tests { | |
| assert!(result.is_err()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn concurrent_recursive_mkdir() { | ||
| for _ in 0..50 { | ||
| let mut dir = tmpdir().join("a"); | ||
|
||
| for _ in 0..100 { | ||
|
||
| dir = dir.join("a"); | ||
| } | ||
| let mut join = vec!(); | ||
| for _ in 0..8 { | ||
| let dir = dir.clone(); | ||
| join.push(thread::spawn(move || { | ||
| check!(fs::create_dir_all(&dir)); | ||
| })) | ||
| } | ||
|
|
||
| // No `Display` on result of `join()` | ||
| join.drain(..).map(|join| join.join().unwrap()).count(); | ||
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn recursive_mkdir_slash() { | ||
| check!(fs::create_dir_all(&Path::new("/"))); | ||
| } | ||
|
|
||
| #[test] | ||
| fn recursive_mkdir_dot() { | ||
| check!(fs::create_dir_all(&Path::new("."))); | ||
| } | ||
|
|
||
| #[test] | ||
| fn recursive_mkdir_empty() { | ||
| check!(fs::create_dir_all(&Path::new(""))); | ||
| } | ||
|
|
||
| #[test] | ||
| fn recursive_rmdir() { | ||
| let tmpdir = tmpdir(); | ||
|
|
||
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.
Does this test really need to be run 50 times? It fails the first time for me on Windows.
Uh oh!
There was an error while loading. Please reload this page.
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.
It's because it relies on there being a race condition between the threads, but race conditions don't occur very often. By doing the test multiple times, it is much more likely to hit the race condition.