Andrew Ducker
Andrew Ducker

Reputation: 5490

How to create a directory in C# *only* if it doesn't already exist?

Directory.CreateDirectory creates a folder if it doesn't already exist, otherwise it just returns the DirectoryInfo for the already-existing folder.

But I want to make sure I create a new sub-folder every time a process is run (run1, run2, run3, etc). And multiple people may be running that process at once. So I can't do

if (!Directory.exists("run77"){CreateDirectory("run77");}

Because that has a race condition - someone else may create the folder in between those two calls.

Is there any way to ensure that I definitely get the next unused folder name?

Upvotes: 1

Views: 856

Answers (4)

null_pointer
null_pointer

Reputation: 1849

One way is using a mutex which is an expensive call. However like most said if possible use sequential Ids instead. Here is an example using a mutex:

Mutex m = null
try
{
    m = new Mutex(false, "CreateDirectoryMutex");
    m.WaitOne();
    if (!Directory.exists("run77"))
        CreateDirectory("run77");
}
finally
{
     if(m != null)
       m.ReleaseMutex();
 }

Upvotes: 1

Joey
Joey

Reputation: 354476

The obvious answer: Don't use sequential IDs in this case. Use GUIDs instead. For things happening in multiple instances at once I rarely found sequential numbers helpful in that there isn't really an inherent sequence.

The other option would be to drop down to the Windows API where you can just try and re-try creating directories with increasing sequence numbers until the call succeeds, at which point you found your directory to work with.

Upvotes: 3

Sinatr
Sinatr

Reputation: 21999

One possible solution is to introduce a simple synchronization using exclusively opened file.

Here is possible implementation (as not fully thought code):

FileStream lock = null;
try
{
    // someUniequeLockFileName - should be the same for all processes and unlikely to match real name in that folder
    lock = File.Open(Path.Combine(currentFolder, someUniqueLockFileName), FileMode.Create, FileAccess.ReadWrite, FileShare.None);
    ... // you successfully obtain lock, can create subfolders
}
catch { }
finally
{
    lock.Close();
}

Upvotes: 1

CodeCaster
CodeCaster

Reputation: 151584

This goes way beyond merely creating directories. You have multiple processes (on multiple machines even, maybe) that want to obtain a sequentially named resources, for themselves only.

If you definitely want to do this, you will have to synchronize it yourself. So either:

  • If the processes run on the same machine, use a machine-wide lock (e.g. Mutex) that synchronizes the directory creation code.
  • Create an out-of-process central authority (other than the filesystem itself) which provides the next number upon each successive, synchronized call.
  • Create a sentinel file / lock file containing a process-specific value, during the presence of which no other processes will try to create a directory, and remove it after creating the directory.
  • Apply filesystem permissions in such a way, that attempting to write into the given directory raises an error (if CreateDirectory() doesn't throw already). Then you'll have to assume that any permission error means a race condition occurred, and try to create the next directory.

All of those are brittle solutions.

Upvotes: 2

Related Questions