Skip to content
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

std.file: Fix race condition when mkdirRecurse runs concurrently #1818

Merged
2 commits merged into from
Jan 6, 2014

Conversation

CyberShadow
Copy link
Member

mkdirRecurse would throw if a directory was created between its exists() and mkdir() calls. This could occur if mkdirRecurse was called at the same time from mutiple threads or processes.

Instead of relying on the exists() check to indicate whether the next mkdir() should succeed, we explicitly ignore "already exists" errors from the OS. Note that this changes the behavior of mkdirRecurse when the leaf directory already existed: previously it would throw, whereas now it will silently succeed. The new behavior is conformant with some other implementations of "recursive mkdir": the -p flag of the GNU tool, Ruby's mkpath, Java's mkdirs, and Perl's make_path. (On the other hand, the old behavior was equivalent to the behavior of Python's makedirs).

@yebblies
Copy link
Member

Note that this changes the behavior of mkdirRecurse when the leaf directory already existed: previously it would throw, whereas now it will silently succeed.

Excellent.

{
if (CreateDirectoryW(std.utf.toUTF16z(pathname), null))
return true;
enum ERROR_ALREADY_EXISTS = 183;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be in druntime somewhere?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Argh, yes. But I already did two Druntime bindings pulls today :)

I'll move it.

@@ -1366,6 +1366,25 @@ void mkdir(in char[] pathname)
}
}

// Ignores "already exists" errors.
private bool tryMkdir(in char[] pathname)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ensureDirExists

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@mleise
Copy link
Contributor

mleise commented Dec 31, 2013

How about:

void mkdir(in char[] pathname, bool recursive = false)

I agree with the "mkdir -p" behavior of silently accepting existing leaf directories, since whenever I used it, it was to ensure that any missing directory is created. It is the more common use case for me by far.

mkdirRecurse would crash if a directory was created between its
exists() and mkdir() calls. This could occur if mkdirRecurse was called
at the same time from mutiple threads or processes.

Instead of relying on the exists() check to indicate whether the next
mkdir() should succeed, we explicitly ignore "already exists" errors
from the OS. Note that this changes the behavior of mkdirRecurse when
the leaf directory already existed: previously it would throw, whereas
now it will silently succeed. The new behavior is conformant with some
other implementations of "recursive mkdir": the -p flag of the GNU
tool, Ruby's mkpath, Java's mkdirs, and Perl's make_path. (On the
other hand, the old behavior was equivalent to the behavior of
Python's makedirs).
@ghost
Copy link

ghost commented Jan 6, 2014

@mleise: boolean parameters suck. From the call site "foo/bar".mkdir(true) tells me absolutely nothing about what true does. It's actually very similar to the std.regex situation, where now we have explicitly named functions replace/replaceAll rather than a "g" flag that may or may not be the default. There's plenty of articles on the net on why booleans suck in APIs.

mkdirRecurse(path);
mkdirRecurse(path); // should not throw

assertThrown!FileException(mkdirRecurse(`1:\foobar`));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm not sure what you're testing here, but it's failing the autotester.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Derp, forgot to put it in version(Windows)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, 1:\foobar, that's a 1, not an l, which confused me.

But how come on Posix this didn't fail?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, both : and \ are allowed in UNIX filenames...

The left.length != pathname.length check both takes care of the
Windows-only check, and it avoids a pointless stat on the FS root.
@ghost
Copy link

ghost commented Jan 6, 2014

Auto-merge toggled on

@ghost
Copy link

ghost commented Jan 6, 2014

LGTM.

ghost pushed a commit that referenced this pull request Jan 6, 2014
std.file: Fix race condition when mkdirRecurse runs concurrently
@ghost ghost merged commit eea81d8 into dlang:master Jan 6, 2014
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants