-
Couldn't load subscription status.
- Fork 13.9k
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
Conversation
|
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @aturon (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
|
Thanks for the PR! Could this actually lift the implementation in the compiler which I believe tries to minimize syscalls in the "normal" case. Also yeah you can just add a test with a few threads hammering at creating directories and just make sure they don't fail. |
|
There's still room for a race condition if the directory is created after |
|
@alexcrichton How to "lift it"? Move from librustc to libstd and then use that new location instead in librustc code (or just call directly from the old librustc name)? @seppo0010 : I don't understand. If another thread called |
|
@dpc sorry, my case is quite weird, let me write it down a more detailed version. Let's say I'm not saying it's a bug, but it might be unexpected, and I don't even know what may be better in that case. |
|
@dpc yeah we can just copy the code and then update the compiler to call libstd (as libstd would handle this race) |
src/libstd/fs.rs
Outdated
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.
Can this import get moved up to the top of the file with the other imports?
|
ping @dpc, thoughts on updating this? |
|
I agree with all feedback, and will get to it, probably on a weekend. If anyone would like you to pick it up, that's also fine with me. |
|
Thanks @dpc! |
bc9df80 to
9865187
Compare
|
Ooops. Sorry. I wasn't able to compile anything on current Also, seems like |
src/libstd/fs.rs
Outdated
| Err(e) => return Err(e), | ||
| } | ||
| match path.parent() { | ||
| Some(p) => try!(create_dir_all(p)), |
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.
Could this call self.create_dir_all to ensure reuse of configuration in self? (if we add it in the future)
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.
Sure.
|
Looking good to me, thanks! Could you also add a test for specifically this use case? |
|
Done |
|
Looks good! I think Travis has a tidy error though (r=me otherwise) |
|
@dpc |
|
@petrochenkov @dpc oh not yet, we'll need to wait for a new release. That's compiled against the stage0 libstd which doesn't have this fix. |
|
@bors: r+ |
|
📌 Commit 4e2114f has been approved by |
|
⌛ Testing commit 4e2114f with merge e1be448... |
|
💔 Test failed - status-appveyor |
|
@bors: retry
|
|
⌛ Testing commit 4e2114f with merge 4d01a7d... |
Fix race condition in fs::create_dir_all The code would crash if the directory was created after create_dir_all checked whether the directory already existed. This was contrary to the documentation which claimed to create the directory if it doesn't exist, implying (but not stating) that there would not be a failure due to the directory existing.
|
This seems to fail on Windows: https://ci.appveyor.com/project/rust-lang/rust/build/1.0.2436/job/ph8mhjt83f837xdl @bors r- |
|
I don't understand what happened, and I don't have Windows to debug. If one of the threads have failed, I would expect Could someone with Windows help? |
src/libstd/fs.rs
Outdated
|
|
||
| #[test] | ||
| fn concurrent_recursive_mkdir() { | ||
| for _ in 0..50 { |
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 the main problem here is this use of tmpdir(). It deletes the path it returns when it goes out of scope and because it's not assigned to a variable that's immediately. See here. Changing this to the following should fix that:
let tmpdir = tmpdir();
let mut dir = tmpdir.join("a");| } | ||
|
|
||
| #[test] | ||
| fn concurrent_recursive_mkdir() { |
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.
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.
src/libstd/fs.rs
Outdated
| #[test] | ||
| fn concurrent_recursive_mkdir() { | ||
| for _ in 0..50 { | ||
| let mut dir = tmpdir().join("a"); |
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.
Looks like you reduced the number of times this test is run rather than the the length of the path 😉.
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.
Oh dear. 🤦
Increase lifetime of `tmpdir`, and really change the length of test path.
|
@bors: r+ |
|
📌 Commit 088696b has been approved by |
|
⌛ Testing commit 088696b with merge 6eb9960... |
Fix race condition in fs::create_dir_all The code would crash if the directory was created after create_dir_all checked whether the directory already existed. This was contrary to the documentation which claimed to create the directory if it doesn't exist, implying (but not stating) that there would not be a failure due to the directory existing.
|
☀️ Test successful - status-appveyor, status-travis |
|
@dpc Thank you for fixing one of my least favorite Rust annoyances! The path to merge was a bit rock, but it's greatly appreciated. |
|
@emk You're welcome. I'm very happy about it myself. First, I got on contributor's list. Second, it was a torn for me too. :) |
This is a minor tweak to handle some problems which I've encountered
several times now. The only real excuse for this change is that it's
unobstrusive and has natural semantics.
This patch modifies --filename to support two use cases:
xsv partition --filename {}/cities.csv state . all-cities.csv
xsv partition --filename {}/monuments.csv state . all-monuments.csv
xsv partition --filename {}/parks.csv state . all-parks.csv
Above, we want to partition our records by state into separate
directories, but we have multiple kinds of data.
xsv partition --filename {}/$(uuidgen).csv . input1.csv
xsv partition --filename {}/$(uuidgen).csv . input2.csv
Above, we're running multiple (possibly parallel) copies of xsv and we
want to parition the data into multiple directories without a filename
clash.
There's one limitation to the implementation: We might theoretically hit
the `create_dir_all` race condition recently fixed by
rust-lang/rust#39799. I'm planning to supply a final patch which works
around this race condition in stable Rust.
See rust-lang/rust#39799 for details.
rustdoc: Use `create_dir_all` to create output directory Currently rustdoc will fail if passed `-o foo/doc` if the `foo` directory doesn't exist. Also remove unneeded `mkdir` as `create_dir_all` can now handle concurrent invocations since #39799.
rustbuild: Replace create_dir_racy with create_dir_all `create_dir_all` has since been fixed (in #39799) so no need for `create_dir_racy`.
as std::fs::create_dir_all is now threadsafe rust-lang/rust#39799
The code would crash if the directory was created after create_dir_all
checked whether the directory already existed. This was contrary to
the documentation which claimed to create the directory if it doesn't
exist, implying (but not stating) that there would not be a failure
due to the directory existing.